614 lines
19 KiB
C++
614 lines
19 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 "creature.h"
|
|
#include "roomobstacle.h"
|
|
#include "car.h"
|
|
#include "creature.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();
|
|
}
|
|
|
|
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)
|
|
{
|
|
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();
|
|
|
|
for (auto& target : objects) {
|
|
target->OnBulletHit(this);
|
|
}
|
|
|
|
sender.Get()->context_dir = old_context_dir;
|
|
sender.Get()->context_pos = old_context_pos;
|
|
sender.Get()->context_ability = old_context_ability;
|
|
}
|
|
|
|
void Bullet::ProcBomb()
|
|
{
|
|
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 (!is_tank_skin || 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;
|
|
room->frame_event.RemoveBullet(a8::Vec2(x, y), bullet_uniid);
|
|
},
|
|
&room->timer_attacher.timer_list_,
|
|
[] (const a8::XParams& param)
|
|
{
|
|
}
|
|
);
|
|
}
|
|
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();
|
|
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();
|
|
}
|
|
break;
|
|
case IS_POSION_GAS_BOMB:
|
|
{
|
|
//毒气弹
|
|
ProcPosionGasBomb(delay_time);
|
|
}
|
|
break;
|
|
case IS_MOLOTOR_COCKTAIL:
|
|
{
|
|
//燃烧瓶
|
|
ProcMolotorCocktailBomb(delay_time);
|
|
}
|
|
break;
|
|
case IS_C4:
|
|
{
|
|
//c4
|
|
ProcC4Bomb(c4_target, delay_time);
|
|
}
|
|
break;
|
|
case IS_SINGAL_GUN:
|
|
{
|
|
ProcSignalGunBomb(delay_time);
|
|
}
|
|
break;
|
|
case IS_SHIELD_WALL:
|
|
{
|
|
ProcShieldWallBomb(delay_time);
|
|
}
|
|
break;
|
|
case IS_OIL_BUCKET:
|
|
{
|
|
ProcOilBucketBomb(delay_time);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
room->RemoveObjectLater(this);
|
|
later_removed_ = true;
|
|
}
|
|
|
|
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_) {
|
|
room->RemoveObjectLater(this);
|
|
later_removed_ = true;
|
|
}
|
|
}
|
|
} else {
|
|
room->grid_service->MoveBullet(this);
|
|
Check(distance);
|
|
}
|
|
} while(!later_removed_ && move_length >= 0.0001f);
|
|
} 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();
|
|
}
|
|
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 (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());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (o_hit_num <= 0) {
|
|
room->grid_service->TraverseCreatures
|
|
(room->GetRoomIdx(),
|
|
GetGridList(),
|
|
[this, &objects, &c_hit_num] (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;
|
|
}
|
|
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 (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() || (!IsBomb() && distance > bullet_range) ||
|
|
(IsBomb() && meta->i->_inventory_slot() != IS_RPG && distance >= fly_distance)
|
|
) {
|
|
if (IsBomb()) {
|
|
ProcBomb();
|
|
} else {
|
|
if (!objects.empty()) {
|
|
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) {
|
|
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();
|
|
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->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,
|
|
1
|
|
);
|
|
}
|
|
sender.Get()->context_dir = old_context_dir;
|
|
sender.Get()->context_pos = old_context_pos;
|
|
sender.Get()->context_ability = old_context_ability;
|
|
}
|
|
}
|
|
|
|
bool Bullet::IsPreBattleBullet()
|
|
{
|
|
return create_frameno_ <= room->GetBattleStartFrameNo() || room->GetBattleStartFrameNo() == 0;
|
|
}
|