#include "precompile.h" #include #include "creature.h" #include "metamgr.h" #include "room.h" #include "skill.h" #include "human.h" #include "hero.h" #include "collider.h" #include "roomobstacle.h" void InternalShot(Creature* c, MetaData::Equip* weapon_meta, MetaData::EquipUpgrade* weapon_upgrade_meta, MetaData::Equip* bullet_meta, int weapon_lv, int skill_id, float fly_distance, bool is_tank_skin) { if (weapon_meta->i->_inventory_slot() == IS_TRAP || weapon_meta->i->_inventory_slot() == IS_MINE) { a8::Vec2 old_buff_vec2_param1 = c->buff_vec2_param1; c->buff_vec2_param1 = c->GetPos(); MetaData::Buff * buff_meta = MetaMgr::Instance()->GetBuff(bullet_meta->i->buffid()); if (buff_meta) { c->AddBuff(c, buff_meta, 1 ); } c->buff_vec2_param1 = old_buff_vec2_param1; return; } for (auto& tuple : weapon_meta->bullet_born_offset) { a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple)); bullet_born_offset.Rotate(c->attack_dir.CalcAngle(a8::Vec2::UP)); a8::Vec2 bullet_born_pos = c->GetPos() + bullet_born_offset; if (c->room->OverBorder(bullet_born_pos, 0.0f)) { return; } } if (skill_id == 0) { c->room->frame_event.AddShot(c); } for (auto& tuple : weapon_meta->bullet_born_offset) { a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple)); bullet_born_offset.Rotate(c->attack_dir.CalcAngle(a8::Vec2::UP)); a8::Vec2 bullet_born_pos = c->GetPos() + c->shoot_offset + bullet_born_offset; a8::Vec2 bullet_dir = c->attack_dir; float bullet_angle = std::get<2>(tuple); if (weapon_meta->i->bullet_angle() >= 0.10f) { int angle = (int)weapon_meta->i->bullet_angle() * 1000; if (angle > 0) { bullet_angle += (rand() % angle) / 1000.0f * (rand() % 2 == 0 ? 1 : -1); } } bullet_dir.Rotate(bullet_angle / 180.0f); c->room->frame_event.AddBullet(c, weapon_meta, weapon_lv, bullet_born_pos, bullet_dir, fly_distance); if (c->room->BattleStarted() || (c->room->GetGasData().gas_mode == GasJump && !c->HasBuffEffect(kBET_Jump))) { c->room->CreateBullet(c, weapon_meta, weapon_upgrade_meta, bullet_meta, bullet_born_pos, bullet_dir, fly_distance, is_tank_skin); } } if (weapon_meta->i->recoil_force() > 0.000001) { a8::Vec2 old_move_dir = c->GetMoveDir(); c->MustBeAddBuff(c, kRecoilBuffId); c->SetMoveDir(c->attack_dir * -1); c->_UpdateMove(weapon_meta->i->recoil_force()); c->SetMoveDir(old_move_dir); } if (c->HasBuffEffect(kBET_Hide)) { c->RemoveBuffByEffectId(kBET_Hide); } if (c->aiming) { c->aiming = false; c->aiming_frameno = 0; c->power_idx = -1; c->ClearAimingBuffs(); } } Creature::Creature():MoveableEntity() { 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.weapon_lv = 0; weapon.ammo = 0; } weak_ptr_chunk_.Set(this); inventory_[IS_1XSCOPE] = 1; if (MetaMgr::Instance()->fighting_mode) { inventory_[IS_9MM] = FIGHTING_MODE_BULLET_NUM; inventory_[IS_556MM] = FIGHTING_MODE_BULLET_NUM; inventory_[IS_762MM] = FIGHTING_MODE_BULLET_NUM; inventory_[IS_12GAUGE] = FIGHTING_MODE_BULLET_NUM; inventory_[IS_RPG] = FIGHTING_MODE_BULLET_NUM; } } Creature::~Creature() { for (auto& pair : skill_hash_) { delete pair.second; } skill_hash_.clear(); ClearPassiveSkill(); } bool Creature::HasBuffEffect(int buff_effect_id) { return GetBuffByEffectId(buff_effect_id) != nullptr; } Buff* Creature::GetBuffByEffectId(int effect_id) { return IsValidBuffEffect(effect_id) ? buff_effect_[effect_id] : nullptr; } Buff* Creature::GetBuffById(int buff_id) { for (Buff& buff : buff_list_) { if (buff.meta->i->buff_id() == buff_id) { return &buff; } } return nullptr; } void Creature::AddBuff(Creature* caster, MetaData::Buff* buff_meta, int skill_lv, MetaData::Skill* buff_skill_meta) { #if 0 if (GetBuffById(buff_meta->i->buff_id())) { return; } #endif if (IsImmuneBuffEffect(buff_meta->i->buff_effect())) { return; } if (!buff_meta->EffectCanStack()) { Buff* buff = GetBuffByEffectId(buff_meta->i->buff_effect()); if (buff) { RemoveBuffById(buff->meta->i->buff_id()); } } if (buff_meta->i->buff_effect() == kBET_OnceChgAttr) { if ((int)buff_meta->param1== kHAT_Hp) { if ((int)buff_meta->param2 == 1) { //绝对值 ability.hp += buff_meta->param3; ability.hp = std::min(ability.max_hp, ability.hp); } else if ((int)buff_meta->param2 == 2) { //百分比 ability.hp *= 1 + buff_meta->param3; ability.hp = std::min(ability.max_hp, ability.hp); } SyncAroundPlayers(__FILE__, __LINE__, __func__); } } Buff* buff = &a8::FastAppend(buff_list_); buff->skill_lv = skill_lv; buff->owner = this; buff->meta = buff_meta; buff->skill_meta = buff_skill_meta; buff->add_frameno = room->GetFrameNo(); buff->xtimer_attacher.xtimer = &room->xtimer; buff_effect_[buff->meta->i->buff_effect()] = buff; { room->xtimer.AddDeadLineTimerAndAttach ( buff_meta->i->duration_time() * SERVER_FRAME_RATE, a8::XParams() .SetSender(this) .SetParam1(buff_meta->i->buff_id()), [] (const a8::XParams& param) { Creature* c = (Creature*)param.sender.GetUserData(); c->RemoveBuffById(param.param1); }, &buff->xtimer_attacher.timer_list_ ); } ProcBuffEffect(caster, buff); AddBuffPostProc(caster, buff); if (!buff->meta->i->only_server()) { room->frame_event.AddBuff(GetWeakPtrRef(), buff); } #ifdef DEBUG SendDebugMsg(a8::Format("添加buff_id:%d buff_effect:%d", { buff_meta->i->buff_id(), buff_meta->i->buff_effect() })); #endif } bool Creature::IsImmuneBuffEffect(int buff_effect) { for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) { if (itr->meta->IsImmuneBuffEffect(buff_effect)) { return true; } } return false; } void Creature::MustBeAddBuff(Creature* caster, int buff_id) { MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id); if (!buff_meta) { abort(); } AddBuff(caster, buff_meta, 1); } void Creature::RemoveBuffById(int buff_id) { std::vector removed_buffs; for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) { Buff& buff = *itr; if (buff.meta->i->buff_id() == buff_id) { if (buff_effect_[buff.meta->i->buff_effect()] == &(*itr)) { buff_effect_[buff.meta->i->buff_effect()] = nullptr; } removed_buffs.push_back(buff.meta); OnBuffRemove(buff); buff_list_.erase(itr); break; } } for (MetaData::Buff* buff_meta : removed_buffs) { 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; default: break; } } } RecalcBuffAttr(); #ifdef DEBUG SendDebugMsg(a8::Format("移除buff_id:%d", { buff_id })); #endif } void Creature::SendDebugMsg(const std::string& debug_msg) { } void Creature::AddBuffPostProc(Creature* caster, Buff* buff) { } void Creature::RecalcBuffAttr() { buff_attr_abs_ = {}; buff_attr_rate_ = {}; for (auto& buff : buff_list_) { if (buff.meta->i->buff_effect() == kBET_ChgAttr || buff.meta->i->buff_effect() == kBET_Car || buff.meta->i->buff_effect() == kBET_CrazyMode) { int attr_type = (int)buff.meta->param1; int calc_type = (int)buff.meta->param2; if (IsValidHumanAttr(attr_type)) { if (calc_type == 1) { buff_attr_abs_[attr_type] += buff.meta->param3; } else if (calc_type == 2) { buff_attr_rate_[attr_type] += buff.meta->param3; } } } } } void Creature::OnBuffRemove(Buff& buff) { } void Creature::RemoveBuffByEffectId(int buff_effect_id) { Buff* buff = GetBuffByEffectId(buff_effect_id); while (buff) { RemoveBuffById(buff->meta->i->buff_id()); buff = GetBuffByEffectId(buff_effect_id); } } void Creature::ClearBuffList() { for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) { if (buff_effect_[itr->meta->i->buff_effect()] == &(*itr)) { buff_effect_[itr->meta->i->buff_effect()] = nullptr; } OnBuffRemove(*itr); } buff_list_.clear(); buff_effect_ = {}; buff_attr_abs_ = {}; buff_attr_rate_ = {}; RecalcBuffAttr(); } float Creature::GetBuffAttrAbs(int attr_type) { if (IsValidHumanAttr(attr_type)) { return buff_attr_abs_[attr_type]; } return 0; } float Creature::GetBuffAttrRate(int attr_type) { if (IsValidHumanAttr(attr_type)) { return buff_attr_rate_[attr_type]; } return 0; } void Creature::FillBuffList(Human* hum, ::google::protobuf::RepeatedPtrField<::cs::MFBuff>* pb_buff_list) { for (auto& itr : buff_list_) { if (itr.NeedSync(hum)) { auto buff = pb_buff_list->Add(); itr.FillMFBuff(buff); } } } void Creature::FillSkillList(::google::protobuf::RepeatedPtrField< cs::MFSkill >* pb_skill_list) { for (auto& pair : skill_hash_) { auto skill = pb_skill_list->Add(); pair.second->FillMFSkill(skill); } } void Creature::AddPassiveSkill(int skill_id) { MetaData::Skill* skill_meta = MetaMgr::Instance()->GetSkill(skill_id); if (skill_meta && !GetPassiveSkill(skill_meta->i->skill_id())) { Skill* skill = new Skill; skill->owner = this; skill->meta = skill_meta; skill->xtimer_attacher.xtimer = &room->xtimer; room->xtimer.AddRepeatTimerAndAttach ( SERVER_FRAME_RATE * skill_meta->i->skill_cd(), a8::XParams() .SetSender(this) .SetParam1(skill_id), [] (const a8::XParams& param) { Creature* c = (Creature*)param.sender.GetUserData(); Skill* skill = c->GetPassiveSkill(param.param1); if (skill) { skill->ClearPassiveSkillBuff(); skill->AddPassiveSkillBuff(); } }, &skill->xtimer_attacher.timer_list_); skill->Initialzie(); passive_skill_hash_[skill_meta->i->skill_id()] = skill; skill->AddPassiveSkillBuff(); if (skill_meta->i->skill_cd() > 10000) { //永久被动被动技能 skill->AddPassiveSkillBuff(); } } } void Creature::RemovePassiveSkill(int skill_id) { auto itr = passive_skill_hash_.find(skill_id); if (itr != passive_skill_hash_.end()) { itr->second->ClearPassiveSkillBuff(); delete itr->second; 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 : nullptr; } void Creature::TriggerBuff(Skill* skill, std::set& target_list, BuffTriggerType_e trigger_type) { for (Creature* entity : target_list) { TriggerOneObjectBuff(skill, entity, trigger_type); } } 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 (MetaData::Buff* buff_meta : itr->second) { switch (buff_meta->i->buff_target()) { case kBuffTargetSelf: //自己 { if (target == this) { target->AddBuff(this, buff_meta, 1, skill->meta); } } break; case kBuffTargetFriendly: //友军 { if (target->team_id == team_id) { target->AddBuff(this, buff_meta, 1, skill->meta); } } break; case kBuffTargetEnemy: //敌军 { if (target->team_id != team_id) { target->AddBuff(this, buff_meta, 1, 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 : nullptr; } bool Creature::CanUseSkill(int skill_id) { Skill* skill = GetSkill(skill_id); if (!skill) { return false; } if (dead) { return false; } if (playing_skill) { return false; } return skill->GetCurrTimes() > 0; } void Creature::DoSkill(int skill_id, int target_id, const a8::Vec2& skill_dir, float skill_distance, const a8::Vec2& target_pos) { DoSkillPreProc(skill_id, target_id, target_pos); Skill* skill = GetSkill(skill_id); if (skill && CanUseSkill(skill_id)) { ResetSkill(); skill_target_id_ = target_id; skill_target_pos_ = target_pos; skill_dir_ = skill_dir; skill_distance_ = skill_distance; curr_skill_ = skill; playing_skill = true; buff_vec2_param1 = GetPos() + skill_dir_ * skill_distance_; CurrentSkill()->last_use_frameno = room->GetFrameNo(); if (CurrentSkill()->meta->i->skill_target() == kST_Self ) { skill_target_id_ = GetEntityUniId(); } if (CurrentSkill()->meta->i->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 { Entity* entity = room->GetEntityByUniId(skill_target_id_); if (entity && entity->IsEntityType(ET_Player)) { Creature* c = (Creature*)entity; std::set target_list; skill_target_pos_ = c->GetPos(); SelectSkillTargets(CurrentSkill(), c->GetPos(), target_list); TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill); if (!CurrentSkill()->meta->phases.empty() && CurrentSkill()->meta->phases[0].time_offset <= 0) { UpdateSkill(); } } else { playing_skill = false; } } if (HasBuffEffect(kBET_Camouflage)) { RemoveBuffByEffectId(kBET_Camouflage); } DoSkillPostProc(true, skill_id, target_id, target_pos); #if 0 if (skill_id != TURN_OVER_SKILL_ID) { #else { #endif CreatureWeakPtr ptr; ptr.Attach(this); room->frame_event.AddPlaySkill(ptr, skill_id); } } else { DoSkillPostProc(false, skill_id, target_id, target_pos); } } void Creature::DoSkillPreProc(int skill_id, int target_id, const a8::Vec2& target_pos) { } void Creature::DoSkillPostProc(bool used, int skill_id, int target_id, const a8::Vec2& target_pos) { } void Creature::ResetSkill() { curr_skill_ = nullptr; curr_skill_phase = 0; skill_dir_ = a8::Vec2(); skill_target_pos_ = a8::Vec2(); skill_param1 = 0.0f; playing_skill = false; } void Creature::UpdateSkill() { if (CurrentSkill() && !dead) { if (curr_skill_phase < CurrentSkill()->meta->phases.size()) { MetaData::SkillPhase* phase = &CurrentSkill()->meta->phases[curr_skill_phase]; if (phase->time_offset >= CurrentSkill()->GetPassedTime()) { ProcSkillPhase(phase); ++curr_skill_phase; } } else { playing_skill = false; } } else { playing_skill = false; } } void Creature::ProcSkillPhase(MetaData::SkillPhase* phase) { switch (phase->func_id) { case kSkill_TurnOver: { } break; case kSkill_JumpTo: { } break; case kSkill_Shot: { MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(phase->param1.GetInt()); if (weapon_meta) { MetaData::EquipUpgrade* weapon_upgrade_meta = MetaMgr::Instance()->GetEquipUpgrade(weapon_meta->i->id()); MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(weapon_meta->i->use_bullet()); if (CurrentSkill()->meta->i->skill_target() == kST_SpecDir) { if (std::abs(skill_dir_.x) > FLT_EPSILON || std::abs(skill_dir_.y) > FLT_EPSILON) { float target_distance = 5; if (bullet_meta && target_distance > 0.00001f) { a8::Vec2 old_attack_dir = attack_dir; attack_dir = skill_dir_; attack_dir.Normalize(); InternalShot ( this, weapon_meta, weapon_upgrade_meta, bullet_meta, 1, CurrentSkill() ? CurrentSkill()->meta->i->skill_id() : 0, target_distance, false); attack_dir = old_attack_dir; } } } else { Entity* entity = room->GetEntityByUniId(skill_target_id_); if (entity) { float target_distance = entity->GetPos().Distance(GetPos()); if (bullet_meta && target_distance > 0.00001f) { a8::Vec2 old_attack_dir = attack_dir; attack_dir = entity->GetPos() - GetPos(); attack_dir.Normalize(); InternalShot ( this, weapon_meta, weapon_upgrade_meta, bullet_meta, 1, CurrentSkill() ? CurrentSkill()->meta->i->skill_id() : 0, target_distance, false); attack_dir = old_attack_dir; } } } } } break; case kSkill_Pull: { } break; default: { } break; } } MetaData::SkillPhase* Creature::GetCurrSkillPhase() { return curr_skill_phase < CurrentSkill()->meta->phases.size() ? &CurrentSkill()->meta->phases[curr_skill_phase] : nullptr; } Skill* Creature::CurrentSkill() { return curr_skill_; } void Creature::ProcBuffEffect(Creature* caster, Buff* buff) { switch (buff->meta->i->buff_effect()) { case kBET_ChgAttr: case kBET_Car: case kBET_CrazyMode: { RecalcBuffAttr(); if (buff->meta->i->buff_effect() == kBET_Car) { RecalcSelfCollider(); MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(buff->meta->param4); if (equip_meta && equip_meta->i->equip_type() == EQUIP_TYPE_CAR && equip_meta->i->equip_subtype() == 1 ) { MetaData::Equip* spec_weapon_meta = MetaMgr::Instance()->GetEquip(equip_meta->int_param1); if (spec_weapon_meta) { second_weapon.weapon_idx = 100; second_weapon.weapon_id = spec_weapon_meta->i->id(); second_weapon.weapon_lv = 1; second_weapon.meta = spec_weapon_meta; second_weapon.Recalc(); second_weapon.ammo = second_weapon.GetClipVolume(); } } } } break; case kBET_TurnOver: { Skill* skill = CurrentSkill(); if (skill) { MetaData::SkillPhase* phase = GetCurrSkillPhase(); #if 1 if (phase) { #else if (phase && phase->time_offset >= skill->GetPassedTime()) { #endif if (HasBuffEffect(kBET_Car)) { _UpdateMove(phase->param1.GetDouble() * 1.5); } else { _UpdateMove(phase->param1); } } } } break; case kBET_Camouflage: { if (aiming) { aiming = false; } if (action_type != AT_None) { CancelAction(); } } break; case kBET_BePull: { if (caster == this) { abort(); } if (caster->GetEntitySubType() == EST_Android) { float target_distance = caster->GetPos().Distance(GetPos()); if (target_distance <= 0.000001f) { SetPos(caster->GetPos()); target_pos = caster->GetPos(); } else { if (target_distance <= buff->meta->param3) { SetPos(caster->GetPos()); target_pos = caster->GetPos(); } else { a8::Vec2 move_dir = caster->GetPos() - GetPos(); move_dir.Normalize(); SetMoveDir(move_dir); target_pos = GetPos() + GetMoveDir() * (target_distance - buff->meta->param3); } } } else { caster->skill_dir_.Normalize(); target_pos = caster->GetPos() + caster->skill_dir_ * caster->skill_distance_; a8::Vec2 move_dir = target_pos - GetPos(); move_dir.Normalize(); SetMoveDir(move_dir); } } break; case kBET_JumpTo: { Entity* entity = room->GetEntityByUniId(skill_target_id_); if (entity) { float target_distance = entity->GetPos().Distance(GetPos()); if (target_distance <= 0.000001f) { SetPos(entity->GetPos()); skill_target_pos_ = entity->GetPos(); } else { if (target_distance <= buff->meta->param3) { SetPos(entity->GetPos()); skill_target_pos_ = entity->GetPos(); } else { a8::Vec2 move_dir = entity->GetPos() - GetPos(); move_dir.Normalize(); SetMoveDir(move_dir); skill_dir_ = GetMoveDir(); skill_target_pos_ = GetPos() + GetMoveDir() * (target_distance - buff->meta->param3); } } } target_pos = skill_target_pos_; } break; case kBET_Pull: { int i = 0; } break; case kBET_Terminator: { #if 0 if (GetRace() == kHumanRace && MetaMgr::Instance()->terminator_meta && meta != MetaMgr::Instance()->terminator_meta) { WinExp(this, MetaMgr::Instance()->terminator_meta->i->exp() + 1); room->NotifySysPiao(TEXT("battle_server_terminator_appear", "终结者出现"), a8::MkRgb(255, 0, 0), 3); OnChgToTerminator(); } #endif } break; case kBET_PlayShotAni: { } break; case kBET_Vertigo: { int i = 0; } break; case kBET_DecHp: { #if 0 float def = hum->ability.def * (1 + hum->GetBuffAttrRate(kHAT_Def)) + hum->GetBuffAttrAbs(kHAT_Def); #endif #if 0 if (caster->GetEntityType() == ET_Player) { DecHP(buff->meta->param1, caster->GetEntityUniId(), ((Human*)caster)->name, 0); } #endif } break; case kBET_DelayAddBuff: { buff->ProcDelayAddBuff(caster); } break; case kBET_IntervalAddBuff: { buff->ProcIntervalAddBuff(caster); } break; case kBET_SummonHero: { SummonHero(buff, GetPos(), GetMoveDir(), buff->meta->hero_infos); } break; case kBET_Shield: { } break; case kBET_Hide: { } break; case kBET_SummonObstacle: { if (!dead) { a8::Vec2 target_pos = caster->GetPos() + caster->skill_dir_ * caster->skill_distance_; target_pos = buff_vec2_param1; SummonObstacle(buff, buff->meta->param1, target_pos); } } break; case kBET_Sprint: { buff->ProcSprint(caster); } break; case kBET_FlashMove: { a8::Vec2 old_pos = GetPos(); a8::Vec2 new_pos = GetPos() + skill_dir_ * std::max(skill_distance_, 300.0f); SetPos(new_pos); if (CollisonDetection()) { SetPos(old_pos); } else { room->grid_service->MoveCreature(this); } } break; case kBET_BatchAddBuff: { buff->ProcBatchAddBuff(caster); } break; case kBET_BeRecycle: { buff->ProcBeRecycle(caster); } break; case kBET_Become: { buff->ProcBecome(caster); } break; case kBET_Driver: { buff->ProcDriver(caster); } break; default: { } break; } } void Creature::Initialize() { MoveableEntity::Initialize(); } void Creature::SetMoveDir(const a8::Vec2& move_dir) { if (!GetBuffByEffectId(kBET_Sprint)) { MoveableEntity::SetMoveDir(move_dir); } } void Creature::AddSkill(int skill_id) { MetaData::Skill* skill_meta = MetaMgr::Instance()->GetSkill(skill_id); if (skill_meta && !GetSkill(skill_id)) { Skill* skill = new Skill; skill->owner = this; skill->meta = skill_meta; skill->xtimer_attacher.xtimer = &room->xtimer; skill->Initialzie(); skill_hash_[skill_id] = skill; } } void Creature::ClearSkill() { Skill* reserve_skill = nullptr; for (auto& pair : skill_hash_) { if (pair.second->meta->IsTurnOverSkill()) { reserve_skill = pair.second; } else { delete pair.second; } } skill_hash_.clear(); if (reserve_skill) { skill_hash_[reserve_skill->meta->i->skill_id()] = reserve_skill; } } bool Creature::CanSee(const Creature* c) const { return room->grid_service->InView(GetGridId(), c->GetGridId()); } float Creature::GetAttrAbs(int attr_id) { float attr_abs_val = GetBuffAttrAbs(attr_id); if (attr_id == kHAT_Atk || attr_id == kHAT_Def) { Buff* buff = GetBuffByEffectId(kBET_Car); if (buff) { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(buff->meta->param4); if (equip_meta) { switch (attr_id) { case kHAT_Atk: { attr_abs_val += equip_meta->i->atk(); } break; case kHAT_Def: { attr_abs_val += equip_meta->i->def(); } break; default: { } break; } } } } return attr_abs_val; } float Creature::GetAttrRate(int attr_id) { float attr_rate_val = GetBuffAttrRate(attr_id); return attr_rate_val; } 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); } 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; need_sync_active_player = true; if (HasBuffEffect(kBET_Camouflage)) { RemoveBuffByEffectId(kBET_Camouflage); } } void Creature::CancelAction() { if (action_type == AT_Relive) { Entity* entity = room->GetEntityByUniId(action_target_id); if (!entity->IsEntityType(ET_Player)) { Human* hum = (Human*)entity; if (hum->action_type == AT_Rescue) { hum->CancelAction(); } } } ResetAction(); } void Creature::ResetAction() { action_type = AT_None; action_duration = 0; action_frameno = 0; action_item_id = 0; action_target_id = 0; need_sync_active_player = true; } bool Creature::IsProperTarget(Creature* target) { if (target->dead) { return false; } if (a8::HasBitFlag(target->status, HS_Disable)) { return false; } if (team_id == target->team_id) { return false; } if (room->GetRoomMode() == kZombieMode && GetRace() == target->GetRace()) { return false; } if (target->IsInvincible()) { return false; } if (target->HasBuffEffect(kBET_Hide)) { return false; } return true; } bool Creature::IsEnemy(Creature* target) { if (room->GetRoomMode() == kZombieMode) { return target->GetRace() != GetRace(); } else { return team_id != target->team_id; } } void Creature::TouchProperTargets(std::function func) { auto callback = [this, func] (Creature* c, bool& stop) { if (IsProperTarget(c)) { func(c, stop); } }; room->grid_service->TouchCreatures(room->GetRoomIdx(), GetGridList(), callback); } void Creature::UpdatePoisoning() { if (dead) { return; } bool need_notify = poisoning_time > 1000; while (poisoning_time > 1000) { if (room->GetGasData().is_last_gas) { DecHP(room->GetGasData().new_area_meta->i->hurt(), VP_SafeArea, TEXT("battle_server_killer_gas", "毒圈"), VW_SafeArea); } else { DecHP(room->GetGasData().old_area_meta->i->hurt(), VP_SafeArea, TEXT("battle_server_killer_gas", "毒圈"), VW_SafeArea); } if (dead) { poisoning_time = 0; break; } poisoning_time -= 1000; } if (need_notify && IsEntitySubType(EST_Player)) { SyncAroundPlayers(__FILE__, __LINE__, __func__); } } void Creature::Shot(a8::Vec2& target_dir, bool& shot_ok, float fly_distance) { shot_ok = false; if (!GetCurrWeapon()->meta) { return; } if (downed) { return; } if (!GetCurrWeapon()->meta) { return; } if (GetCurrWeapon()->weapon_idx != 0 && GetCurrWeapon()->ammo <= 0) { 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 ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) < GetCurrWeapon()->GetAttrValue(kHAT_FireRate) ) { return; } if (GetCurrWeapon()->meta->power_charge.empty() || power_idx < 0) { InternalShot(this, GetCurrWeapon()->meta, GetCurrWeapon()->GetUpgradeMeta(), GetCurrWeapon()->bullet_meta, GetCurrWeapon()->weapon_lv, 0, fly_distance, false); } else if (power_idx < GetCurrWeapon()->meta->power_charge.size()) { MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip (std::get<1>(GetCurrWeapon()->meta->power_charge[power_idx])); if (weapon_meta) { MetaData::EquipUpgrade* weapon_upgrade_meta = MetaMgr::Instance()->GetEquipUpgrade (weapon_meta->i->id()); MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(weapon_meta->i->use_bullet());; if (bullet_meta) { #ifdef DEBUG SendDebugMsg(a8::Format("蓄力射击 %d 枪:%d 子弹:%d", { power_idx, weapon_meta->i->id(), bullet_meta->i->id() })); #endif InternalShot(this, weapon_meta, weapon_upgrade_meta, bullet_meta, GetCurrWeapon()->weapon_lv, 0, fly_distance, false); } else { abort(); } } else { abort(); } } else { abort(); } if (GetCurrWeapon()->weapon_idx != 0) { --GetCurrWeapon()->ammo; } int slot_id = GetCurrWeapon()->meta->i->_inventory_slot(); #ifdef DEBUG if (IsPlayer()) { SendDebugMsg(a8::Format("使用武器 %s slot:%d", {GetCurrWeapon()->meta->i->name(), slot_id})); } #endif switch (slot_id) { case IS_FRAG: //手雷 case IS_SMOKE: //烟雾弹 { if (GetCurrWeapon()->ammo <= 0) { if (GetInventory(slot_id) > 0) { DecInventory(slot_id, 1); ++GetCurrWeapon()->ammo; } else { int weapon_idx = GetCurrWeapon()->weapon_idx; *GetCurrWeapon() = Weapon(); GetCurrWeapon()->weapon_idx = weapon_idx; Weapon* next_weapon = ChooseNextWeapon(slot_id, SPEC1_IS_BEGIN, SPEC1_IS_END); if (!next_weapon) { next_weapon = ChooseNextWeapon(SPEC2_IS_BEGIN, SPEC2_IS_BEGIN, SPEC2_IS_END); } if (!next_weapon) { next_weapon = AutoChgWeapon(); } SetCurrWeapon(next_weapon); AutoLoadingBullet(); } } need_sync_active_player = true; SyncAroundPlayers(__FILE__, __LINE__, __func__); } break; case IS_POSION_GAS_BOMB: //毒气弹 case IS_MOLOTOR_COCKTAIL: //燃烧瓶 case IS_TRAP: //陷井 case IS_MINE: //地雷 { if (GetCurrWeapon()->ammo <= 0) { if (GetInventory(slot_id) > 0) { DecInventory(slot_id, 1); ++GetCurrWeapon()->ammo; } else { int weapon_idx = GetCurrWeapon()->weapon_idx; *GetCurrWeapon() = Weapon(); GetCurrWeapon()->weapon_idx = weapon_idx; Weapon* next_weapon = ChooseNextWeapon(slot_id, SPEC2_IS_BEGIN, SPEC2_IS_END); if (!next_weapon) { next_weapon = ChooseNextWeapon(SPEC1_IS_BEGIN, SPEC1_IS_BEGIN, SPEC1_IS_END); } if (!next_weapon) { next_weapon = AutoChgWeapon(); } SetCurrWeapon(next_weapon); AutoLoadingBullet(); } } need_sync_active_player = true; SyncAroundPlayers(__FILE__, __LINE__, __func__); } break; default: { } break; } if (GetCurrWeapon()->weapon_idx != 0 && GetCurrWeapon()->ammo <= 0) { AutoLoadingBullet(); } last_shot_frameno_ = room->GetFrameNo(); if (!need_sync_active_player && IsPlayer()) { room->frame_event.AddBulletNumChg((Human*)this); room->frame_event.AddWeaponAmmoChg((Human*)this); } 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())) ) { MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(p_weapon->meta->i->use_bullet()); if (bullet_meta && bullet_meta->i->_inventory_slot() >= 0 && bullet_meta->i->_inventory_slot() < (int)inventory_.size() ) { if (GetInventory(bullet_meta->i->_inventory_slot()) > 0) { if (on_loading_bullet) { on_loading_bullet(); } StartAction(AT_Reload, p_weapon->GetAttrValue(kHAT_ReloadTime), p_weapon->weapon_id, p_weapon->weapon_idx); } } return; } } int Creature::GetInventory(int slot_id) { if (!IsValidSlotId(slot_id)) { abort(); } return inventory_[slot_id]; } void Creature::AddInventory(int slot_id, int num) { assert(num > 0); if (!IsValidSlotId(slot_id)) { abort(); } inventory_[slot_id] += num; } void Creature::DecInventory(int slot_id, int num) { assert(num > 0); if (!IsValidSlotId(slot_id)) { abort(); } inventory_[slot_id] -= num; } void Creature::CheckSpecObject() { std::set colliders; room->map_service->GetSpecColliders(SPEC_MAP_OBJECT_FLAGS, room, GetPos().x, GetPos().y, colliders); #ifdef DEBUG long long old_cell_flags = cell_flags_; int water_w = 0; int water_h = 0; #endif cell_flags_ = 0; for (const ColliderComponent* collider : colliders) { switch (collider->owner->GetEntityType()) { case ET_Obstacle: case ET_Building: { if (TestCollision(room, (ColliderComponent*)collider)) { cell_flags_ |= collider->tag; #ifdef DEBUG if (a8::HasBitFlag(collider->tag, kColliderTag_Water)) { water_w = collider->param1; water_h = collider->param2; } #endif } } break; default: break; } } #ifdef DEBUG if (IsPlayer()) { if (old_cell_flags != cell_flags_) { std::string msg = "地形变化 old:"; { if (a8::HasBitFlag(old_cell_flags, kColliderTag_Grass)) { msg += " 草:1"; } else { msg += " 草:0"; } if (a8::HasBitFlag(old_cell_flags, kColliderTag_Water)) { msg += " 水:1"; } else { msg += " 水:0"; } if (a8::HasBitFlag(old_cell_flags, kColliderTag_Ice)) { msg += " 灰:1"; } else { msg += " 灰:0"; } } { msg += " new:"; if (a8::HasBitFlag(cell_flags_, kColliderTag_Grass)) { msg += " 草:1"; } else { msg += " 草:0"; } if (a8::HasBitFlag(cell_flags_, kColliderTag_Water)) { msg += " 水:1"; } else { msg += " 水:0"; } if (a8::HasBitFlag(cell_flags_, kColliderTag_Ice)) { msg += " 灰:1"; } else { msg += " 灰:0"; } } msg += a8::Format(" o:%d n:%d w:%d h:%d", {old_cell_flags, cell_flags_, water_w, water_h}); SendDebugMsg(msg); } } #endif } RoomObstacle* Creature::SummonObstacle(Buff* buff, int id, const a8::Vec2& pos) { RoomObstacle* obstacle = room->CreateObstacle(id, pos.x, pos.y); if (obstacle) { obstacle->master.Attach(this); obstacle->SetTeamId(room, team_id); obstacle->SetMasterId(room, GetEntityUniId()); obstacle->Active(); slave_things_.push_back(std::make_tuple(buff->meta->i->buff_id(), obstacle)); } else { abort(); } return obstacle; } bool Creature::CollisonDetection() { bool through_wall = HasBuffEffect(kBET_ThroughWall) || HasBuffEffect(kBET_Fly); AabbCollider aabb_box; GetAabbBox(aabb_box); return room->map_service->CollisionDetection ( room, through_wall, GetPos(), &aabb_box ); } void Creature::FillSkillCasterState(SkillCasterState* caster_state) { caster_state->caster.Attach(this); caster_state->caster_pos = GetPos(); caster_state->caster_skill_id = CurrentSkill() ? CurrentSkill()->meta->i->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) { SetPos(caster_state->caster_pos); 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) { abort(); } if (curr_weapon_slot_id > end_slot_id) { abort(); } if (begin_slot_id > end_slot_id) { abort(); } if (begin_slot_id < 0 || end_slot_id < 0) { 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 (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) { abort(); } #endif curr_weapon_ = weapon; } void Creature::ResetAllSkillCd() { for (auto& pair : skill_hash_) { pair.second->ResetSkillCd(); } } void Creature::SummonHero(Buff* buff, const a8::Vec2& pos, const a8::Vec2& dir, std::vector>& infos) { for (auto& info : 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); MetaData::Player* hero_meta = MetaMgr::Instance()->GetPlayer(hero_id); if (hero_meta && !dead) { for (int i = 0; i < 4; ++i) { a8::Vec2 born_dir = dir; a8::Vec2 born_offset(x, y); born_offset.Rotate(born_dir.CalcAngle(a8::Vec2::UP)); born_offset.Rotate(i * 0.5); a8::Vec2 hero_pos = pos + born_offset; CircleCollider collider; collider.pos = hero_pos; collider.rad = hero_meta->i->radius(); if (!room->map_service->CollisionDetection ( room, through_wall, hero_pos, &collider )) { Hero* hero = room->CreateHero (this, hero_meta, hero_pos, dir, team_id ); slave_heros_.push_back(std::make_tuple(buff->meta->i->buff_id(), hero)); break; } } } } } bool Creature::FreezeOperate() { Buff* buff = GetBuffByEffectId(kBET_Become); bool freeze = buff && buff->FreezeOperate(); if (!freeze) { freeze = GetBuffByEffectId(kBET_Vertigo) || GetBuffByEffectId(kBET_Driver) || 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 ((Entity*)std::get<1>(*itr) == slave) { slave_things_.erase(itr); break; } } } break; default: { 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->i->id() == id) { matched_heros.push_back(std::get<1>(itr)); } } while (matched_heros.size() >= num) { matched_heros[0]->DetachFromMaster(); 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)->meta->i->thing_id() == id) { matched_things.push_back(std::get<1>(itr)); } } while (matched_things.size() >= num) { matched_things[0]->DetachFromMaster(); matched_things.erase(matched_things.begin()); } } } bool Creature::IsInvincible() { return HasBuffEffect(kBET_Invincible) || HasBuffEffect(kBET_AdPlaying) || 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() { return ability.max_hp; }