#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" #include "trigger.h" #include "car.h" struct BulletInfo { CreatureWeakPtr c; MetaData::Equip* weapon_meta = nullptr; MetaData::EquipUpgrade* weapon_upgrade_meta = nullptr; MetaData::Equip* bullet_meta = nullptr; a8::Vec2 bullet_born_pos; a8::Vec2 bullet_dir; float fly_distance = 0; bool is_tank_skin = false; int weapon_lv = 0; int delay_time = 0; int invincible_buff_uniid = 0; }; static void InternalCreateBullet(BulletInfo& bullet_info) { if (!bullet_info.c.Get()) { return; } Creature* c = bullet_info.c.Get(); if (c->dead) { return; } if (c->downed) { return; } if (bullet_info.delay_time <= 0) { int bullet_uniid = 0; if (c->room->BattleStarted() || (c->room->GetGasData().gas_mode == GasJump && !c->HasBuffEffect(kBET_Jump))) { bullet_uniid = c->room->CreateBullet (c, c->shot_passenger, bullet_info.weapon_meta, bullet_info.weapon_upgrade_meta, bullet_info.bullet_meta, bullet_info.bullet_born_pos, bullet_info.bullet_dir, bullet_info.fly_distance, bullet_info.is_tank_skin); } if (bullet_uniid == 0) { bullet_uniid = c->room->AllocUniid(); } c->room->frame_event.AddBullet (bullet_uniid, c->GetWeakPtrRef(), bullet_info.weapon_meta, bullet_info.weapon_lv, bullet_info.bullet_born_pos, bullet_info.bullet_dir, bullet_info.fly_distance); } else { BulletInfo* info_copy = new BulletInfo(); *info_copy = bullet_info; bullet_info.c.Get()->room->xtimer.AddDeadLineTimerAndAttach ( bullet_info.delay_time / FRAME_RATE_MS, a8::XParams() .SetSender(info_copy), [] (const a8::XParams& param) { BulletInfo* info_copy = (BulletInfo*)param.sender.GetUserData(); info_copy->delay_time = 0; InternalCreateBullet(*info_copy); }, &bullet_info.c.Get()->xtimer_attacher.timer_list_, [] (const a8::XParams& param) { BulletInfo* info_copy = (BulletInfo*)param.sender.GetUserData(); delete info_copy; } ); } } 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_context_pos = c->context_pos; c->context_pos = c->GetPos() + c->GetAttackDir() * fly_distance; MetaData::Buff * buff_meta = MetaMgr::Instance()->GetBuff(bullet_meta->i->buffid()); if (buff_meta) { c->AddBuff(c, buff_meta, 1 ); } c->context_pos = old_context_pos; 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->GetAttackDir().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->GetWeakPtrRef()); } int invincible_buff_uniid = 0; if (weapon_meta->lock_time > 0) { int buff_uniid = c->TryAddBuff(c, kVertigoBuffId); Buff* buff = c->GetBuffByUniId(buff_uniid); if (buff && buff->remover_timer) { c->room->xtimer.ModifyTimer(buff->remover_timer, weapon_meta->lock_time / FRAME_RATE_MS); } } 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->GetAttackDir().CalcAngle(a8::Vec2::UP)); a8::Vec2 bullet_born_pos = c->GetPos() + c->shoot_offset + bullet_born_offset; a8::Vec2 bullet_dir = c->GetAttackDir(); 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); { BulletInfo bullet_info; bullet_info.c = c->GetWeakPtrRef(); bullet_info.weapon_meta = weapon_meta; bullet_info.weapon_upgrade_meta = weapon_upgrade_meta; bullet_info.bullet_meta = bullet_meta; bullet_info.bullet_born_pos = bullet_born_pos; bullet_info.bullet_dir = bullet_dir; bullet_info.fly_distance = fly_distance; bullet_info.is_tank_skin = is_tank_skin; bullet_info.weapon_lv = weapon_lv; bullet_info.delay_time = std::get<3>(tuple); bullet_info.invincible_buff_uniid = invincible_buff_uniid; InternalCreateBullet(bullet_info); } } c->GetTrigger()->Shot(weapon_meta); if (weapon_meta->i->recoil_force() > 0.000001) { a8::Vec2 old_move_dir = c->GetMoveDir(); c->MustBeAddBuff(c, kRecoilBuffId); c->SetMoveDir(c->GetAttackDir() * -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() { weak_ptr_chunk_.Set(this); trigger_ = new Trigger(this); trigger_->Init(); ability_ = std::make_shared(); ability_->owner = 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.weapon_lv = 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; } Creature::~Creature() { for (auto& pair : skill_hash_) { delete pair.second; } skill_hash_.clear(); ClearPassiveSkill(); trigger_->UnInit(); A8_SAFE_DELETE(trigger_); } 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 (Buff& buff : buff_list_) { if (buff.meta->i->buff_id() == buff_id) { return &buff; } } return nullptr; } Buff* Creature::GetBuffByUniId(int buff_uniid) { for (Buff& buff : buff_list_) { if (buff.buff_uniid == buff_uniid) { return &buff; } } return nullptr; } int Creature::GetBuffNum(int buff_id) { int num = 0; for (Buff& buff : buff_list_) { if (buff.meta->i->buff_id() == buff_id) { ++num; } } return num; } int Creature::AddBuff(Creature* caster, MetaData::Buff* buff_meta, int skill_lv, MetaData::Skill* buff_skill_meta) { if (buff_meta->i->buff_interval() > 0) { if (buff_interval_hash_.find(buff_meta->i->buff_id()) != buff_interval_hash_.end()) { return 0; } buff_interval_hash_[buff_meta->i->buff_id()] = room->GetFrameNo(); room->xtimer.AddDeadLineTimerAndAttach ( buff_meta->i->buff_interval() / FRAME_RATE_MS, a8::XParams() .SetSender(this) .SetParam1(buff_meta->i->buff_id()), [] (const a8::XParams& param) { Creature* c = (Creature*)param.sender.GetUserData(); c->buff_interval_hash_.erase(param.param1); }, &xtimer_attacher.timer_list_); } if (buff_meta->i->coexist_num() > 0){ if (GetBuffNum(buff_meta->i->buff_id()) >= buff_meta->i->coexist_num()) { return 0; } } if (IsImmuneBuffEffect(buff_meta->i->buff_effect())) { return 0; } if (buff_meta->i->depend_effect() != 0 && !HasBuffEffect(buff_meta->i->depend_effect())) { return 0; } 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) { ProcOnceChgAttrBuff(buff_meta); } ++buff_uniid_; int new_buff_uniid = buff_uniid_; Buff* buff = &a8::FastAppend(buff_list_); buff->buff_uniid = new_buff_uniid; buff->SetCaster(caster); 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->Init(); list_add_tail(&buff->effect_entry, &buff_effect_[buff->meta->i->buff_effect()]); if (buff->meta->i->depend_effect() != 0 && IsValidBuffEffect(buff->meta->i->depend_effect())) { list_add_tail(&buff->depend_entry, &depend_effect_[buff->meta->i->depend_effect()]); } { buff->remover_timer = room->xtimer.AddDeadLineTimerAndAttach ( buff_meta->i->duration_time() * SERVER_FRAME_RATE, a8::XParams() .SetSender(this) .SetParam1(buff_meta->i->buff_id()) .SetParam2(buff), [] (const a8::XParams& param) { Creature* c = (Creature*)param.sender.GetUserData(); c->RemoveBuffById(param.param1); }, &buff->xtimer_attacher.timer_list_, [] (const a8::XParams& param) { Buff* buff = (Buff*)param.param2.GetUserData(); if (buff->remover_timer) { buff->remover_timer = nullptr; } }); } GetTrigger()->ActiveBuff(buff->meta); ProcBuffEffect(caster, buff); AddBuffPostProc(caster, buff); if (!buff->meta->i->only_server()) { room->frame_event.AddBuff(GetWeakPtrRef(), buff); } if (!buff->meta->child_buff_list.empty()) { for (int child_buff_id : buff->meta->child_buff_list) { MetaData::Buff* child_buff_meta = MetaMgr::Instance()->GetBuff(child_buff_id); if (child_buff_meta) { AddBuff(caster, child_buff_meta, skill_lv, buff_skill_meta); } } } return new_buff_uniid; #ifdef DEBUG SendDebugMsg(a8::Format("添加buff_id:%d effect:%d %s params:%d,%d,%d,%d,%d", { buff_meta->i->buff_id(), buff_meta->i->buff_effect(), buff_meta->i->name(), buff_meta->int_param1, buff_meta->int_param2, buff_meta->int_param3, buff_meta->int_param4, buff_meta->int_param5, })); #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; } int Creature::MustBeAddBuff(Creature* caster, int buff_id) { MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id); if (!buff_meta) { abort(); } return AddBuff(caster, buff_meta, 1); } int Creature::TryAddBuff(Creature* caster, int buff_id) { MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id); if (buff_meta) { return AddBuff(caster, buff_meta, 1); } return -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) { removed_buffs.push_back(std::make_tuple(buff.meta, buff.GetCaster().Get())); OnBuffRemove(buff); buff.UnInit(); buff_list_.erase(itr); break; } } for (auto& tuple1 : removed_buffs) { MetaData::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->i->buff_effect()) && !list_empty(&depend_effect_[buff_meta->i->buff_effect()])) { struct list_head work_list; list_replace_init(&depend_effect_[buff_meta->i->buff_effect()], &work_list); while (!list_empty(&work_list)) { Buff* buff = list_first_entry(&work_list, Buff, depend_entry); RemoveBuffById(buff->meta->i->buff_id()); } } GetTrigger()->DeactiveBuff(buff_meta); 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(); #ifdef DEBUG SendDebugMsg(a8::Format("移除buff_id:%d", { buff_id })); #endif } 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_) { 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) { float* p = ability_->GetBuffAttrAbsPtr(attr_type); if (p) { *p += buff.meta->param3; } } else if (calc_type == 2) { float* p = ability_->GetBuffAttrRatePtr(attr_type); if (p) { *p += buff.meta->param3; } } } if (buff.add_frameno == room->GetFrameNo() && attr_type == kHAT_MaxHp) { need_refresh_hp = true; } } } if (need_refresh_hp) { SetHP(GetMaxHP()); GetTrigger()->HpChg(); } if (IsHuman()) { need_sync_active_player = true; } } 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) { list_del_init(&itr->effect_entry); OnBuffRemove(*itr); } buff_list_.clear(); buff_effect_ = {}; RecalcBuffAttr(); } 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 0 if (skill_meta->i->skill_cd() > 10000) { //永久被动被动技能 skill->AddPassiveSkillBuff(); } #endif } } 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 (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; } 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; context_pos = GetPos() + skill_dir_ * skill_distance_; CurrentSkill()->last_use_frameno = room->GetFrameNo(); if (CurrentSkill()->meta->i->skill_target() == kST_Self ) { skill_target_id_ = GetUniId(); } 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 { if (skill_target_id_ == 0) { if (CurrentSkill()->meta->i->skill_target() == kST_FriendlyIncludeSelf || CurrentSkill()->meta->i->skill_target() == kST_FriendlyExcludeSelf) { skill_target_id_ = GetUniId(); } } 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); } GetTrigger()->UseSkill(skill); 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 = GetAttackDir(); a8::Vec2 attack_dir = skill_dir_; attack_dir.Normalize(); SetAttackDir(attack_dir); InternalShot ( this, weapon_meta, weapon_upgrade_meta, bullet_meta, 1, CurrentSkill() ? CurrentSkill()->meta->i->skill_id() : 0, target_distance, false); SetAttackDir(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 = GetAttackDir(); a8::Vec2 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); SetAttackDir(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: { buff->ProcTurnOver(); } 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->GetUniId(), ((Human*)caster)->name, 0); } #endif } break; case kBET_DelayAddBuff: { buff->ProcDelayAddBuff(); } break; case kBET_IntervalAddBuff: { buff->ProcIntervalAddBuff(); } break; case kBET_OnceAddHp: { AddHp(buff->meta->param1); } break; case kBET_SummonHero: { if (!dead || buff->meta->i->dead_valid() != 0) { SummonHero(buff, GetPos(), GetMoveDir()); } } break; case kBET_Shield: { } break; case kBET_Hide: { } break; case kBET_SummonObstacle: { if (!dead || buff->meta->i->dead_valid() != 0) { SummonObstacle(buff, buff->meta->param1, context_pos); } } break; case kBET_Sprint: { buff->ProcSprint(); } 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(); } break; case kBET_BeRecycle: { buff->ProcBeRecycle(); } break; case kBET_Become: { buff->ProcBecome(); } break; case kBET_SelectTargetWithSelfPos: { buff->ProcSeletTargetWithSelfPos(); } break; case kBET_Driver: { buff->ProcDriver(); } break; case kBET_Passenger: { buff->ProcPassenger(); } break; case kBET_PullToWalkable: { buff->ProcPullToWalkable(); } break; case kBET_CondAddBuff: { if (!IsValidCondBuff(buff->meta->int_param1)) { abort(); } list_add_tail(&buff->cond_entry, &cond_buffs_[buff->meta->int_param1]); } break; case kBET_AddInventory: { for (int slot : buff->meta->param2_int_list) { if (IsValidSlotId(slot)) { buff_inventory_[slot].num += buff->meta->int_param1; } } if (IsHuman()) { AsHuman()->RecalcVolume(); } need_sync_active_player = true; } break; case kBET_AddCarBuff: { if (IsHuman() && AsHuman()->GetCar()) { AsHuman()->GetCar()->TryAddBuff(this, buff->meta->int_param1); } } break; case kBET_RemoveCarBuff: { if (IsHuman() && AsHuman()->GetCar()) { AsHuman()->GetCar()->RemoveBuffById(buff->meta->int_param1); } } break; case kBET_UseSkill: { Skill* skill = GetSkill(buff->meta->int_param1); if (skill) { ++skill->force_use_times; a8::Vec2 target_pos = GetPos(); DoSkill( skill->meta->i->skill_id(), GetUniId(), GetAttackDir(), 0, target_pos ); } } break; case kBET_CamouflageAddition: { Buff* camouflage_buff = GetBuffByEffectId(kBET_Camouflage); if (camouflage_buff && camouflage_buff->remover_timer && buff->meta->int_param1 > 0) { int remain_time = room->xtimer.GetRemainTime(camouflage_buff->remover_timer) * FRAME_RATE_MS; remain_time += buff->meta->int_param1; room->xtimer.ModifyTimer(camouflage_buff->remover_timer, remain_time / FRAME_RATE_MS); } if (buff->meta->int_param2 != 0) { ++camouflage_move_addition_; } if (buff->meta->int_param3 != 0) { ++camouflage_aiming_addition_; } } break; default: { } break; } } 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 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)) { if (skill_meta->i->skill_type() == 1) { Skill* skill = new Skill; skill->owner = this; skill->meta = skill_meta; skill->xtimer_attacher.xtimer = &room->xtimer; skill->Initialzie(); skill_hash_[skill_id] = skill; } else if (skill_meta->i->skill_type() == 2) { AddPassiveSkill(skill_id); } else { return; } } } 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()); } 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; 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(); } } } if (action_type == AT_Rescue) { RemoveBuffById(kRescueBuffId); } 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, 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 (room->GetRoomMode() == kZombieMode && GetRace() == target->GetRace()) { 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) { if (room->GetRoomMode() == kZombieMode) { return target->GetRace() != GetRace(); } else { 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) { 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->i->hurt(); } else { dmg = room->GetGasData().old_area_meta->i->hurt(); } dmg *= 1 + GetAbility()->GetAttrRate(kHAT_PoisoningReduction); dmg = std::max(10.0f, dmg); DecHP(dmg, 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 1 DecInventory(slot_id, 1); #endif 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 1 DecInventory(slot_id, 1); #endif 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; 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 { int weapon_idx = GetCurrWeapon()->weapon_idx; *GetCurrWeapon() = Weapon(); GetCurrWeapon()->weapon_idx = weapon_idx; Weapon* next_weapon = ChooseNextWeapon(slot_id, SPEC3_IS_BEGIN, SPEC3_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 ((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())) ) { 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(); } int duration_time = p_weapon->GetAttrValue(kHAT_ReloadTime) * (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)) { abort(); } return inventory_[slot_id].num; } void Creature::AddInventory(int slot_id, int num) { assert(num > 0); if (!IsValidSlotId(slot_id)) { abort(); } inventory_[slot_id].num += num; } void Creature::DecInventory(int slot_id, int num) { assert(num > 0); if (!IsValidSlotId(slot_id)) { abort(); } inventory_[slot_id].num -= num; } void Creature::CheckSpecObject() { std::set colliders; room->map_service->GetSpecColliders(SPEC_MAP_OBJECT_FLAGS, room, GetPos().x, GetPos().y, colliders); #ifdef DEBUG int water_w = 0; int water_h = 0; #endif long long old_cell_flags = cell_flags_; 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; } } if (old_cell_flags != cell_flags_) { if (!a8::SameBitFlag(old_cell_flags, cell_flags_, kColliderTag_Grass)) { if (a8::HasBitFlag(cell_flags_, kColliderTag_Grass)) { TryAddBuff(this, kInGrassBuffId); } else { RemoveBuffById(kInGrassBuffId); } } if (!a8::SameBitFlag(old_cell_flags, cell_flags_, kColliderTag_Water)) { if (a8::HasBitFlag(cell_flags_, kColliderTag_Water)) { TryAddBuff(this, kInWaterBuffId); } else { RemoveBuffById(kInWaterBuffId); } } if (!a8::SameBitFlag(old_cell_flags, cell_flags_, kColliderTag_Ice)) { if (a8::HasBitFlag(cell_flags_, kColliderTag_Ice)) { TryAddBuff(this, kInIceBuffId); } else { RemoveBuffById(kInIceBuffId); } } if (!a8::SameBitFlag(old_cell_flags, cell_flags_, kColliderTag_MountainTop)) { if (a8::HasBitFlag(cell_flags_, kColliderTag_MountainTop)) { TryAddBuff(this, kInMountainTopBuffId); } else { RemoveBuffById(kInMountainTopBuffId); } } } #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"; } if (a8::HasBitFlag(old_cell_flags, kColliderTag_MountainTop)) { 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"; } if (a8::HasBitFlag(cell_flags_, kColliderTag_MountainTop)) { 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 } void Creature::SummonObstacle(Buff* buff, int id, const a8::Vec2& pos) { if (buff->meta->int_param2 > 0) { RemoveSurplusObstacle(buff->meta->i->buff_id(), id, buff->meta->int_param2); } RoomObstacle* obstacle = room->CreateObstacle(id, pos.x, pos.y); if (obstacle) { obstacle->master.Attach(this); obstacle->SetTeamId(room, team_id); obstacle->SetMasterId(room, GetUniId()); obstacle->Active(); obstacle->context_ability = context_ability; slave_things_.push_back(std::make_tuple(buff->meta->i->buff_id(), obstacle)); } else { abort(); } } bool Creature::CollisonDetection() { ColliderComponent* pickup_collider = nullptr; return CollisonDetectionAndGetCollider(&pickup_collider); } bool Creature::CollisonDetectionAndGetCollider(ColliderComponent** pickup_collider) { bool through_wall = HasBuffEffect(kBET_ThroughWall) || HasBuffEffect(kBET_Fly); AabbCollider aabb_box; GetAabbBox(aabb_box); return room->map_service->CollisionDetectionAndGetCollider ( room, through_wall, GetPos(), &aabb_box, pickup_collider ); } 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 if (curr_weapon_ != weapon) { GetTrigger()->TakeonWeapon(curr_weapon_, weapon); } 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 ) { 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); MetaData::Player* hero_meta = MetaMgr::Instance()->GetPlayer(hero_id); if (hero_meta) { 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 )) { RemoveSurplusHero(buff->meta->i->buff_id(), hero_id, num); 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 * (1 + GetAbility()->GetAttrRate(kHAT_MaxHp)) + GetAbility()->GetAttrAbs(kHAT_MaxHp); } float Creature::GetDef() { return ability.def; } void Creature::SetDef(float def) { ability.def = def; } void Creature::GetHitEnemys(std::set& enemys) { room->grid_service->TraverseCreatures (room->GetRoomIdx(), GetGridList(), [this, &enemys] (Creature* c, bool& stop) { if (IsProperTarget(c)) { AabbCollider aabb_box; c->GetHitAabbBox(aabb_box); if (!c->dead && TestCollision(room, &aabb_box)) { 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; SyncAroundPlayers(__FILE__, __LINE__, __func__); } } void Creature::SetHP(float hp) { ability.hp = hp; } void Creature::SetMaxHP(float max_hp) { ability.max_hp = max_hp; } bool Creature::TryMove(const a8::Vec2& target_pos, a8::Vec2& out_pos) { bool move_ok = false; a8::Vec2 old_pos = GetPos(); out_pos = GetPos(); SetPos(target_pos); if (CollisonDetection()) { out_pos = target_pos; move_ok = true; } else { } return move_ok; } 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; } void Creature::FindLocationWithTarget(Entity* target, ColliderComponent* target_collider) { a8::Vec2 old_pos = GetPos(); a8::Vec2 new_pos = GetPos(); AabbCollider a_collider; GetAabbBox(a_collider); { if (target_collider) { bool ret = a_collider.CalcSafePoint(target_collider, new_pos); if (!ret) { abort(); } } else { AabbCollider aabb_collider; target->GetAabbBox(aabb_collider); bool ret = a_collider.CalcSafePoint(&aabb_collider, new_pos); if (!ret) { abort(); } } } a8::Vec2 new_pos_dir = new_pos - old_pos; new_pos_dir.Normalize(); float distance = (new_pos - old_pos).Norm(); for (int i = distance; i < 10000000; i += 5) { SetPos(old_pos + new_pos_dir * i); room->grid_service->MoveCreature(this); Entity* building = nullptr; std::set tmp_grids; room->grid_service->GetAllCellsByXy(room, GetX(), GetY(), tmp_grids); room->grid_service->TraverseAllLayerEntityList ( room->GetRoomIdx(), tmp_grids, [this, &building] (Entity* entity, bool& stop) { switch (entity->GetEntityType()) { case ET_Building: { if (TestCollision(room, entity)) { building = entity; stop = true; } } break; default: break; } }); if (!building) { bool is_collision = false; std::set colliders; room->map_service->GetColliders(room, GetX(), GetY(), colliders); for (ColliderComponent* collider : colliders) { if (TestCollision(room, collider)) { is_collision = true; break; } } if (!is_collision) { break; } } } } void Creature::FindLocation() { ColliderComponent* target_collider = nullptr; if (CollisonDetectionAndGetCollider(&target_collider)) { if (!target_collider) { abort(); } } if (target_collider) { FindLocationWithTarget(target_collider->owner, target_collider); } } 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; } bool Creature::CheckCollision() { Global::last_collider = nullptr; if (room->OverBorder(GetPos(), GetRadius())){ return true; } if (HasBuffEffect(kBET_ThroughWall) || HasBuffEffect(kBET_Fly)) { return false; } std::set colliders; room->map_service->GetColliders(room, GetX(), GetY(), colliders); AabbCollider aabb_box; GetAabbBox(aabb_box); for (ColliderComponent* collider : colliders) { if (last_collision_door_ != collider->owner) { switch (collider->owner->GetEntityType()) { case ET_Obstacle: { Obstacle* obstacle = (Obstacle*)collider->owner; if (!obstacle->CanThroughable(this)) { if ( (collider->type == CT_Aabb && aabb_box.Intersect(collider)) || (collider->type == CT_Circle && self_collider_->Intersect(collider)) ) { if (obstacle->OnCollisionTrigger(this, collider) == 1) { return true; } } } } break; case ET_Building: { if ( (collider->type == CT_Aabb && aabb_box.Intersect(collider)) || (collider->type == CT_Circle && self_collider_->Intersect(collider)) ) { Global::last_collider = collider; return true; } } break; case ET_Dummy: { if ( (collider->type == CT_Aabb && aabb_box.Intersect(collider)) || (collider->type == CT_Circle && self_collider_->Intersect(collider)) ) { Global::last_collider = collider; return true; } } break; default: { } break; } } } return false; } void Creature::CheckAbilityUsed() { if (ability_.use_count() > 1) { std::shared_ptr old_val = ability_; ability_ = std::make_shared(); *ability_.get() = *(old_val.get()); } } void Creature::RefreshHP() { } void Creature::ProcOnceChgAttrBuff(MetaData::Buff* buff_meta) { switch (buff_meta->int_param1) { case kHAT_Hp: { if (buff_meta->int_param2 == 1) { //绝对值 ability.hp += buff_meta->param3; ability.hp = std::min(ability.max_hp, ability.hp); } else if (buff_meta->int_param2 == 2) { //百分比 ability.hp *= 1 + buff_meta->param3; ability.hp = std::min(ability.max_hp, ability.hp); } SyncAroundPlayers(__FILE__, __LINE__, __func__); } break; case kHAT_SkillTime: { if (buff_meta->int_param2 == 1) { for (auto& pair : skill_hash_) { pair.second->Accelerate(buff_meta->int_param3); } } else if (buff_meta->int_param2 == 2) { for (auto& pair : skill_hash_) { int time = pair.second->GetCd() * buff_meta->param3; pair.second->Accelerate(time); } } } break; default: { } break; } }