aozhiwei 6b470c5fd3 1
2024-01-12 15:25:54 +08:00

691 lines
19 KiB
C++

#include "precompile.h"
#include <math.h>
#include "car.h"
#include "human.h"
#include "room.h"
#include "loot.h"
#include "perfmonitor.h"
#include "typeconvert.h"
#include "bullet.h"
#include "explosion.h"
#include "obstacle.h"
#include "ability.h"
#include "entityfactory.h"
#include "app.h"
#include "glmhelper.h"
#include "pbutils.h"
#include "trigger.h"
#include "ability.h"
#include "netdata.h"
#include "buff.h"
#include "stats.h"
#include "hero.h"
#include "skill.h"
#include "movement.h"
#include "mt/Hero.h"
#include "mt/Equip.h"
#include "mt/Buff.h"
#include "mt/MapThing.h"
Car::Car():Creature()
{
++PerfMonitor::Instance()->entity_num[ET_Car];
}
Car::~Car()
{
--PerfMonitor::Instance()->entity_num[ET_Car];
}
void Car::Initialize()
{
Creature::Initialize();
hero_meta_ = mt::Hero::GetById(meta->heroid());
if (!hero_meta_) {
A8_ABORT();
}
const mt::Equip* weapon_meta = mt::Equip::GetById(hero_meta_->default_weapon());
if (weapon_meta) {
Weapon& weapon = weapons[GUN_SLOT1];
weapon.weapon_idx = GUN_SLOT1;
weapon.weapon_id = weapon_meta->id();
weapon.meta = weapon_meta;
weapon.Recalc();
#if 0
weapon.ammo = weapon.GetClipVolume(this);
#endif
SetCurrWeapon(&weapon);
}
born_frameno_ = room->GetFrameNo();
TryAddBuff(this, meta->_car_deactive_buff_id);
cur_oil_ = meta->max_oil();
{
auto context = std::make_shared<BattleDataContext>();
SetNetData(context);
context->ForceInit
(
App::Instance()->AllocTempHeroUniId(),
hero_meta_,
GetCurrWeapon() ? App::Instance()->AllocTempWeaponUniId() : 0,
GetCurrWeapon() ? GetCurrWeapon()->meta : 0,
0,
nullptr
);
GetNetData()->Init(this);
if (weapon_meta) {
GetCurrWeapon()->ammo = GetCurrWeapon()->GetClipVolume(this);
}
}
SetHP(GetNetData()->GetMaxHP());
SetMaxHP(std::max(GetHP(), GetMaxHP()));
{
std::vector<int> skill_list;
GetNetData()->GetSkillList(skill_list);
for (auto& skill_id : skill_list) {
AddSkill(skill_id);
}
}
}
Human* Car::GetPassengerBySeat(int seat)
{
for (auto hum : passengers_) {
if (hum->GetSeat() == seat) {
return hum;
}
}
return nullptr;
}
void Car::GetDown(Human* passenger)
{
if (later_removed_) {
return;
}
if (passengers_.find(passenger) == passengers_.end()) {
return;
}
if (driver_ == passenger) {
driver_ = nullptr;
}
passengers_.erase(passenger);
passenger->SetCar(nullptr);
passenger->SetSeat(0);
passenger->second_weapon = Weapon();
passenger->CancelAction();
passenger->RemoveBuffByEffectId(kBET_Driver);
passenger->RemoveBuffByEffectId(kBET_Passenger);
App::Instance()->verify_set_pos = 1;
passenger->SetPos(passenger->GetPos());
App::Instance()->verify_set_pos = 0;
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
if (passengers_.empty()) {
team_id = 0;
room->TakeOffCarObject(GetUniId(), GetPos().ToGlmVec3());
RemoveBuffByEffectId(kBET_CarActive);
TryAddBuff(this, meta->_car_deactive_buff_id);
if (meta->buffid() != 0) {
passenger->RemoveBuffById(meta->buffid());
}
}
SyncAroundPlayers(__FILE__, __LINE__, __func__);
room->NotifyUiUpdate();
}
void Car::GetOn(Human* passenger)
{
if (later_removed_) {
return;
}
if (!CanOn(passenger)) {
return;
}
if (meta->_int_param2 <= 0) {
A8_ABORT();
}
if (meta->_int_param2 <= passengers_.size()) {
return;
}
if (passengers_.find(passenger) != passengers_.end()) {
return;
}
if (passenger->GetCar()) {
return;
}
for (auto hum : passengers_) {
if (hum->team_id != passenger->team_id) {
return;
}
}
if (passengers_.empty()) {
team_id = passenger->team_id;
}
int seat = AllocSeat();
if (seat < 0) {
return;
}
passengers_.insert(passenger);
if (!driver_) {
driver_ = passenger;
driver_->SetAttackDir(GlmHelper::RIGHT);
SetAttackDir(driver_->GetAttackDir());
room->TakeOnCarObject(GetUniId());
}
passenger->SetCar(this);
passenger->SetSeat(seat);
App::Instance()->verify_set_pos = 1;
passenger->SetPos(GetPos());
App::Instance()->verify_set_pos = 0;
{
const mt::Buff* buff_meta = mt::Buff::GetById
(driver_ == passenger ? DRIVER_BUFFID : PASSENGER_BUFFID);
if (buff_meta) {
passenger->AddBuff(passenger, buff_meta);
}
}
if (meta->buffid() != 0) {
passenger->TryAddBuff(passenger, meta->buffid());
}
passenger->CancelAction();
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
if (passengers_.size() == 1) {
RemoveBuffByEffectId(kBET_CarDeactive);
TryAddBuff(this, meta->_car_active_buff_id);
}
SyncAroundPlayers(__FILE__, __LINE__, __func__);
room->NotifyUiUpdate();
}
void Car::SwitchSeat(Human* passenger, int seat)
{
if (later_removed_) {
return;
}
if (seat < 0 || seat >= meta->_int_param2) {
return;
}
if (!IsPassenger(passenger)) {
return;
}
if (GetPassengerBySeat(seat)) {
return;
}
if (seat == 0) {
passenger->RemoveBuffByEffectId(kBET_Passenger);
passenger->SetSeat(seat);
driver_ = passenger;
passenger->MustBeAddBuff(passenger, DRIVER_BUFFID);
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
SyncAroundPlayers(__FILE__, __LINE__, __func__);
} else {
passenger->SetSeat(seat);
if (driver_ == passenger) {
driver_ = nullptr;
passenger->RemoveBuffByEffectId(kBET_Driver);
passenger->MustBeAddBuff(passenger, PASSENGER_BUFFID);
} else {
Buff* buff = passenger->GetBuffByEffectId(kBET_Passenger);
buff->CalcPassengerShotOffset();
passenger->MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
}
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
}
bool Car::CanShot(Human* passenger)
{
if (!IsPassenger(passenger)) {
return false;
}
return passenger->GetSeat() < meta->_shoot_offsets.size() &&
std::get<0>(meta->_shoot_offsets[passenger->GetSeat()]);
}
int Car::AllocSeat()
{
int seat = -1;
for (int i = 0; i < meta->_int_param2; ++i) {
bool found = false;
for (auto hum : passengers_) {
if (hum->GetSeat() == i) {
found = true;
break;
}
}
if (!found) {
seat = i;
break;
}
}
return seat;
}
void Car::SyncPos()
{
if (driver_) {
App::Instance()->verify_set_pos = 1;
SetPos(driver_->GetPos());
SetMoveDir(driver_->GetMoveDir());
for (auto hum : passengers_) {
if (hum != driver_) {
hum->SetPos(GetPos());
hum->SetMoveDir(GetMoveDir());
room->grid_service->MoveCreature(hum);
}
}
App::Instance()->verify_set_pos = 0;
room->grid_service->MoveCreature(this);
}
}
float Car::GetRadius()
{
return hero_meta_->radius();
}
float Car::GetSpeed()
{
return Creature::GetSpeed();
}
void Car::OnBulletHit(IBullet* bullet)
{
if (!IsDead(room)) {
//超能电磁枪已删除
float finaly_dmg = bullet->GetSender().Get()->GetNetData()->CalcDmg(this, bullet);
if (bullet->GetBulletMeta()->_buff_meta) {
MustBeAddBuff(bullet->GetSender().Get(), bullet->GetBulletMeta()->buffid());
}
if (!bullet->IsPreBattleBullet()) {
float dmg_out = 0.0f;
DecHP(finaly_dmg,
bullet->GetSender().Get()->GetUniId(),
bullet->GetSender().Get()->GetName(),
bullet->GetGunMeta()->id(),
bullet->GetSender().Get()->GetUniId(),
bullet->GetSender().Get()->GetName(),
dmg_out,
0,
0);
if (bullet->GetSender().Get() &&
!bullet->GetSender().Get()->dead &&
dmg_out > 0.0f &&
!bullet->GetSkillMeta()
) {
{
if (bullet->GetSender().Get()->GetNetData()->GetBrainLifePct() > 0.0f) {
float recover_hp = dmg_out *
bullet->GetSender().Get()->GetNetData()->GetBrainLifePct();
if (recover_hp > 0.0f) {
bullet->GetSender().Get()->AddHp(recover_hp);
}
}
}
{
if (bullet->GetSender().Get()->IsEntityType(ET_Hero)) {
Hero* hero = (Hero*)bullet->GetSender().Get();
if (hero->GetAbility()->GetSwitchTimes(kEnableDmgForwardTimes) > 0 &&
hero->master.Get() &&
!hero->master.Get()->dead &&
hero->master.Get()->GetNetData()->GetBrainLifePct() > 0.0f){
float recover_hp = dmg_out *
hero->master.Get()->GetNetData()->GetBrainLifePct();
if (recover_hp > 0.0f) {
hero->master.Get()->AddHp(recover_hp);
}
}
}
}
}
}
if (bullet->GetBulletMeta()->_buff_meta) {
MustBeAddBuff(this, bullet->GetBulletMeta()->buffid());
}
}
}
void Car::OnExplosionHit(Explosion* e)
{
if (IsInvincible()) {
return;
}
if (dead) {
return;
}
if (e->IsPreBattleExplosion()) {
return;
}
if (HasBuffEffect(kBET_Jump) ||
HasBuffEffect(kBET_Fly)) {
return;
}
int real_killer_id = 0;
std::string real_killer_name;
if (e->GetSender().Get()) {
real_killer_id = e->GetSender().Get()->GetUniId();
real_killer_name = e->GetSender().Get()->GetName();
}
float dmg_out = 0.0f;
float finaly_dmg = GetNetData()->CalcDmg(e);
DecHP(finaly_dmg,
VP_Explosion,
"",
e->GetExplosionEffect(),
real_killer_id,
real_killer_name,
dmg_out,
0,
0);
}
void Car::DecHP(float dec_hp, int killer_id, const std::string killer_name, int weapon_id,
int real_killer_id, const std::string real_killer_name,
float& real_dmg_out, int dmg_type, int dmg_bp)
{
real_dmg_out = 0.0f;
if (dec_hp < 0.001f) {
return;
}
last_receive_dmg_frameno = room->GetFrameNo();
float old_health = GetHP();
float new_health = std::max(0.0f, GetHP() - dec_hp);
SetHP(std::max(0.0f, new_health));
if (old_health - GetHP() > 0.001f) {
real_dmg_out = old_health - GetHP();
}
if (GetHP() <= 0.0001f && !IsDead(room)) {
BeKill(killer_id, killer_name, weapon_id);
}
room->frame_event.AddHpChg(GetWeakPtrRef());
float hp_rate = GetHP() / GetMaxHP();
int new_buff_idx = cur_buff_idx_;
while (new_buff_idx + 1 < meta->_car_buff_list.size()) {
if (hp_rate > std::get<0>(meta->_car_buff_list[new_buff_idx + 1])) {
break;
}
++new_buff_idx;
}
if (new_buff_idx != cur_buff_idx_) {
if (cur_buff_id_ != 0) {
RemoveBuffById(cur_buff_id_);
}
cur_buff_idx_ = new_buff_idx;
cur_buff_id_ = std::get<1>(meta->_car_buff_list[cur_buff_idx_]);
if (cur_buff_id_ != 0) {
MustBeAddBuff(this, cur_buff_id_);
#ifdef MYDEBUG
#endif
}
}
Creature* killer = room->GetCreatureByUniId(real_killer_id);
if (killer && real_dmg_out > 0.999) {
if (killer->IsPlayer()) {
room->frame_event.AddPropChgEx
(
killer->GetWeakPtrRef(),
kPropDmgShow,
GetUniId(),
real_dmg_out,
0,
killer->GetUniId(),
true);
} else if (killer->IsHero() &&
killer->AsHero()->master.Get() &&
killer->AsHero()->master.Get()->IsPlayer()) {
room->frame_event.AddPropChgEx
(
killer->AsHero()->master.Get()->GetWeakPtrRef(),
kPropDmgShow,
GetUniId(),
real_dmg_out,
0,
killer->AsHero()->master.Get()->GetUniId(),
true);
}
}
}
void Car::BeKill(int killer_id, const std::string& killer_name, int weapon_id)
{
Human* killer = room->GetHumanByUniId(killer_id);
if (killer) {
++killer->stats->destory_car_times;
}
dead = true;
later_removed_ = true;
if (hero_meta_->delay_remove() > 0) {
room->xtimer.SetTimeoutEx
(
hero_meta_->delay_remove() / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
RemoveFromScene();
}
},
&xtimer_attacher);
} else {
RemoveFromScene();
}
int team_id = 0;
for (Human* passenger : passengers_) {
team_id = passenger->team_id;
passenger->SetCar(nullptr);
passenger->SetSeat(0);
passenger->second_weapon = Weapon();
passenger->CancelAction();
passenger->RemoveBuffByEffectId(kBET_Driver);
passenger->RemoveBuffByEffectId(kBET_Passenger);
room->frame_event.AddCarChg(passenger->GetWeakPtrRef());
}
std::shared_ptr<Explosion> explosion = EntityFactory::Instance()->MakeExplosion();
explosion->SetDamageDelay(meta->explosion_damage_delay());
explosion->IndifferenceAttack(
room,
GetPos(),
meta->explosion_range(),
meta->explosion_effect(),
meta->_atk
);
room->NotifyUiUpdate();
room->frame_event.AddDead(GetWeakPtrRef(), 0);
GetTrigger()->Die(killer_id, weapon_id);
}
std::string Car::GetName()
{
return hero_meta_->name();
}
void Car::SendDebugMsg(const std::string& debug_msg)
{
#if 1
room->BroadcastDebugMsg("载具debugmsg: " + debug_msg);
#else
for (auto& passenger : passengers_) {
passenger->SendDebugMsg("载具debugmsg: " + debug_msg);
}
#endif
}
bool Car::IsPassenger(Human* hum)
{
return passengers_.find(hum) != passengers_.end();
}
void Car::SetAttackDir(const glm::vec3& attack_dir)
{
Creature::SetAttackDir(attack_dir);
}
void Car::DecOil(float dec_oil)
{
dec_oil *= 1 + GetAbility()->GetAttr(kHVAT_CarOil);
cur_oil_ -= dec_oil;
cur_oil_ = std::max(0.0f, cur_oil_);
if (!HasOil()) {
if (driver_) {
driver_->SetAttackDir(GlmHelper::RIGHT);
}
SetAttackDir(GlmHelper::RIGHT);
}
}
float Car::GetMaxOil()
{
return meta->max_oil();
}
void Car::DropItems(Obstacle* obstacle)
{
if (obstacle->meta->HasDrop()) {
std::vector<int> drops = obstacle->meta->RandDrop();
for (int drop_id : drops) {
room->ScatterDrop(obstacle->GetPos().ToGlmVec3(), drop_id);
}
}
}
bool Car::NeedCreatureCollision()
{
return false;
}
void Car::CheckCreatureCollision()
{
}
void Car::RemoveFromScene()
{
room->TakeOnCarObject(GetUniId());
BroadcastDeleteState(room);
RemoveFromAroundPlayers(room);
room->grid_service->RemoveCreature(this);
room->RemoveObjectLater(this);
room->NotifyUiUpdate();
}
void Car::OnKillTarget(Creature* target)
{
if (target->IsHuman()) {
for (auto passenger : passengers_) {
++passenger->stats->ride_car_kills;
}
if (driver_) {
++driver_->stats->ride_car_kills;
}
}
}
bool Car::IsSingle()
{
return meta->_int_param2 <= 1;
}
void Car::DoSkillPostProc(bool used, int skill_id, int target_id)
{
if (used) {
#if 0
++stats->skill_times;
#endif
Skill* skill = GetSkill(skill_id);
if (skill) {
skill->DecTimes();
room->frame_event.AddSkillCdChg(AllocWeakPtr(), skill_id, skill->GetLeftTime());
room->frame_event.AddSkillCurrTimesChg(AllocWeakPtr(), skill_id, skill->GetCurrTimes());
}
#if 0
OnAttack();
#endif
}
}
void Car::Update(int delta_time)
{
++updated_times_;
if (poisoning) {
poisoning_time += delta_time;
UpdatePoisoning();
}
if (GetMovement()->GetPathSize() > 0 ||
HasBuffEffect(kBET_Sprint)) {
App::Instance()->verify_set_pos = 1;
UpdateMove();
App::Instance()->verify_set_pos = 0;
if (GetDriver()) {
App::Instance()->verify_set_pos = 1;
GetDriver()->SetPos(GetPos());
App::Instance()->verify_set_pos = 0;
}
SyncPos();
#ifdef MYDEBUG1
a8::XPrintf("updatemove old_pos:%f,%f new_pos:%f,%f\n",
{
old_pos.x,
old_pos.y,
GetPos().x,
GetPos().y,
});
#endif
}
if (playing_skill) {
UpdateSkill();
}
}
bool Car::CanOn(Human* hum)
{
return special_operators_.empty() ||
special_operators_.find(hum->GetUniId()) != special_operators_.end();
}
void Car::SetSpecialOperators(std::set<int>& special_operators)
{
special_operators_ = special_operators;
}
bool Car::CanGetOn(Human* passenger)
{
if (later_removed_) {
return false;
}
if (driver_) {
return false;
}
if (passenger->HasBuffEffect(kBET_Floating)) {
return false;
}
if (!CanOn(passenger)) {
return false;
}
if (meta->_int_param2 <= 0) {
A8_ABORT();
}
if (meta->_int_param2 <= passengers_.size()) {
return false;
}
if (passengers_.find(passenger) != passengers_.end()) {
return false;
}
if (passenger->GetCar()) {
return false;
}
for (auto hum : passengers_) {
if (hum->team_id != passenger->team_id) {
return false;
}
}
int seat = AllocSeat();
if (seat < 0) {
return false;
}
return true;
}