530 lines
15 KiB
C++
530 lines
15 KiB
C++
#include "precompile.h"
|
|
|
|
#include <f8/udplog.h>
|
|
|
|
#include "hero.h"
|
|
#include "human.h"
|
|
#include "room.h"
|
|
#include "loot.h"
|
|
#include "perfmonitor.h"
|
|
#include "typeconvert.h"
|
|
#include "obstacle.h"
|
|
#include "bullet.h"
|
|
#include "explosion.h"
|
|
#include "roomobstacle.h"
|
|
#include "app.h"
|
|
#include "pbutils.h"
|
|
#include "trigger.h"
|
|
#include "netdata.h"
|
|
#include "stats.h"
|
|
#include "hero_agent.h"
|
|
#include "movement.h"
|
|
#include "ability.h"
|
|
#include "skill.h"
|
|
|
|
#include "f8/btmgr.h"
|
|
|
|
#include "mt/Hero.h"
|
|
#include "mt/Equip.h"
|
|
#include "mt/MapThing.h"
|
|
#include "mt/BattleBasicAttribute.h"
|
|
#include "mt/Map.h"
|
|
#include "mt/MobaRoom.h"
|
|
|
|
Hero::Hero():Creature()
|
|
{
|
|
++PerfMonitor::Instance()->entity_num[ET_Hero];
|
|
INIT_LIST_HEAD(&entry);
|
|
agent_ = behaviac::Agent::Create<HeroAgent>();
|
|
if (!agent_) {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
Hero::~Hero()
|
|
{
|
|
SetDestorying();
|
|
DetachFromMaster();
|
|
if (agent_) {
|
|
f8::BtMgr::Instance()->BtDestory(agent_);
|
|
agent_ = nullptr;
|
|
}
|
|
--PerfMonitor::Instance()->entity_num[ET_Hero];
|
|
}
|
|
|
|
void Hero::Initialize()
|
|
{
|
|
Creature::Initialize();
|
|
const mt::Equip* weapon_meta = mt::Equip::GetById(meta->default_weapon());
|
|
if (weapon_meta) {
|
|
Weapon& weapon = weapons[GUN_SLOT1];
|
|
weapon.weapon_idx = GUN_SLOT1;
|
|
weapon.weapon_id = weapon_meta->id();
|
|
weapon.ammo = 10000;
|
|
weapon.meta = weapon_meta;
|
|
weapon.Recalc();
|
|
SetCurrWeapon(&weapon);
|
|
}
|
|
SetInfiniteBulletMode();
|
|
InitAI();
|
|
{
|
|
auto context = std::make_shared<BattleDataContext>();
|
|
SetNetData(context);
|
|
context->ForceInit
|
|
(
|
|
App::Instance()->AllocTempHeroUniId(),
|
|
meta,
|
|
GetCurrWeapon() ? App::Instance()->AllocTempWeaponUniId() : 0,
|
|
GetCurrWeapon() ? GetCurrWeapon()->meta : 0,
|
|
0,
|
|
nullptr
|
|
);
|
|
GetNetData()->Init(this);
|
|
}
|
|
SetHP(GetNetData()->GetMaxHP());
|
|
SetMaxHP(GetHP());
|
|
{
|
|
std::vector<int> skill_list;
|
|
GetNetData()->GetSkillList(skill_list);
|
|
for (auto& skill_id : skill_list) {
|
|
AddSkill(skill_id);
|
|
}
|
|
}
|
|
if (GetHeroMeta()->GetBasicMeta()) {
|
|
SetHeroLevel(1, 0, GetHeroMeta()->GetBasicMeta()->GetGrowMeta(room->GetMapMeta()->map_id()));
|
|
} else {
|
|
SetHeroLevel(1, 0, nullptr);
|
|
}
|
|
}
|
|
|
|
void Hero::Update(int delta_time)
|
|
{
|
|
++updated_times_;
|
|
if (poisoning) {
|
|
poisoning_time += delta_time;
|
|
UpdatePoisoning();
|
|
}
|
|
if (GetMovement()->GetPathSize() > 0) {
|
|
#ifdef MYDEBUG1
|
|
glm::vec3 old_pos = GetPos().ToGlmVec3();
|
|
#endif
|
|
App::Instance()->verify_set_pos = 1;
|
|
UpdateMove();
|
|
#ifdef MYDEBUG1
|
|
a8::XPrintf("updatemove old_pos:%f,%f new_pos:%f,%f\n",
|
|
{
|
|
old_pos.x,
|
|
old_pos.z,
|
|
GetPos().GetX(),
|
|
GetPos().GetZ(),
|
|
});
|
|
#endif
|
|
App::Instance()->verify_set_pos = 0;
|
|
} else {
|
|
if (HasBuffEffect(kBET_Jump)) {
|
|
glm::vec3 dir = GlmHelper::UP;
|
|
GlmHelper::RotateY(dir, (10 + rand() % 360)/ 180.0f);
|
|
GlmHelper::Normalize(dir);
|
|
GetMovement()->CalcTargetPos(60 + rand() % 200);
|
|
return;
|
|
}
|
|
}
|
|
if (playing_skill) {
|
|
UpdateSkill();
|
|
}
|
|
shot_hold = false;
|
|
CheckShotHold();
|
|
agent_->Exec();
|
|
}
|
|
|
|
void Hero::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();
|
|
}
|
|
|
|
RemoveBuffByEffectId(kBET_PeaceMode);
|
|
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 Hero::OnBulletHit(IBullet* bullet)
|
|
{
|
|
if (IsInvincible()) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Jump) ||
|
|
HasBuffEffect(kBET_Fly)) {
|
|
return;
|
|
}
|
|
|
|
RemoveBuffByEffectId(kBET_PeaceMode);
|
|
GetTrigger()->Attacked(bullet->GetSender().Get());
|
|
if (!IsDead(room) && (bullet->IsBomb() || bullet->GetSender().Get()->team_id != team_id)) {
|
|
float finaly_dmg = bullet->GetSender().Get()->GetNetData()->CalcDmg(this, bullet);
|
|
if (bullet->GetSender().Get()->IsHuman()) {
|
|
bullet->GetSender().Get()->AsHuman()->stats->damage_amount_out += finaly_dmg;
|
|
SetLastAttacker(bullet->GetSender());
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
float Hero::GetSpeed()
|
|
{
|
|
return Creature::GetSpeed();
|
|
}
|
|
|
|
void Hero::UpdateMove()
|
|
{
|
|
Creature::UpdateMove();
|
|
}
|
|
|
|
float Hero::GetRadius()
|
|
{
|
|
return meta->radius();
|
|
}
|
|
|
|
float Hero::GetHitRadius()
|
|
{
|
|
return meta->hit_radius();
|
|
}
|
|
|
|
void Hero::DetachFromMaster()
|
|
{
|
|
if (!detached_) {
|
|
if (master.Get()) {
|
|
master.Get()->SlaveOnRemove(this);
|
|
}
|
|
detached_ = true;
|
|
}
|
|
if (!list_empty(&entry)) {
|
|
list_del_init(&entry);
|
|
}
|
|
}
|
|
|
|
void Hero::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 (!room->BattleStarted()) {
|
|
return;
|
|
}
|
|
if (dec_hp < 0.001f) {
|
|
return;
|
|
}
|
|
if (HasBuffEffect(kBET_Invincible)) {
|
|
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 (new_health < old_health && room->IsPveRoom()) {
|
|
room->pve_data.AddDamageInfo(killer_id, GetUniId(), old_health - new_health);
|
|
}
|
|
if (GetHP() <= 0.0001f && !IsDead(room)) {
|
|
BeKill(killer_id, killer_name, weapon_id);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
room->frame_event.AddHpChg(GetWeakPtrRef());
|
|
}
|
|
|
|
void Hero::BeKill(int killer_id, const std::string& killer_name, int weapon_id)
|
|
{
|
|
std::vector<int> items;
|
|
dead = true;
|
|
if (meta->HasDrop() && !room->IsPveRoom()) {
|
|
room->ScatterDrop(GetPos().ToGlmVec3(), meta->RandDrop(), false, &items);
|
|
}
|
|
if (!room->IsPveRoom()) {
|
|
for (int id : meta->_drop_list) {
|
|
room->ScatterDrop(GetPos().ToGlmVec3(), id, false, &items);
|
|
}
|
|
}
|
|
room->frame_event.AddDead(GetWeakPtrRef(), 0);
|
|
room->xtimer.SetTimeoutEx
|
|
(
|
|
meta->delay_delete() / FRAME_RATE_MS,
|
|
[this] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
BroadcastDeleteState(room);
|
|
RemoveFromAroundPlayers(room);
|
|
room->grid_service->RemoveCreature(this);
|
|
room->RemoveObjectLater(this);
|
|
delete_frameno = room->GetFrameNo();
|
|
std::vector<Human*> watch_list;
|
|
room->GetPartObjectWatchList(this, watch_list);
|
|
if (!watch_list.empty()) {
|
|
A8_ABORT();
|
|
}
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
if (room->IsPveRoom()) {
|
|
--room->pve_data.mon_num;
|
|
++room->pve_data.killed_num;
|
|
room->pve_data.OnBeKill(this);
|
|
room->NotifyUiUpdate();
|
|
}
|
|
AllocDeadExp(killer_id);
|
|
GetTrigger()->Die(killer_id, weapon_id);
|
|
if (room->IsMobaModeRoom() && !room->IsGameOver()) {
|
|
{
|
|
int revive_time = room->GetMapMeta()->GetMobaRoomMeta()->GetMonsterReviveTime(GetHeroMeta()->id());
|
|
if (revive_time >= 0) {
|
|
room->xtimer.SetTimeoutEx
|
|
(revive_time * SERVER_FRAME_RATE,
|
|
[room = room, hero_meta = GetHeroMeta(), pos = src_pos] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
Hero* hero = room->CreateHero
|
|
(nullptr,
|
|
hero_meta,
|
|
pos,
|
|
GlmHelper::UP,
|
|
666,
|
|
0);
|
|
}
|
|
},
|
|
&room->xtimer_attacher_);
|
|
}
|
|
}
|
|
{
|
|
int life_time = room->GetMapMeta()->GetMobaRoomMeta()->GetMonsterLootLifeTime(GetHeroMeta()->id());
|
|
if (life_time >= 0) {
|
|
room->xtimer.SetTimeoutEx
|
|
(life_time * SERVER_FRAME_RATE,
|
|
[room = room, items] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
for (int obj_uniid : items) {
|
|
Entity* e = room->GetEntityByUniId(obj_uniid);
|
|
if (e && e->IsLoot()) {
|
|
Loot* l = e->AsLoot();
|
|
if (!l->pickuped && !l->removing) {
|
|
room->RemoveObjectLater(l);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
&room->xtimer_attacher_);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Hero::OnAddToTargetPartObject(Entity* target)
|
|
{
|
|
if (delete_frameno > 0) {
|
|
#if 1
|
|
f8::UdpLog::Instance()->Warning
|
|
("Hero::OnAddToTargetPartObject "
|
|
"delete_frameno:%d "
|
|
"room.frameno:%d",
|
|
{
|
|
delete_frameno,
|
|
room->GetFrameNo()
|
|
});
|
|
#else
|
|
A8_ABORT();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Hero::OnRemoveFromTargetPartObject(Entity* target)
|
|
{
|
|
|
|
}
|
|
|
|
void Hero::InitAI()
|
|
{
|
|
agent_->SetOwner(this);
|
|
f8::BtMgr::Instance()->BtLoad(agent_, meta->new_bt().c_str());
|
|
f8::BtMgr::Instance()->BtSetCurrent(agent_, meta->new_bt().c_str());
|
|
}
|
|
|
|
void Hero::OnBattleStart(Room* room)
|
|
{
|
|
if (master.Get()) {
|
|
Destory();
|
|
}
|
|
}
|
|
|
|
void Hero::Destory()
|
|
{
|
|
#if MYDEBUG
|
|
room->BroadcastDebugMsg(a8::Format("hero destory uniid:%d pos:%d,%d",
|
|
{
|
|
GetUniId(),
|
|
GetPos().GetX(),
|
|
GetPos().GetY()
|
|
}));
|
|
#endif
|
|
room->xtimer.SetTimeoutEx
|
|
(
|
|
0,
|
|
[this] (int event, const a8::Args* args)
|
|
{
|
|
if (a8::TIMER_EXEC_EVENT == event) {
|
|
if (delete_frameno == 0) {
|
|
BroadcastDeleteState(room);
|
|
RemoveFromAroundPlayers(room);
|
|
room->grid_service->RemoveCreature(this);
|
|
room->RemoveObjectLater(this);
|
|
delete_frameno = room->GetFrameNo();
|
|
std::vector<Human*> watch_list;
|
|
room->GetPartObjectWatchList(this, watch_list);
|
|
if (!watch_list.empty()) {
|
|
A8_ABORT();
|
|
}
|
|
}
|
|
}
|
|
},
|
|
&xtimer_attacher);
|
|
}
|
|
|
|
std::string Hero::GetName()
|
|
{
|
|
return meta->name();
|
|
}
|
|
|
|
void Hero::DropItems(Obstacle* obstacle)
|
|
{
|
|
bool is_treasure_box = false;
|
|
std::vector<int> drops = obstacle->meta->RandDrop();
|
|
for (int drop_id : drops) {
|
|
room->ScatterDrop(obstacle->GetPos().ToGlmVec3(), drop_id);
|
|
}
|
|
}
|
|
|
|
void Hero::Active()
|
|
{
|
|
|
|
}
|
|
|
|
void Hero::Deactive()
|
|
{
|
|
|
|
}
|
|
|
|
void Hero::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
|
|
}
|
|
}
|