game2005/server/gameserver/creature.cc
2021-06-08 18:57:27 +08:00

1841 lines
55 KiB
C++

#include "precompile.h"
#include <float.h>
#include "creature.h"
#include "metamgr.h"
#include "room.h"
#include "skill.h"
#include "human.h"
#include "hero.h"
#include "collider.h"
#include "roomobstacle.h"
void InternalShot(Creature* c,
MetaData::Equip* weapon_meta,
MetaData::EquipUpgrade* weapon_upgrade_meta,
MetaData::Equip* bullet_meta,
int weapon_lv,
int skill_id,
float fly_distance,
bool is_tank_skin)
{
if (weapon_meta->i->_inventory_slot() == IS_TRAP ||
weapon_meta->i->_inventory_slot() == IS_MINE) {
a8::Vec2 old_buff_vec2_param1 = c->buff_vec2_param1;
c->buff_vec2_param1 = c->GetPos();
MetaData::Buff * buff_meta = MetaMgr::Instance()->GetBuff(bullet_meta->i->buffid());
if (buff_meta) {
c->AddBuff(c,
buff_meta,
1
);
}
c->buff_vec2_param1 = old_buff_vec2_param1;
return;
}
for (auto& tuple : weapon_meta->bullet_born_offset) {
a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple));
bullet_born_offset.Rotate(c->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());
}
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);
c->room->frame_event.AddBullet(c->GetWeakPtrRef(),
weapon_meta,
weapon_lv,
bullet_born_pos,
bullet_dir,
fly_distance);
if (c->room->BattleStarted() ||
(c->room->GetGasData().gas_mode == GasJump &&
!c->HasBuffEffect(kBET_Jump))) {
c->room->CreateBullet(c,
c->shot_passenger,
weapon_meta,
weapon_upgrade_meta,
bullet_meta,
bullet_born_pos,
bullet_dir,
fly_distance,
is_tank_skin);
}
}
if (weapon_meta->i->recoil_force() > 0.000001) {
a8::Vec2 old_move_dir = c->GetMoveDir();
c->MustBeAddBuff(c, kRecoilBuffId);
c->SetMoveDir(c->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()
{
weapons.reserve(MAX_WEAPON_NUM);
for (size_t i = 0; i < MAX_WEAPON_NUM; ++i) {
auto& weapon = a8::FastAppend(weapons);
weapon.weapon_idx = i;
weapon.weapon_id = 0;
weapon.weapon_lv = 0;
weapon.ammo = 0;
}
weak_ptr_chunk_.Set(this);
inventory_[IS_1XSCOPE] = 1;
if (MetaMgr::Instance()->fighting_mode) {
inventory_[IS_9MM] = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_556MM] = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_762MM] = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_12GAUGE] = FIGHTING_MODE_BULLET_NUM;
inventory_[IS_RPG] = FIGHTING_MODE_BULLET_NUM;
}
}
Creature::~Creature()
{
for (auto& pair : skill_hash_) {
delete pair.second;
}
skill_hash_.clear();
ClearPassiveSkill();
}
bool Creature::HasBuffEffect(int buff_effect_id)
{
return GetBuffByEffectId(buff_effect_id) != nullptr;
}
Buff* Creature::GetBuffByEffectId(int effect_id)
{
return IsValidBuffEffect(effect_id) ? buff_effect_[effect_id] : nullptr;
}
Buff* Creature::GetBuffById(int buff_id)
{
for (Buff& buff : buff_list_) {
if (buff.meta->i->buff_id() == buff_id) {
return &buff;
}
}
return nullptr;
}
void Creature::AddBuff(Creature* caster,
MetaData::Buff* buff_meta,
int skill_lv,
MetaData::Skill* buff_skill_meta)
{
#if 0
if (GetBuffById(buff_meta->i->buff_id())) {
return;
}
#endif
if (IsImmuneBuffEffect(buff_meta->i->buff_effect())) {
return;
}
if (!buff_meta->EffectCanStack()) {
Buff* buff = GetBuffByEffectId(buff_meta->i->buff_effect());
if (buff) {
RemoveBuffById(buff->meta->i->buff_id());
}
}
if (buff_meta->i->buff_effect() == kBET_OnceChgAttr) {
if ((int)buff_meta->param1== kHAT_Hp) {
if ((int)buff_meta->param2 == 1) {
//绝对值
ability.hp += buff_meta->param3;
ability.hp = std::min(ability.max_hp, ability.hp);
} else if ((int)buff_meta->param2 == 2) {
//百分比
ability.hp *= 1 + buff_meta->param3;
ability.hp = std::min(ability.max_hp, ability.hp);
}
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
}
Buff* buff = &a8::FastAppend(buff_list_);
buff->skill_lv = skill_lv;
buff->owner = this;
buff->meta = buff_meta;
buff->skill_meta = buff_skill_meta;
buff->add_frameno = room->GetFrameNo();
buff->xtimer_attacher.xtimer = &room->xtimer;
buff_effect_[buff->meta->i->buff_effect()] = buff;
{
room->xtimer.AddDeadLineTimerAndAttach
(
buff_meta->i->duration_time() * SERVER_FRAME_RATE,
a8::XParams()
.SetSender(this)
.SetParam1(buff_meta->i->buff_id()),
[] (const a8::XParams& param)
{
Creature* c = (Creature*)param.sender.GetUserData();
c->RemoveBuffById(param.param1);
},
&buff->xtimer_attacher.timer_list_
);
}
ProcBuffEffect(caster, buff);
AddBuffPostProc(caster, buff);
if (!buff->meta->i->only_server()) {
room->frame_event.AddBuff(GetWeakPtrRef(), buff);
}
#ifdef DEBUG
SendDebugMsg(a8::Format("添加buff_id:%d buff_effect:%d",
{
buff_meta->i->buff_id(),
buff_meta->i->buff_effect()
}));
#endif
}
bool Creature::IsImmuneBuffEffect(int buff_effect)
{
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
if (itr->meta->IsImmuneBuffEffect(buff_effect)) {
return true;
}
}
return false;
}
void Creature::MustBeAddBuff(Creature* caster, int buff_id)
{
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id);
if (!buff_meta) {
abort();
}
AddBuff(caster, buff_meta, 1);
}
void Creature::TryAddBuff(Creature* caster, int buff_id)
{
MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id);
if (buff_meta) {
AddBuff(caster, buff_meta, 1);
}
}
void Creature::RemoveBuffById(int buff_id)
{
std::vector<MetaData::Buff*> removed_buffs;
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
Buff& buff = *itr;
if (buff.meta->i->buff_id() == buff_id) {
if (buff_effect_[buff.meta->i->buff_effect()] == &(*itr)) {
buff_effect_[buff.meta->i->buff_effect()] = nullptr;
}
removed_buffs.push_back(buff.meta);
OnBuffRemove(buff);
buff_list_.erase(itr);
break;
}
}
for (MetaData::Buff* buff_meta : removed_buffs) {
for (const auto& tuple : buff_meta->post_remove_action) {
switch (std::get<0>(tuple)) {
case kRemoveBuffByIdAction:
{
for (int buff_id :std::get<1>(tuple)) {
RemoveBuffById(buff_id);
}
}
break;
case kRemoveBuffByEffectAction:
{
for (int buff_effect :std::get<1>(tuple)) {
RemoveBuffByEffectId(buff_effect);
}
}
break;
default:
break;
}
}
}
RecalcBuffAttr();
#ifdef DEBUG
SendDebugMsg(a8::Format("移除buff_id:%d",
{
buff_id
}));
#endif
}
void Creature::SendDebugMsg(const std::string& debug_msg)
{
}
void Creature::AddBuffPostProc(Creature* caster, Buff* buff)
{
}
void Creature::RecalcBuffAttr()
{
buff_attr_abs_ = {};
buff_attr_rate_ = {};
for (auto& buff : buff_list_) {
if (buff.meta->i->buff_effect() == kBET_ChgAttr ||
buff.meta->i->buff_effect() == kBET_Car ||
buff.meta->i->buff_effect() == kBET_CrazyMode) {
int attr_type = (int)buff.meta->param1;
int calc_type = (int)buff.meta->param2;
if (IsValidHumanAttr(attr_type)) {
if (calc_type == 1) {
buff_attr_abs_[attr_type] += buff.meta->param3;
} else if (calc_type == 2) {
buff_attr_rate_[attr_type] += buff.meta->param3;
}
}
}
}
}
void Creature::OnBuffRemove(Buff& buff)
{
}
void Creature::RemoveBuffByEffectId(int buff_effect_id)
{
Buff* buff = GetBuffByEffectId(buff_effect_id);
while (buff) {
RemoveBuffById(buff->meta->i->buff_id());
buff = GetBuffByEffectId(buff_effect_id);
}
}
void Creature::ClearBuffList()
{
for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) {
if (buff_effect_[itr->meta->i->buff_effect()] == &(*itr)) {
buff_effect_[itr->meta->i->buff_effect()] = nullptr;
}
OnBuffRemove(*itr);
}
buff_list_.clear();
buff_effect_ = {};
buff_attr_abs_ = {};
buff_attr_rate_ = {};
RecalcBuffAttr();
}
float Creature::GetBuffAttrAbs(int attr_type)
{
if (IsValidHumanAttr(attr_type)) {
return buff_attr_abs_[attr_type];
}
return 0;
}
float Creature::GetBuffAttrRate(int attr_type)
{
if (IsValidHumanAttr(attr_type)) {
return buff_attr_rate_[attr_type];
}
return 0;
}
void Creature::FillBuffList(Human* hum, ::google::protobuf::RepeatedPtrField<::cs::MFBuff>* pb_buff_list)
{
for (auto& itr : buff_list_) {
if (itr.NeedSync(hum)) {
auto buff = pb_buff_list->Add();
itr.FillMFBuff(buff);
}
}
}
void Creature::FillSkillList(::google::protobuf::RepeatedPtrField< cs::MFSkill >*
pb_skill_list)
{
for (auto& pair : skill_hash_) {
auto skill = pb_skill_list->Add();
pair.second->FillMFSkill(skill);
}
}
void Creature::AddPassiveSkill(int skill_id)
{
MetaData::Skill* skill_meta = MetaMgr::Instance()->GetSkill(skill_id);
if (skill_meta && !GetPassiveSkill(skill_meta->i->skill_id())) {
Skill* skill = new Skill;
skill->owner = this;
skill->meta = skill_meta;
skill->xtimer_attacher.xtimer = &room->xtimer;
room->xtimer.AddRepeatTimerAndAttach
(
SERVER_FRAME_RATE * skill_meta->i->skill_cd(),
a8::XParams()
.SetSender(this)
.SetParam1(skill_id),
[] (const a8::XParams& param)
{
Creature* c = (Creature*)param.sender.GetUserData();
Skill* skill = c->GetPassiveSkill(param.param1);
if (skill) {
skill->ClearPassiveSkillBuff();
skill->AddPassiveSkillBuff();
}
},
&skill->xtimer_attacher.timer_list_);
skill->Initialzie();
passive_skill_hash_[skill_meta->i->skill_id()] = skill;
skill->AddPassiveSkillBuff();
if (skill_meta->i->skill_cd() > 10000) {
//永久被动被动技能
skill->AddPassiveSkillBuff();
}
}
}
void Creature::RemovePassiveSkill(int skill_id)
{
auto itr = passive_skill_hash_.find(skill_id);
if (itr != passive_skill_hash_.end()) {
itr->second->ClearPassiveSkillBuff();
delete itr->second;
passive_skill_hash_.erase(itr);
}
}
void Creature::ClearPassiveSkill()
{
std::vector<int> del_skills;
del_skills.reserve(passive_skill_hash_.size());
for (auto& pair : passive_skill_hash_) {
del_skills.push_back(pair.first);
}
for (int skill_id : del_skills) {
RemovePassiveSkill(skill_id);
}
}
Skill* Creature::GetPassiveSkill(int skill_id)
{
auto itr = passive_skill_hash_.find(skill_id);
return itr != passive_skill_hash_.end() ? itr->second : nullptr;
}
void Creature::TriggerBuff(Skill* skill, std::set<Creature*>& target_list, BuffTriggerType_e trigger_type)
{
for (Creature* entity : target_list) {
TriggerOneObjectBuff(skill, entity, trigger_type);
}
}
void Creature::TriggerOneObjectBuff(Skill* skill, Creature* target, BuffTriggerType_e trigger_type)
{
if (target->dead) {
return;
}
auto itr = skill->meta->trigger_type_buffs.find(trigger_type);
if (itr != skill->meta->trigger_type_buffs.end()) {
for (MetaData::Buff* buff_meta : itr->second) {
switch (buff_meta->i->buff_target()) {
case kBuffTargetSelf: //自己
{
if (target == this) {
target->AddBuff(this, buff_meta, 1, skill->meta);
}
}
break;
case kBuffTargetFriendly: //友军
{
if (target->team_id == team_id) {
target->AddBuff(this, buff_meta, 1, skill->meta);
}
}
break;
case kBuffTargetEnemy: //敌军
{
if (target->team_id != team_id) {
target->AddBuff(this, buff_meta, 1, skill->meta);
}
}
break;
default:
{
}
break;
}
}
}
}
Skill* Creature::GetSkill(int skill_id)
{
auto itr = skill_hash_.find(skill_id);
return itr != skill_hash_.end() ? itr->second : nullptr;
}
bool Creature::CanUseSkill(int skill_id)
{
Skill* skill = GetSkill(skill_id);
if (!skill) {
return false;
}
if (dead) {
return false;
}
if (playing_skill) {
return false;
}
return skill->GetCurrTimes() > 0;
}
void Creature::DoSkill(int skill_id,
int target_id,
const a8::Vec2& skill_dir,
float skill_distance,
const a8::Vec2& target_pos)
{
DoSkillPreProc(skill_id, target_id, target_pos);
Skill* skill = GetSkill(skill_id);
if (skill && CanUseSkill(skill_id)) {
ResetSkill();
skill_target_id_ = target_id;
skill_target_pos_ = target_pos;
skill_dir_ = skill_dir;
skill_distance_ = skill_distance;
curr_skill_ = skill;
playing_skill = true;
buff_vec2_param1 = GetPos() + skill_dir_ * skill_distance_;
CurrentSkill()->last_use_frameno = room->GetFrameNo();
if (CurrentSkill()->meta->i->skill_target() == kST_Self
) {
skill_target_id_ = GetUniId();
}
if (CurrentSkill()->meta->i->skill_target() == kST_SpecDir) {
std::set<Creature*> target_list;
SelectSkillTargets(CurrentSkill(), GetPos(), target_list);
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
if (!CurrentSkill()->meta->phases.empty() &&
CurrentSkill()->meta->phases[0].time_offset <= 0) {
UpdateSkill();
}
} else {
if (skill_target_id_ == 0) {
if (CurrentSkill()->meta->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<Creature*> target_list;
skill_target_pos_ = c->GetPos();
SelectSkillTargets(CurrentSkill(), c->GetPos(), target_list);
TriggerBuff(CurrentSkill(), target_list, kBTT_UseSkill);
if (!CurrentSkill()->meta->phases.empty() &&
CurrentSkill()->meta->phases[0].time_offset <= 0) {
UpdateSkill();
}
} else {
playing_skill = false;
}
}
if (HasBuffEffect(kBET_Camouflage)) {
RemoveBuffByEffectId(kBET_Camouflage);
}
DoSkillPostProc(true, skill_id, target_id, target_pos);
#if 0
if (skill_id != TURN_OVER_SKILL_ID) {
#else
{
#endif
CreatureWeakPtr ptr;
ptr.Attach(this);
room->frame_event.AddPlaySkill(ptr, skill_id);
}
} else {
DoSkillPostProc(false, skill_id, target_id, target_pos);
}
}
void Creature::DoSkillPreProc(int skill_id, int target_id, const a8::Vec2& target_pos)
{
}
void Creature::DoSkillPostProc(bool used, int skill_id, int target_id, const a8::Vec2& target_pos)
{
}
void Creature::ResetSkill()
{
curr_skill_ = nullptr;
curr_skill_phase = 0;
skill_dir_ = a8::Vec2();
skill_target_pos_ = a8::Vec2();
skill_param1 = 0.0f;
playing_skill = false;
}
void Creature::UpdateSkill()
{
if (CurrentSkill() && !dead) {
if (curr_skill_phase < CurrentSkill()->meta->phases.size()) {
MetaData::SkillPhase* phase = &CurrentSkill()->meta->phases[curr_skill_phase];
if (phase->time_offset >= CurrentSkill()->GetPassedTime()) {
ProcSkillPhase(phase);
++curr_skill_phase;
}
} else {
playing_skill = false;
}
} else {
playing_skill = false;
}
}
void Creature::ProcSkillPhase(MetaData::SkillPhase* phase)
{
switch (phase->func_id) {
case kSkill_TurnOver:
{
}
break;
case kSkill_JumpTo:
{
}
break;
case kSkill_Shot:
{
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(phase->param1.GetInt());
if (weapon_meta) {
MetaData::EquipUpgrade* weapon_upgrade_meta =
MetaMgr::Instance()->GetEquipUpgrade(weapon_meta->i->id());
MetaData::Equip* bullet_meta =
MetaMgr::Instance()->GetEquip(weapon_meta->i->use_bullet());
if (CurrentSkill()->meta->i->skill_target() == kST_SpecDir) {
if (std::abs(skill_dir_.x) > FLT_EPSILON ||
std::abs(skill_dir_.y) > FLT_EPSILON) {
float target_distance = 5;
if (bullet_meta && target_distance > 0.00001f) {
a8::Vec2 old_attack_dir = 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(caster);
}
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(caster);
}
break;
case kBET_IntervalAddBuff:
{
buff->ProcIntervalAddBuff(caster);
}
break;
case kBET_OnceAddHp:
{
AddHp(buff->meta->param1);
}
break;
case kBET_SummonHero:
{
if (!dead) {
SummonHero(buff, GetPos(), GetMoveDir());
}
}
break;
case kBET_Shield:
{
}
break;
case kBET_Hide:
{
}
break;
case kBET_SummonObstacle:
{
if (!dead) {
SummonObstacle(buff, buff->meta->param1, buff_vec2_param1);
}
}
break;
case kBET_Sprint:
{
buff->ProcSprint(caster);
}
break;
case kBET_FlashMove:
{
a8::Vec2 old_pos = GetPos();
a8::Vec2 new_pos = GetPos() + skill_dir_ * std::max(skill_distance_, 300.0f);
SetPos(new_pos);
if (CollisonDetection()) {
SetPos(old_pos);
} else {
room->grid_service->MoveCreature(this);
}
}
break;
case kBET_BatchAddBuff:
{
buff->ProcBatchAddBuff(caster);
}
break;
case kBET_BeRecycle:
{
buff->ProcBeRecycle(caster);
}
break;
case kBET_Become:
{
buff->ProcBecome(caster);
}
break;
case kBET_SelectTargetWithSelfPos:
{
buff->ProcSeletTargetWithSelfPos(caster);
}
break;
case kBET_Driver:
{
buff->ProcDriver(caster);
}
break;
case kBET_Passenger:
{
buff->ProcPassenger(caster);
}
break;
default:
{
}
break;
}
}
void Creature::Initialize()
{
MoveableEntity::Initialize();
}
void Creature::SetMoveDir(const a8::Vec2& move_dir)
{
if (!GetBuffByEffectId(kBET_Sprint)) {
MoveableEntity::SetMoveDir(move_dir);
}
}
void Creature::AddSkill(int skill_id)
{
MetaData::Skill* skill_meta = MetaMgr::Instance()->GetSkill(skill_id);
if (skill_meta && !GetSkill(skill_id)) {
Skill* skill = new Skill;
skill->owner = this;
skill->meta = skill_meta;
skill->xtimer_attacher.xtimer = &room->xtimer;
skill->Initialzie();
skill_hash_[skill_id] = skill;
}
}
void Creature::ClearSkill()
{
Skill* reserve_skill = nullptr;
for (auto& pair : skill_hash_) {
if (pair.second->meta->IsTurnOverSkill()) {
reserve_skill = pair.second;
} else {
delete pair.second;
}
}
skill_hash_.clear();
if (reserve_skill) {
skill_hash_[reserve_skill->meta->i->skill_id()] = reserve_skill;
}
}
bool Creature::CanSee(const Creature* c) const
{
return room->grid_service->InView(GetGridId(), c->GetGridId());
}
float Creature::GetAttrAbs(int attr_id)
{
float attr_abs_val = GetBuffAttrAbs(attr_id);
if (attr_id == kHAT_Atk || attr_id == kHAT_Def) {
Buff* buff = GetBuffByEffectId(kBET_Car);
if (buff) {
MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(buff->meta->param4);
if (equip_meta) {
switch (attr_id) {
case kHAT_Atk:
{
attr_abs_val += equip_meta->i->atk();
}
break;
case kHAT_Def:
{
attr_abs_val += equip_meta->i->def();
}
break;
default:
{
}
break;
}
}
}
}
return attr_abs_val;
}
float Creature::GetAttrRate(int attr_id)
{
float attr_rate_val = GetBuffAttrRate(attr_id);
return attr_rate_val;
}
bool Creature::IsPlayer() const
{
return IsHuman() && IsEntitySubType(EST_Player);
}
bool Creature::IsAndroid() const
{
return IsHuman() && IsEntitySubType(EST_Android);
}
bool Creature::IsHuman() const
{
return IsEntityType(ET_Player);
}
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();
}
}
}
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, HS_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::TraverseCreatures(std::function<void (Creature*, bool&)> func)
{
room->grid_service->TraverseCreatures(room->GetRoomIdx(),
GetGridList(),
func);
}
void Creature::TraverseProperTargets(std::function<void (Creature*, bool&)> func)
{
auto callback =
[this, func] (Creature* c, bool& stop)
{
if (IsProperTarget(c)) {
func(c, stop);
}
};
room->grid_service->TraverseCreatures(room->GetRoomIdx(),
GetGridList(),
callback);
}
void Creature::TraverseProperTargetsNoTeammate(std::function<void (Creature*, bool&)> func)
{
auto callback =
[this, func] (Creature* c, bool& stop)
{
if (IsProperTarget(c, true)) {
func(c, stop);
}
};
room->grid_service->TraverseCreatures(room->GetRoomIdx(),
GetGridList(),
callback);
}
void Creature::UpdatePoisoning()
{
if (dead) {
return;
}
bool need_notify = poisoning_time > 1000;
while (poisoning_time > 1000) {
if (room->GetGasData().is_last_gas) {
DecHP(room->GetGasData().new_area_meta->i->hurt(), VP_SafeArea, TEXT("battle_server_killer_gas", "毒圈"), VW_SafeArea);
} else {
DecHP(room->GetGasData().old_area_meta->i->hurt(), VP_SafeArea, TEXT("battle_server_killer_gas", "毒圈"), VW_SafeArea);
}
if (dead) {
poisoning_time = 0;
break;
}
poisoning_time -= 1000;
}
if (need_notify && IsEntitySubType(EST_Player)) {
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
}
void Creature::Shot(a8::Vec2& target_dir, bool& shot_ok, float fly_distance)
{
shot_ok = false;
if (!GetCurrWeapon()->meta) {
return;
}
if (downed) {
return;
}
if (!GetCurrWeapon()->meta) {
return;
}
if (GetCurrWeapon()->weapon_idx != 0 &&
GetCurrWeapon()->ammo <= 0) {
AutoLoadingBullet();
return;
}
if (action_type == AT_Reload) {
CancelAction();
}
if (action_type == AT_Reload ||
action_type == AT_Rescue ||
action_type == AT_UseItem ||
action_type == AT_Relive) {
CancelAction();
}
if ((room->GetFrameNo() - last_shot_frameno_) * (1000 / SERVER_FRAME_RATE) <
GetCurrWeapon()->GetAttrValue(kHAT_FireRate)
) {
return;
}
if (GetCurrWeapon()->meta->power_charge.empty() || power_idx < 0) {
InternalShot(this,
GetCurrWeapon()->meta,
GetCurrWeapon()->GetUpgradeMeta(),
GetCurrWeapon()->bullet_meta,
GetCurrWeapon()->weapon_lv,
0,
fly_distance,
false);
} else if (power_idx < GetCurrWeapon()->meta->power_charge.size()) {
MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip
(std::get<1>(GetCurrWeapon()->meta->power_charge[power_idx]));
if (weapon_meta) {
MetaData::EquipUpgrade* weapon_upgrade_meta = MetaMgr::Instance()->GetEquipUpgrade
(weapon_meta->i->id());
MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(weapon_meta->i->use_bullet());;
if (bullet_meta) {
#ifdef DEBUG
SendDebugMsg(a8::Format("蓄力射击 %d 枪:%d 子弹:%d",
{
power_idx,
weapon_meta->i->id(),
bullet_meta->i->id()
}));
#endif
InternalShot(this,
weapon_meta,
weapon_upgrade_meta,
bullet_meta,
GetCurrWeapon()->weapon_lv,
0,
fly_distance,
false);
} else {
abort();
}
} else {
abort();
}
} else {
abort();
}
if (GetCurrWeapon()->weapon_idx != 0) {
--GetCurrWeapon()->ammo;
}
int slot_id = GetCurrWeapon()->meta->i->_inventory_slot();
#ifdef DEBUG
if (IsPlayer()) {
SendDebugMsg(a8::Format("使用武器 %s slot:%d", {GetCurrWeapon()->meta->i->name(), slot_id}));
}
#endif
switch (slot_id) {
case IS_FRAG: //手雷
case IS_SMOKE: //烟雾弹
{
if (GetCurrWeapon()->ammo <= 0) {
if (GetInventory(slot_id) > 0) {
DecInventory(slot_id, 1);
++GetCurrWeapon()->ammo;
} else {
int weapon_idx = GetCurrWeapon()->weapon_idx;
*GetCurrWeapon() = Weapon();
GetCurrWeapon()->weapon_idx = weapon_idx;
Weapon* next_weapon = ChooseNextWeapon(slot_id, SPEC1_IS_BEGIN, SPEC1_IS_END);
if (!next_weapon) {
next_weapon = ChooseNextWeapon(SPEC2_IS_BEGIN, SPEC2_IS_BEGIN, SPEC2_IS_END);
}
if (!next_weapon) {
next_weapon = AutoChgWeapon();
}
SetCurrWeapon(next_weapon);
AutoLoadingBullet();
}
}
need_sync_active_player = true;
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
break;
case IS_POSION_GAS_BOMB: //毒气弹
case IS_MOLOTOR_COCKTAIL: //燃烧瓶
case IS_TRAP: //陷井
case IS_MINE: //地雷
{
if (GetCurrWeapon()->ammo <= 0) {
if (GetInventory(slot_id) > 0) {
DecInventory(slot_id, 1);
++GetCurrWeapon()->ammo;
} else {
int weapon_idx = GetCurrWeapon()->weapon_idx;
*GetCurrWeapon() = Weapon();
GetCurrWeapon()->weapon_idx = weapon_idx;
Weapon* next_weapon = ChooseNextWeapon(slot_id, SPEC2_IS_BEGIN, SPEC2_IS_END);
if (!next_weapon) {
next_weapon = ChooseNextWeapon(SPEC1_IS_BEGIN, SPEC1_IS_BEGIN, SPEC1_IS_END);
}
if (!next_weapon) {
next_weapon = AutoChgWeapon();
}
SetCurrWeapon(next_weapon);
AutoLoadingBullet();
}
}
need_sync_active_player = true;
SyncAroundPlayers(__FILE__, __LINE__, __func__);
}
break;
default:
{
}
break;
}
if (GetCurrWeapon()->weapon_idx != 0 &&
GetCurrWeapon()->ammo <= 0) {
AutoLoadingBullet();
}
last_shot_frameno_ = room->GetFrameNo();
if (!need_sync_active_player && IsPlayer()) {
room->frame_event.AddBulletNumChg(GetWeakPtrRef());
room->frame_event.AddWeaponAmmoChg(GetWeakPtrRef());
}
shot_ok = true;
}
void Creature::AutoLoadingBullet(bool manual)
{
Weapon* p_weapon = GetCurrWeapon();
if (second_weapon.meta) {
p_weapon = &second_weapon;
}
if (p_weapon->weapon_idx != 0 &&
(p_weapon->ammo <= 0 ||
(manual && p_weapon->ammo < p_weapon->GetClipVolume()))
) {
MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(p_weapon->meta->i->use_bullet());
if (bullet_meta &&
bullet_meta->i->_inventory_slot() >= 0 &&
bullet_meta->i->_inventory_slot() < (int)inventory_.size()
) {
if (GetInventory(bullet_meta->i->_inventory_slot()) > 0) {
if (on_loading_bullet) {
on_loading_bullet();
}
StartAction(AT_Reload,
p_weapon->GetAttrValue(kHAT_ReloadTime),
p_weapon->weapon_id,
p_weapon->weapon_idx);
}
}
return;
}
}
int Creature::GetInventory(int slot_id)
{
if (!IsValidSlotId(slot_id)) {
abort();
}
return inventory_[slot_id];
}
void Creature::AddInventory(int slot_id, int num)
{
assert(num > 0);
if (!IsValidSlotId(slot_id)) {
abort();
}
inventory_[slot_id] += num;
}
void Creature::DecInventory(int slot_id, int num)
{
assert(num > 0);
if (!IsValidSlotId(slot_id)) {
abort();
}
inventory_[slot_id] -= num;
}
void Creature::CheckSpecObject()
{
std::set<ColliderComponent*> 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);
}
}
}
#ifdef DEBUG
if (IsPlayer()) {
if (old_cell_flags != cell_flags_) {
std::string msg = "地形变化 old:";
{
if (a8::HasBitFlag(old_cell_flags, kColliderTag_Grass)) {
msg += " 草:1";
} else {
msg += " 草:0";
}
if (a8::HasBitFlag(old_cell_flags, kColliderTag_Water)) {
msg += " 水:1";
} else {
msg += " 水:0";
}
if (a8::HasBitFlag(old_cell_flags, kColliderTag_Ice)) {
msg += " 灰:1";
} else {
msg += " 灰:0";
}
}
{
msg += " new:";
if (a8::HasBitFlag(cell_flags_, kColliderTag_Grass)) {
msg += " 草:1";
} else {
msg += " 草:0";
}
if (a8::HasBitFlag(cell_flags_, kColliderTag_Water)) {
msg += " 水:1";
} else {
msg += " 水:0";
}
if (a8::HasBitFlag(cell_flags_, kColliderTag_Ice)) {
msg += " 灰:1";
} else {
msg += " 灰:0";
}
}
msg += a8::Format(" o:%d n:%d w:%d h:%d", {old_cell_flags, cell_flags_, water_w, water_h});
SendDebugMsg(msg);
}
}
#endif
}
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();
slave_things_.push_back(std::make_tuple(buff->meta->i->buff_id(), obstacle));
} else {
abort();
}
}
bool Creature::CollisonDetection()
{
bool through_wall = HasBuffEffect(kBET_ThroughWall) ||
HasBuffEffect(kBET_Fly);
AabbCollider aabb_box;
GetAabbBox(aabb_box);
return room->map_service->CollisionDetection
(
room,
through_wall,
GetPos(),
&aabb_box
);
}
void Creature::FillSkillCasterState(SkillCasterState* caster_state)
{
caster_state->caster.Attach(this);
caster_state->caster_pos = GetPos();
caster_state->caster_skill_id = CurrentSkill() ? CurrentSkill()->meta->i->skill_id() : 0;
caster_state->caster_skill_target_id = skill_target_id_;
caster_state->caster_skill_dir = skill_dir_;
caster_state->caster_skill_distance = skill_distance_;
caster_state->caster_skill_param1 = skill_param1;
}
void Creature::RecoverSkillCasterState(SkillCasterState* caster_state)
{
SetPos(caster_state->caster_pos);
skill_target_id_ = caster_state->caster_skill_target_id;
skill_dir_ = caster_state->caster_skill_dir;
skill_distance_ = caster_state->caster_skill_distance;
skill_param1 = caster_state->caster_skill_param1;
}
CreatureWeakPtr Creature::AllocWeakPtr()
{
CreatureWeakPtr ptr;
ptr.Attach(this);
return ptr;
}
CreatureWeakPtr& Creature::GetWeakPtrRef()
{
if (!weak_ptr_.Get()) {
weak_ptr_.Attach(this);
}
return weak_ptr_;
}
Weapon* Creature::AutoChgWeapon()
{
if (weapons[GUN_SLOT1].weapon_id != 0) {
SetCurrWeapon(&weapons[GUN_SLOT1]);
} else if (weapons[GUN_SLOT2].weapon_id != 0) {
SetCurrWeapon(&weapons[GUN_SLOT2]);
} else {
SetCurrWeapon(&weapons[0]);
}
return GetCurrWeapon();
}
Weapon* Creature::ChooseNextWeapon(int curr_weapon_slot_id, int begin_slot_id, int end_slot_id)
{
if (curr_weapon_slot_id < begin_slot_id) {
abort();
}
if (curr_weapon_slot_id > end_slot_id) {
abort();
}
if (begin_slot_id > end_slot_id) {
abort();
}
if (begin_slot_id < 0 ||
end_slot_id < 0) {
abort();
}
Weapon* next_weapon = nullptr;
for (int i = 1; i <= (end_slot_id - begin_slot_id + 1); ++i) {
int slot_id = begin_slot_id +
(i + curr_weapon_slot_id - begin_slot_id) % (end_slot_id - begin_slot_id + 1);
int idx = -1;
if (slot_id >= SPEC1_IS_BEGIN && slot_id <= SPEC1_IS_END) {
idx = SPEC1_SLOT_BEGIN + (slot_id - SPEC1_IS_BEGIN);
}
if (slot_id >= SPEC2_IS_BEGIN && slot_id <= SPEC2_IS_END) {
idx = SPEC2_SLOT_BEGIN + (slot_id - SPEC2_IS_BEGIN);
}
if (idx != -1 && weapons.at(idx).weapon_id != 0) {
next_weapon = &weapons[idx];
break;
}
}
return next_weapon;
}
void Creature::SetCurrWeapon(Weapon* weapon)
{
#ifdef DEBUG
bool found = false;
for (auto& p : weapons) {
if (&p == weapon) {
found = true;
}
}
if (!found) {
abort();
}
#endif
curr_weapon_ = weapon;
}
void Creature::ResetAllSkillCd()
{
for (auto& pair : skill_hash_) {
pair.second->ResetSkillCd();
}
}
void Creature::SummonHero(Buff* buff,
const a8::Vec2& pos,
const a8::Vec2& dir
)
{
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 && !dead) {
for (int i = 0; i < 4; ++i) {
a8::Vec2 born_dir = dir;
a8::Vec2 born_offset(x, y);
born_offset.Rotate(born_dir.CalcAngle(a8::Vec2::UP));
born_offset.Rotate(i * 0.5);
a8::Vec2 hero_pos = pos + born_offset;
CircleCollider collider;
collider.pos = hero_pos;
collider.rad = hero_meta->i->radius();
if (!room->map_service->CollisionDetection
(
room,
through_wall,
hero_pos,
&collider
)) {
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<Hero*> 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<RoomObstacle*> 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;
}
void Creature::GetHitEnemys(std::set<Creature*>& 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__);
}
}
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;
}