#include "precompile.h" #include #include #include #include #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 "battledatacontext.h" #include "buff.h" #include "mapinstance.h" #include "collision.h" #include "gungrasp.h" #include "effect.h" #include "stats.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(this); trigger_->Init(); ability_ = std::make_shared(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(this); gun_grasp_ = std::make_shared(this); } Creature::~Creature() { 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 init_args, std::shared_ptr> buff_vars) { #if 999 if (buff_meta->buff_id() == 8042) { if (GetBuffById(buff_meta->buff_id())) { return 0; } } #endif 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(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()->IncDisableShotTimes(); } if (buff->meta->disable_useskill()) { GetAbility()->IncDisableUseSkillTimes(); } 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()]); } { buff->remover_timer = room->xtimer.SetTimeoutWpEx ( buff_meta->GetDurationTime(buff.get()) * SERVER_FRAME_RATE, [buff_wp = (std::weak_ptr)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 init_args, std::shared_ptr> 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 init_args, std::shared_ptr> 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 init_args, std::shared_ptr> 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 init_args, std::shared_ptr> 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 init_args, std::shared_ptr> 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> 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(); ability_->Clear(); bool need_refresh_hp = false; for (auto& buff : buff_list_) { } for (auto& tuple : talent_list) { auto talent_meta = mt::GunTalentGrow::GetByIdQyality(std::get<0>(tuple), std::get<1>(tuple)); if (talent_meta) { for (auto& tuple1 : talent_meta->_addattr) { int attr_type = std::get<0>(tuple1); int attr_val = std::get<1>(tuple1); if (talent_meta->addtype() == 0) { float* p = ability_->GetBuffAttrAbsPtr(attr_type); if (p) { *p += attr_val; } } else if (talent_meta->addtype() == 1) { float* p = ability_->GetBuffAttrRatePtr(attr_type); if (p) { *p += attr_val; } } } } } 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()->DecDisableShotTimes(); } if (buff.meta->disable_useskill()) { GetAbility()->DecDisableUseSkillTimes(); } #if 999 if (buff.meta->buff_id() == 8042) { if (GetAbility()->GetSpeedRuduceTimes() > 0) { GetAbility()->DelSpeedRuduce(-buff.meta->_buff_param3/100.0f); } #ifdef DEBUG { a8::XPrintf("移除减速%f\nad", {buff.meta->_buff_param3}); } #endif } #endif 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() { 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->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 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& 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()->GetDisableUseSkillTimes() > 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 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 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 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->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 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; } 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); } 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 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 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()->GetDisableShotTimes() > 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(); } } void Creature::SummonHero(Buff* buff, const Position& pos, const glm::vec3& dir ) { #if 1 const mt::Hero* hero_meta = mt::Hero::GetById(buff->meta->_int_buff_param1); if (hero_meta) { int life_time = 10000; bool through_wall = false; InternalSummonHero(buff, hero_meta, GetAttackDir(), pos, through_wall, 1, life_time); } #else for (auto& info : buff->meta->_hero_infos) { int through_wall = std::get<0>(info); float x = std::get<1>(info); float y = std::get<2>(info) ; int hero_id = std::get<3>(info); int num = std::get<4>(info); int life_time = std::get<5>(info); const mt::Hero* hero_meta = mt::Hero::GetById(hero_id); if (hero_meta) { for (int j = 0; j < num; ++j) { bool can_create = false; Position born_pos; Hero* hero = nullptr; for (int i = 0; i < 4; ++i) { glm::vec3 born_dir = dir; glm::vec3 born_offset(x, 0, y); GlmHelper::RotateY(born_offset, i * 0.5); born_pos = GetPos(); born_pos.AddGlmVec3(born_offset); can_create = TrySummonHero(hero_meta, dir, born_pos, through_wall); if (can_create) { break; } }//end for i if (!can_create) { born_pos = GetPos(); can_create = TrySummonHero(hero_meta, GetAttackDir(), born_pos, through_wall); } if (can_create) { InternalSummonHero(buff, hero_meta, GetAttackDir(), born_pos, through_wall, num, life_time); } }//end for j } } #endif } 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: { for (auto itr = slave_heros_.begin(); itr != slave_heros_.end(); ++itr) { if ((Entity*)std::get<1>(*itr) == slave) { slave_heros_.erase(itr); break; } } } 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 buff_id, int id, int num) { if (slave_heros_.size() >= num && num > 0) { std::vector matched_heros; for (auto& itr : slave_heros_) { if (std::get<0>(itr) == buff_id && std::get<1>(itr)->meta->id() == id) { matched_heros.push_back(std::get<1>(itr)); } } while (matched_heros.size() > num) { matched_heros[0]->Destory(); matched_heros.erase(matched_heros.begin()); } } } void Creature::RemoveSurplusObstacle(int buff_id, int id, int num) { if (slave_things_.size() >= num && num > 0) { std::vector 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& enemys, float radius) { GetHitEnemys(enemys, GetPos().ToGlmVec3(), radius); } void Creature::GetHitEnemys(std::set& 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 old_val = ability_; ability_ = std::make_shared(GetWeakPtrRef()); *ability_.get() = *(old_val.get()); #endif } } void Creature::RefreshHP() { } void Creature::TraverseBuff(std::function func) { bool stop = false; for (auto& buff : buff_list_) { func(buff.get(), stop); if (stop) { return; } } } void Creature::TraverseSkill(std::function func) { bool stop = false; for (auto& pair : skill_hash_) { func(pair.second.get(), stop); if (stop) { return; } } } bool Creature::TrySummonHero(const mt::Hero* hero_meta, glm::vec3 dir, Position born_pos, bool through_wall) { #if 1 return true; #else abort(); #endif } Hero* Creature::InternalSummonHero(Buff* buff, const mt::Hero* hero_meta, glm::vec3 dir, Position born_pos, bool through_wall, int num, int life_time) { struct SummonHeroInfo { const mt::Hero* hero_meta = nullptr; glm::vec3 dir; glm::vec3 born_pos; bool through_wall = false; int num = 0; int life_time = 0; }; if (TrySummonHero(hero_meta, dir, born_pos, through_wall)) { int delay_time = 0; for (auto& tuple : hero_meta->_pre_appear_effect) { RoomObstacle* obstacle = room->CreateObstacle ( std::get<0>(tuple), born_pos.GetX(), born_pos.GetY(), born_pos.GetZ() ); if (obstacle) { obstacle->DestoryAt(std::get<1>(tuple)); } delay_time = std::max(std::get<1>(tuple), delay_time); } std::shared_ptr summon_info = std::make_shared(); summon_info->hero_meta = hero_meta; summon_info->dir = dir; summon_info->born_pos = born_pos.ToGlmVec3(); summon_info->through_wall = through_wall; summon_info->num = num; summon_info->life_time = life_time; int buff_id = buff->meta->buff_id(); room->xtimer.SetTimeoutEx ( delay_time / FRAME_RATE_MS + NEXT_FRAME_TIMER, [this, summon_info, buff_id] (int event, const a8::Args* args) { if (a8::TIMER_EXEC_EVENT == event) { Hero* hero = room->CreateHero (this, summon_info->hero_meta, summon_info->born_pos, summon_info->dir, team_id ); if (!hero) { return; } RemoveSurplusHero(buff_id, summon_info->hero_meta->id(), summon_info->num); slave_heros_.push_back(std::make_tuple(buff_id, hero)); hero->room->xtimer.SetTimeoutEx ( summon_info->life_time / FRAME_RATE_MS, [] (int event, const a8::Args* args) { }, &hero->xtimer_attacher); #ifdef DEBUG SendDebugMsg(a8::Format("summon hero id:%d pos:%f,%f", { summon_info->hero_meta->id(), summon_info->born_pos.x, summon_info->born_pos.y })); #endif } }, &xtimer_attacher); } return nullptr; } 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 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 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 spec_polys; bool hit_result = false; bool ret = room->map_instance->RaycastEx(start, end, hit_point, hit_result, same_polys_flags, spec_polys); 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 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 cb; }; std::shared_ptr context = std::make_shared(); 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::AddTraceBullet(int bullet_uniid, int target_uniid, int gun_id) { trace_bullet_hash_[bullet_uniid] = std::make_tuple(target_uniid, gun_id); room->xtimer.SetTimeoutEx ( SERVER_FRAME_RATE * 10, [this, bullet_uniid] (int event, const a8::Args* args) { if (a8::TIMER_EXEC_EVENT == event) { trace_bullet_hash_.erase(bullet_uniid); Entity* entity = room->GetEntityByUniId(bullet_uniid); if (entity && entity->GetEntityType() == ET_Bullet) { Bullet* bullet = (Bullet*)entity; bullet->ForceRemove(); } } }, &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 0 if (IsAndroid() && team_uuid.empty()) { 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 Creature::AddEffect(int effect_id) { auto effect = std::make_shared(); effect->effect_uniid = ++buff_uniid_; effect->owner = this; effect->effect_id = effect_id; effect_hash_[effect->effect_uniid] = effect; return effect; } std::weak_ptr 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) { if (!effect.expired()) { RemoveEffect(effect.lock()->effect_uniid); } } void Creature::RemoveEffects(std::vector effect_uniids) { for (int effect_uniid : effect_uniids) { RemoveEffect(effect_uniid); } } void Creature::ClearEffect() { effect_hash_.clear(); } void Creature::TraverseEffect(std::function 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 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; return std::max(min_speed, finaly_speed) / SERVER_FRAME_RATE; }