1174 lines
37 KiB
C++
1174 lines
37 KiB
C++
#include "precompile.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include <glm/gtc/quaternion.hpp>
|
|
#include <glm/gtx/quaternion.hpp>
|
|
#include <glm/gtx/euler_angles.hpp>
|
|
|
|
#include "bullet.h"
|
|
#include "room.h"
|
|
#include "obstacle.h"
|
|
#include "player.h"
|
|
#include "android.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"
|
|
#include "trigger.h"
|
|
#include "ability.h"
|
|
#include "buff.h"
|
|
#include "collision.h"
|
|
#include "mapinstance.h"
|
|
#include "debugcmd.h"
|
|
#include "stats.h"
|
|
#include "movement.h"
|
|
#include "hero.h"
|
|
#include "skill.h"
|
|
|
|
#include "mt/Param.h"
|
|
#include "mt/Equip.h"
|
|
#include "mt/Skill.h"
|
|
#include "mt/SkillNumber.h"
|
|
#include "mt/Buff.h"
|
|
#include "mt/Hero.h"
|
|
#include "mt/MapThing.h"
|
|
|
|
Bullet::Bullet():MoveableEntity()
|
|
{
|
|
++PerfMonitor::Instance()->entity_num[ET_Bullet];
|
|
}
|
|
|
|
Bullet::~Bullet()
|
|
{
|
|
--PerfMonitor::Instance()->entity_num[ET_Bullet];
|
|
if (IsFlyHook()) {
|
|
if (sender.Get()) {
|
|
sender.Get()->hook_hash.erase(GetUniId());
|
|
}
|
|
}
|
|
}
|
|
|
|
void Bullet::Initialize()
|
|
{
|
|
if (sender.Get() && sender.Get()->IsPlayer()) {
|
|
int i = 0;
|
|
}
|
|
MoveableEntity::Initialize();
|
|
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.expired()) {
|
|
buff_list_.push_back(buff_uniid);
|
|
sender.Get()->room->xtimer.ModifyTime(buff->remover_timer, SERVER_FRAME_RATE * 10);
|
|
}
|
|
}
|
|
if (sender.Get()) {
|
|
sender.Get()->GetTrigger()->FlyHookCreate(this);
|
|
sender.Get()->hook_hash[GetUniId()] = GetUniId();
|
|
}
|
|
Raycast();
|
|
}
|
|
#ifdef MYDEBUG
|
|
if (DebugCmd::Enable() && sender.Get() && sender.Get() && sender.Get()->IsPlayer()) {
|
|
glm::vec3 hit_pos;
|
|
float ray_length;
|
|
bool hit = room->map_instance->SceneRaycast
|
|
(GetPos().ToGlmVec3(),
|
|
dir,
|
|
gun_meta->range(),
|
|
hit_pos,
|
|
ray_length);
|
|
if (hit) {
|
|
DebugCmd::CreateSphere(sender.Get(),
|
|
hit_pos,
|
|
glm::vec3(10, 10, 10),
|
|
room->AllocUniid());
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Bullet::Update(int delta_time)
|
|
{
|
|
if (shot_animi_time <= (room->GetFrameNo() - create_frameno_) * FRAME_RATE_MS) {
|
|
if (!trace_target_id && !reporter_list && !IsClientHook()) {
|
|
MapServiceUpdate();
|
|
++updated_times_;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Bullet::OnHit(std::set<Entity*>& objects)
|
|
{
|
|
std::shared_ptr<Ability> old_context_ability = sender.Get()->context_ability;
|
|
glm::vec3 old_context_dir = sender.Get()->context_dir;
|
|
Position 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) {
|
|
Creature* c = target->IsCreature(room) ? (Creature*)target : nullptr;
|
|
ProcFlyHook(target);
|
|
if (!c || (c->team_id != sender.Get()->team_id)) {
|
|
if (!c || !c->IsCar()) {
|
|
target->OnBulletHit(this);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (auto& target : objects) {
|
|
bool eat = false;
|
|
if (!trace_target_id && !reporter_list) {
|
|
if (mt::Param::s().bullet_through_wall_check) {
|
|
if (!room->BulletCanReach(born_pos.ToGlmVec3(), target->GetPos().ToGlmVec3())) {
|
|
eat = true;
|
|
}
|
|
if (sender.Get() && sender.Get()->IsPlayer()) {
|
|
#ifdef MYDEBUG
|
|
a8::XPrintf("eat %d xxxxxxxxxxxxxxxxxxxxxxxx\n", {eat});
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
if (!eat) {
|
|
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()
|
|
{
|
|
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->_inventory_slot() == 4 ||
|
|
meta->_inventory_slot() == 5) &&
|
|
sender.Get()->team_id == c->team_id) {
|
|
return;
|
|
}
|
|
if (meta->_inventory_slot() == IS_C4) {
|
|
if (!objects.empty()) {
|
|
stop = true;
|
|
return;
|
|
}
|
|
if (!c->IsCar()) {
|
|
return;
|
|
}
|
|
if (Collision::CheckBullet(this, c)) {
|
|
c4_target = c->AsCar();
|
|
stop = true;
|
|
objects.insert(c);
|
|
return;
|
|
}
|
|
} else {
|
|
if (Collision::CheckBullet(this, c)) {
|
|
objects.insert(c);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
TraverseAllLayerEntityList
|
|
(
|
|
[this, &objects, c4_target] (Entity* entity, bool& stop)
|
|
{
|
|
if (meta->_inventory_slot() == IS_C4) {
|
|
if (c4_target) {
|
|
stop = true;
|
|
return;
|
|
}
|
|
}
|
|
switch (entity->GetEntityType()) {
|
|
case ET_Obstacle:
|
|
case ET_Building:
|
|
case ET_Dummy:
|
|
{
|
|
if (Collision::CheckBullet(this, entity)) {
|
|
objects.insert(entity);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
|
|
bool block = false;
|
|
if (objects.empty()) {
|
|
float distance = GetPos().Distance2D2(born_pos);
|
|
if (distance < fly_distance) {
|
|
block = true;
|
|
}
|
|
}
|
|
int delay_time = 0;
|
|
if (!block) {
|
|
delay_time = gun_meta->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);
|
|
}
|
|
{
|
|
Position old_pos = GetPos();
|
|
room->xtimer.SetTimeoutEx
|
|
(delay_time / FRAME_RATE_MS,
|
|
[this, old_pos] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
}
|
|
},
|
|
&xtimer_attacher
|
|
);
|
|
}
|
|
switch (meta->_inventory_slot()) {
|
|
case IS_RPG:
|
|
{
|
|
//榴弹炮
|
|
if (sender.Get() && sender.Get()->IsHuman()) {
|
|
sender.Get()->AsHuman()->stats->IncWeaponUseTimes(gun_meta->id(), 1);
|
|
}
|
|
bool is_player = sender.Get()->IsPlayer();
|
|
if (is_player) {
|
|
int i = 0;
|
|
}
|
|
Position bomb_pos = GetPos();
|
|
room->frame_event.AddExplosionEx(sender,
|
|
meta->id(),
|
|
bomb_pos,
|
|
gun_meta->explosion_effect(),
|
|
GetUniId());
|
|
OnHit(objects);
|
|
AddGunBuff();
|
|
}
|
|
break;
|
|
case IS_FRAG:
|
|
{
|
|
//手雷
|
|
if (sender.Get() && sender.Get()->IsHuman()) {
|
|
sender.Get()->AsHuman()->stats->IncWeaponUseTimes(gun_meta->id(), 1);
|
|
}
|
|
ProcFragBomb(delay_time);
|
|
}
|
|
break;
|
|
case IS_SMOKE:
|
|
{
|
|
//烟雾弹
|
|
if (sender.Get() && sender.Get()->IsHuman()) {
|
|
sender.Get()->AsHuman()->stats->IncWeaponUseTimes(gun_meta->id(), 1);
|
|
}
|
|
AddGunBuff();
|
|
ProcSmokeBomb();
|
|
}
|
|
break;
|
|
case IS_POSION_GAS_BOMB:
|
|
{
|
|
//毒气弹
|
|
if (sender.Get() && sender.Get()->IsHuman()) {
|
|
sender.Get()->AsHuman()->stats->IncWeaponUseTimes(gun_meta->id(), 1);
|
|
}
|
|
ProcPosionGasBomb(delay_time);
|
|
}
|
|
break;
|
|
case IS_MOLOTOR_COCKTAIL:
|
|
{
|
|
//燃烧瓶
|
|
if (sender.Get() && sender.Get()->IsHuman()) {
|
|
sender.Get()->AsHuman()->stats->IncWeaponUseTimes(gun_meta->id(), 1);
|
|
}
|
|
ProcMolotorCocktailBomb(delay_time);
|
|
}
|
|
break;
|
|
case IS_C4:
|
|
{
|
|
//c4
|
|
if (sender.Get() && sender.Get()->IsHuman()) {
|
|
sender.Get()->AsHuman()->stats->IncWeaponUseTimes(gun_meta->id(), 1);
|
|
}
|
|
if (block) {
|
|
delay_time = gun_meta->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->id(), 1);
|
|
}
|
|
ProcSignalGunBomb(delay_time);
|
|
}
|
|
break;
|
|
case IS_SHIELD_WALL:
|
|
{
|
|
if (sender.Get() && sender.Get()->IsHuman()) {
|
|
sender.Get()->AsHuman()->stats->IncWeaponUseTimes(gun_meta->id(), 1);
|
|
}
|
|
ProcShieldWallBomb(delay_time);
|
|
}
|
|
break;
|
|
case IS_OIL_BUCKET:
|
|
{
|
|
if (sender.Get() && sender.Get()->IsHuman()) {
|
|
sender.Get()->AsHuman()->stats->IncWeaponUseTimes(gun_meta->id(), 1);
|
|
}
|
|
ProcOilBucketBomb(delay_time);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
ForceRemove();
|
|
}
|
|
|
|
void Bullet::ProcSmokeBomb()
|
|
{
|
|
const mt::Buff* buff_meta = mt::Buff::GetById(HUNLUAN_BUFFID);
|
|
if (buff_meta) {
|
|
std::shared_ptr<SmokeMiTask> task = std::make_shared<SmokeMiTask>();
|
|
task->room = room;
|
|
task->bomb_pos = GetPos();
|
|
task->buff_meta = buff_meta;
|
|
task->gun_meta = gun_meta;
|
|
task->Initialize();
|
|
auto task_wp = std::weak_ptr<SmokeMiTask>(task);
|
|
room->xtimer.SetIntervalEx
|
|
(SERVER_FRAME_RATE / 2,
|
|
[task_wp] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
if (!task_wp.expired()) {
|
|
task_wp.lock()->Check();
|
|
}
|
|
}
|
|
},
|
|
&task->xtimer_attacher);
|
|
|
|
room->xtimer.SetTimeoutEx
|
|
(SERVER_FRAME_RATE * mt::Param::GetIntParam("smoke_duration", 10),
|
|
[task] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_DELETE_EVENT == event) {
|
|
task->Done();
|
|
}
|
|
},
|
|
&room->xtimer_attacher_);
|
|
}
|
|
}
|
|
|
|
bool Bullet::IsBomb()
|
|
{
|
|
if (!meta) {
|
|
return false;
|
|
}
|
|
return
|
|
meta->_inventory_slot() == IS_RPG ||
|
|
meta->_inventory_slot() == IS_FRAG ||
|
|
meta->_inventory_slot() == IS_SMOKE ||
|
|
meta->_inventory_slot() == IS_POSION_GAS_BOMB ||
|
|
meta->_inventory_slot() == IS_MOLOTOR_COCKTAIL ||
|
|
meta->_inventory_slot() == IS_C4 ||
|
|
meta->_inventory_slot() == IS_SINGAL_GUN ||
|
|
meta->_inventory_slot() == IS_SHIELD_WALL ||
|
|
meta->_inventory_slot() == IS_OIL_BUCKET;
|
|
}
|
|
|
|
void Bullet::MapServiceUpdate()
|
|
{
|
|
if (later_removed_) {
|
|
return;
|
|
}
|
|
if (sender.Get()) {
|
|
float move_length = gun_meta->bullet_speed() / (float)SERVER_FRAME_RATE;
|
|
do {
|
|
float step_len = move_length - mt::Param::s().bullet_planck_step_length;
|
|
if (step_len <= 0.001f) {
|
|
if (move_length > 0.1f) {
|
|
step_len = move_length;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
move_length -= step_len;
|
|
auto old_pos = GetPos();
|
|
GetMutablePos().AddGlmVec3(dir * step_len);
|
|
float distance = GetPos().DistanceGlmVec3(born_pos.ToGlmVec3());
|
|
#ifdef MYDEBUG
|
|
if (IsFlyHook()) {
|
|
a8::XPrintf("step_len:%f born_pos:%f,%f,%f curr_pos:%f,%f,%f old_pos:%f,%f,%f distance:%f %f\n",
|
|
{
|
|
step_len,
|
|
born_pos.GetX(),
|
|
born_pos.GetY(),
|
|
born_pos.GetZ(),
|
|
|
|
old_pos.GetX(),
|
|
old_pos.GetY(),
|
|
old_pos.GetZ(),
|
|
|
|
GetPos().GetX(),
|
|
GetPos().GetY(),
|
|
GetPos().GetZ(),
|
|
|
|
distance,
|
|
raycast_len_
|
|
});
|
|
}
|
|
#endif
|
|
if (room->OverBorder(GetPos().ToGlmVec3(), gun_meta->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 MYDEBUG1
|
|
{
|
|
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 src_fly_distance:%f\n",
|
|
{
|
|
gun_meta->id(),
|
|
(room->GetFrameNo() - create_frameno_) / 2,
|
|
gun_meta->bullet_speed(),
|
|
gun_meta->range(),
|
|
distance,
|
|
born_pos.x,
|
|
born_pos.y,
|
|
GetPos().x,
|
|
GetPos().y,
|
|
fly_distance
|
|
});
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
ForceRemove();
|
|
}
|
|
}
|
|
|
|
float Bullet::GetAtk()
|
|
{
|
|
float atk = gun_meta->_atk;
|
|
if (sender.Get()->IsAndroid()) {
|
|
Android* android = (Android*)sender.Get();
|
|
#if 0
|
|
atk *= android->ai->GetAttackRate();
|
|
#endif
|
|
}
|
|
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->explosion_range();
|
|
if (IsCurrWeapon()) {
|
|
e_range *= (1 + ability_->GetAttrRate(kHAT_WeaponExplosionRange));
|
|
}
|
|
return e_range;
|
|
}
|
|
|
|
void Bullet::Check(float distance)
|
|
{
|
|
if (gun_meta->id() == 60201) {
|
|
int i = 0;
|
|
}
|
|
BulletCheckResult result;
|
|
result.flyed_distance = distance;
|
|
GetHitThings(result);
|
|
if (result.o_hit_num <= 0) {
|
|
GetHitCreatures(result);
|
|
}
|
|
if (!result.objects.empty() ||
|
|
#if 1
|
|
// 999
|
|
(fly_distance < 0.00001f && distance > gun_meta->range()) ||
|
|
(fly_distance > 0.00001f && distance >= fly_distance) ||
|
|
#else
|
|
(!IsBomb() && distance > gun_meta->range()) ||
|
|
#endif
|
|
result.eat ||
|
|
(gun_meta->id() == 30918 && distance >= fly_distance) ||
|
|
(IsBomb() && meta->_inventory_slot() != IS_RPG && distance >= fly_distance) ||
|
|
(IsFlyHook() && distance >= raycast_len_)
|
|
) {
|
|
if (IsBomb()) {
|
|
ProcBomb();
|
|
} else {
|
|
ProcNormalBullet(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Bullet::ProcFragBomb(int delay_time)
|
|
{
|
|
if (sender.Get()) {
|
|
std::shared_ptr<FragMiTask> task = std::make_shared<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();
|
|
task->Initialize();
|
|
room->xtimer.SetTimeoutEx
|
|
(std::max(1, (int)(delay_time / FRAME_RATE_MS)),
|
|
[task] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
task->Done();
|
|
}
|
|
},
|
|
&task->xtimer_attacher);
|
|
}
|
|
}
|
|
|
|
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()) {
|
|
std::shared_ptr<FragMiTask> task = std::shared_ptr<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.expired()) {
|
|
target->room->xtimer.ModifyTime(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();
|
|
task->Initialize();
|
|
room->xtimer.SetTimeoutEx
|
|
(std::max(1, (int)(delay_time / FRAME_RATE_MS)),
|
|
[task] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
task->Done();
|
|
}
|
|
},
|
|
&task->xtimer_attacher);
|
|
}
|
|
}
|
|
|
|
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->id(), GetPos().ToGlmVec3(), GetPos().ToGlmVec3(), 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;
|
|
glm::vec3 old_context_dir = sender.Get()->context_dir;
|
|
Position 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;
|
|
}
|
|
int real_buff_id = gun_meta->buffid();
|
|
if (spec_gun_buff_id) {
|
|
real_buff_id = spec_gun_buff_id;
|
|
}
|
|
const mt::Buff * buff_meta = mt::Buff::GetById(real_buff_id);
|
|
if (buff_meta) {
|
|
auto buff_vars = std::make_shared<std::vector<float>>();
|
|
buff_vars->push_back(GetPos().GetX());
|
|
buff_vars->push_back(GetPos().GetY());
|
|
buff_vars->push_back(GetPos().GetZ());
|
|
sender.Get()->TryAddBuff(sender.Get(),
|
|
real_buff_id,
|
|
skill_meta,
|
|
nullptr,
|
|
buff_vars
|
|
);
|
|
}
|
|
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;
|
|
}
|
|
if (room->IsNewBieRoom()) {
|
|
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->id(), 1);
|
|
sender.Get()->GetTrigger()->BulletKill(this, c);
|
|
}
|
|
if (sender.Get()->IsHero() &&
|
|
sender.Get()->AsHero()->master.Get() &&
|
|
sender.Get()->AsHero()->master.Get()->IsHuman()) {
|
|
sender.Get()->AsHero()->master.Get()->AsHuman()->stats->kills++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Bullet::OnStrengthen(Obstacle* ob)
|
|
{
|
|
if (ob->IsRoomObstacle()) {
|
|
RoomObstacle* room_ob = ob->AsRoomObstacle();
|
|
if (room_ob->skill_meta) {
|
|
const mt::Skill* skill_meta = room_ob->skill_meta;
|
|
if (skill_meta && skill_meta->_number_meta) {
|
|
switch (skill_meta->GetMagicId()) {
|
|
case MAGIC_20601_DJS:
|
|
{
|
|
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.Distance2D2(GetPos());
|
|
if (distance < 0.001f) {
|
|
return;
|
|
}
|
|
|
|
Creature* c = (Creature*)target;
|
|
if (target->IsCreature(room) && !c->IsCar()) {
|
|
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, buff_id);
|
|
if (buff_uniid) {
|
|
buff_uniids.push_back(buff_uniid);
|
|
}
|
|
}
|
|
CreatureWeakPtr sender_bk = sender;
|
|
int ok_buff_id = gun_meta->_int_param1;
|
|
c->GetMovement()->ClearPath();
|
|
c->AutoNavigation(born_pos.ToGlmVec3(), gun_meta->bullet_speed() * 2,
|
|
[buff_uniids, ok_buff_id, sender_bk] (Creature* c) mutable
|
|
{
|
|
for (int buff_uniid : buff_uniids) {
|
|
c->RemoveBuffByUniId(buff_uniid);
|
|
}
|
|
if (sender_bk.Get()) {
|
|
int buff_uniid = sender_bk.Get()->TryAddBuff(sender_bk.Get(), ok_buff_id);
|
|
}
|
|
}
|
|
);
|
|
} else {
|
|
sender.Get()->GetMovement()->ClearPath();
|
|
sender.Get()->AutoNavigation(GetPos().ToGlmVec3(), gun_meta->bullet_speed() * 2,
|
|
[] (Creature* c)
|
|
{
|
|
|
|
});
|
|
}
|
|
|
|
sender.Get()->IncDisableMoveTimes();
|
|
auto sender_p = sender;
|
|
sender.Get()->room->xtimer.SetTimeoutEx
|
|
(
|
|
(distance / gun_meta->bullet_speed() / 2 + 0.3) * SERVER_FRAME_RATE,
|
|
[sender_p] (int event, const a8::Args* args) mutable
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
sender_p.Get()->DecDisableMoveTimes();
|
|
sender_p.Get()->GetTrigger()->FlyHookDestory();
|
|
}
|
|
},
|
|
&sender.Get()->xtimer_attacher
|
|
);
|
|
sender.Get()->RemoveBuffById(kKeepShotAnimiBuffId);
|
|
sender.Get()->TryAddBuff(sender.Get(), gun_meta->_int_param2);
|
|
}
|
|
|
|
void Bullet::ForceRemove()
|
|
{
|
|
if (!later_removed_) {
|
|
ClearBuffList();
|
|
room->RemoveObjectLater(this);
|
|
later_removed_ = true;
|
|
if (!keep_shot_animi_timer_ptr.expired()) {
|
|
room->xtimer.Delete(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);
|
|
}
|
|
}
|
|
c->GetTrigger()->BulletHitBuff(this);
|
|
}
|
|
}
|
|
|
|
bool Bullet::IsFlyHook()
|
|
{
|
|
return gun_meta->equip_subtype() == GUN_SUB_EQUIP_TYPE_FLY_HOOk;
|
|
}
|
|
|
|
void Bullet::GetHitThings(BulletCheckResult& result)
|
|
{
|
|
room->grid_service->TraverseObstacles
|
|
(room->GetRoomIdx(),
|
|
GetGridList(),
|
|
[this, result] (Obstacle* ob, bool& stop) mutable
|
|
{
|
|
#if 1
|
|
if (!ob->CanThroughable(this) && !ob->IsDead(room)) {
|
|
if (Collision::CheckBullet(this, ob)) {
|
|
result.objects.insert(ob);
|
|
}
|
|
}
|
|
#else
|
|
if (ob->meta->thing_type() == kObstacleStrengthenWall) {
|
|
if (!strengthened_ && sender.Get() &&
|
|
sender.Get()->team_id == ob->GetTeamId(room)) {
|
|
bool ret = Collision::Check2dRotationRectangle
|
|
(GetPos().GetX(),
|
|
GetPos().GetZ(),
|
|
gun_meta->bullet_rad(),
|
|
ob->GetPos().GetX(),
|
|
ob->GetPos().GetZ(),
|
|
ob->meta->width(),
|
|
ob->meta->height(),
|
|
ob->GetRotate() * 180.0f
|
|
);
|
|
if (ret) {
|
|
strengthened_ = true;
|
|
OnStrengthen(ob);
|
|
#ifdef MYDEBUG
|
|
a8::XPrintf("命中能量墙\n", {});
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
});
|
|
}
|
|
|
|
void Bullet::GetHitCreatures(BulletCheckResult& result)
|
|
{
|
|
room->grid_service->TraverseCreatures
|
|
(room->GetRoomIdx(),
|
|
GetGridList(),
|
|
[this, &result] (Creature* c, bool& stop)
|
|
{
|
|
bool no_teammate = IsFlyHook();
|
|
long long ignore_buff_effects = 0;
|
|
a8::SetBitFlag(ignore_buff_effects, kBET_Hide);
|
|
if (sender.Get()->IsProperTarget(c, no_teammate, ignore_buff_effects)) {
|
|
if (gun_meta->ispenetrate() &&
|
|
hit_objects_.find(c->GetUniId()) != hit_objects_.end()) {
|
|
//穿人
|
|
return;
|
|
}
|
|
if (c->HasBuffEffect(kBET_BulletThrough)) {
|
|
return;
|
|
}
|
|
if (c->HasBuffEffect(kBET_HoldShield)) {
|
|
c->CheckBulletHitHoldShield(this, result.eat);
|
|
if (result.eat) {
|
|
stop = true;
|
|
return;
|
|
}
|
|
}
|
|
if (c != sender.Get() && !c->dead &&
|
|
Collision::CheckBullet(this, c)) {
|
|
#ifdef MYDEBUG
|
|
if (IsFlyHook()) {
|
|
a8::XPrintf("bullet hit\n", {});
|
|
}
|
|
#endif
|
|
if (meta->_inventory_slot() == IS_C4) {
|
|
if (!c->IsHuman()) {
|
|
result.objects.insert(c);
|
|
if (gun_meta->ispenetrate()) {
|
|
++result.c_hit_num;
|
|
hit_objects_.insert(c->GetUniId());
|
|
}
|
|
}
|
|
} else {
|
|
result.objects.insert(c);
|
|
if (gun_meta->ispenetrate()) {
|
|
++result.c_hit_num;
|
|
hit_objects_.insert(c->GetUniId());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
void Bullet::ProcNormalBullet(BulletCheckResult& result)
|
|
{
|
|
bool hited = false;
|
|
if (!result.eat && !result.objects.empty()) {
|
|
hited = true;
|
|
OnHit(result.objects);
|
|
}
|
|
bool need_remove = true;
|
|
if (result.flyed_distance < gun_meta->range()) {
|
|
if (!gun_meta->is_penetrate_thing() && !gun_meta->ispenetrate()) {
|
|
} else {
|
|
if ((!gun_meta->is_penetrate_thing() && (result.t_hit_num > 0)) ||
|
|
(!gun_meta->ispenetrate() && (result.c_hit_num > 0))) {
|
|
} else {
|
|
need_remove = false;
|
|
}
|
|
}
|
|
}
|
|
if (need_remove) {
|
|
if (IsFlyHook()) {
|
|
#ifdef MYDEBUG
|
|
a8::XPrintf("ProcHookHit\n",
|
|
{
|
|
});
|
|
#endif
|
|
if (!hited) {
|
|
if (raycast_hited) {
|
|
if (result.flyed_distance > 0.001f) {
|
|
ClearBuffList();
|
|
auto sender_p = sender;
|
|
sender.Get()->AutoNavigation
|
|
(raycast_hit_point_, gun_meta->bullet_speed() * 2,
|
|
[sender_p] (Creature* c) mutable
|
|
{
|
|
sender_p.Get()->GetTrigger()->FlyHookDestory();
|
|
});
|
|
sender.Get()->RemoveBuffById(kKeepShotAnimiBuffId);
|
|
sender.Get()->TryAddBuff(sender.Get(), gun_meta->_int_param2);
|
|
}
|
|
} else {
|
|
sender.Get()->IncDisableMoveTimes();
|
|
sender.Get()->IncDisableAttackDirTimes();
|
|
auto sender_p = sender;
|
|
sender.Get()->room->xtimer.SetTimeoutEx
|
|
(
|
|
std::ceil((result.flyed_distance / gun_meta->bullet_speed() / 2) * SERVER_FRAME_RATE),
|
|
[sender_p] (int event, const a8::Args* args) mutable
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
sender_p.Get()->RemoveBuffById(kKeepShotAnimiBuffId);
|
|
}
|
|
},
|
|
&sender.Get()->xtimer_attacher
|
|
);
|
|
sender.Get()->room->xtimer.SetTimeoutEx
|
|
(
|
|
(0.75 + result.flyed_distance / gun_meta->bullet_speed() / 2) * SERVER_FRAME_RATE,
|
|
[sender_p] (int event, const a8::Args* args) mutable
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
sender_p.Get()->DecDisableMoveTimes();
|
|
sender_p.Get()->DecDisableAttackDirTimes();
|
|
sender_p.Get()->GetTrigger()->FlyHookDestory();
|
|
}
|
|
},
|
|
&sender.Get()->xtimer_attacher
|
|
);
|
|
sender.Get()->TryAddBuff(sender.Get(), gun_meta->_int_param2);
|
|
}
|
|
}
|
|
}
|
|
ForceRemove();
|
|
}
|
|
}
|
|
|
|
float Bullet::GetHitRadius()
|
|
{
|
|
float hit_radius = gun_meta->bullet_rad();
|
|
if (IsBomb()) {
|
|
if (meta->_inventory_slot() != IS_C4) {
|
|
hit_radius = GetExplosionRange();
|
|
}
|
|
}
|
|
return hit_radius;
|
|
}
|
|
|
|
const Position& Bullet::GetPos()
|
|
{
|
|
return Entity::GetPos();
|
|
}
|
|
|
|
void Bullet::Raycast()
|
|
{
|
|
raycasted_ = true;
|
|
glm::vec3 start = born_pos.ToGlmVec3();
|
|
glm::vec3 end = born_pos.ToGlmVec3() + born_dir * (float)gun_meta->range();
|
|
|
|
bool hit_result = false;
|
|
glm::vec3 hit_point;
|
|
sender.Get()->room->map_instance->Scale(start);
|
|
sender.Get()->room->map_instance->Scale(end);
|
|
bool ret = sender.Get()->room->map_instance->Raycast(start, end, hit_point, hit_result);
|
|
if (ret && hit_result) {
|
|
raycast_hited = true;
|
|
sender.Get()->room->map_instance->UnScale(hit_point);
|
|
raycast_hit_point_ = hit_point;
|
|
raycast_len_ = GlmHelper::Norm(hit_point - born_pos.ToGlmVec3());
|
|
} else {
|
|
raycast_len_ = gun_meta->range();
|
|
}
|
|
#ifdef MYDEBUG
|
|
float distance = 0.0f;
|
|
if (hit_result) {
|
|
glm::vec3 v1 = raycast_hit_point_;
|
|
glm::vec3 v2 = born_pos.ToGlmVec3();
|
|
v1.y = 0.0f;
|
|
v2.y = 0.0f;
|
|
distance = GlmHelper::Norm(v1 - v2);
|
|
}
|
|
a8::XPrintf("bullet.raycast ret:%d hit_result:%d raycast_hit_point_:%f,%f,%f speed:%f range:%f distance:%f\n",
|
|
{
|
|
ret,
|
|
hit_result,
|
|
raycast_hit_point_.x,
|
|
raycast_hit_point_.y,
|
|
raycast_hit_point_.z,
|
|
gun_meta->bullet_speed(),
|
|
gun_meta->range(),
|
|
distance
|
|
});
|
|
#endif
|
|
}
|
|
|
|
void Bullet::ProcRequestBulletDmg(int shield_hit, int strength_wall_uniid, int target_uniid, const glm::vec3& pos)
|
|
{
|
|
if (room->IsGameOver()) {
|
|
return;
|
|
}
|
|
if (room->OverBorder(GetPos().ToGlmVec3(), gun_meta->bullet_rad())) {
|
|
return;
|
|
}
|
|
if (!sender.Get()) {
|
|
return;
|
|
}
|
|
|
|
GetMutablePos().FromGlmVec3(pos);
|
|
room->grid_service->MoveBullet(this);
|
|
float distance = GlmHelper::Norm(GetPos().ToGlmVec3() - born_pos.ToGlmVec3());
|
|
BulletCheckResult result;
|
|
result.flyed_distance = distance;
|
|
if (result.flyed_distance > gun_meta->range() * 5) {
|
|
return;
|
|
}
|
|
|
|
if (trace_target_id) {
|
|
if (trace_target_id == target_uniid) {
|
|
Entity* entity = room->GetEntityByUniId(trace_target_id);
|
|
if (entity && entity->IsCreature(room)) {
|
|
Creature* c = (Creature*)entity;
|
|
if (sender.Get() && sender.Get()->IsHuman()) {
|
|
sender.Get()->AsHuman()->stats->IncWeaponUseTimes(gun_meta->id(), 1);
|
|
}
|
|
if (c->HasBuffEffect(kBET_HoldShield)) {
|
|
c->CheckBulletHitHoldShield(this, result.eat);
|
|
if (result.eat) {
|
|
}
|
|
}
|
|
if (!result.eat) {
|
|
std::set<Entity*> objects;
|
|
objects.insert(entity);
|
|
OnHit(objects);
|
|
AddGunBuff();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
GetHitThings(result);
|
|
if (result.o_hit_num <= 0) {
|
|
GetHitCreatures(result);
|
|
}
|
|
if (IsBomb()) {
|
|
ProcBomb();
|
|
} else {
|
|
ProcNormalBullet(result);
|
|
}
|
|
}
|
|
}
|
|
|
|
const mt::Skill* Bullet::GetSkillMeta()
|
|
{
|
|
if (sender.Get() && skill_meta) {
|
|
return sender.Get()->GetAdjustSkillMeta(skill_meta);
|
|
}
|
|
return skill_meta;
|
|
}
|
|
|
|
bool Bullet::NoAdjustPos()
|
|
{
|
|
switch (meta->_inventory_slot()) {
|
|
case IS_RPG:
|
|
{
|
|
if (reporter_list) {
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Bullet::ReportHookHitPos(int hit_obj_uniid, const glm::vec3& hit_pos)
|
|
{
|
|
if (later_removed_) {
|
|
return;
|
|
}
|
|
if (sender.Get()) {
|
|
//float distance = GetPos().Distance2D2(hit_pos) + 3;
|
|
float distance = gun_meta->range() + 1;
|
|
GetMutablePos().FromGlmVec3(hit_pos);
|
|
room->grid_service->MoveBullet(this);
|
|
BulletCheckResult result;
|
|
result.flyed_distance = distance;
|
|
Creature* hit_obj = room->GetCreatureByUniId(hit_obj_uniid);
|
|
//GetHitCreatures(result);
|
|
if (hit_obj) {
|
|
++result.c_hit_num;
|
|
result.objects.insert(hit_obj);
|
|
}
|
|
ProcNormalBullet(result);
|
|
}
|
|
}
|
|
|
|
bool Bullet::IsClientHook()
|
|
{
|
|
return true;
|
|
}
|