game2006/server/gameserver/creature.cc
aozhiwei c7f92ec486 1
2023-04-04 15:00:33 +08:00

3236 lines
94 KiB
C++

#include "precompile.h"
#include <float.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/vec2.hpp>
#include "creature.h"
#include "room.h"
#include "skill.h"
#include "human.h"
#include "hero.h"
#include "roomobstacle.h"
#include "trigger.h"
#include "car.h"
#include "bullet.h"
#include "skillhelper.h"
#include "shot.h"
#include "movement.h"
#include "trigger.h"
#include "ability.h"
#include "netdata.h"
#include "buff.h"
#include "mapinstance.h"
#include "collision.h"
#include "gungrasp.h"
#include "effect.h"
#include "stats.h"
#include "team.h"
#include "mt/Param.h"
#include "mt/Hero.h"
#include "mt/Equip.h"
#include "mt/Buff.h"
#include "mt/Skill.h"
#include "mt/SkillNumber.h"
#include "mt/GunTalentGrow.h"
#include "mt/SafeArea.h"
#include "mt/MapThing.h"
#include "mt/Text.h"
#include "f8/utils.h"
#include "buff/bufffactory.h"
Creature::Creature():MoveableEntity()
{
weak_ptr_chunk_.Set(this);
trigger_ = std::make_shared<Trigger>(this);
trigger_->Init();
ability_ = std::make_shared<Ability>(GetWeakPtrRef());
weapons.reserve(MAX_WEAPON_NUM);
for (size_t i = 0; i < MAX_WEAPON_NUM; ++i) {
auto& weapon = a8::FastAppend(weapons);
weapon.weapon_idx = i;
weapon.weapon_id = 0;
weapon.ammo = 0;
}
for (size_t i = 0; i < inventory_.size(); ++i) {
inventory_[i].slot = i;
inventory_[i].itemid = 0;
inventory_[i].num = 0;
}
inventory_[IS_1XSCOPE].num = 1;
movement_ = std::make_shared<Movement>(this);
gun_grasp_ = std::make_shared<GunGrasp>(this);
}
Creature::~Creature()
{
for (auto itr = slave_heros_.begin(); itr != slave_heros_.end(); ++itr) {
while (!list_empty(&itr->second)) {
Hero* hero = list_first_entry(&itr->second,
Hero,
entry);
list_del_init(&hero->entry);
}
}
slave_heros_.clear();
xtimer_attacher.ClearTimerList();
ClearBuffList();
skill_hash_.clear();
ClearPassiveSkill();
trigger_->UnInit();
room->grid_service->RemoveCreature(this);
}
bool Creature::HasBuffEffect(int buff_effect_id)
{
return GetBuffByEffectId(buff_effect_id) != nullptr;
}
Buff* Creature::GetBuffByEffectId(int effect_id)
{
if (!IsValidBuffEffect(effect_id)) {
return nullptr;
}
list_head* list = &buff_effect_[effect_id];
if (list_empty(list)) {
return nullptr;
} else {
Buff* buff = list_first_entry(list, Buff, effect_entry);
return buff;
}
}
Buff* Creature::GetBuffById(int buff_id)
{
for (auto buff : buff_list_) {
if (buff->meta->buff_id() == buff_id) {
return buff.get();
}
}
return nullptr;
}
Buff* Creature::GetBuffByUniId(int buff_uniid)
{
for (auto buff : buff_list_) {
if (buff->buff_uniid == buff_uniid) {
return buff.get();
}
}
return nullptr;
}
int Creature::GetBuffNum(int buff_id)
{
int num = 0;
for (auto buff : buff_list_) {
if (buff->meta->buff_id() == buff_id) {
++num;
}
}
return num;
}
int Creature::AddBuff(Creature* caster,
const mt::Buff* buff_meta,
const mt::Skill* buff_skill_meta,
bool no_check_immune,
std::shared_ptr<a8::Args> init_args,
std::shared_ptr<std::vector<float>> buff_vars)
{
int buff_id = buff_meta->buff_id();
if (buff_meta->_only_spec_race) {
if (!a8::HasBitFlag(buff_meta->_only_spec_race, GetEntityType())) {
return 0;
}
}
if (buff_meta->_exclude_spec_race) {
if (a8::HasBitFlag(buff_meta->_exclude_spec_race, GetEntityType())) {
return 0;
}
}
if (!buff_meta->_tags.empty() && GetAbility()->CanImmune(buff_meta->_tags)) {
return 0;
}
if (buff_meta->HasBuffInterval()) {
if (buff_interval_hash_.find(buff_meta->buff_id()) != buff_interval_hash_.end()) {
return 0;
}
}
if (buff_meta->coexist_num() > 0){
if (GetBuffNum(buff_meta->buff_id()) >= buff_meta->coexist_num()) {
return 0;
}
}
if (buff_meta->buff_effect() == kBET_CondAdd) {
if (buff_meta->_int_buff_param1 == 1) {
switch (buff_meta->_int_buff_param2) {
case 1:
{
if (!(GetBuffTag(buff_meta->_int_buff_param3) >
buff_meta->_int_buff_param4)) {
return 0;
}
}
break;
case 2:
{
if (!(GetBuffTag(buff_meta->_int_buff_param3) <
buff_meta->_int_buff_param4)) {
return 0;
}
}
break;
case 3:
{
if (!(GetBuffTag(buff_meta->_int_buff_param3) ==
buff_meta->_int_buff_param4)) {
return 0;
}
}
break;
case 4:
{
if (!(GetBuffTag(buff_meta->_int_buff_param3) !=
buff_meta->_int_buff_param4)) {
return 0;
}
}
break;
default:
{
return 0;
}
break;
}
} else if (buff_meta->_int_buff_param1 == 2) {
#if 0
if (!(buff_skill_meta && buff_skill_meta->)){
}
#endif
} else if (buff_meta->_int_buff_param1 == 3 && !dead) {
return 0;
}
}
if (buff_meta->no_immune()) {
no_check_immune = 1;
}
if (!no_check_immune && IsImmuneBuffEffect(buff_meta->buff_effect())) {
return 0;
}
if (buff_meta->depend_effect() != 0 && !HasBuffEffect(buff_meta->depend_effect())) {
return 0;
}
if (!buff_meta->EffectCanStack()) {
Buff* buff = GetBuffByEffectId(buff_meta->buff_effect());
if (buff) {
RemoveBuffById(buff->meta->buff_id());
}
}
++buff_uniid_;
int new_buff_uniid = buff_uniid_;
auto buff = std::shared_ptr<Buff>(BuffFactory::MakeBuff(buff_meta));
buff_list_.push_back(buff);
buff->buff_uniid = new_buff_uniid;
buff->SetCaster(caster);
buff->owner = this;
buff->meta = buff_meta;
buff->skill_meta = buff_skill_meta;
buff->add_frameno = room->GetFrameNo();
buff->xtimer_attacher.SetOwner(&room->xtimer);
buff->init_args = init_args;
buff->buff_vars = buff_vars;
buff->Init();
buff->PreProcess();
if (on_add_buff) {
on_add_buff(buff.get());
}
if (buff->meta->lock_move()) {
IncDisableMoveTimes();
GetMovement()->ClearPath();
}
if (buff->meta->lock_dir()) {
IncDisableAttackDirTimes();
}
if (buff->meta->lock_move_dir()) {
IncDisableMoveDirTimes();
}
if (buff->meta->disable_shot()) {
GetAbility()->IncSwitch(kDisableShotTimes);
}
if (buff->meta->disable_useskill()) {
GetAbility()->IncSwitch(kDisableUseSkillTimes);
}
list_add_tail(&buff->effect_entry, &buff_effect_[buff->meta->buff_effect()]);
if (buff->meta->depend_effect() != 0 &&
IsValidBuffEffect(buff->meta->depend_effect())) {
list_add_tail(&buff->depend_entry, &depend_effect_[buff->meta->depend_effect()]);
}
{
float dur_time = buff_meta->GetDurationTime(buff.get());
if (buff->meta->tenacity()) {
dur_time *= (1.0f - GetBattleContext()->GetTenacityPct());
}
buff->remover_timer = room->xtimer.SetTimeoutWpEx
(
dur_time * SERVER_FRAME_RATE,
[buff_wp = (std::weak_ptr<Buff>)buff] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
if (!buff_wp.expired()) {
{
Buff* buff = nullptr;
buff = buff_wp.lock().get();
buff->owner->RemoveBuffByUniId(buff->buff_uniid);
}
}
}
},
&buff->xtimer_attacher);
}
{
if (buff_meta->HasBuffInterval()) {
buff_interval_hash_[buff_meta->buff_id()] = room->GetFrameNo();
float buff_internval = buff->meta->GetBuffInterval(buff.get());
room->xtimer.SetTimeoutEx
(
buff_internval / FRAME_RATE_MS,
[this, buff_meta] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
buff_interval_hash_.erase(buff_meta->buff_id());
}
},
&xtimer_attacher);
}
}
GetTrigger()->ActiveBuff(buff->meta);
#ifdef DEBUG
if (!f8::IsTestEnv()) {
if (!GetBuffByUniId(new_buff_uniid)) {
abort();
}
}
#endif
buff->Activate();
#ifdef DEBUG
if (!f8::IsTestEnv()) {
if (!GetBuffByUniId(new_buff_uniid)) {
abort();
}
}
#endif
AddBuffPostProc(caster, buff.get());
#ifdef DEBUG
if (!f8::IsTestEnv()) {
if (!GetBuffByUniId(new_buff_uniid)) {
abort();
}
}
#endif
if (!buff->meta->only_server()) {
room->frame_event.AddBuff(GetWeakPtrRef(), buff.get());
}
#ifdef DEBUG1
{
SendDebugMsg(a8::Format("添加buff_id:%d effect:%d %s params:%d,%d,%d,%d,%d uniid:%d dur:%d",
{
buff_meta->buff_id(),
buff_meta->buff_effect(),
buff_meta->name(),
buff_meta->_int_buff_param1,
buff_meta->_int_buff_param2,
buff_meta->_int_buff_param3,
buff_meta->_int_buff_param4,
buff_meta->_int_buff_param5,
buff->buff_uniid,
room->xtimer.GetRemainTime(buff->remover_timer)
}));
a8::XPrintf("%s\n", {(a8::Format("添加buff_id:%d effect:%d %s params:%d,%d,%d,%d,%d uniid:%d dur:%d",
{
buff_meta->buff_id(),
buff_meta->buff_effect(),
buff_meta->name(),
buff_meta->_int_buff_param1,
buff_meta->_int_buff_param2,
buff_meta->_int_buff_param3,
buff_meta->_int_buff_param4,
buff_meta->_int_buff_param5,
buff->buff_uniid,
room->xtimer.GetRemainTime(buff->remover_timer)
}))});
}
#endif
if (!buff->meta->_child_buff_list.empty()) {
for (int child_buff_id : buff->meta->_child_buff_list) {
const mt::Buff* child_buff_meta = mt::Buff::GetById(child_buff_id);
if (child_buff_meta) {
AddBuff(caster, child_buff_meta, buff_skill_meta, true, init_args, buff_vars);
}
}
}
return new_buff_uniid;
}
bool Creature::IsImmuneBuffEffect(int buff_effect)
{
for (auto buff : buff_list_) {
if (buff->meta->IsImmuneBuffEffect(buff_effect)) {
return true;
}
}
return false;
}
int Creature::MustBeAddBuff(Creature* caster,
int buff_id,
std::shared_ptr<a8::Args> init_args,
std::shared_ptr<std::vector<float>> buff_vars)
{
const mt::Buff* buff_meta = mt::Buff::GetById(buff_id);
if (!buff_meta) {
A8_ABORT();
}
return AddBuff(caster, buff_meta, nullptr, false, init_args, buff_vars);
}
int Creature::TryAddBuff(Creature* caster,
int buff_id,
const mt::Skill* skill_meta,
std::shared_ptr<a8::Args> init_args,
std::shared_ptr<std::vector<float>> buff_vars)
{
const mt::Buff* buff_meta = mt::Buff::GetById(buff_id);
if (buff_meta) {
return AddBuff(caster, buff_meta, skill_meta, false, init_args, buff_vars);
}
return 0;
}
int Creature::TryAddBuffAndSetTime(Creature* caster,
int buff_id,
int time,
const mt::Skill* skill_meta,
std::shared_ptr<a8::Args> init_args,
std::shared_ptr<std::vector<float>> buff_vars)
{
int buff_uniid = TryAddBuff(caster, buff_id, skill_meta, init_args, buff_vars);
if (buff_uniid) {
Buff* buff = GetBuffByUniId(buff_uniid);
room->xtimer.ModifyTime
(buff->remover_timer,
time / FRAME_RATE_MS);
}
return buff_uniid;
}
a8::XTimerWp Creature::TryDelayAddBuff(Creature* caster,
int buff_id,
int time,
DelayAddBuffHandle* handle,
std::shared_ptr<a8::Args> init_args,
std::shared_ptr<std::vector<float>> buff_vars)
{
auto caster_wp = caster->GetWeakPtrRef();
return room->xtimer.SetTimeoutWpEx
(
time / FRAME_RATE_MS,
[this, caster_wp, buff_id, handle, init_args, buff_vars]
(int event, const a8::Args* args) mutable
{
if (a8::TIMER_EXEC_EVENT == event) {
if (caster_wp.Get() && !caster_wp.Get()->IsDead(caster_wp.Get()->room)) {
if (handle && handle->pre_add_cb) {
handle->pre_add_cb(this);
}
int buff_uniid = TryAddBuff(caster_wp.Get(), buff_id, nullptr, init_args, buff_vars);
if (handle && handle->post_add_cb) {
handle->post_add_cb(this, buff_uniid);
}
}
} else if (a8::TIMER_DELETE_EVENT == event) {
if (handle) {
delete handle;
}
}
},
&xtimer_attacher);
}
int Creature::TryAddBuffWithTarget(Creature* caster,
int buff_id,
std::shared_ptr<a8::Args> init_args,
std::shared_ptr<std::vector<float>> buff_vars)
{
const mt::Buff* buff_meta = mt::Buff::GetById(buff_id);
if (buff_meta) {
bool be_add = false;
switch (buff_meta->buff_target()) {
case kBuffTargetSelf:
{
be_add = caster == this;
}
break;
case kBuffTargetFriendly:
{
be_add = team_id == caster->team_id;
}
break;
case kBuffTargetEnemy:
{
be_add = team_id != caster->team_id;
}
break;
default:
{
}
break;
}
return TryAddBuff(caster, buff_id, nullptr, init_args, buff_vars);
}
return -1;
}
void Creature::RemoveBuffById(int buff_id)
{
int buff_uniid = 0;
for (auto buff : buff_list_) {
if (buff->meta->buff_id() == buff_id) {
buff_uniid = buff->buff_uniid;
break;
}
}
if (buff_uniid != 0) {
RemoveBuffByUniId(buff_uniid);
}
}
void Creature::RemoveBuffByUniId(int buff_uniid)
{
int buff_id = 0;
std::vector<std::tuple<const mt::Buff*, Creature*>> removed_buffs;
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
auto buff = *itr;
if (buff->buff_uniid == buff_uniid) {
buff_id = buff->meta->buff_id();
removed_buffs.push_back(std::make_tuple(buff->meta, buff->GetCaster().Get()));
OnBuffRemove(*buff.get());
buff->UnInit();
buff_list_.erase(itr);
#ifdef DEBUG1
if (buff.use_count() > 1) {
abort();
}
#endif
break;
}
}
for (auto& tuple1 : removed_buffs) {
const mt::Buff* buff_meta = std::get<0>(tuple1);
Creature* caster = std::get<1>(tuple1);
for (int child_buff_id : buff_meta->_child_buff_list) {
RemoveBuffById(child_buff_id);
}
if (!HasBuffEffect(buff_meta->buff_effect()) &&
!list_empty(&depend_effect_[buff_meta->buff_effect()])) {
struct list_head work_list;
list_replace_init(&depend_effect_[buff_meta->buff_effect()], &work_list);
while (!list_empty(&work_list)) {
Buff* buff = list_first_entry(&work_list, Buff, depend_entry);
RemoveBuffById(buff->meta->buff_id());
}
}
GetTrigger()->DeactiveBuff(buff_meta);
#ifdef DEBUG
{
SendDebugMsg(a8::Format("移除buff_id:%d uniid:%d",
{
buff_id,
buff_uniid
}));
}
#endif
for (const auto& tuple : buff_meta->_post_remove_action) {
switch (std::get<0>(tuple)) {
case kRemoveBuffByIdAction:
{
for (int buff_id :std::get<1>(tuple)) {
RemoveBuffById(buff_id);
}
}
break;
case kRemoveBuffByEffectAction:
{
for (int buff_effect :std::get<1>(tuple)) {
RemoveBuffByEffectId(buff_effect);
}
}
break;
case kAddBuffAction:
{
for (int buff_id :std::get<1>(tuple)) {
TryAddBuff(caster, buff_id);
}
}
break;
default:
break;
}
}
}
RecalcBuffAttr();
}
void Creature::ClearBuffById(int buff_id)
{
while (GetBuffById(buff_id)) {
RemoveBuffById(buff_id);
}
}
void Creature::SendDebugMsg(const std::string& debug_msg)
{
}
void Creature::AddBuffPostProc(Creature* caster, Buff* buff)
{
}
void Creature::RecalcBuffAttr()
{
CheckAbilityUsed();
bool need_refresh_hp = false;
for (auto& buff : buff_list_) {
}
for (auto& tuple : talent_list) {
}
if (need_refresh_hp) {
SetHP(GetMaxHP());
GetTrigger()->HpChg();
#if 1
{
//666
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
}
#endif
}
#if 0
//666
if (IsHuman()) {
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
}
#endif
}
void Creature::OnBuffRemove(Buff& buff)
{
if (buff.meta->lock_move()) {
DecDisableMoveTimes();
}
if (buff.meta->lock_dir()) {
DecDisableAttackDirTimes();
}
if (buff.meta->lock_move_dir()) {
DecDisableMoveDirTimes();
}
if (buff.meta->disable_shot()) {
GetAbility()->DecSwitch(kDisableShotTimes);
}
if (buff.meta->disable_useskill()) {
GetAbility()->DecSwitch(kDisableUseSkillTimes);
}
buff.Deactivate();
if (!buff.meta->only_server()) {
room->frame_event.RemoveBuff(GetWeakPtrRef(), &buff);
}
}
void Creature::RemoveBuffByEffectId(int buff_effect_id)
{
Buff* buff = GetBuffByEffectId(buff_effect_id);
while (buff) {
RemoveBuffById(buff->meta->buff_id());
buff = GetBuffByEffectId(buff_effect_id);
}
}
void Creature::ClearBuffList()
{
if (room->IsDestorying()) {
buff_list_.clear();
} else {
for (auto buff : buff_list_) {
list_del_init(&buff->effect_entry);
OnBuffRemove(*buff.get());
}
buff_list_.clear();
buff_effect_ = {};
RecalcBuffAttr();
}
}
void Creature::AddPassiveSkill(int skill_id)
{
const mt::Skill* skill_meta = mt::Skill::GetById(skill_id);
if (skill_meta && !GetPassiveSkill(skill_meta->skill_id())) {
auto skill = std::make_shared<Skill>();
skill->owner = this;
skill->meta = skill_meta;
skill->xtimer_attacher.SetOwner(&room->xtimer);
room->xtimer.SetIntervalEx
(
SERVER_FRAME_RATE * skill_meta->skill_cd(),
[this, skill_id] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
Skill* skill = GetPassiveSkill(skill_id);
if (skill) {
}
}
},
&skill->xtimer_attacher);
skill->Initialzie();
passive_skill_hash_[skill_meta->skill_id()] = skill;
}
}
void Creature::RemovePassiveSkill(int skill_id)
{
auto itr = passive_skill_hash_.find(skill_id);
if (itr != passive_skill_hash_.end()) {
passive_skill_hash_.erase(itr);
}
}
void Creature::ClearPassiveSkill()
{
std::vector<int> del_skills;
del_skills.reserve(passive_skill_hash_.size());
for (auto& pair : passive_skill_hash_) {
del_skills.push_back(pair.first);
}
for (int skill_id : del_skills) {
RemovePassiveSkill(skill_id);
}
}
Skill* Creature::GetPassiveSkill(int skill_id)
{
auto itr = passive_skill_hash_.find(skill_id);
return itr != passive_skill_hash_.end() ? itr->second.get() : nullptr;
}
void Creature::TriggerBuff(Skill* skill, std::set<Creature*>& target_list, BuffTriggerType_e trigger_type)
{
for (Creature* entity : target_list) {
TriggerOneObjectBuff(skill, entity, trigger_type);
}
if (trigger_type == kBTT_UseSkill && !dead) {
auto itr = skill->meta->_trigger_type_buffs.find(trigger_type);
if (itr != skill->meta->_trigger_type_buffs.end()) {
for (const mt::Buff* buff_meta : itr->second) {
switch (buff_meta->buff_target()) {
case kBuffTargetSelf: //自己
{
AddBuff(this, buff_meta, skill->meta);
}
break;
}
}
}
}
}
void Creature::TriggerOneObjectBuff(Skill* skill, Creature* target, BuffTriggerType_e trigger_type)
{
if (target->dead) {
return;
}
auto itr = skill->meta->_trigger_type_buffs.find(trigger_type);
if (itr != skill->meta->_trigger_type_buffs.end()) {
for (const mt::Buff* buff_meta : itr->second) {
switch (buff_meta->buff_target()) {
case kBuffTargetSelf: //自己
{
if (target == this && trigger_type != kBTT_UseSkill) {
target->AddBuff(this, buff_meta, skill->meta);
}
}
break;
case kBuffTargetFriendly: //友军
{
if (target->team_id == team_id) {
target->AddBuff(this, buff_meta, skill->meta);
}
}
break;
case kBuffTargetEnemy: //敌军
{
if (target->team_id != team_id) {
target->AddBuff(this, buff_meta, skill->meta);
}
}
break;
default:
{
}
break;
}
}
}
}
Skill* Creature::GetSkill(int skill_id)
{
auto itr = skill_hash_.find(skill_id);
return itr != skill_hash_.end() ? itr->second.get() : nullptr;
}
bool Creature::CanUseSkill(int skill_id)
{
Skill* skill = GetSkill(skill_id);
if (!skill) {
return false;
}
if (GetAbility()->GetSwitchTimes(kDisableUseSkillTimes) > 0) {
return false;
}
if (room->GetGasData().GetGasMode() == GasInactive &&
!mt::Param::s().prebattle_can_use_skill) {
return false;
}
if (HasBuffEffect(kBET_Vertigo) ||
HasBuffEffect(kBET_Jump) ||
HasBuffEffect(kBET_Fly) ||
HasBuffEffect(kBET_MachineGun)) {
return false;
}
if (skill->force_use_times > 0) {
--skill->force_use_times;
return true;
}
if (dead) {
return false;
}
if (playing_skill) {
return false;
}
if (IsHuman() && AsHuman()->downed) {
return false;
}
return skill->GetCurrTimes() > 0 || skill->GetMinorType() != SMT_NONE;
}
void Creature::DoSkill(int skill_id,
int target_id,
const glm::vec3& skill_dir,
float skill_distance
)
{
Skill* skill = GetSkill(skill_id);
if (skill && CanUseSkill(skill_id)) {
#ifdef DEBUG1
a8::XPrintf("DoSkill minor_type:%d\n", {skill->GetMinorType()});
#endif
RemoveBuffByEffectId(kBET_Hide);
if (skill->GetMinorType()) {
SetAttackDir(skill_dir);
skill->DoMinorMode();
return;
}
if (skill->meta->attack_dir_lock_time()) {
SetAttackDir(skill_dir);
IncDisableAttackDirTimes();
room->xtimer.SetTimeoutEx
(
skill->meta->attack_dir_lock_time() / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
DecDisableAttackDirTimes();
}
},
&xtimer_attacher);
}
DoSkillPreProc(skill_id, target_id);
ResetSkill();
skill_target_id_ = target_id;
skill_dir_ = skill_dir;
skill_distance_ = skill_distance;
curr_skill_ = skill;
playing_skill = true;
skill_pos = GetPos().ToGlmVec3();
context_dir = skill_dir_;
context_pos = GetPos();
context_pos.AddGlmVec3(skill_dir_ * skill_distance_);
CurrentSkill()->last_use_frameno = room->GetFrameNo();
skill->LockCastPhase();
if (CurrentSkill()->meta->skill_target() == kST_Self
) {
skill_target_id_ = GetUniId();
}
if (CurrentSkill()->meta->skill_target() == kST_SpecDir) {
std::set<Creature*> target_list;
SelectSkillTargets(CurrentSkill(), GetPos(), target_list);
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
if (!CurrentSkill()->meta->_phases.empty() &&
CurrentSkill()->meta->_phases[0].time_offset <= 0) {
UpdateSkill();
}
} else {
if (skill_target_id_ == 0) {
if (CurrentSkill()->meta->skill_target() == kST_FriendlyIncludeSelf ||
CurrentSkill()->meta->skill_target() == kST_FriendlyExcludeSelf) {
skill_target_id_ = GetUniId();
}
}
Entity* entity = room->GetEntityByUniId(skill_target_id_);
if (entity && entity->IsCreature(room)) {
Creature* c = (Creature*)entity;
std::set<Creature*> target_list;
SelectSkillTargets(CurrentSkill(), c->GetPos(), target_list);
if (!CurrentSkill()->meta->_phases.empty() &&
CurrentSkill()->meta->_phases[0].time_offset <= 0) {
if (CurrentSkill()->meta->_phases[0].func_id == kSkill_HoldShield) {
if (HasBuffEffect(kBET_HoldShield)) {
RemoveBuffByEffectId(kBET_HoldShield);
playing_skill = false;
} else {
UpdateSkill();
}
} else {
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
UpdateSkill();
}
} else {
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
}
} else {
std::set<Creature*> target_list;
SelectSkillTargets(CurrentSkill(), GetPos(), target_list);
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
if (!CurrentSkill()->meta->_phases.empty() &&
CurrentSkill()->meta->_phases[0].func_id == kSkill_Shot) {
playing_skill = true;
} else {
playing_skill = false;
}
}
}
if (HasBuffEffect(kBET_Camouflage)) {
RemoveBuffByEffectId(kBET_Camouflage);
}
GetTrigger()->UseSkill(skill);
DoSkillPostProc(true, skill_id, target_id);
if (IsHuman()) {
++AsHuman()->stats->use_skill_times;
}
#if 0
if (skill_id != TURN_OVER_SKILL_ID) {
#else
{
#endif
CreatureWeakPtr ptr;
ptr.Attach(this);
room->frame_event.AddPlaySkill(ptr, skill_id);
}
}
}
void Creature::DoSkillPreProc(int skill_id, int target_id)
{
}
void Creature::DoSkillPostProc(bool used, int skill_id, int target_id)
{
}
void Creature::ResetSkill()
{
curr_skill_ = nullptr;
curr_skill_phase = 0;
skill_dir_ = GlmHelper::ZERO;
skill_param1 = 0.0f;
playing_skill = false;
}
void Creature::UpdateSkill()
{
if (CurrentSkill() && !dead) {
if (curr_skill_phase < CurrentSkill()->meta->_phases.size()) {
const mt::SkillPhase* phase = &CurrentSkill()->meta->_phases[curr_skill_phase];
if (phase->time_offset <= CurrentSkill()->GetPassedTime()) {
CurrentSkill()->ProcSkillPhase(phase);
++curr_skill_phase;
}
} else {
playing_skill = false;
}
} else {
playing_skill = false;
}
}
const mt::SkillPhase* Creature::GetCurrSkillPhase()
{
return curr_skill_phase < CurrentSkill()->meta->_phases.size() ?
&CurrentSkill()->meta->_phases[curr_skill_phase] : nullptr;
}
Skill* Creature::CurrentSkill()
{
return curr_skill_;
}
const mt::Skill* Creature::CurrentSkillMeta()
{
const mt::Skill* skill_meta = CurrentSkill() ? CurrentSkill()->meta : nullptr;
return skill_meta;
}
void Creature::ActiveAllSkill()
{
for (auto& pair : skill_hash_) {
pair.second->Accelerate(-10000000);
}
}
void Creature::Initialize()
{
MoveableEntity::Initialize();
for (auto& node : buff_effect_) {
INIT_LIST_HEAD(&node);
}
for (auto& node : depend_effect_) {
INIT_LIST_HEAD(&node);
}
for (auto& node : cond_buffs_) {
INIT_LIST_HEAD(&node);
}
}
void Creature::SetMoveDir(const glm::vec3& move_dir)
{
if (GetDisableMoveDirTimes() <= 0) {
MoveableEntity::SetMoveDir(move_dir);
}
}
void Creature::AddSkill(int skill_id)
{
const mt::Skill* skill_meta = mt::Skill::GetById(skill_id);
if (skill_meta && !GetSkill(skill_id)) {
if (skill_meta->skill_type() == 1) {
auto skill = std::make_shared<Skill>();
skill->owner = this;
skill->meta = skill_meta;
skill->xtimer_attacher.SetOwner(&room->xtimer);
skill->Initialzie();
skill_hash_[skill_id] = skill;
} else if (skill_meta->skill_type() == 2) {
AddPassiveSkill(skill_id);
} else {
return;
}
}
}
void Creature::ClearSkill()
{
std::shared_ptr<Skill> reserve_skill = nullptr;
for (auto& pair : skill_hash_) {
if (pair.second->meta->IsTurnOverSkill()) {
reserve_skill = pair.second;
}
}
skill_hash_.clear();
if (reserve_skill) {
skill_hash_[reserve_skill->meta->skill_id()] = reserve_skill;
}
}
bool Creature::CanSee(const Creature* c) const
{
return room->grid_service->InView(GetGridId(), c->GetGridId());
}
bool Creature::IsPlayer() const
{
return IsHuman() && IsEntitySubType(EST_Player);
}
bool Creature::IsAndroid() const
{
return IsHuman() && IsEntitySubType(EST_Android);
}
bool Creature::IsHuman() const
{
return IsEntityType(ET_Player);
}
bool Creature::IsCar() const
{
return IsEntityType(ET_Car);
}
void Creature::StartAction(ActionType_e action_type,
int action_duration,
int item_id,
int target_id)
{
if (this->action_type == action_type &&
this->action_item_id == item_id &&
this->action_target_id == target_id) {
return;
}
if (action_type == AT_Reload) {
GetTrigger()->StartReload();
}
action_duration = std::max(0, action_duration);
this->action_type = action_type;
this->action_frameno = room->GetFrameNo();
this->action_duration = action_duration;
this->action_item_id = item_id;
this->action_target_id = target_id;
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
if (HasBuffEffect(kBET_Camouflage)) {
RemoveBuffByEffectId(kBET_Camouflage);
}
}
void Creature::CancelAction()
{
if (action_type == AT_Relive) {
Entity* entity = room->GetEntityByUniId(action_target_id);
if (entity && entity->IsEntityType(ET_Player)) {
Human* hum = (Human*)entity;
if (hum->action_type == AT_Rescue) {
hum->CancelAction();
}
}
RemoveBuffByEffectId(kBET_Rescuer);
}
if (action_type == AT_Rescue) {
RemoveBuffByEffectId(kBET_InRescue);
}
if (action_type == AT_Reload) {
GetTrigger()->EndReload();
}
ResetAction();
}
void Creature::ResetAction()
{
action_type = AT_None;
action_duration = 0;
action_frameno = 0;
action_item_id = 0;
action_target_id = 0;
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
if (!reload_delay_timer_.expired()) {
room->xtimer.Delete(reload_delay_timer_);
}
}
bool Creature::IsProperTarget(Creature* target, bool no_teammate)
{
if (target->dead) {
return false;
}
if (a8::HasBitFlag(target->status, CS_Disable)) {
return false;
}
if (!no_teammate && team_id == target->team_id) {
return false;
}
if (target->IsInvincible()) {
return false;
}
if (target->HasBuffEffect(kBET_Hide)) {
return false;
}
if (target->HasBuffEffect(kBET_Driver)) {
return false;
}
if (target->HasBuffEffect(kBET_Passenger)) {
return false;
}
return true;
}
bool Creature::IsEnemy(Creature* target)
{
return team_id != target->team_id;
}
void Creature::TraverseProperTargets(std::function<void (Creature*, bool&)> func)
{
auto callback =
[this, func] (Creature* c, bool& stop)
{
if (IsProperTarget(c)) {
func(c, stop);
}
};
room->grid_service->TraverseCreatures(room->GetRoomIdx(),
GetGridList(),
callback);
}
void Creature::TraverseProperTargetsNoTeammate(std::function<void (Creature*, bool&)> func)
{
auto callback =
[this, func] (Creature* c, bool& stop)
{
if (IsProperTarget(c, true)) {
func(c, stop);
}
};
room->grid_service->TraverseCreatures(room->GetRoomIdx(),
GetGridList(),
callback);
}
void Creature::UpdatePoisoning()
{
if (dead) {
poisoning_time = 0;
return;
}
bool need_notify = poisoning_time > 1000;
while (poisoning_time > 1000) {
float dmg = 0;
if (room->GetGasData().is_last_gas) {
dmg = room->GetGasData().new_area_meta->hurt();
} else {
dmg = room->GetGasData().old_area_meta->hurt();
}
if (room->IsPveRoom()) {
dmg = room->GetGasData().new_area_meta->hurt();
}
dmg = dmg * GetMaxHP();
dmg *= 1 + GetAbility()->GetAttrRate(kHAT_PoisoningReduction);
if (room->IsPveRoom()) {
dmg = std::max(1.0f, dmg);
} else {
dmg = std::max(1.0f, dmg);
}
float dmg_out = 0.0f;
DecHP(dmg, VP_Gas, TEXT("battle_server_killer_gas", "毒圈"), VW_Gas,
VP_Gas,
TEXT("battle_server_killer_gas", "毒圈"),
dmg_out);
if (dead) {
poisoning_time = 0;
break;
}
poisoning_time -= 1000;
}
#if 0
if (need_notify && IsEntitySubType(EST_Player)) {
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
#endif
}
void Creature::Shot(glm::vec3& target_dir, bool& shot_ok, float fly_distance, int trace_target_uniid)
{
shot_ok = false;
if (!GetCurrWeapon()->meta) {
return;
}
if (downed) {
return;
}
if (!GetCurrWeapon()->meta) {
return;
}
if (HasBuffEffect(kBET_Jump) ||
HasBuffEffect(kBET_Fly)) {
return;
}
if (GetAbility()->GetSwitchTimes(kDisableShotTimes) > 0) {
return;
}
if (GetCurrWeapon()->weapon_idx != 0 &&
GetCurrWeapon()->ammo <= 0) {
CheckLoadingBullet();
//AutoLoadingBullet();
return;
}
if (action_type == AT_Reload) {
CancelAction();
}
if (action_type == AT_Reload ||
action_type == AT_Rescue ||
action_type == AT_UseItem ||
action_type == AT_Relive) {
CancelAction();
}
if (IsCar()) {
if (room->GetFrameNo() - last_shot_frameno_ > 0) {
if ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) <
GetCurrWeapon()->GetFireRate(this)
) {
return;
}
}
} else {
if ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) <
GetCurrWeapon()->GetFireRate(this)
) {
return;
}
}
if (GetCurrWeapon()->meta->_power_charge.empty() || power_idx < 0) {
InternalShot(this,
GetCurrWeapon()->meta,
GetCurrWeapon()->bullet_meta,
GetCurrWeapon()->skill_meta,
fly_distance,
GetCurrWeapon()->weapon_uniid,
trace_target_uniid);
} else if (power_idx < GetCurrWeapon()->meta->_power_charge.size()) {
const mt::Equip* weapon_meta = mt::Equip::GetById
(std::get<1>(GetCurrWeapon()->meta->_power_charge[power_idx]));
if (weapon_meta) {
const mt::Equip* bullet_meta = mt::Equip::GetById(weapon_meta->use_bullet());;
if (bullet_meta) {
#ifdef DEBUG
SendDebugMsg(a8::Format("蓄力射击 %d 枪:%d 子弹:%d",
{
power_idx,
weapon_meta->id(),
bullet_meta->id()
}));
#endif
InternalShot(this,
weapon_meta,
bullet_meta,
nullptr,
fly_distance,
0,
trace_target_uniid);
} else {
A8_ABORT();
}
} else {
A8_ABORT();
}
} else {
A8_ABORT();
}
if (GetCurrWeapon()->weapon_idx != 0 &&
GetCurrWeapon()->meta->bullet_consume_type() == kBulletConsumeOne) {
--GetCurrWeapon()->ammo;
}
int slot_id = GetCurrWeapon()->meta->_inventory_slot();
#ifdef DEBUG
if (IsPlayer()) {
SendDebugMsg(a8::Format("使用武器 %s slot:%d", {GetCurrWeapon()->meta->name(), slot_id}));
}
#endif
bool auto_switch_weapon = false;
switch (slot_id) {
case IS_FRAG: //手雷
case IS_SMOKE: //烟雾弹
{
#if 1
DecInventory(slot_id, 1);
#endif
if (GetCurrWeapon()->ammo <= 0) {
if (GetInventory(slot_id) > 0) {
DecInventory(slot_id, 1);
++GetCurrWeapon()->ammo;
} else {
auto_switch_weapon = true;
}
}
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
break;
case IS_POSION_GAS_BOMB: //毒气弹
case IS_MOLOTOR_COCKTAIL: //燃烧瓶
case IS_TRAP: //陷井
case IS_MINE: //地雷
{
#if 1
DecInventory(slot_id, 1);
#endif
if (GetCurrWeapon()->ammo <= 0) {
if (GetInventory(slot_id) > 0) {
DecInventory(slot_id, 1);
++GetCurrWeapon()->ammo;
} else {
auto_switch_weapon = true;
}
}
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
break;
case IS_C4:
case IS_SHIELD_WALL:
case IS_SINGAL_GUN:
case IS_OIL_BUCKET:
{
#if 1
DecInventory(slot_id, 1);
#endif
if (GetCurrWeapon()->ammo <= 0) {
if (GetInventory(slot_id) > 0) {
DecInventory(slot_id, 1);
++GetCurrWeapon()->ammo;
} else {
auto_switch_weapon = true;
}
}
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
break;
default:
{
}
break;
}
if (auto_switch_weapon) {
AutoSwitchWeapon();
} else {
CheckLoadingBullet();
}
last_shot_frameno_ = room->GetFrameNo();
if ((IsPlayer() || IsCar())) {
room->frame_event.AddBulletNumChg(GetWeakPtrRef());
room->frame_event.AddWeaponAmmoChg(GetWeakPtrRef());
}
if (HasBuffEffect(kBET_Camouflage)) {
RemoveBuffByEffectId(kBET_Camouflage);
}
shot_ok = true;
}
void Creature::AutoLoadingBullet(bool manual)
{
Weapon* p_weapon = GetCurrWeapon();
if (second_weapon.meta) {
p_weapon = &second_weapon;
}
if (p_weapon->weapon_idx != 0 &&
(p_weapon->ammo <= 0 ||
(manual && p_weapon->ammo < p_weapon->GetClipVolume(this)))
) {
const mt::Equip* bullet_meta = mt::Equip::GetById(p_weapon->meta->use_bullet());
if (bullet_meta &&
bullet_meta->_inventory_slot() >= 0 &&
bullet_meta->_inventory_slot() < (int)inventory_.size()
) {
if (GetInventory(bullet_meta->_inventory_slot()) > 0) {
if (on_loading_bullet) {
on_loading_bullet();
}
int duration_time = p_weapon->GetReloadTime(this) *
(1 + GetAbility()->GetAttrRate(kHAT_WeaponReloadTime));
StartAction(AT_Reload,
duration_time,
p_weapon->weapon_id,
p_weapon->weapon_idx);
}
}
return;
}
}
int Creature::GetInventory(int slot_id)
{
if (!IsValidSlotId(slot_id)) {
A8_ABORT();
}
return inventory_[slot_id].num;
}
void Creature::AddInventory(int slot_id, int num)
{
assert(num > 0);
if (!IsValidSlotId(slot_id)) {
A8_ABORT();
}
inventory_[slot_id].num += num;
}
void Creature::DecInventory(int slot_id, int num)
{
assert(num > 0);
if (!IsValidSlotId(slot_id)) {
A8_ABORT();
}
inventory_[slot_id].num -= num;
}
void Creature::CheckSpecObject(int new_poly_flags)
{
if (poly_ext_flags_ != new_poly_flags) {
for (int i = 0; i < 16; ++i) {
if (!a8::SameBitFlag(poly_ext_flags_, new_poly_flags, i)) {
if ((1 << i) == SAMPLE_POLYFLAGS_SWIM) {
if (a8::HasBitFlag(poly_ext_flags_, i)) {
RemoveBuffByEffectId(kBET_InWater);
} else {
TryAddBuff(this, kInWater1BuffId);
}
}
if ((1 << i) == SAMPLE_POLYFLAGS_GLASS) {
if (a8::HasBitFlag(poly_ext_flags_, i)) {
RemoveBuffById(8058);
RemoveBuffById(kInGrassBuffId);
} else {
TryAddBuffAndSetTime(this, 8058, 9999999);
TryAddBuffAndSetTime(this, kInGrassBuffId, 9999999);
}
}
}
}
}
poly_ext_flags_ = new_poly_flags;
}
void Creature::SummonObstacle(Buff* buff, int id, const Position& target_pos)
{
if (buff->meta->_int_buff_param2 > 0) {
RemoveSurplusObstacle(buff->meta->buff_id(), id, buff->meta->_int_buff_param2);
}
float *p_rotate = nullptr;
float rotate = 0.0f;
Position pos = target_pos;
RoomObstacle* obstacle = room->CreateObstacle(id, pos.GetX(), pos.GetY(), pos.GetZ());
if (obstacle) {
obstacle->buff_meta = buff->meta;
obstacle->skill_meta = buff->skill_meta;
obstacle->master.Attach(this);
obstacle->SetTeamId(room, team_id);
obstacle->SetMasterId(room, GetUniId());
obstacle->Active();
if (p_rotate) {
obstacle->SetRotate(*p_rotate);
}
obstacle->context_ability = context_ability;
slave_things_.push_back(std::make_tuple(buff->meta->buff_id(), obstacle->GetWeakPtrRef()));
if (buff->skill_meta) {
SkillHelper::ProcSummonObstacle(buff->skill_meta, obstacle);
}
#ifdef DEBUG
SendDebugMsg(a8::Format("召唤物件 buff_id:%d thing_id:%d pos:%f,%f target_pos:%f,%f",
{buff->meta->buff_id(),
id,
GetPos().GetX(),
GetPos().GetY(),
pos.GetX(),
pos.GetY()
}));
#endif
} else {
#ifdef DEBUG
A8_ABORT();
#endif
}
}
void Creature::FillSkillCasterState(SkillCasterState* caster_state)
{
caster_state->caster.Attach(this);
caster_state->caster_pos = GetPos();
caster_state->caster_skill_id = CurrentSkill() ? CurrentSkill()->meta->skill_id() : 0;
caster_state->caster_skill_target_id = skill_target_id_;
caster_state->caster_skill_dir = skill_dir_;
caster_state->caster_skill_distance = skill_distance_;
caster_state->caster_skill_param1 = skill_param1;
}
void Creature::RecoverSkillCasterState(SkillCasterState* caster_state)
{
#if 0
Global::Instance()->verify_set_pos = 1;
SetPos(caster_state->caster_pos);
Global::Instance()->verify_set_pos = 0;
#endif
skill_target_id_ = caster_state->caster_skill_target_id;
skill_dir_ = caster_state->caster_skill_dir;
skill_distance_ = caster_state->caster_skill_distance;
skill_param1 = caster_state->caster_skill_param1;
}
CreatureWeakPtr Creature::AllocWeakPtr()
{
CreatureWeakPtr ptr;
ptr.Attach(this);
return ptr;
}
CreatureWeakPtr& Creature::GetWeakPtrRef()
{
if (!weak_ptr_.Get()) {
weak_ptr_.Attach(this);
}
return weak_ptr_;
}
Weapon* Creature::AutoChgWeapon()
{
if (weapons[GUN_SLOT1].weapon_id != 0) {
SetCurrWeapon(&weapons[GUN_SLOT1]);
} else if (weapons[GUN_SLOT2].weapon_id != 0) {
SetCurrWeapon(&weapons[GUN_SLOT2]);
} else {
SetCurrWeapon(&weapons[0]);
}
return GetCurrWeapon();
}
Weapon* Creature::ChooseNextWeapon(int curr_weapon_slot_id, int begin_slot_id, int end_slot_id)
{
if (curr_weapon_slot_id < begin_slot_id) {
A8_ABORT();
}
if (curr_weapon_slot_id > end_slot_id) {
A8_ABORT();
}
if (begin_slot_id > end_slot_id) {
A8_ABORT();
}
if (begin_slot_id < 0 ||
end_slot_id < 0) {
A8_ABORT();
}
Weapon* next_weapon = nullptr;
for (int i = 1; i <= (end_slot_id - begin_slot_id + 1); ++i) {
int slot_id = begin_slot_id +
(i + curr_weapon_slot_id - begin_slot_id) % (end_slot_id - begin_slot_id + 1);
int idx = -1;
if (slot_id >= SPEC1_IS_BEGIN && slot_id <= SPEC1_IS_END) {
idx = SPEC1_SLOT_BEGIN + (slot_id - SPEC1_IS_BEGIN);
}
if (slot_id >= SPEC2_IS_BEGIN && slot_id <= SPEC2_IS_END) {
idx = SPEC2_SLOT_BEGIN + (slot_id - SPEC2_IS_BEGIN);
}
if (slot_id >= SPEC3_IS_BEGIN && slot_id <= SPEC3_IS_END) {
idx = SPEC3_SLOT_BEGIN + (slot_id - SPEC3_IS_BEGIN);
}
if (idx != -1 && weapons.at(idx).weapon_id != 0) {
next_weapon = &weapons[idx];
break;
}
}
return next_weapon;
}
void Creature::SetCurrWeapon(Weapon* weapon)
{
#ifdef DEBUG
bool found = false;
for (auto& p : weapons) {
if (&p == weapon) {
found = true;
}
}
if (!found) {
A8_ABORT();
}
#endif
bool need_recalc = curr_weapon_ != weapon;
if (!auto_switch_weapon_timer_.expired()) {
room->xtimer.Delete(auto_switch_weapon_timer_);
}
if (curr_weapon_ != weapon) {
GetTrigger()->TakeonWeapon(curr_weapon_, weapon);
}
curr_weapon_ = weapon;
if (need_recalc) {
RecalcDtoAttr();
}
}
void Creature::ResetAllSkillCd()
{
for (auto& pair : skill_hash_) {
pair.second->ResetSkillCd();
}
}
Hero* Creature::SummonHero(Buff* buff,
const glm::vec3& pos,
const glm::vec3& dir
)
{
Hero* hero = nullptr;
const mt::Hero* hero_meta = mt::Hero::GetById(buff->meta->_int_buff_param2);
if (hero_meta) {
hero = InternalSummonHero(buff,
hero_meta,
GetAttackDir(),
pos
);
hero->hero_level = GetBattleContext()->GetHeroLevel();
}
return hero;
}
bool Creature::FreezeOperate()
{
bool freeze = false;
if (!freeze) {
freeze = GetBuffByEffectId(kBET_Vertigo) ||
GetBuffByEffectId(kBET_Driver) ||
GetBuffByEffectId(kBET_MachineGun) ||
GetBuffByEffectId(kBET_Passenger);
}
return freeze;
}
void Creature::SlaveOnRemove(Entity* slave)
{
switch (slave->GetEntityType()) {
case ET_Hero:
{
}
break;
case ET_Obstacle:
{
for (auto itr = slave_things_.begin(); itr != slave_things_.end(); ++itr) {
if (std::get<1>(*itr).Get() &&
std::get<1>(*itr).Get() == slave) {
slave_things_.erase(itr);
break;
}
}
}
break;
default:
{
A8_ABORT();
}
break;
}
}
void Creature::RemoveSurplusHero(int hero_id, int num)
{
auto itr = slave_heros_.find(hero_id);
if (itr != slave_heros_.end() && num >= 0) {
int exists_num = 0;
{
list_head* pos = nullptr;
list_head* next = nullptr;
list_head* head = &itr->second;
list_for_each_safe(pos, next, head) {
++exists_num;
}
}
while (exists_num > num) {
Hero* hero = list_first_entry(&itr->second,
Hero,
entry);
hero->Destory();
list_del_init(&hero->entry);
--exists_num;
}
}
}
void Creature::RemoveSurplusObstacle(int buff_id, int id, int num)
{
if (slave_things_.size() >= num && num > 0) {
std::vector<RoomObstacleWeakPtr> matched_things;
for (auto& itr : slave_things_) {
if (std::get<0>(itr) == buff_id &&
std::get<1>(itr).Get() &&
std::get<1>(itr).Get()->meta->thing_id() == id) {
matched_things.push_back(std::get<1>(itr));
}
}
while (matched_things.size() > num) {
matched_things[0].Get()->Destory();
matched_things.erase(matched_things.begin());
}
}
}
bool Creature::IsInvincible()
{
return
HasBuffEffect(kBET_Invincible) ||
HasBuffEffect(kBET_Driver) ||
HasBuffEffect(kBET_Passenger)
;
}
void Creature::ClearAimingBuffs()
{
for (int buff_id : aiming_buffs) {
RemoveBuffById(buff_id);
}
aiming_buffs.clear();
}
float Creature::GetHP()
{
return ability.hp;
}
float Creature::GetMaxHP()
{
#if 1
return ability.max_hp;
#else
return ability.max_hp * (1 + GetAbility()->GetAttrRate(kHAT_MaxHp)) +
GetAbility()->GetAttrAbs(kHAT_MaxHp);
#endif
}
float Creature::GetHPRate()
{
return GetMaxHP() > 0.00001 ? GetHP() / GetMaxHP() : 0;
}
void Creature::GetHitEnemys(std::set<Creature*>& enemys, float radius)
{
GetHitEnemys(enemys, GetPos().ToGlmVec3(), radius);
}
void Creature::GetHitEnemys(std::set<Creature*>& enemys, const glm::vec3 center, float radius)
{
float min_distance = 9999999999;
room->grid_service->TraverseCreatures
(room->GetRoomIdx(),
GetGridList(),
[this, &enemys, center, radius, &min_distance] (Creature* c, bool& stop)
{
if (IsProperTarget(c)) {
#if 1
Position p;
p.FromGlmVec3(center);
float distance = c->GetPos().Distance2D2(p);
#else
float distance = c->GetPos().DistanceGlmVec3(center);
#endif
min_distance = std::min(min_distance, distance);
if (distance < radius) {
enemys.insert(c);
}
}
});
}
void Creature::AddHp(float hp)
{
if (dead) {
return;
}
float old_health = GetHP();
ability.hp += hp;
ability.hp = std::min(GetHP(), GetMaxHP());
if (IsHuman()) {
Human* hum = (Human*)this;
hum->stats->heal_amount += GetHP() - old_health;
#if 1
room->frame_event.AddHpChg(GetWeakPtrRef());
#else
SyncAroundPlayers(__FILE__, __LINE__, __func__);
#endif
}
}
void Creature::SetHP(float hp)
{
ability.hp = hp;
#ifdef DEBUG1
if (IsPlayer()) {
a8::XPrintf("hp:%f maxhp:%f\n",
{
GetHP(),
GetMaxHP()
});
}
#endif
}
void Creature::SetMaxHP(float max_hp)
{
ability.max_hp = max_hp;
#ifdef DEBUG1
if (IsPlayer()) {
a8::XPrintf("hp:%f maxhp:%f\n",
{
GetHP(),
GetMaxHP()
});
}
#endif
}
void Creature::SetInfiniteBulletMode()
{
inventory_[IS_9MM].num = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_556MM].num = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_762MM].num = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_12GAUGE].num = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_RPG].num = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_ICE].num = FIGHTING_MODE_BULLET_NUM;
}
void Creature::FindLocation()
{
}
bool Creature::Attackable(Room* room)
{
if (dead) {
return false;
}
if (a8::HasBitFlag(status, CS_Disable)) {
return false;
}
if (IsInvincible()) {
return false;
}
if (HasBuffEffect(kBET_Hide)) {
return false;
}
if (HasBuffEffect(kBET_Driver)) {
return false;
}
if (HasBuffEffect(kBET_Passenger)) {
return false;
}
return true;
}
bool Creature::ReceiveExplosionDmg(Explosion* explosion)
{
return true;
}
void Creature::CheckAbilityUsed()
{
if (ability_.use_count() > 1) {
#if 0
std::shared_ptr<Ability> old_val = ability_;
ability_ = std::make_shared<Ability>(GetWeakPtrRef());
*ability_.get() = *(old_val.get());
#endif
}
}
void Creature::RefreshHP()
{
}
void Creature::TraverseBuff(std::function<void (Buff*, bool&)> func)
{
bool stop = false;
for (auto& buff : buff_list_) {
func(buff.get(), stop);
if (stop) {
return;
}
}
}
void Creature::TraverseSkill(std::function<void (Skill*, bool&)> func)
{
bool stop = false;
for (auto& pair : skill_hash_) {
func(pair.second.get(), stop);
if (stop) {
return;
}
}
}
Hero* Creature::InternalSummonHero(Buff* buff, const mt::Hero* hero_meta, glm::vec3 dir, const glm::vec3 born_pos)
{
Hero* hero = room->CreateHero
(this,
hero_meta,
born_pos,
dir,
team_id
);
if (hero) {
auto itr = slave_heros_.find(hero_meta->id());
if (itr != slave_heros_.end()) {
list_add_tail(&hero->entry, &itr->second);
} else {
slave_heros_[hero_meta->id()] = list_head();
itr = slave_heros_.find(hero_meta->id());
INIT_LIST_HEAD(&itr->second);
list_add_tail(&hero->entry, &itr->second);
}
}
return hero;
}
std::string Creature::DebugOutBuffList()
{
std::string data;
for (auto& itr : buff_list_) {
data += a8::Format("buffid:%d effect:%d\n",
{
itr->meta->buff_id(),
itr->meta->buff_effect()
});
}
return data;
}
void Creature::AutoSwitchWeapon()
{
auto weapon_idx = GetCurrWeapon()->weapon_idx;
auto weapon_meta = GetCurrWeapon()->meta;
auto switch_func =
[this, weapon_idx, weapon_meta] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
if (!(GetCurrWeapon()->weapon_idx == weapon_idx &&
GetCurrWeapon()->meta == weapon_meta)) {
return;
}
bool switch_ok = false;
int slot_id = weapon_meta->_inventory_slot();
if (GetCurrWeapon()->ammo <= 0 && slot_id > 0) {
if (GetInventory(slot_id) > 0) {
} else {
int weapon_idx = GetCurrWeapon()->weapon_idx;
*GetCurrWeapon() = Weapon();
GetCurrWeapon()->weapon_idx = weapon_idx;
Weapon* next_weapon = ChooseNextSpecWeapon(slot_id);
if (!next_weapon) {
next_weapon = AutoChgWeapon();
}
if (next_weapon) {
SetCurrWeapon(next_weapon);
switch_ok = true;
} else {
A8_ABORT();
}
}
}
if (switch_ok) {
MarkSyncActivePlayer(__FILE__, __LINE__, __func__);
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
CheckLoadingBullet();
}
};
if (GetCurrWeapon()->meta->auto_switch_weapon_time() > 0) {
if (!auto_switch_weapon_timer_.expired()) {
room->xtimer.Delete(auto_switch_weapon_timer_);
}
auto_switch_weapon_timer_ = room->xtimer.SetTimeoutWpEx
(
GetCurrWeapon()->meta->auto_switch_weapon_time() / FRAME_RATE_MS,
switch_func,
&xtimer_attacher);
} else {
switch_func(a8::TIMER_EXEC_EVENT, nullptr);
}
}
void Creature::CheckLoadingBullet()
{
if (GetCurrWeapon()->weapon_idx != 0 &&
GetCurrWeapon()->ammo <= 0 &&
reload_delay_timer_.expired()) {
#ifdef DEBUG
if (IsPlayer()) {
a8::XPrintf("CheckLoadingBullet1\n", {});
}
#endif
if (GetCurrWeapon()->meta->reload_delay_time() > 0) {
int weapon_id = GetCurrWeapon()->weapon_id;
reload_delay_timer_ = room->xtimer.SetTimeoutWpEx
(
GetCurrWeapon()->meta->reload_delay_time() / FRAME_RATE_MS,
[this, weapon_id] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
if (GetCurrWeapon()->weapon_id == weapon_id) {
#ifdef DEBUG
if (IsPlayer()) {
a8::XPrintf("CheckLoadingBullet3\n", {});
}
#endif
AutoLoadingBullet();
}
}
},
&xtimer_attacher);
} else {
#ifdef DEBUG
if (IsPlayer()) {
a8::XPrintf("CheckLoadingBullet2\n", {});
}
#endif
AutoLoadingBullet();
}
}
}
Weapon* Creature::ChooseNextSpecWeapon(int curr_weapon_slot_id)
{
Weapon* next_weapon = nullptr;
int data[][3]= {
{SPEC1_IS_BEGIN, SPEC1_IS_END, 0},
{SPEC2_IS_BEGIN, SPEC2_IS_END, 0},
{SPEC3_IS_BEGIN, SPEC3_IS_END, 0}
};
int idx = -1;
for (int i = 0; i < sizeof(data) /sizeof(data[0]); ++i) {
int start_id = data[i][0];
int end_id = data[i][1];
if (curr_weapon_slot_id >= start_id &&
curr_weapon_slot_id <= end_id) {
idx = i;
data[i][2] = 1;
break;
}
}
if (idx > -1) {
for (int i = 0; i < sizeof(data) /sizeof(data[0]); ++i) {
int real_i = (i + idx) % (sizeof(data) /sizeof(data[0]));
int start_id = data[real_i][0];
int end_id = data[real_i][1];
int flag = data[real_i][2];
next_weapon = ChooseNextWeapon(flag ? curr_weapon_slot_id : start_id, start_id, end_id);
if (next_weapon) {
break;
}
}
}
return next_weapon;
}
void Creature::OnBattleStart(Room* room)
{
#ifdef DEBUG1
if (IsPlayer()) {
SendDebugMsg(a8::Format("OnBattleStart clear buff", {}));
}
#endif
int try_count = 0;
while (try_count++ < 10) {
std::vector<int> del_buffs;
del_buffs.reserve(buff_list_.size());
for (auto& buff : buff_list_) {
if (!buff->meta->post_battle_valid()) {
if (!buff->skill_meta || buff->skill_meta->skill_type() != kPassiveSkill) {
del_buffs.push_back(buff->buff_uniid);
}
}
}
for (auto itr = del_buffs.rbegin(); itr != del_buffs.rend(); ++itr) {
RemoveBuffByUniId(*itr);
}
if (del_buffs.empty()) {
break;
}
}
#ifdef DEBUG
if (try_count > 5) {
A8_ABORT();
}
#endif
}
bool Creature::CanFollow(Creature* follower)
{
#ifdef DEBUG1
return false;
#endif
if (follower->GetUniId() == GetUniId()) {
return false;
}
#if 0
if (!follower->IsPlayer()) {
return false;
}
#endif
if (follower->team_id != team_id) {
return false;
}
#if 0
if (follower->follow_target.Get()) {
return false;
}
#endif
if (follower->HasBuffEffect(kBET_Jump)) {
return false;
}
if (!follower->HasBuffEffect(kBET_Fly) && room->GetGasData().GetGasMode() != GasInactive) {
return false;
}
#if 0
if (!IsPlayer()) {
return false;
}
#endif
if (HasBuffEffect(kBET_Jump)) {
return false;
}
if (!HasBuffEffect(kBET_Fly) && room->GetGasData().GetGasMode() != GasInactive) {
return false;
}
return true;
}
void Creature::FollowToTarget()
{
if (HasBuffEffect(kBET_Jump) &&
follow_target.Get() &&
follow_target.Get()->GetChgAttackDirTimes() > follow_target_last_chg_move_dir_times_) {
#if 1
{
#else
if (GetPos().ManhattanDistance(follow_target.Get()->GetPos()) > 70) {
#endif
glm::vec3 dir = GlmHelper::UP;
GlmHelper::RotateY(dir, a8::RandAngle());
GlmHelper::Normalize(dir);
Position target_pos;
target_pos.FromGlmVec3(follow_target.Get()->GetPos().ToGlmVec3() + dir * (float)(30 + (rand() % 10)));
if (GetPos().ManhattanDistance2D(target_pos) > 5) {
glm::vec3 move_dir = GetPos().CalcDir(target_pos);
GlmHelper::Normalize(move_dir);
SetMoveDir(move_dir);
}
follow_target_last_chg_move_dir_times_ = follow_target.Get()->GetChgAttackDirTimes();
}
}
}
void Creature::DoRecoilForce(int distance)
{
if (distance > 0) {
glm::vec3 old_move_dir = GetMoveDir();
MustBeAddBuff(this, kRecoilBuffId);
SetMoveDir(GetAttackDir() * -1.0f);
ForwardMove(distance);
SetMoveDir(old_move_dir);
}
}
void Creature::WinSkillExp(int win_exp)
{
if (!IsHuman()) {
return;
}
}
void Creature::RecalcDtoAttr()
{
if (!IsHuman()) {
return;
}
if (GetCurrWeapon()) {
if (GetBattleContext()) {
}
} else {
if (GetBattleContext()) {
}
}
}
void Creature::SetBattleContext(std::shared_ptr<BattleDataContext> c)
{
battle_context_ = c;
}
void Creature::UpdateMove()
{
if (HasBuffEffect(kBET_Vertigo)) {
if (!HasBuffEffect(kBET_Recoil)) {
return;
}
}
if (GetDisableMoveTimes() > 0) {
return;
}
{
Buff* hide_buff = GetBuffByEffectId(kBET_Hide);
if (hide_buff && hide_buff->meta->_int_buff_param1 > 0) {
RemoveBuffByEffectId(kBET_Hide);
}
}
{
Position old_pos = GetPos();
if (GetMovement()->UpdatePosition()) {
} else {
if (!GetMovement()->IsFindPath()) {
GetMovement()->CalcTargetPos(mt::Param::s().move_step_len);
if (!GetMovement()->UpdatePosition()) {
if (IsPlayer()) {
ShortFindPath();
}
}
}
}
room->grid_service->MoveCreature(this);
if (room->IsPveRoom() && IsEntityType(ET_Hero) && IsNearGas()) {
GetMutablePos() = old_pos;
GetMovement()->ClearPath();
glm::vec3 dir = GetPos().ToGlmVec3() - old_pos.ToGlmVec3();
if (std::abs(dir.x) > FLT_EPSILON ||
std::abs(dir.y) > FLT_EPSILON ||
std::abs(dir.z) > FLT_EPSILON
) {
GlmHelper::Normalize(dir);
SetMoveDir(dir);
}
}
}
}
void Creature::ForwardMove(float distance)
{
SpecDirMove(GetMoveDir(), distance);
}
void Creature::SpecDirMove(glm::vec3 dir, float distance)
{
Position pos = GetPos();
glm::vec3 start = GetPos().ToGlmVec3();
glm::vec3 end = pos.AddGlmVec3(dir * distance).ToGlmVec3();
glm::vec3 hit_point;
room->map_instance->Scale(start);
room->map_instance->Scale(end);
int same_polys_flags = 0;
std::vector<dtPolyRef> spec_polys;
bool hit_result = false;
bool ret = room->map_instance->RaycastEx(start, end, hit_point, hit_result, same_polys_flags, spec_polys, 0);
if (ret) {
room->map_instance->UnScale(hit_point);
GetMutablePos().FromGlmVec3(hit_point);
room->map_instance->CheckTerrain(this, same_polys_flags, spec_polys);
}
room->grid_service->MoveCreature(this);
GetMovement()->ClearPath();
}
bool Creature::HasSpecMove()
{
return
GetBuffByEffectId(kBET_BePull);
}
void Creature::_UpdateSpecMove()
{
if (!HasSpecMove()) {
return;
}
bool move_end = false;
float target_distance = target_pos.Distance2D2(GetPos());
if (target_distance <= 0.000001f) {
move_end = true;
} else {
glm::vec3 old_move_dir = GetMoveDir();
glm::vec3 move_dir = GetPos().CalcDir(target_pos);
GlmHelper::Normalize(move_dir);
SetMoveDir(move_dir);
bool is_collision = false;
UpdateMove();
move_dir = old_move_dir;
target_distance = target_pos.Distance2D2(GetPos());
if (is_collision || target_distance <= 1.0001f) {
move_end = true;
}
}
if (move_end) {
Buff* buff = GetBuffByEffectId(kBET_BePull);
if (buff) {
const mt::Buff* new_buff = mt::Buff::GetById(1039);
if (new_buff) {
AddBuff(this, new_buff);
}
RemoveBuffByEffectId(kBET_BePull);
}
}
}
void Creature::AutoNavigation(const glm::vec3& target_pos, float speed,
std::function<void (Creature*)> cb)
{
float distance = GetPos().DistanceGlmVec3(target_pos);
if (distance < 0.001f) {
cb(this);
return;
}
if (speed < 0.001f) {
cb(this);
return;
}
struct NavContext
{
CreatureWeakPtr c;
glm::vec3 src_pos;
glm::vec3 target_pos;
glm::vec3 dir;
int exec_frameno = 0;
float speed = 0.0f;
std::function<void (Creature*)> cb;
};
std::shared_ptr<NavContext> context = std::make_shared<NavContext>();
context->c = GetWeakPtrRef();
context->src_pos = GetPos().ToGlmVec3();
context->target_pos = target_pos;
context->dir = target_pos - GetPos().ToGlmVec3();
GlmHelper::Normalize(context->dir);
context->speed = speed;
context->cb = cb;
SetAttackDir(context->dir);
SetMoveDir(context->dir);
IncDisableAttackDirTimes();
IncDisableMoveDirTimes();
IncDisableMoveTimes();
room->xtimer.SetIntervalEx
(1,
[context] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
++context->exec_frameno;
if (context->c.Get()) {
Room* room = context->c.Get()->room;
Creature* c = context->c.Get();
glm::vec3 curr_pos = context->src_pos +
context->dir * (context->speed / FRAME_RATE_MS) * (float)context->exec_frameno;
Global::Instance()->verify_set_pos = 1;
c->GetMutablePos().FromGlmVec3(curr_pos);
Global::Instance()->verify_set_pos = 0;
room->grid_service->MoveCreature(c);
bool ok = false;
if (GlmHelper::Norm(context->target_pos - context->src_pos) <=
GlmHelper::Norm(curr_pos - context->src_pos)) {
Global::Instance()->verify_set_pos = 1;
c->GetMutablePos().FromGlmVec3(context->target_pos);
Global::Instance()->verify_set_pos = 0;
ok = true;
}
#ifdef DEBUG
a8::XPrintf("speed:%d src_pos:%f,%f,%f curr_pos:%f,%f,%f "
"target_pos:%f,%f,%f ok:%d\n",
{
context->speed,
context->src_pos.x,
context->src_pos.y,
context->src_pos.z,
curr_pos.x,
curr_pos.y,
curr_pos.z,
context->target_pos.x,
context->target_pos.y,
context->target_pos.z,
ok
});
#endif
if (ok || c->dead) {
context->cb(c);
c->DecDisableMoveTimes();
c->DecDisableMoveDirTimes();
c->DecDisableAttackDirTimes();
room->xtimer.DeleteCurrentTimer();
}
}
}
},
&xtimer_attacher);
}
void Creature::LockAttackDir(int time)
{
IncDisableAttackDirTimes();
room->xtimer.SetTimeoutEx
(
time / FRAME_RATE_MS,
[this] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
} else if (a8::TIMER_DELETE_EVENT == event){
DecDisableAttackDirTimes();
}
},
&xtimer_attacher);
}
void Creature::PullTarget(const glm::vec3& target_pos)
{
}
bool Creature::IsHpFull()
{
return std::abs(GetMaxHP() - GetHP()) < 0.0001f;
}
void Creature::MarkSyncActivePlayer(const char* file, int line, const char* func)
{
#ifdef DEBUG
if (!f8::IsTestEnv()) {
if (IsPlayer()) {
a8::XPrintf("MarkSyncActivePlayer file:%s line:%d func:%s\n",
{
file,
line,
func
});
}
}
#endif
need_sync_active_player_ = true;
}
void Creature::SetPos(Position pos)
{
if (!Global::Instance()->verify_set_pos) {
abort();
}
Entity::SetPos(pos);
}
void Creature::OnLand()
{
//着陆
RemoveBuffByEffectId(kBET_Jump);
RemoveBuffByEffectId(kBET_ThroughWall);
#if 1
if (IsAndroid() && AsHuman()->team_uuid.empty() && !GetTeam()->HasPlayer()) {
MustBeAddBuff(this, kBeRecycleBuffId);
}
#endif
if (IsPlayer()) {
AsHuman()->StartRefreshViewTimer();
}
{
glm::vec3 center = GetPos().ToGlmVec3();
room->map_instance->PtInHouse(GetPos().ToGlmVec3(), center);
room->map_instance->Scale(center);
glm::vec3 point;
bool ok = false;
for (int i = 0; i < 10; ++i) {
ok = room->map_instance->FindNearestPoint(center, 1.0f + 10 * i, point);
if (ok) {
break;
}
}
if (!ok) {
abort();
}
#if 1
if (point.y < 1.2) {
point.y = 3.0f;
}
#endif
room->map_instance->UnScale(point);
Global::Instance()->verify_set_pos = 1;
GetMutablePos().FromGlmVec3(point);
Global::Instance()->verify_set_pos = 0;
GetMovement()->ClearPath();
room->grid_service->MoveCreature(this);
#ifdef DEBUG
a8::XPrintf("OnLoad ok:%d pos:%f,%f,%f\n",
{
ok ? 1 : 0,
point.x,
point.y,
point.z
});
#endif
}
if (IsAndroid()) {
int buff_uniid = MustBeAddBuff(this,
kPeaceModeBuffId
);
if (buff_uniid) {
Buff* buff = GetBuffByUniId(buff_uniid);
if (buff) {
int peace_time = 2000;
room->xtimer.ModifyTime(buff->remover_timer, peace_time / FRAME_RATE_MS);
}
}
}
gun_grasp_->Init();
}
void Creature::CheckBulletHitHoldShield(IBullet* bullet, bool& eat)
{
Buff* hold_shield_buff = GetBuffByEffectId(kBET_HoldShield);
if (hold_shield_buff && !bullet->IsBomb() && !dead && this != bullet->GetSender().Get()) {
//param2是距离 param4是宽度
glm::vec3 shield_pos = GetPos().ToGlmVec3() + GetAttackDir() * hold_shield_buff->meta->_buff_param2;
bool ret = Collision::Check2dRotationRectangle
(bullet->GetPos().GetX(),
bullet->GetPos().GetZ(),
10,
//bullet->GetGunMeta()->bullet_rad(),
shield_pos.x,
shield_pos.z,
hold_shield_buff->meta->_buff_param4,
mt::Param::s().bullet_planck_step_length,
GetAttackDirRotate() * 180.0f
);
if (ret) {
float finaly_dmg = GetBattleContext()->CalcDmg(this, bullet);
shield_hp_ = std::max(0.0f, shield_hp_ - finaly_dmg);
#ifdef DEBUG
a8::XPrintf("命中盾牌 finally_dmg:%f shield_hp:%f\n",
{
finaly_dmg,
shield_hp_
});
#endif
room->frame_event.AddPropChg(GetWeakPtrRef(),
kPropShieldHp,
shield_max_hp_,
shield_hp_);
if (shield_hp_ <= 0) {
#ifdef DEBUG
a8::XPrintf("shiled destory\n", {});
#endif
GetTrigger()->ShieldDestory();
if (hold_shield_buff->meta->_buff_param1_int_list.size() > 0) {
TryAddBuff(this, hold_shield_buff->meta->_buff_param1_int_list[0]);
}
RemoveBuffByUniId(hold_shield_buff->buff_uniid);
}
eat = true;
return;
}
}
}
void Creature::AddEnergyShield(int value)
{
energy_shield = value;
max_energy_shield = value;
room->frame_event.AddPropChg
(
GetWeakPtrRef(),
kPropEnergyShield,
energy_shield,
max_energy_shield,
true);
}
void Creature::ClearEnergyShield()
{
energy_shield = 0;
max_energy_shield = 0;
room->frame_event.AddPropChg
(
GetWeakPtrRef(),
kPropEnergyShield,
energy_shield,
max_energy_shield,
false);
}
std::weak_ptr<Effect> Creature::AddEffect(int effect_id)
{
auto effect = std::make_shared<Effect>();
effect->effect_uniid = ++buff_uniid_;
effect->owner = this;
effect->effect_id = effect_id;
effect_hash_[effect->effect_uniid] = effect;
return effect;
}
std::weak_ptr<Effect> Creature::AddEffectAndSetTime(int effect_id, int time)
{
auto p = AddEffect(effect_id);
room->xtimer.SetTimeoutEx
(
time / FRAME_RATE_MS,
[this, p] (int event, const a8::Args* args)
{
if (a8::TIMER_EXEC_EVENT == event) {
if (!p.expired()) {
p.lock()->RemoveFromOwner();
}
}
},
&xtimer_attacher);
return p;
}
void Creature::RemoveEffect(int effect_uniid)
{
effect_hash_.erase(effect_uniid);
}
void Creature::RemoveEffect(std::weak_ptr<Effect> effect)
{
if (!effect.expired()) {
RemoveEffect(effect.lock()->effect_uniid);
}
}
void Creature::RemoveEffects(std::vector<int> effect_uniids)
{
for (int effect_uniid : effect_uniids) {
RemoveEffect(effect_uniid);
}
}
void Creature::ClearEffect()
{
effect_hash_.clear();
}
void Creature::TraverseEffect(std::function<void (Effect*, bool&)> cb)
{
for (auto& pair : effect_hash_) {
bool stop = false;
cb(pair.second.get(), stop);
if (stop) {
break;
}
}
}
void Creature::RemoveSkillObstacle(const mt::Skill* skill_meta)
{
std::vector<RoomObstacleWeakPtr> del_obs;
for (auto itr = slave_things_.begin(); itr != slave_things_.end(); ++itr) {
RoomObstacleWeakPtr ob = std::get<1>(*itr);
if (ob.Get() && ob.Get()->skill_meta == skill_meta) {
del_obs.push_back(ob);
}
}
if (skill_meta && skill_meta->GetMagicId() == MAGIC_20301_XL) {
int save_num = skill_meta->_number_meta->number() * 2 - 2;
if (del_obs.size() > save_num & save_num > 0) {
for (int i = 0; i < del_obs.size() - save_num; ++i) {
del_obs[i].Get()->Destory();
}
}
} else {
for (auto ob : del_obs) {
ob.Get()->Destory();
}
}
}
void Creature::SetBuffTag(int tag, int val)
{
buff_tags_[tag] = val;
}
int Creature::GetBuffTag(int tag)
{
auto itr = buff_tags_.find(tag);
return itr != buff_tags_.end() ? itr->second : 0;
}
void Creature::IncBuffTag(int tag, int val)
{
auto itr = buff_tags_.find(tag);
if (itr != buff_tags_.end()) {
itr->second += val;
} else {
buff_tags_[tag] = val;
}
}
void Creature::UnSetBuffTag(int tag)
{
buff_tags_.erase(tag);
}
void Creature::ShortFindPath()
{
if (std::abs(GetMoveDir().x) > FLT_EPSILON ||
std::abs(GetMoveDir().z) > FLT_EPSILON
) {
auto try_move =
[this] (glm::vec3 start, glm::vec3 end, glm::vec3& new_point) -> bool
{
room->map_instance->Scale(start);
room->map_instance->Scale(end);
glm::vec3 hit_point;
bool hit_result = false;
bool ret = room->map_instance->Raycast(start, end, hit_point, hit_result);
if (ret) {
new_point = hit_point;
room->map_instance->UnScale(new_point);
return true;
} else {
return false;
}
};
glm::vec3 start_pos = GetPos().ToGlmVec3() + GetMoveDir() * -1.0f * 2.0f;
bool right_ok = false;
float right_distance = 0.0f;
glm::vec3 right_pos = glm::vec3(0.0f, 0.0f, 0.0f);
{
float angle = 1.0f;
glm::vec3 move_dir = GetMoveDir();
GlmHelper::RotateY(move_dir, angle);
glm::vec3 end_pos = start_pos + move_dir * 80.0f;
glm::vec3 new_point = glm::vec3(0.0f, 0.0f, 0.0f);
bool ret = try_move(start_pos, end_pos, new_point);
if (ret) {
right_ok = true;
right_pos = new_point;
right_distance = GlmHelper::Norm(new_point - GetPos().ToGlmVec3());
}
}
bool left_ok = false;
float left_distance = 0.0f;
glm::vec3 left_pos = glm::vec3(0.0f, 0.0f, 0.0f);
{
float angle = -1.0f;
glm::vec3 move_dir = GetMoveDir();
GlmHelper::RotateY(move_dir, angle);
glm::vec3 end_pos = start_pos + move_dir * 80.0f;
glm::vec3 new_point = glm::vec3(0.0f, 0.0f, 0.0f);
bool ret = try_move(start_pos, end_pos, new_point);
if (ret) {
left_ok = true;
left_pos = new_point;
left_distance = GlmHelper::Norm(new_point - GetPos().ToGlmVec3());
}
}
if (right_ok || left_ok) {
glm::vec3 finaly_pos = glm::vec3(0.0f, 0.0f, 0.0f);
if (right_ok && left_ok) {
if (right_distance > left_distance) {
finaly_pos = right_pos;
} else {
finaly_pos = left_pos;
}
} else if (right_ok) {
finaly_pos = right_pos;
} else if (left_ok) {
finaly_pos = left_pos;
}
glm::vec3 move_dir = finaly_pos - GetPos().ToGlmVec3();
GlmHelper::Normalize(move_dir);
glm::vec3 end_pos = start_pos + move_dir * GetSpeed() / 1.0f;
glm::vec3 new_point = glm::vec3(0.0f, 0.0f, 0.0f);
bool ret = try_move(start_pos, end_pos, new_point);
if (ret) {
Global::Instance()->verify_set_pos = 1;
GetMutablePos().FromGlmVec3(new_point);
Global::Instance()->verify_set_pos = 0;
}
}
}
}
float Creature::GetSkillRaycastDistance()
{
float distance = context_pos.Distance2D2(GetPos());
if (distance > 0.00001f) {
glm::vec3 move_dir = GetPos().CalcDir(context_pos);
GlmHelper::Normalize(move_dir);
float move_distance = distance;
glm::vec3 hit_point = glm::vec3(0.0f, 0.0f, 0.0f);
bool hit_result = false;
glm::vec3 start = GetPos().ToGlmVec3();
glm::vec3 end = GetPos().ToGlmVec3() + move_dir * move_distance;
room->map_instance->Scale(start);
room->map_instance->Scale(end);
bool ret = room->map_instance->Raycast
(
start,
end,
hit_point,
hit_result);
if (ret) {
room->map_instance->UnScale(hit_point);
float real_distance = GlmHelper::Norm(hit_point - GetPos().ToGlmVec3());
return real_distance;
}
}
return 0;
}
void Creature::NetInitOk()
{
}
bool Creature::IsNearGas()
{
Position p;
p.FromGlmVec3(glm::vec3(room->GetGasData().pos_new.x, 0, room->GetGasData().pos_new.y));
float distance = GetPos().Distance2D2(p);
return distance + GetRadius() * 2 + 20 > room->GetGasData().rad_new;
}
float Creature::GetSpeed()
{
const float min_speed = 30.0f;
const float base_speed = GetHeroMeta()->move_speed();
float finaly_speed = base_speed;
const float ONE_LIMIT = 0.99999f;
/*
kBET_HoldShield
kBET_BePull
{
Buff* buff = GetBuffByEffectId(kBET_BePull);
if (buff) {
return buff->meta->_buff_param2;
}
}
if (GetAbility()->GetFixedSpeedTimes() > 0) {
return GetAbility()->GetFixedSped();
}
*/
if (HasBuffEffect(kBET_Jump)) {
finaly_speed = GetHeroMeta()->jump_speed();
} else if (HasBuffEffect(kBET_Down)) {
finaly_speed = GetHeroMeta()->fall_speed();
} else {
float addition = 0.0f;
float ruduce = 0.0f;
if (aiming) {
if (GetHeroMeta()->aim_speed() > ONE_LIMIT) {
addition += GetHeroMeta()->aim_speed();
} else {
ruduce += GetHeroMeta()->aim_speed();
}
} else if (action_type == AT_Reload) {
if (GetHeroMeta()->reload_speed() > ONE_LIMIT) {
addition += GetHeroMeta()->reload_speed();
} else {
ruduce += GetHeroMeta()->reload_speed();
}
} else if (action_type == AT_UseItem) {
if (GetHeroMeta()->medicine_speed() > ONE_LIMIT) {
addition += GetHeroMeta()->medicine_speed();
} else {
ruduce += GetHeroMeta()->medicine_speed();
}
} else if (HasBuffEffect(kBET_InWater)) {
if (GetHeroMeta()->medicine_speed() > ONE_LIMIT) {
addition += GetHeroMeta()->medicine_speed();
} else {
ruduce += GetHeroMeta()->medicine_speed();
}
} else if (shot_hold &&
(
GetCurrWeapon()->weapon_idx == GUN_SLOT1 ||
GetCurrWeapon()->weapon_idx == GUN_SLOT2
)) {
if (GetHeroMeta()->shoot_speed() > ONE_LIMIT) {
addition += GetHeroMeta()->shoot_speed();
} else {
ruduce += GetHeroMeta()->shoot_speed();
}
}
if (addition > ONE_LIMIT) {
addition = addition - ONE_LIMIT;
}
if (ruduce > 0.00001f) {
ruduce = 1.0f - ruduce;
}
addition += GetAbility()->GetAttrAddition(kHAT_Speed);
ruduce = std::max(ruduce, GetAbility()->GetAttrRuduce(kHAT_Speed));
float rate = addition - ruduce;
finaly_speed *= 1.0f + rate;
#ifdef DEBUG
if (IsPlayer() && finaly_speed >500) {
a8::XPrintf("player speed:%f\n", {finaly_speed});
}
#endif
}
return std::max(min_speed, finaly_speed) / SERVER_FRAME_RATE;
}
#if 0
Hero* Creature::SummonHero(Buff* buff,
const glm::vec3& pos,
const glm::vec3& dir
)
{
Hero* hero = nullptr;
const mt::Hero* hero_meta = mt::Hero::GetById(buff->meta->_int_buff_param2);
if (hero_meta) {
hero = InternalSummonHero(buff,
hero_meta,
GetAttackDir(),
pos
);
}
return hero;
}
void CreatuRemoveSurplusObstacle(int thing_id, int num)
{
}
#endif
std::shared_ptr<std::set<int>> Creature::CalcReporterList(bool is_trace_bullet,
const mt::Equip* weapon_meta,
const mt::Equip* bullet_meta)
{
std::shared_ptr<std::set<int>> p;
bool need_gen = false;
switch (bullet_meta->_inventory_slot()) {
case IS_RPG:
{
need_gen = true;
}
break;
default:
{
if (IsEntityType(ET_Hero) && is_trace_bullet) {
need_gen = true;
}
}
break;
}
if (need_gen) {
float nearest_distance = FLT_MAX;
Creature* nearest_hum = nullptr;
TraverseCreatures
(
[this, &nearest_distance, &nearest_hum] (Creature* c, bool& stop) mutable
{
if (!c->IsHuman()) {
return;
}
if (a8::HasBitFlag(c->status, CS_Disable)) {
return;
}
if (c->dead) {
return;
}
float distance = std::fabs(c->GetPos().GetX() - GetPos().GetX()) + std::fabs(c->GetPos().GetZ() - GetPos().GetZ());
if (c->IsPlayer()) {
if (c->AsHuman()->socket_handle) {
if (distance < nearest_distance) {
nearest_distance = distance;
nearest_hum = c;
}
}
} else if (c->AsHuman()->HasObserver()) {
c->AsHuman()->TraverseObservers
(
[this, &nearest_distance, &nearest_hum] (Human* hum, bool& stop) mutable
{
if (!hum->IsPlayer()) {
return;
}
if (hum->socket_handle) {
float distance = std::fabs(hum->GetPos().GetX() - GetPos().GetX()) + std::fabs(hum->GetPos().GetZ() - GetPos().GetZ());
if (distance < nearest_distance) {
nearest_distance = distance;
nearest_hum = hum;
}
}
});
}
});
if (nearest_hum) {
p = std::make_shared<std::set<int>>();
p->insert(nearest_hum->GetUniId());
}
}
return p;
}
Obstacle* Creature::SummonObstacle(Buff* buff,
const glm::vec3& pos,
const glm::vec3& dir
)
{
Obstacle* ob = nullptr;
const mt::MapThing* thing_meta = mt::MapThing::GetById(buff->meta->_int_buff_param2);
if (thing_meta) {
ob = InternalSummonObstacle(buff,
thing_meta,
GetAttackDir(),
pos
);
}
return ob;
}
void Creature::RemoveSurplusObstacle(int id, int num)
{
auto itr = slave_things2_.find(id);
if (itr != slave_things2_.end() && num >= 0) {
int exists_num = 0;
{
list_head* pos = nullptr;
list_head* next = nullptr;
list_head* head = &itr->second;
list_for_each_safe(pos, next, head) {
++exists_num;
}
}
while (exists_num > num) {
RoomObstacle* ob = list_first_entry(&itr->second,
RoomObstacle,
entry);
ob->Destory();
list_del_init(&ob->entry);
--exists_num;
}
}
}
Obstacle* Creature::InternalSummonObstacle(Buff* buff, const mt::MapThing* thing_meta,
glm::vec3 dir, const glm::vec3 born_pos)
{
float *p_rotate = nullptr;
float rotate = 0.0f;
RoomObstacle* ob = room->CreateObstacle(thing_meta->thing_id(),
born_pos.x,
born_pos.y,
born_pos.z);
if (ob) {
ob->buff_meta = buff->meta;
ob->skill_meta = buff->skill_meta;
ob->master.Attach(this);
ob->SetTeamId(room, team_id);
ob->SetMasterId(room, GetUniId());
ob->Active();
if (p_rotate) {
ob->SetRotate(*p_rotate);
}
{
auto itr = slave_things2_.find(thing_meta->thing_id());
if (itr != slave_things2_.end()) {
list_add_tail(&ob->entry, &itr->second);
} else {
slave_things2_[thing_meta->thing_id()] = list_head();
itr = slave_things2_.find(thing_meta->thing_id());
INIT_LIST_HEAD(&itr->second);
list_add_tail(&ob->entry, &itr->second);
}
}
#ifdef DEBUG
SendDebugMsg(a8::Format("召唤物件 buff_id:%d thing_id:%d pos:%f,%f,%f target_pos:%f,%f,%f",
{buff->meta->buff_id(),
thing_meta->thing_id(),
GetPos().GetX(),
GetPos().GetY(),
GetPos().GetZ(),
born_pos.x,
born_pos.y,
born_pos.z
}));
#endif
}
return ob;
}
bool Creature::CanShot(bool try_reload)
{
if (!GetCurrWeapon()->meta) {
return false;
}
if (downed) {
return false;
}
if (!GetCurrWeapon()->meta) {
return false;
}
if (HasBuffEffect(kBET_Jump) ||
HasBuffEffect(kBET_Fly)) {
return false;
}
if (GetAbility()->GetSwitchTimes(kDisableShotTimes) > 0) {
return false;
}
if (GetCurrWeapon()->weapon_idx != 0 &&
GetCurrWeapon()->ammo <= 0) {
if (try_reload) {
CheckLoadingBullet();
}
//AutoLoadingBullet();
return false;
}
if (action_type == AT_Reload) {
return false;
}
if (action_type == AT_Reload ||
action_type == AT_Rescue ||
action_type == AT_UseItem ||
action_type == AT_Relive) {
if (try_reload) {
CancelAction();
}
}
if (IsCar()) {
if (room->GetFrameNo() - last_shot_frameno_ > 0) {
if ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) <
GetCurrWeapon()->GetFireRate(this)
) {
return false;
}
}
} else {
if ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) <
GetCurrWeapon()->GetFireRate(this)
) {
return false;
}
}
return true;
}