aozhiwei e1ba7d045c 1
2021-04-02 19:21:22 +08:00

384 lines
12 KiB
C++

#include "precompile.h"
#include "bullet.h"
#include "metamgr.h"
#include "room.h"
#include "collider.h"
#include "obstacle.h"
#include "player.h"
#include "android.h"
#include "android.ai.h"
#include "app.h"
#include "perfmonitor.h"
#include "smoke_mitask.h"
#include "frag_mitask.h"
#include "posiongas_mitask.h"
#include "molotor_cocktail_mitask.h"
#include "creature.h"
#include "roomobstacle.h"
Bullet::Bullet():MoveableEntity()
{
++PerfMonitor::Instance()->entity_num[ET_Bullet];
}
Bullet::~Bullet()
{
--PerfMonitor::Instance()->entity_num[ET_Bullet];
}
void Bullet::Initialize()
{
MoveableEntity::Initialize();
RecalcSelfCollider();
}
void Bullet::Update(int delta_time)
{
MapServiceUpdate();
++updated_times_;
}
void Bullet::RecalcSelfCollider()
{
if (!self_collider_) {
self_collider_ = new CircleCollider();
self_collider_->owner = this;
AddEntityCollider(self_collider_);
}
self_collider_->pos = a8::Vec2();
self_collider_->rad = gun_meta->i->bullet_rad();
}
void Bullet::OnHit(std::set<Entity*>& objects)
{
for (auto& target : objects) {
switch (target->GetEntityType()) {
case ET_Player:
{
Human* hum = (Human*)target;
if (hum->HasBuffEffect(kBET_Invincible) ||
hum->HasBuffEffect(kBET_AdPlaying)) {
continue;
}
if (sender.Get()->room->GetRoomMode() == kZombieMode &&
sender.Get()->GetRace() == hum->GetRace()) {
continue;
}
if (!hum->dead && (IsBomb() || sender.Get()->team_id != hum->team_id)) {
float dmg = GetAtk() * (1 + sender.Get()->GetAttrRate(kHAT_Atk)) +
sender.Get()->GetAttrAbs(kHAT_Atk);
float def = hum->ability.def * (1 + hum->GetAttrRate(kHAT_Def)) +
hum->GetAttrAbs(kHAT_Def);
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
finaly_dmg = std::max(finaly_dmg, 0.0f);
#if 0
sender->stats.damage_amount_out += finaly_dmg;
#endif
hum->DecHP(finaly_dmg, sender.Get()->GetEntityUniId(), sender.Get()->GetName(), gun_meta->i->id());
#ifdef DEBUG
sender.Get()->SendDebugMsg(a8::Format("bullet weapon_id:%d atk:%f",
{
gun_meta->i->id(),
GetAtk()
})
);
#endif
}
}
break;
case ET_Obstacle:
{
Obstacle* obstacle = (Obstacle*)target;
if (!obstacle->IsDead(room) &&
obstacle->Attackable() &&
!obstacle->IsTerminatorAirDropBox(room)) {
float dmg = GetAtk() * (1 + sender.Get()->GetAttrRate(kHAT_Atk)) +
sender.Get()->GetAttrAbs(kHAT_Atk);
float def = 0;
float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K);
obstacle->SetHealth(room, std::max(0.0f, obstacle->GetHealth(room) - finaly_dmg));
if (obstacle->GetHealth(room) <= 0.01f) {
obstacle->Die(room);
}
if (obstacle->IsDead(room)) {
if (obstacle->meta->i->damage_dia() > 0.01f &&
obstacle->meta->i->damage() > 0.01f) {
obstacle->Explosion(this);
}
sender.Get()->DropItems(obstacle);
}
obstacle->BroadcastFullState(room);
#ifdef DEBUG
sender.Get()->SendDebugMsg(a8::Format("bullet weapon_id:%d atk:%f",
{
gun_meta->i->id(),
GetAtk()
})
);
#endif
}
}
break;
default:
break;
}
}
}
void Bullet::ProcBomb()
{
self_collider_->rad = gun_meta->i->explosion_range();
std::set<Entity*> objects;
TouchAllLayerHumanList
(
[this, &objects] (Human* hum, bool& stop)
{
if (!is_tank_skin || sender.Get()->team_id != hum->team_id) {
//友军火箭筒伤害取消
if ((meta->i->_inventory_slot() == 4 ||
meta->i->_inventory_slot() == 5) &&
sender.Get()->team_id == hum->team_id) {
return;
}
if (TestCollision(room, hum)) {
objects.insert(hum);
}
}
});
TouchAllLayerEntityList
(
[this, &objects] (Entity* entity, bool& stop)
{
switch (entity->GetEntityType()) {
case ET_Obstacle:
case ET_Building:
{
if (TestCollision(room, entity)) {
objects.insert(entity);
}
}
break;
default:
{
}
break;
}
});
bool block = false;
if (objects.empty()) {
float distance = (GetPos() - born_pos).Norm();
if (distance >= fly_distance) {
block = true;
}
}
int delay_time = 0;
if (!block) {
delay_time = gun_meta->i->missiles_time();
}
switch (meta->i->_inventory_slot()) {
case IS_RPG:
{
//榴弹炮
a8::Vec2 bomb_pos = GetPos();
room->frame_event.AddExplosionEx(sender, meta->i->id(), bomb_pos,
gun_meta->i->explosion_effect());
OnHit(objects);
}
break;
case IS_FRAG:
{
//手雷
ProcFragBomb(delay_time);
}
break;
case IS_SMOKE:
{
//烟雾弹
a8::Vec2 bomb_pos = GetPos();
room->frame_event.AddSmoke(this, meta->i->id(), bomb_pos);
ProcSmokeBomb();
}
break;
case IS_POSION_GAS_BOMB:
{
//毒气弹
a8::Vec2 bomb_pos = GetPos();
room->frame_event.AddSmoke(this, meta->i->id(), bomb_pos);
ProcPosionGasBomb(delay_time);
}
break;
case IS_MOLOTOR_COCKTAIL:
{
//燃烧瓶
a8::Vec2 bomb_pos = GetPos();
room->frame_event.AddSmoke(this, meta->i->id(), bomb_pos);
ProcMolotorCocktailBomb(delay_time);
}
break;
}
room->RemoveObjectLater(this);
}
void Bullet::ProcSmokeBomb()
{
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(HUNLUAN_BUFFID);
if (buff_meta) {
SmokeMiTask* task = new SmokeMiTask();
task->room = room;
task->bomb_pos = GetPos();
task->buff_meta = buff_meta;
task->gun_meta = gun_meta;
room->xtimer.AddRepeatTimerAndAttach
(SERVER_FRAME_RATE / 2,
a8::XParams()
.SetSender(task),
[] (const a8::XParams& param)
{
SmokeMiTask* task = (SmokeMiTask*)param.sender.GetUserData();
task->Check();
},
&task->timer_attacher.timer_list_);
room->xtimer.AddDeadLineTimerAndAttach
(SERVER_FRAME_RATE * MetaMgr::Instance()->GetSysParamAsInt("smoke_duration", 10),
a8::XParams()
.SetSender(task),
[] (const a8::XParams& param)
{
},
&room->timer_attacher.timer_list_,
[] (const a8::XParams& param)
{
SmokeMiTask* task = (SmokeMiTask*)param.sender.GetUserData();
task->Done();
delete task;
}
);
}
}
bool Bullet::IsBomb()
{
return
meta->i->_inventory_slot() == IS_RPG ||
meta->i->_inventory_slot() == IS_FRAG ||
meta->i->_inventory_slot() == IS_SMOKE ||
meta->i->_inventory_slot() == IS_POSION_GAS_BOMB ||
meta->i->_inventory_slot() == IS_MOLOTOR_COCKTAIL;
}
void Bullet::MapServiceUpdate()
{
if (sender.Get()) {
SetPos(GetPos() + dir * gun_meta->i->bullet_speed() / (float)SERVER_FRAME_RATE);
float distance = (GetPos() - born_pos).Norm();
if (room->OverBorder(GetPos(), gun_meta->i->bullet_rad())) {
if (IsBomb()) {
ProcBomb();
} else {
Check(distance);
if (!later_removed_) {
room->RemoveObjectLater(this);
}
}
} else {
room->grid_service->MoveBullet(this);
Check(distance);
}
} else {
room->RemoveObjectLater(this);
later_removed_ = true;
}
}
float Bullet::GetAtk()
{
float atk = gun_meta->i->atk() +
(gun_upgrade_meta ? gun_upgrade_meta->GetAttrValue(gun_lv, kHAT_Atk) : 0);
if (sender.Get()->IsAndroid()) {
Android* android = (Android*)sender.Get();
atk *= android->ai->GetAttackRate();
}
return atk;
}
void Bullet::Check(float distance)
{
std::set<Entity*> objects;
TouchAllLayerHumanList
(
[this, &objects] (Human* hum, bool& stop)
{
if (hum != sender.Get() && !hum->dead && TestCollision(room, hum)) {
objects.insert(hum);
}
});
{
std::set<ColliderComponent*> colliders;
room->map_service->GetColliders(room, GetX(), GetY(), colliders);
for (ColliderComponent* collider : colliders) {
if (TestCollision(room, collider) && !a8::HasBitFlag(collider->tag, kHalfWallTag)) {
objects.insert(collider->owner);
}
}
}
float bullet_range = gun_meta->i->range();
if (gun_upgrade_meta && gun_upgrade_meta->GetAttrValue(gun_lv, kHAT_ShotRange) > 0) {
bullet_range += gun_upgrade_meta->GetAttrValue(gun_lv, kHAT_ShotRange);
}
if (!objects.empty() || distance > bullet_range ||
(IsBomb() && distance >= fly_distance)
) {
if (IsBomb()) {
ProcBomb();
} else {
if (!objects.empty()) {
OnHit(objects);
}
room->RemoveObjectLater(this);
later_removed_ = true;
}
}
}
void Bullet::ProcFragBomb(int delay_time)
{
if (sender.Get()) {
FragMiTask* task = new FragMiTask();
task->room = room;
task->sender.Attach(sender.Get());
task->bomb_pos = GetPos();
task->gun_meta = gun_meta;
task->meta = meta;
task->atk = GetAtk();
room->xtimer.AddDeadLineTimerAndAttach
(std::max(1, (int)(delay_time / FRAME_RATE_MS)),
a8::XParams()
.SetSender(task),
[] (const a8::XParams& param)
{
FragMiTask* task = (FragMiTask*)param.sender.GetUserData();
task->Done();
},
&task->timer_attacher.timer_list_,
[] (const a8::XParams& param)
{
FragMiTask* task = (FragMiTask*)param.sender.GetUserData();
delete task;
}
);
}
}
void Bullet::ProcPosionGasBomb(int delay_time)
{
}
void Bullet::ProcMolotorCocktailBomb(int delay_time)
{
}