aozhiwei c5bb28dc12 1
2022-10-13 12:47:10 +08:00

939 lines
32 KiB
C++

#include "precompile.h"
#include <math.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 "creature.h"
#include "roomobstacle.h"
#include "car.h"
#include "creature.h"
#include "skillhelper.h"
Bullet::Bullet():MoveableEntity()
{
++PerfMonitor::Instance()->entity_num[ET_Bullet];
}
Bullet::~Bullet()
{
--PerfMonitor::Instance()->entity_num[ET_Bullet];
}
void Bullet::Initialize()
{
MoveableEntity::Initialize();
RecalcSelfCollider();
ability_ = sender.Get()->GetAbility();
is_curr_weapon = sender.Get()->GetCurrWeapon()->meta == gun_meta;
create_frameno_ = room->GetFrameNo();
if (IsFlyHook()) {
int buff_uniid = sender.Get()->TryAddBuff(sender.Get(), kVertigoBuffId);
if (buff_uniid) {
Buff* buff = sender.Get()->GetBuffByUniId(buff_uniid);
if (buff && buff->remover_timer) {
buff_list_.push_back(buff_uniid);
sender.Get()->room->xtimer.ModifyTimer(buff->remover_timer, SERVER_FRAME_RATE * 10);
}
}
if (sender.Get()) {
sender.Get()->GetTrigger()->FlyHookCreate(this);
}
}
}
void Bullet::Update(int delta_time)
{
if (shot_animi_time <= (room->GetFrameNo() - create_frameno_) * FRAME_RATE_MS) {
if (!trace_target_id) {
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)
{
std::shared_ptr<Ability> old_context_ability = sender.Get()->context_ability;
a8::Vec2 old_context_dir = sender.Get()->context_dir;
a8::Vec2 old_context_pos = sender.Get()->context_pos;
sender.Get()->context_dir = dir;
sender.Get()->context_pos = GetPos();
if (IsFlyHook()) {
if (sender.Get() && !sender.Get()->dead) {
for (auto& target : objects) {
if (
!(target->IsCreature(room) &&
((Creature*)target)->IsCar())) {
ProcFlyHook(target);
}
break;
}
}
} else {
for (auto& target : objects) {
bool old_is_dead = target->IsDead(room);
TriggerHitBuff(target);
target->OnBulletHit(this);
if (target->IsDead(room) && !old_is_dead) {
OnKillTarget(target);
}
}
}
sender.Get()->context_dir = old_context_dir;
sender.Get()->context_pos = old_context_pos;
sender.Get()->context_ability = old_context_ability;
}
void Bullet::ProcBomb()
{
float old_collider_rad = self_collider_->rad;
if (meta->i->_inventory_slot() != IS_C4) {
self_collider_->rad = GetExplosionRange();
}
if (IsCurrWeapon()) {
}
std::set<Entity*> objects;
Car* c4_target = nullptr;
TraverseCreatures
(
[this, &objects, &c4_target] (Creature* c, bool& stop)
{
if (c->dead) {
return;
}
if (sender.Get()->team_id != c->team_id) {
//友军火箭筒伤害取消
if ((meta->i->_inventory_slot() == 4 ||
meta->i->_inventory_slot() == 5) &&
sender.Get()->team_id == c->team_id) {
return;
}
AabbCollider aabb_box;
c->GetHitAabbBox(aabb_box);
if (meta->i->_inventory_slot() == IS_C4) {
if (!objects.empty()) {
stop = true;
return;
}
if (!c->IsCar()) {
return;
}
if (TestCollision(room, &aabb_box)) {
c4_target = c->AsCar();
stop = true;
objects.insert(c);
return;
}
} else {
if (TestCollision(room, &aabb_box)) {
objects.insert(c);
}
}
}
});
TraverseAllLayerEntityList
(
[this, &objects, c4_target] (Entity* entity, bool& stop)
{
if (meta->i->_inventory_slot() == IS_C4) {
if (c4_target) {
stop = true;
return;
}
}
switch (entity->GetEntityType()) {
case ET_Obstacle:
case ET_Building:
case ET_Dummy:
{
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();
}
if (IsCurrWeapon() &&
sender.Get() &&
sender.Get()->GetAbility()->GetAttrAbs(kHAT_WeaponExplosionDealyTime) > 0) {
delay_time = delay_time -
sender.Get()->GetAbility()->GetAttrAbs(kHAT_WeaponExplosionDealyTime);
delay_time = std::max(0, delay_time);
}
{
room->xtimer.AddDeadLineTimerAndAttach
(delay_time / FRAME_RATE_MS,
a8::XParams()
.SetSender(GetPos().x)
.SetParam1(GetPos().y)
.SetParam2(GetUniId())
.SetParam3(room),
[] (const a8::XParams& param)
{
Room* room = (Room*)param.param3.GetUserData();
float x = param.sender.GetDouble();
float y = param.param1.GetDouble();
int bullet_uniid = param.param2;
#if 0
room->frame_event.RemoveBullet(a8::Vec2(x, y), bullet_uniid);
#endif
},
&room->timer_attacher.timer_list_,
[] (const a8::XParams& param)
{
}
);
}
switch (meta->i->_inventory_slot()) {
case IS_RPG:
{
//榴弹炮
if (sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponUseTimes(gun_meta->i->id(), 1);
}
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:
{
//手雷
if (sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponUseTimes(gun_meta->i->id(), 1);
}
ProcFragBomb(delay_time);
}
break;
case IS_SMOKE:
{
//烟雾弹
if (sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponUseTimes(gun_meta->i->id(), 1);
}
#if 1
AddGunBuff();
#else
a8::Vec2 bomb_pos = GetPos();
float time_addition = 0;
if (IsCurrWeapon() && sender.Get()) {
time_addition += sender.Get()->GetAbility()->GetAttrAbs(kHAT_WeaponExplosionContinueTime);
}
room->frame_event.AddSmoke(this, meta->i->id(), bomb_pos, time_addition);
ProcSmokeBomb();
#endif
}
break;
case IS_POSION_GAS_BOMB:
{
//毒气弹
if (sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponUseTimes(gun_meta->i->id(), 1);
}
ProcPosionGasBomb(delay_time);
}
break;
case IS_MOLOTOR_COCKTAIL:
{
//燃烧瓶
if (sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponUseTimes(gun_meta->i->id(), 1);
}
ProcMolotorCocktailBomb(delay_time);
}
break;
case IS_C4:
{
//c4
if (sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponUseTimes(gun_meta->i->id(), 1);
}
if (block) {
delay_time = gun_meta->i->missiles_time();
}
ProcC4Bomb(c4_target, delay_time);
}
break;
case IS_SINGAL_GUN:
{
if (sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponUseTimes(gun_meta->i->id(), 1);
}
ProcSignalGunBomb(delay_time);
}
break;
case IS_SHIELD_WALL:
{
if (sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponUseTimes(gun_meta->i->id(), 1);
}
ProcShieldWallBomb(delay_time);
}
break;
case IS_OIL_BUCKET:
{
if (sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponUseTimes(gun_meta->i->id(), 1);
}
ProcOilBucketBomb(delay_time);
}
break;
default:
{
}
break;
}
self_collider_->rad = old_collider_rad;
ForceRemove();
}
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()
{
if (!meta) {
return false;
}
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 ||
meta->i->_inventory_slot() == IS_C4 ||
meta->i->_inventory_slot() == IS_SINGAL_GUN ||
meta->i->_inventory_slot() == IS_SHIELD_WALL ||
meta->i->_inventory_slot() == IS_OIL_BUCKET;
}
void Bullet::MapServiceUpdate()
{
if (later_removed_) {
return;
}
if (sender.Get()) {
float move_length = gun_meta->i->bullet_speed() / (float)SERVER_FRAME_RATE;
do {
float step_len = move_length - MetaMgr::Instance()->bullet_planck_step_length;
if (step_len <= 0.001f) {
if (move_length > 0.1f) {
step_len = move_length;
} else {
break;
}
}
move_length -= step_len;
SetPos(GetPos() + dir * step_len);
float distance = (GetPos() - born_pos).Norm();
if (room->OverBorder(GetPos(), gun_meta->i->bullet_rad())) {
if (IsBomb()) {
ProcBomb();
} else {
Check(distance);
if (!later_removed_) {
ForceRemove();
}
}
} else {
room->grid_service->MoveBullet(this);
Check(distance);
}
} while(!later_removed_ && move_length >= 0.0001f);
#ifdef DEBUG1
{
if (sender.Get()->IsPlayer()) {
if ((room->GetFrameNo() - create_frameno_) % 2 == 0) {
float distance = (GetPos() - born_pos).Norm();
a8::XPrintf("bullet_id:%d frame_no:%d speed:%f range:%f fly_distance:%f born_pos:%f,%f pos:%f,%f\n",
{
gun_meta->i->id(),
(room->GetFrameNo() - create_frameno_) / 2,
gun_meta->i->bullet_speed(),
gun_meta->i->range(),
distance,
born_pos.x,
born_pos.y,
GetPos().x,
GetPos().y
});
}
}
}
#endif
} else {
ForceRemove();
}
}
float Bullet::GetAtk()
{
float atk = gun_meta->i->atk();
if (sender.Get()->IsAndroid()) {
Android* android = (Android*)sender.Get();
atk *= android->ai->GetAttackRate();
}
float attr_rate = 1 + ability_->GetAttrRate(kHAT_Atk);
if (IsCurrWeapon()) {
attr_rate += ability_->GetAttrRate(kHAT_WeaponDmg);
}
return atk * attr_rate;
}
float Bullet::GetExplosionRange()
{
float e_range = gun_meta->i->explosion_range();
if (IsCurrWeapon()) {
e_range *= (1 + ability_->GetAttrRate(kHAT_WeaponExplosionRange));
}
return e_range;
}
void Bullet::Check(float distance)
{
int c_hit_num = 0;
int t_hit_num = 0;
int o_hit_num = 0;
std::set<Entity*> objects;
{
std::set<ColliderComponent*> colliders;
room->map_service->GetColliders(room, GetX(), GetY(), colliders);
for (ColliderComponent* collider : colliders) {
if (collider->owner->IsEntityType(ET_Dummy)) {
if (a8::HasBitFlag(collider->tag, kHalfWallTag)) {
continue;
}
if (TestCollision(room, collider)) {
++o_hit_num;
objects.insert(collider->owner);
}
} else if (collider->owner->IsEntityType(ET_Obstacle)) {
Obstacle* obstacle = (Obstacle*)collider->owner;
if (gun_meta->i->is_penetrate_thing() &&
hit_objects_.find(obstacle->GetUniId()) != hit_objects_.end()) {
//穿物件
continue;
}
if (!obstacle->CanThroughable(this)) {
if (TestCollision(room, collider)) {
objects.insert(collider->owner);
if (gun_meta->i->is_penetrate_thing()) {
++t_hit_num;
++o_hit_num;
hit_objects_.insert(collider->owner->GetUniId());
}
}
} else if (obstacle->meta->i->thing_type() == kObstacleStrengthenWall) {
if (!strengthened_ && sender.Get() &&
sender.Get()->team_id == obstacle->GetTeamId(room)) {
bool ret = Check2dRotationRectangle
(GetPos().x,
GetPos().y,
gun_meta->i->bullet_rad(),
obstacle->GetPos().x,
obstacle->GetPos().y,
obstacle->meta->i->width(),
obstacle->meta->i->height(),
obstacle->GetRotate() * 180.0f
);
if (ret) {
strengthened_ = true;
OnStrengthen(obstacle);
#ifdef DEBUG
a8::XPrintf("命中能量墙\n", {});
#endif
}
}
}
}
}
}
bool eat = false;
if (o_hit_num <= 0) {
room->grid_service->TraverseCreatures
(room->GetRoomIdx(),
GetGridList(),
[this, &objects, &c_hit_num, &eat] (Creature* c, bool& stop)
{
if (sender.Get()->IsProperTarget(c)) {
if (gun_meta->i->ispenetrate() &&
hit_objects_.find(c->GetUniId()) != hit_objects_.end()) {
//穿人
return;
}
if (c->HasBuffEffect(kBET_BulletThrough)) {
return;
}
{
Buff* hold_shield_buff = c->GetBuffByEffectId(kBET_HoldShield);
if (hold_shield_buff && !IsBomb() && !c->dead && c != sender.Get()) {
//param2是距离 param4是宽度
a8::Vec2 shield_pos = c->GetPos() + c->GetAttackDir() * hold_shield_buff->meta->param2;
bool ret = Check2dRotationRectangle(GetPos().x,
GetPos().y,
//10,
gun_meta->i->bullet_rad(),
shield_pos.x,
shield_pos.y,
hold_shield_buff->meta->param4,
MetaMgr::Instance()->bullet_planck_step_length,
c->GetAttackDirRotate() * 180.0f
);
if (ret) {
float finaly_dmg = c->GetBattleContext()->CalcDmg(c, this);
c->shield_hp_ = std::max(0.0f, c->shield_hp_ - finaly_dmg);
#ifdef DEBUG
a8::XPrintf("命中盾牌 finally_dmg:%f shield_hp:%f\n",
{
finaly_dmg,
c->shield_hp_
});
#endif
room->frame_event.AddPropChg(c->GetWeakPtrRef(),
kPropShieldHp,
c->shield_max_hp_,
c->shield_hp_);
if (c->shield_hp_ <= 0) {
#ifdef DEBUG
a8::XPrintf("shiled destory\n", {});
#endif
c->GetTrigger()->ShieldDestory();
c->RemoveBuffByUniId(hold_shield_buff->buff_uniid);
}
eat = true;
stop = true;
return;
}
}
}
AabbCollider aabb_box;
c->GetHitAabbBox(aabb_box);
if (c != sender.Get() && !c->dead && TestCollision(room, &aabb_box)) {
if (meta->i->_inventory_slot() == IS_C4) {
if (!c->IsHuman()) {
objects.insert(c);
if (gun_meta->i->ispenetrate()) {
++c_hit_num;
hit_objects_.insert(c->GetUniId());
}
}
} else {
objects.insert(c);
if (gun_meta->i->ispenetrate()) {
++c_hit_num;
hit_objects_.insert(c->GetUniId());
}
}
}
}
});
}
float bullet_range = gun_meta->i->range();
if (!objects.empty() || (!IsBomb() && distance > bullet_range) || eat ||
(IsBomb() && meta->i->_inventory_slot() != IS_RPG && distance >= fly_distance)
) {
if (IsBomb()) {
ProcBomb();
} else {
bool hited = false;
if (!eat && !objects.empty()) {
hited = true;
OnHit(objects);
}
bool need_remove = true;
if (distance < bullet_range) {
if (!gun_meta->i->is_penetrate_thing() && !gun_meta->i->ispenetrate()) {
} else {
if ((!gun_meta->i->is_penetrate_thing() && (t_hit_num > 0)) ||
(!gun_meta->i->ispenetrate() && (c_hit_num > 0))) {
} else {
need_remove = false;
}
}
}
if (need_remove) {
if (IsFlyHook()) {
if (!hited) {
sender.Get()->IncDisableMoveTimes();
sender.Get()->IncDisableAttackDirTimes();
sender.Get()->room->xtimer.AddDeadLineTimerAndAttach
(
(distance / gun_meta->i->bullet_speed() / 2) * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(sender.Get()),
[] (const a8::XParams& param)
{
Creature* c = (Creature*)param.sender.GetUserData();
c->RemoveBuffById(kKeepShotAnimiBuffId);
},
&sender.Get()->xtimer_attacher.timer_list_
);
sender.Get()->room->xtimer.AddDeadLineTimerAndAttach
(
(0.75 + distance / gun_meta->i->bullet_speed() / 2) * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(sender.Get()),
[] (const a8::XParams& param)
{
Creature* c = (Creature*)param.sender.GetUserData();
c->DecDisableMoveTimes();
c->DecDisableAttackDirTimes();
c->GetTrigger()->FlyHookDestory();
},
&sender.Get()->xtimer_attacher.timer_list_
);
}
}
ForceRemove();
}
}
}
}
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();
task->explosion_range = GetExplosionRange();
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)
{
AddGunBuff();
}
void Bullet::ProcMolotorCocktailBomb(int delay_time)
{
AddGunBuff();
}
void Bullet::ProcC4Bomb(Car* target, int delay_time)
{
if (sender.Get()) {
FragMiTask* task = new FragMiTask();
task->room = room;
task->sender.Attach(sender.Get());
task->bomb_pos = GetPos();
if (target) {
task->follow_target.Attach(target);
task->force_target.Attach(target);
if (gun_meta->int_param1 > 0) {
int buff_uniid = target->TryAddBuff(sender.Get(), gun_meta->int_param1);
if (buff_uniid != 0) {
Buff* buff = target->GetBuffByUniId(buff_uniid);
if (buff && buff->remover_timer) {
target->room->xtimer.ModifyTimer(buff->remover_timer,
std::max(1, (int)(delay_time / FRAME_RATE_MS)));
}
}
}
}
task->explosion_range = GetExplosionRange();
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::ProcSignalGunBomb(int delay_time)
{
AddGunBuff();
}
void Bullet::ProcShieldWallBomb(int delay_time)
{
AddGunBuff();
}
void Bullet::ProcOilBucketBomb(int delay_time)
{
if (sender.Get()) {
room->CreateLoot(gun_meta->i->id(), GetPos(), 1, 1);
}
}
bool Bullet::IsCurrWeapon()
{
return is_curr_weapon;
}
void Bullet::AddGunBuff()
{
if (sender.Get()) {
std::shared_ptr<Ability> old_context_ability = sender.Get()->context_ability;
a8::Vec2 old_context_dir = sender.Get()->context_dir;
a8::Vec2 old_context_pos = sender.Get()->context_pos;
sender.Get()->context_dir = dir;
sender.Get()->context_pos = GetPos();
if (IsCurrWeapon()) {
sender.Get()->context_ability = ability_;
} else {
sender.Get()->context_ability = nullptr;
}
MetaData::Buff * buff_meta = MetaMgr::Instance()->GetBuff(gun_meta->i->buffid());
if (buff_meta) {
sender.Get()->AddBuff(sender.Get(),
buff_meta
);
}
sender.Get()->context_dir = old_context_dir;
sender.Get()->context_pos = old_context_pos;
sender.Get()->context_ability = old_context_ability;
}
}
bool Bullet::IsPreBattleBullet()
{
if (room->IsPveRoom()) {
return false;
}
return create_frameno_ <= room->GetBattleStartFrameNo() || room->GetBattleStartFrameNo() == 0;
}
void Bullet::OnKillTarget(Entity* target)
{
if (target->IsCreature(room)) {
Creature* c = (Creature*)target;
if (c->IsHuman() && sender.Get() && sender.Get()->IsHuman()) {
sender.Get()->AsHuman()->stats.IncWeaponKills(gun_meta->i->id(), 1);
}
}
}
void Bullet::OnStrengthen(Obstacle* ob)
{
if (ob->IsRoomObstacle()) {
RoomObstacle* room_ob = ob->AsRoomObstacle();
if (room_ob->skill_meta) {
MetaData::Skill* skill_meta = room_ob->skill_meta;
if (skill_meta && skill_meta->number_meta) {
switch (skill_meta->GetMagicId()) {
case MAGIC_WLFB:
{
strengthen_wall = skill_meta->number_meta->float_ratio2;
}
break;
default:
{
}
break;
}
}
}
}
}
void Bullet::ClearBuffList()
{
for (auto& buff_uniid : buff_list_) {
if (sender.Get()) {
sender.Get()->RemoveBuffByUniId(buff_uniid);
}
}
buff_list_.clear();
}
void Bullet::ProcFlyHook(Entity* target)
{
ClearBuffList();
float distance = born_pos.Distance(GetPos());
if (distance < 0.001f) {
return;
}
if (target->IsCreature(room)) {
Creature* c = (Creature*)target;
room->frame_event.AddPropChg(c->GetWeakPtrRef(), kPropBeHook, 0, sender.Get()->GetUniId());
std::vector<int> buff_uniids;
for (int buff_id : gun_meta->hit_buff_list) {
int buff_uniid = c->TryAddBuff(c, gun_meta->hit_buff_list[0]);
if (buff_uniid) {
buff_uniids.push_back(buff_uniid);
}
}
c->AutoNavigation(born_pos, gun_meta->i->bullet_speed() * 2,
[buff_uniids] (Creature* c)
{
for (int buff_uniid : buff_uniids) {
c->RemoveBuffByUniId(buff_uniid);
}
}
);
} else {
sender.Get()->AutoNavigation(GetPos(), gun_meta->i->bullet_speed() * 2,
[] (Creature* c)
{
});
}
sender.Get()->IncDisableMoveTimes();
sender.Get()->room->xtimer.AddDeadLineTimerAndAttach
(
(distance / gun_meta->i->bullet_speed() / 2 + 0.75) * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(sender.Get()),
[] (const a8::XParams& param)
{
Creature* c = (Creature*)param.sender.GetUserData();
c->DecDisableMoveTimes();
c->GetTrigger()->FlyHookDestory();
},
&sender.Get()->xtimer_attacher.timer_list_
);
sender.Get()->RemoveBuffById(kKeepShotAnimiBuffId);
}
void Bullet::ForceRemove()
{
if (!later_removed_) {
ClearBuffList();
room->RemoveObjectLater(this);
later_removed_ = true;
if (!keep_shot_animi_timer_ptr.expired()) {
room->xtimer.DeleteTimer(keep_shot_animi_timer_ptr);
}
}
}
void Bullet::TriggerHitBuff(Entity* e)
{
if (!e->IsDead(room) && e->IsCreature(room)) {
Creature* c = (Creature*)e;
for (int buff_id : gun_meta->hit_buff_list) {
int buff_uniid = c->TryAddBuff(
sender.Get(),
buff_id,
skill_meta
);
if (skill_meta && buff_uniid) {
SkillHelper::ProcBulletHitBuff(this, c, buff_uniid);
}
}
}
}
bool Bullet::IsFlyHook()
{
return gun_meta->i->equip_subtype() == GUN_SUB_EQUIP_TYPE_FLY_HOOk;
}