#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; long long weapon_uniid = 0; 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 recoil_force = 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) { if (c->GetCurrWeapon()->meta->i->bullet_consume_type() == kBulletConsumeMulti) { if (c->GetCurrWeapon()->ammo <= 0) { return; } --c->GetCurrWeapon()->ammo; if (c->GetCurrWeapon()->ammo <= 0) { c->AutoLoadingBullet(); } if ((c->IsPlayer() || c->IsCar())) { c->room->frame_event.AddBulletNumChg(c->GetWeakPtrRef()); c->room->frame_event.AddWeaponAmmoChg(c->GetWeakPtrRef()); } } if (bullet_info.recoil_force > 0) { if (c->GetCurrWeapon()->ammo <= 0) { c->DoRecoilForce(bullet_info.recoil_force); bullet_info.bullet_born_pos = bullet_info.bullet_born_pos - (bullet_info.bullet_dir * bullet_info.recoil_force); } } int bullet_uniid = 0; if (MetaMgr::Instance()->prebattle_can_use_skill || !(c->HasBuffEffect(kBET_Jump) || c->HasBuffEffect(kBET_Fly))) { 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, bullet_info.weapon_uniid); #ifdef DEBUG if (bullet_info.c.Get()->IsPlayer()) { bullet_info.c.Get()->SendDebugMsg(a8::Format("CreateBullet id:%d", {bullet_info.bullet_meta->i->id()})); } #endif } bullet_uniid = bullet_uniid ? 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; xtimer_list* timer = 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; } ); if (!c->room->BattleStarted()) { c->room->AddToPostBattleAutoFreeList(timer); } } } 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, long long weapon_uniid) { if (weapon_meta->i->_inventory_slot() == IS_TRAP || weapon_meta->i->_inventory_slot() == IS_MINE) { if (skill_id == 0) { c->room->frame_event.AddShot(c->GetWeakPtrRef()); } if (weapon_meta->i->cast_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->i->cast_time() / FRAME_RATE_MS); } } a8::Vec2 old_context_dir = c->context_dir; a8::Vec2 old_context_pos = c->context_pos; c->context_dir =c->GetAttackDir(); 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_dir = old_context_dir; 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)); float bullet_born_angle = c->GetAttackDir().CalcAngleEx(a8::Vec2::UP); if (c->GetAttackDir().x > 0.00001f) { bullet_born_angle = -bullet_born_angle; } bullet_born_offset.Rotate(bullet_born_angle); 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); } } if (weapon_meta->i->cast_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->i->cast_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)); float bullet_born_angle = c->GetAttackDir().CalcAngleEx(a8::Vec2::UP); if (c->GetAttackDir().x > 0.00001f) { bullet_born_angle = -bullet_born_angle; } bullet_born_offset.Rotate(bullet_born_angle); 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_uniid = weapon_uniid; 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.recoil_force = std::get<4>(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) { c->DoRecoilForce(weapon_meta->i->recoil_force()); } 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() { xtimer_attacher.ClearTimerList(); ClearBuffList(); for (auto& pair : skill_hash_) { delete pair.second; } skill_hash_.clear(); ClearPassiveSkill(); trigger_->UnInit(); A8_SAFE_DELETE(trigger_); 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 (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, bool no_check_immune) { 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->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 (buff_meta->i->no_immune()) { no_check_immune = 1; } if (!no_check_immune && 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->buff_uniid) .SetParam2(buff), [] (const a8::XParams& param) { Creature* c = (Creature*)param.sender.GetUserData(); c->RemoveBuffByUniId(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); } #ifdef DEBUG { SendDebugMsg(a8::Format("添加buff_id:%d effect:%d %s params:%d,%d,%d,%d,%d uniid:%d dur:%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, 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) { 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, true); } } } return new_buff_uniid; } 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) { A8_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; } int Creature::TryAddBuffWithTarget(Creature* caster, int buff_id) { MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id); if (buff_meta) { bool be_add = false; switch (buff_meta->i->buff_target()) { case kBuffTargetSelf: { be_add = caster == this; } break; case kBuffTargetFriendly: { team_id == caster->team_id; } break; case kBuffTargetEnemy: { team_id != caster->team_id; } break; default: { } break; } return TryAddBuff(caster, buff_id); } return -1; } void Creature::RemoveBuffById(int buff_id) { int buff_uniid = 0; for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) { Buff& buff = *itr; if (buff.meta->i->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) { Buff& buff = *itr; if (buff.buff_uniid == buff_uniid) { buff_id = buff.meta->i->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); #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_) { 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; } } } for (auto& tuple : talent_list) { auto talent_meta = MetaMgr::Instance()->GetTalent(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->i->addtype() == 0) { float* p = ability_->GetBuffAttrAbsPtr(attr_type); if (p) { *p += attr_val; } } else if (talent_meta->i->addtype() == 1) { float* p = ability_->GetBuffAttrRatePtr(attr_type); if (p) { *p += attr_val; } } } } } if (need_refresh_hp) { SetHP(GetMaxHP()); GetTrigger()->HpChg(); } if (IsHuman()) { need_sync_active_player = true; } } void Creature::OnBuffRemove(Buff& buff) { switch (buff.meta->i->buff_effect()) { case kBET_Sprint: { DecDisableMoveDirTimes(); } break; case kBET_HoldShield: { buff.ProcRemoveHoldShield(); } break; case kBET_BePull: { int i = 0; } break; default: { } break; } if (!buff.meta->i->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->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 (room->GetGasData().gas_mode == GasInactive && !MetaMgr::Instance()->prebattle_can_use_skill) { return false; } if (HasBuffEffect(kBET_Vertigo) || HasBuffEffect(kBET_Jump) || HasBuffEffect(kBET_Fly) || HasBuffEffect(kBET_Sprint)) { 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 a8::Vec2& skill_dir, float skill_distance, const a8::Vec2& target_pos) { Skill* skill = GetSkill(skill_id); if (skill && CanUseSkill(skill_id)) { if (skill->GetMinorType()) { skill->DoMinorMode(); return; } DoSkillPreProc(skill_id, target_id, target_pos); 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_dir = skill_dir_; context_pos = GetPos() + skill_dir_ * skill_distance_; CurrentSkill()->last_use_frameno = room->GetFrameNo(); skill->LockCastPhase(); 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); 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 { UpdateSkill(); } } else { TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill); } } else { playing_skill = false; } } if (HasBuffEffect(kBET_Camouflage)) { RemoveBuffByEffectId(kBET_Camouflage); } GetTrigger()->UseSkill(skill); DoSkillPostProc(true, skill_id, target_id, target_pos); 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, 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, 0); 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(); SetAttackDir(attack_dir); InternalShot ( this, weapon_meta, weapon_upgrade_meta, bullet_meta, 1, CurrentSkill() ? CurrentSkill()->meta->i->skill_id() : 0, target_distance, false, 0); SetAttackDir(old_attack_dir); } } } } } break; case kSkill_Pull: { } break; case kSkill_HoldShield: { } break; case kSkill_ForthBackJump: { a8::Vec2 old_dir = GetMoveDir(); a8::Vec2 old_pos = GetPos(); if (CurrentSkill()->GetMinorType() == SMT_NONE) { int buff_id = phase->param3.GetInt(); //TryAddBuff(this, buff_id); a8::XPrintf("old_pos:%f,%f", {GetPos().x, GetPos().y}); _UpdateMove(phase->param1.GetInt()); a8::Vec2 pre_dir = old_dir; a8::Vec2 pre_pos = old_pos; a8::XPrintf("new_pos:%f,%f", {GetPos().x, GetPos().y}); CurrentSkill()->AddMinorMode ( SMT_BLINK, phase->param2.GetInt(), [this, pre_pos, pre_dir, buff_id] () { a8::Vec2 old_dir = GetMoveDir(); a8::Vec2 old_pos = GetPos(); float distance = GetPos().Distance(pre_pos); if (distance > 0.0001f) { a8::Vec2 new_dir = pre_pos - GetPos(); new_dir.Normalize(); SetMoveDir(new_dir); //TryAddBuff(this, buff_id); _UpdateMove(distance); } SetMoveDir(old_dir); } ); } SetMoveDir(old_dir); } 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::ActiveAllSkill() { for (auto& pair : skill_hash_) { pair.second->Accelerate(-10000000); } } void Creature::ProcBuffEffect(Creature* caster, Buff* buff) { MetaData::Buff* buff_meta = buff->meta; 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(); } if (IsHuman()) { ++AsHuman()->stats.use_camouflage_times; } } break; case kBET_BePull: { if (caster == this) { A8_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: { if (buff->meta->int_param1 == 0) { 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)) { A8_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; case kBET_AutoShot: { buff->ProcAutoShot(); } break; case kBET_BeatBack: { buff->ProcBeatBack(); } break; case kBET_Disperse: { buff->ProcDisperse(); } break; case kBET_PeaceMode: { } break; case kBET_Dive: { buff->ProcDive(); } break; case kBET_InWater: { buff->ProcInWater(); } break; case kBET_Reverse: { buff->ProcReserve(); } break; case kBET_ReverseMove: { buff->ProcReserveMove(); } break; case kBET_MachineGun: { buff->ProcMachineGun(); } break; case kBET_HoldShield: { buff->ProcHoldShield(); } 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 (GetDisableMoveDirTimes() <= 0) { 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 (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) { 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_Gas, TEXT("battle_server_killer_gas", "毒圈"), VW_Gas); 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 (HasBuffEffect(kBET_Jump) || HasBuffEffect(kBET_Fly)) { 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 (IsCar()) { if (room->GetFrameNo() - last_shot_frameno_ > 0) { if ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) < GetCurrWeapon()->GetAttrValue(kHAT_FireRate) ) { return; } } } else { 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, GetCurrWeapon()->weapon_uniid); } 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, 0); } else { A8_ABORT(); } } else { A8_ABORT(); } } else { A8_ABORT(); } if (GetCurrWeapon()->weapon_idx != 0 && GetCurrWeapon()->meta->i->bullet_consume_type() == kBulletConsumeOne) { --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 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; } } 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 { auto_switch_weapon = true; } } 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 { auto_switch_weapon = true; } } need_sync_active_player = true; 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())) ) { 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)); #if 0 duration_time = 1000 * 5; #endif 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() { 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: case ET_Dummy: case ET_MapBlock: { 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 = "terrain changed old:"; { if (a8::HasBitFlag(old_cell_flags, kColliderTag_Grass)) { msg += " grass:1"; } else { msg += " grass:0"; } if (a8::HasBitFlag(old_cell_flags, kColliderTag_Water)) { msg += " water:1"; } else { msg += " water:0"; } if (a8::HasBitFlag(old_cell_flags, kColliderTag_Ice)) { msg += " ice:1"; } else { msg += " ice:0"; } if (a8::HasBitFlag(old_cell_flags, kColliderTag_MountainTop)) { msg += " mountaintop:1"; } else { msg += " mountaintop:0"; } } { msg += " new:"; if (a8::HasBitFlag(cell_flags_, kColliderTag_Grass)) { msg += " grass:1"; } else { msg += " grass:0"; } if (a8::HasBitFlag(cell_flags_, kColliderTag_Water)) { msg += " water:1"; } else { msg += " water:0"; } if (a8::HasBitFlag(cell_flags_, kColliderTag_Ice)) { msg += " ice:1"; } else { msg += " ice:0"; } if (a8::HasBitFlag(cell_flags_, kColliderTag_MountainTop)) { msg += " mountaintop:1"; } else { msg += " mountaintop: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->buff_meta = buff->meta; 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)); #ifdef DEBUG SendDebugMsg(a8::Format("召唤物件 buff_id:%d thing_id:%d", {buff->meta->i->buff_id(), id})); #endif } else { #ifdef DEBUG A8_ABORT(); #endif } } 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) { 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_) { room->xtimer.DeleteTimer(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 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); int life_time = std::get<5>(info); MetaData::Player* hero_meta = MetaMgr::Instance()->GetPlayer(hero_id); if (hero_meta) { for (int j = 0; j < num; ++j) { bool can_create = false; a8::Vec2 born_pos; Hero* hero = nullptr; 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); born_pos = pos + 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 } } } 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: { 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->i->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)->meta->i->thing_id() == id) { matched_things.push_back(std::get<1>(itr)); } } while (matched_things.size() > num) { matched_things[0]->Destory(); 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, float radius) { float min_distance = 9999999999; room->grid_service->TraverseCreatures (room->GetRoomIdx(), GetGridList(), [this, &enemys, radius, &min_distance] (Creature* c, bool& stop) { if (IsProperTarget(c)) { float distance = GetPos().Distance(c->GetPos()); min_distance = std::min(min_distance, distance); if (distance < radius) { enemys.insert(c); } } }); int i = 0; } 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; inventory_[IS_ICE].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) { A8_ABORT(); } } else { AabbCollider aabb_collider; target->GetAabbBox(aabb_collider); bool ret = a_collider.CalcSafePoint(&aabb_collider, new_pos); if (!ret) { A8_ABORT(); } } } a8::Vec2 new_pos_dir = a8::Vec2::UP; //特殊情况处理 if (new_pos.ManhattanDistance(old_pos) > 0.0001f) { 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) { A8_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())){ ++collision_times_; 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) { ++collision_times_; 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; ++collision_times_; 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; ++collision_times_; 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) { if (dead) { return; } switch (buff_meta->int_param1) { case kHAT_Hp: { if (buff_meta->int_param2 == 1) { //绝对值 if (buff_meta->param3 > 0) { AddHp(buff_meta->param3); } else if (buff_meta->param3 < 0) { DecHP(std::abs(buff_meta->param3), VP_Buff, "", buff_meta->i->buff_id()); } } else if (buff_meta->int_param2 == 2) { //百分比 float chg_hp = ability.hp * buff_meta->param3; if (chg_hp > 0.0001f) { AddHp(chg_hp); } else if (chg_hp < 0.0001f){ DecHP(std::abs(chg_hp), VP_Buff, "", buff_meta->i->buff_id()); } } 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; } } void Creature::TraverseBuff(std::function func) { bool stop = false; for (auto& buff : buff_list_) { func(&buff, stop); if (stop) { return; } } } bool Creature::TrySummonHero(MetaData::Player* hero_meta, a8::Vec2 dir, a8::Vec2 born_pos, bool through_wall) { AabbCollider collider; collider._min.x = -hero_meta->i->radius(); collider._min.y = -hero_meta->i->radius(); collider._max.x = hero_meta->i->radius(); collider._max.y = hero_meta->i->radius(); collider.MoveCenter(hero_meta->i->move_offset_x(), hero_meta->i->move_offset_y()); return !room->map_service->CollisionDetection ( room, through_wall, born_pos, &collider ); } Hero* Creature::InternalSummonHero(Buff* buff, MetaData::Player* hero_meta, a8::Vec2 dir, a8::Vec2 born_pos, bool through_wall, int num, int life_time) { struct SummonHeroInfo { MetaData::Player* hero_meta = nullptr; a8::Vec2 dir; a8::Vec2 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.x, born_pos.y ); if (obstacle) { obstacle->DestoryAt(std::get<1>(tuple)); } delay_time = std::max(std::get<1>(tuple), delay_time); } SummonHeroInfo* summon_info = new SummonHeroInfo; summon_info->hero_meta = hero_meta; summon_info->dir = dir; summon_info->born_pos = born_pos; summon_info->through_wall = through_wall; summon_info->num = num; summon_info->life_time = life_time; room->xtimer.AddDeadLineTimerAndAttach ( delay_time / FRAME_RATE_MS + NEXT_FRAME_TIMER, a8::XParams() .SetSender(this) .SetParam1(summon_info) .SetParam2(buff->meta->i->buff_id()), [] (const a8::XParams& param) { Creature* sender = (Creature*)param.sender.GetUserData(); SummonHeroInfo* summon_info = (SummonHeroInfo*)param.param1.GetUserData(); Hero* hero = sender->room->CreateHero (sender, summon_info->hero_meta, summon_info->born_pos, summon_info->dir, sender->team_id ); if (!hero) { return; } int buff_id = param.param2; sender->RemoveSurplusHero(buff_id, summon_info->hero_meta->i->id(), summon_info->num); sender->slave_heros_.push_back(std::make_tuple(buff_id, hero)); hero->room->xtimer.AddDeadLineTimerAndAttach ( summon_info->life_time / FRAME_RATE_MS, a8::XParams(), [] (const a8::XParams& param) { }, &hero->xtimer_attacher.timer_list_, [] (const a8::XParams& param) { }); #ifdef DEBUG sender->SendDebugMsg(a8::Format("summon hero id:%d pos:%f,%f", { summon_info->hero_meta->i->id(), summon_info->born_pos.x, summon_info->born_pos.y })); #endif }, &xtimer_attacher.timer_list_, [] (const a8::XParams& param) { SummonHeroInfo* summon_info = (SummonHeroInfo*)param.param1.GetUserData(); delete summon_info; }); } return nullptr; } std::string Creature::DebugOutBuffList() { std::string data; for (auto& itr : buff_list_) { data += a8::Format("buffid:%d effect:%d\n", { itr.meta->i->buff_id(), itr.meta->i->buff_effect() }); } return data; } void Creature::AutoSwitchWeapon() { auto switch_func = [] (const a8::XParams& param) { Creature* c = (Creature*)param.sender.GetUserData(); int weapon_idx = param.param1; MetaData::Equip* weapon_meta = (MetaData::Equip*)param.param2.GetUserData(); if (!(c->GetCurrWeapon()->weapon_idx == weapon_idx && c->GetCurrWeapon()->meta == weapon_meta)) { return; } bool switch_ok = false; int slot_id = weapon_meta->i->_inventory_slot(); if (c->GetCurrWeapon()->ammo <= 0 && slot_id > 0) { if (c->GetInventory(slot_id) > 0) { } else { int weapon_idx = c->GetCurrWeapon()->weapon_idx; *c->GetCurrWeapon() = Weapon(); c->GetCurrWeapon()->weapon_idx = weapon_idx; Weapon* next_weapon = c->ChooseNextSpecWeapon(slot_id); if (!next_weapon) { next_weapon = c->AutoChgWeapon(); } if (next_weapon) { c->SetCurrWeapon(next_weapon); switch_ok = true; } else { A8_ABORT(); } } } if (switch_ok) { c->need_sync_active_player = true; c->SyncAroundPlayers(__FILE__, __LINE__, __func__); } c->CheckLoadingBullet(); }; a8::XParams param; param.SetSender(this); param.SetParam1(GetCurrWeapon()->weapon_idx); param.SetParam2(GetCurrWeapon()->meta); if (GetCurrWeapon()->meta->i->auto_switch_weapon_time() > 0) { if (auto_switch_weapon_timer_) { room->xtimer.DeleteTimer(auto_switch_weapon_timer_); } auto_switch_weapon_timer_ = room->xtimer.AddDeadLineTimerAndAttach ( GetCurrWeapon()->meta->i->auto_switch_weapon_time() / FRAME_RATE_MS, param, switch_func, &xtimer_attacher.timer_list_, [] (const a8::XParams& param) { Creature* c = (Creature*)param.sender.GetUserData(); c->auto_switch_weapon_timer_ = nullptr; }); } else { switch_func(param); } } void Creature::CheckLoadingBullet() { if (GetCurrWeapon()->weapon_idx != 0 && GetCurrWeapon()->ammo <= 0) { if (GetCurrWeapon()->meta->i->reload_delay_time() > 0) { room->xtimer.AddDeadLineTimerAndAttach ( GetCurrWeapon()->meta->i->reload_delay_time() / FRAME_RATE_MS, a8::XParams() .SetSender(this) .SetParam1(GetCurrWeapon()->weapon_id), [] (const a8::XParams& param) { Creature* c = (Creature*)param.sender.GetUserData(); int weapon_id = param.param1; if (c->GetCurrWeapon()->weapon_id == weapon_id) { c->AutoLoadingBullet(); } }, &xtimer_attacher.timer_list_ ); } else { 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->i->post_battle_valid()) { if (!buff.skill_meta || buff.skill_meta->i->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().gas_mode != GasInactive) { return false; } #if 0 if (!IsPlayer()) { return false; } #endif if (HasBuffEffect(kBET_Jump)) { return false; } if (!HasBuffEffect(kBET_Fly) && room->GetGasData().gas_mode != 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 a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); dir.Normalize(); a8::Vec2 target_pos = follow_target.Get()->GetPos() + dir * (30 + (rand() % 10)); if (GetPos().ManhattanDistance(target_pos) > 5) { a8::Vec2 move_dir = target_pos - GetPos(); move_dir.Normalize(); SetMoveDir(move_dir); } follow_target_last_chg_move_dir_times_ = follow_target.Get()->GetChgAttackDirTimes(); } } } void Creature::DoRecoilForce(int distance) { if (distance > 0) { a8::Vec2 old_move_dir = GetMoveDir(); MustBeAddBuff(this, kRecoilBuffId); SetMoveDir(GetAttackDir() * -1); _UpdateMove(distance); SetMoveDir(old_move_dir); } } void Creature::WinSkillExp(int win_exp) { if (!IsHuman()) { return; } } float Creature::GetAttrAbs(int attr_id) { float val = 0; if (GetAbility()){ val += GetAbility()->GetAttrAbs(attr_id); } return val; } float Creature::GetAttrRate(int attr_id) { float val = 0; if (GetAbility()){ val += GetAbility()->GetAttrRate(attr_id); } return val; } void Creature::RecalcDtoAttr() { if (!IsHuman()) { return; } if (GetCurrWeapon()) { if (GetBattleContext()) { } } else { if (GetBattleContext()) { } } } void Creature::SetBattleContext(std::shared_ptr c) { battle_context_ = c; } bool Creature::HasSpecMove() { return GetBuffByEffectId(kBET_JumpTo) || GetBuffByEffectId(kBET_BePull); } void Creature::_UpdateSpecMove() { if (!HasSpecMove()) { return; } bool move_end = false; float target_distance = target_pos.Distance(GetPos()); if (target_distance <= 0.000001f) { move_end = true; } else { a8::Vec2 old_move_dir = GetMoveDir(); a8::Vec2 move_dir = target_pos - GetPos(); move_dir.Normalize(); SetMoveDir(move_dir); bool is_collision = false; std::function old_on_move_collision = on_move_collision; on_move_collision = [&is_collision] () { is_collision = true; return false; }; _UpdateMove(std::min((int)target_distance, (int)GetSpeed())); on_move_collision = old_on_move_collision; move_dir = old_move_dir; target_distance = target_pos.Distance(GetPos()); if (is_collision || target_distance <= 1.0001f) { move_end = true; } } if (move_end) { Buff* buff = GetBuffByEffectId(kBET_JumpTo); if (buff) { if (CurrentSkill() && buff->skill_meta == CurrentSkill()->meta && !CurrentSkill()->meta->phases.empty()) { std::set target_list; metatable::Skill* mutable_skill_meta = (metatable::Skill*)CurrentSkill()->meta->i; float old_skill_distance = CurrentSkill()->meta->i->skill_distance(); mutable_skill_meta->set_skill_distance(CurrentSkill()->meta->phases[0].param1.GetDouble()); SelectSkillTargets(CurrentSkill(), GetPos(), target_list); mutable_skill_meta->set_skill_distance(old_skill_distance); TriggerBuff(CurrentSkill(), target_list, kBTT_SkillHit); } RemoveBuffByEffectId(kBET_JumpTo); } buff = GetBuffByEffectId(kBET_BePull); if (buff) { MetaData::Buff* new_buff = MetaMgr::Instance()->GetBuff(1039); if (new_buff) { AddBuff(this, new_buff, 1); } RemoveBuffByEffectId(kBET_BePull); } } }