#include "precompile.h" #include #include #include #include "human.h" #include "cs_proto.pb.h" #include "metamgr.h" #include "room.h" #include "bullet.h" #include "collider.h" #include "loot.h" #include "building.h" #include "hero.h" #include "app.h" #include "roommgr.h" #include "android.h" #include "gamelog.h" #include "typeconvert.h" #include "obstacle.h" #include "player.h" #include "buff.h" #include "framework/cpp/utils.h" #include "framework/cpp/httpclientpool.h" const int kReviveTimeAdd = 12; Human::Human():Entity() { default_weapon.weapon_idx = 0; default_weapon.weapon_id = 12101; default_weapon.weapon_lv = 1; default_weapon.ammo = 1; default_weapon.meta = MetaMgr::Instance()->GetEquip(default_weapon.weapon_id); default_weapon.Recalc(); 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; } weapons[0] = default_weapon; curr_weapon = &weapons[0]; 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; } } Human::~Human() { } void Human::Initialize() { Entity::Initialize(); skill_xtimer_attacher_.xtimer = &room->xtimer; RecalcSelfCollider(); volume_ = meta->volume; observers_.insert(this); } float Human::GetSpeed() { if (a8::HasBitFlag(status, HS_Jump)) { #if 1 return meta->i->jump_speed(); #else return meta->i->jump_speed() + buff.speed; #endif } if (downed) { #if 1 return meta->i->move_speed3(); #else return meta->i->move_speed3() + buff.speed; #endif } else { if (shot_hold) { if (curr_weapon->weapon_idx == GUN_SLOT1 || curr_weapon->weapon_idx == GUN_SLOT2) { if (action_type != AT_Reload) { return meta->i->shot_speed(); } } } else if (aiming) { return meta->i->aiming_speed(); } #if 1 return meta->i->move_speed(); #else return meta->i->move_speed() + buff.speed; #endif } } float Human::GetSpeed4() { return meta->i->move_speed4(); } void Human::FillMFObjectPart(cs::MFObjectPart* part_data) { part_data->set_object_type(ET_Player); cs::MFPlayerPart* p = part_data->mutable_union_obj_1(); p->set_obj_uniid(entity_uniid); TypeConvert::ToPb(GetPos(), p->mutable_pos()); TypeConvert::ToPb(attack_dir, p->mutable_dir()); } void Human::FillMFObjectFull(cs::MFObjectFull* full_data) { full_data->set_object_type(ET_Player); cs::MFPlayerFull* p = full_data->mutable_union_obj_1(); p->set_obj_uniid(entity_uniid); TypeConvert::ToPb(GetPos(), p->mutable_pos()); TypeConvert::ToPb(attack_dir, p->mutable_dir()); p->set_health(GetHP()); p->set_max_health(GetMaxHP()); p->set_dead(dead); p->set_downed(downed); p->set_disconnected(disconnected); p->set_anim_type(anim_type); p->set_anim_seq(anim_seq); #if 1 for (auto itr : skins) { auto skin = p->add_skin(); itr.ToPB(skin); } #else if (skin_tank.skin_id != 0) { skin_tank.ToPB(p->mutable_skin()); } else { if (skin_jlf.skin_id != 0) { skin_jlf.ToPB(p->mutable_skin()); } else { skin.ToPB(p->mutable_skin()); } } #endif p->set_backpack(backpack); p->set_helmet(helmet); p->set_chest(chest); curr_weapon->ToPB(p->mutable_weapon()); p->set_energy_shield(energy_shield); #if 1 { p->set_max_energy_shield(max_energy_shield); } #endif p->set_vip(vip); p->set_sdmg(sdmg); p->set_kill_count(stats.kills); if (emoji1 != 0) { p->set_emoji1(emoji1); } if (emoji2 != 0) { p->set_emoji2(emoji2); } if (parachute != 0) { p->set_parachute(parachute); } FillBodyState(p->mutable_states()); if (dead) { p->set_killer_name(stats.killer_name); p->set_killer_id(stats.killer_id); if (real_dead){ p->set_can_revive(false); } else { p->set_can_revive(true); int countdown = 0; if (revive_timer) { countdown = std::ceil(room->xtimer.GetRemainTime(revive_timer) / SERVER_FRAME_RATE); } else { a8::UdpLog::Instance()->Warning("Human::FillMFObjectfull revive_timer == nullptr", {}); } countdown = std::max(0, countdown - kReviveTimeAdd); p->set_revive_countdown(countdown); } } } void Human::FillMFPlayerStats(cs::MFPlayerStats* stats_pb) { stats_pb->set_player_id(entity_uniid); stats_pb->set_player_avatar_url(avatar_url); if (!dead) { stats_pb->set_time_alive(room->frame_no * 1000.0f / SERVER_FRAME_RATE); } else { stats_pb->set_time_alive(dead_frameno * 1000.0f / SERVER_FRAME_RATE); } stats_pb->set_kills(stats.kills); stats_pb->set_damage_amount(stats.damage_amount_out); stats_pb->set_heal_amount(stats.heal_amount); stats_pb->set_history_time_alive(stats.history_time_alive); stats_pb->set_history_kills(stats.history_kills); stats_pb->set_history_damage_amount(stats.history_damage_amount); stats_pb->set_history_heal_amount(stats.history_heal_amount); stats_pb->set_gold(stats.gold); stats_pb->set_score(stats.score); stats_pb->set_pass_score(stats.pass_score); stats_pb->set_rank_score(stats.rank_score); stats_pb->set_has_pass(has_pass); stats_pb->set_dead(dead); stats_pb->set_killer_id(stats.killer_id); stats_pb->set_killer_name(stats.killer_name); { Player* killer = room->GetPlayerByUniId(stats.killer_id); if (killer) { stats_pb->set_killer_avatar_url(killer->avatar_url); stats_pb->set_killer_account_id(killer->account_id); } else { stats_pb->set_killer_avatar_url(""); stats_pb->set_killer_account_id(""); } } stats_pb->set_account_id(account_id); for (auto& pair : stats.extra_drop) { auto p = stats_pb->add_extra_drop(); p->set_key(pair.first); p->set_value(pair.second); } } void Human::GetAabbBox(AabbCollider& aabb_box) { if (!meta) { abort(); } aabb_box.active = true; aabb_box.owner = this; aabb_box._min.x = -meta->i->radius(); aabb_box._min.y = -meta->i->radius(); aabb_box._max.x = meta->i->radius(); aabb_box._max.y = meta->i->radius(); } void Human::FillMFTeamData(cs::MFTeamData* team_data) { #if 1 { #else if (room->gas_data.gas_mode == GasJump || room->frame_no - last_sync_teamdata_frameno_ > SERVER_FRAME_RATE * 2) { #endif last_sync_teamdata_frameno_ = room->frame_no; team_data->set_player_id(entity_uniid); team_data->set_name(name); TypeConvert::ToPb(GetPos(), team_data->mutable_pos()); TypeConvert::ToPb(attack_dir, team_data->mutable_dir()); team_data->set_health(GetHP()); team_data->set_max_health(GetMaxHP()); team_data->set_disconnected(false); team_data->set_dead(dead); team_data->set_downed(downed); } } void Human::Shot(a8::Vec2& target_dir) { if (!curr_weapon->meta) { return; } if (curr_weapon->weapon_idx != 0 && curr_weapon->ammo <= 0) { AutoLoadingBullet(); return; } #if 1 float fly_distance = 5; #endif for (auto& tuple : curr_weapon->meta->bullet_born_offset) { a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple)); bullet_born_offset.Rotate(attack_dir.CalcAngle(a8::Vec2::UP)); a8::Vec2 bullet_born_pos = GetPos() + bullet_born_offset; if (room->OverBorder(bullet_born_pos, 0)) { return; } } room->frame_event.AddShot(this); for (auto& tuple : curr_weapon->meta->bullet_born_offset) { a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple)); bullet_born_offset.Rotate(attack_dir.CalcAngle(a8::Vec2::UP)); a8::Vec2 bullet_born_pos = GetPos() + bullet_born_offset; a8::Vec2 bullet_dir = attack_dir; float bullet_angle = std::get<2>(tuple); if (curr_weapon->meta->i->bullet_angle() >= 0.10f) { int angle = (int)curr_weapon->meta->i->bullet_angle() * 1000; if (curr_weapon->upgrade_meta) { angle -= curr_weapon->upgrade_meta->GetAttrValue(curr_weapon->weapon_lv, EA_BulletAngle) * 1000; } if (angle > 0) { bullet_angle += (rand() % angle) / 1000.0f * (rand() % 2 == 0 ? 1 : -1); } } bullet_dir.Rotate(bullet_angle / 180.0f); room->frame_event.AddBullet(this, bullet_born_pos, attack_dir, fly_distance); room->CreateBullet(this, curr_weapon, bullet_born_pos, attack_dir, fly_distance); } --curr_weapon->ammo; int slot_id = curr_weapon->meta->i->_inventory_slot(); switch (slot_id) { case 5: { //手雷 if (GetInventory(slot_id) > 0) { DecInventory(slot_id, 1); ++curr_weapon->ammo; } else { int weapon_idx = curr_weapon->weapon_idx; *curr_weapon = Weapon(); curr_weapon->weapon_idx = weapon_idx; if (weapons[SMOKE_SLOT].weapon_id != 0) { curr_weapon = &weapons[SMOKE_SLOT]; } else { curr_weapon = &weapons[0]; } AutoLoadingBullet(); } need_sync_active_player = true; SyncAroundPlayers(__FILE__, __LINE__, __func__); } break; case 6: { //烟雾弹 if (GetInventory(slot_id) > 0) { DecInventory(slot_id, 1); ++curr_weapon->ammo; } else { int weapon_idx = curr_weapon->weapon_idx; *curr_weapon = Weapon(); curr_weapon->weapon_idx = weapon_idx; if (weapons[FRAG_SLOT].weapon_id != 0) { curr_weapon = &weapons[FRAG_SLOT]; } else { curr_weapon = &weapons[0]; } AutoLoadingBullet(); } need_sync_active_player = true; SyncAroundPlayers(__FILE__, __LINE__, __func__); } break; } last_shot_frameno_ = room->frame_no; if (!need_sync_active_player) { room->frame_event.AddBulletNumChg(this); } } void Human::TankShot(a8::Vec2& target_dir) { if (!tank_weapon.meta) { return; } if (tank_weapon.ammo <= 0) { return; } for (auto& tuple : tank_weapon.meta->bullet_born_offset) { a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple)); bullet_born_offset.Rotate(attack_dir.CalcAngle(a8::Vec2::UP)); a8::Vec2 bullet_born_pos = GetPos() + bullet_born_offset; if (room->OverBorder(bullet_born_pos, 0)) { return; } } room->frame_event.AddShot(this); for (auto& tuple : tank_weapon.meta->bullet_born_offset) { a8::Vec2 bullet_born_offset = a8::Vec2(std::get<0>(tuple), std::get<1>(tuple)); bullet_born_offset.Rotate(attack_dir.CalcAngle(a8::Vec2::UP)); a8::Vec2 bullet_born_pos = GetPos() + bullet_born_offset; a8::Vec2 bullet_dir = attack_dir; float bullet_angle = std::get<2>(tuple); if (tank_weapon.meta->i->bullet_angle() >= 0.01f) { int angle = (int)tank_weapon.meta->i->bullet_angle() * 1000; if (tank_weapon.upgrade_meta) { angle -= tank_weapon.upgrade_meta->GetAttrValue(tank_weapon.weapon_lv, EA_BulletAngle) * 1000; } if (angle > 0) { bullet_angle += (rand() % angle) / 1000.0f * (rand() % 2 == 0 ? 1 : -1); } } bullet_dir.Rotate(bullet_angle / 180.0f); room->frame_event.AddBullet(this, bullet_born_pos, bullet_dir, fly_distance); if (room->BattleStarted() || (room->gas_data.gas_mode == GasJump && !a8::HasBitFlag(status, HS_Jump))) { room->CreateBullet(this, &tank_weapon, bullet_born_pos, bullet_dir, fly_distance, true); } } --tank_weapon.ammo; last_shot_frameno_ = room->frame_no; room->frame_event.AddTankBulletNumChg(this); } void Human::RecalcSelfCollider() { if (!self_collider_) { self_collider_ = new CircleCollider(); self_collider_->owner = this; AddCollider(self_collider_); } self_collider_->pos = a8::Vec2(); if (skin_tank_meta) { self_collider_->rad = skin_tank_meta->i->rad(); } else { self_collider_->rad = meta->i->radius(); } } bool Human::IsCollisionInMapService() { Global::last_collider = nullptr; if (room->OverBorder(GetPos(), meta->i->radius())){ return true; } if (a8::HasBitFlag(status, HS_Jump)) { return false; } std::set colliders; room->map_service.GetColliders(GetX(), GetY(), colliders); for (ColliderComponent* collider : colliders) { switch (collider->owner->entity_type) { case ET_Obstacle: { if (!collider->owner->dead && TestCollision((ColliderComponent*)collider)) { if (last_collision_door != collider->owner) { Obstacle* obstacle = (Obstacle*)collider->owner; if (!obstacle->dead && obstacle->meta->i->attack_type() == 1 && obstacle->meta->i->drop() != 0 && room->gas_data.gas_mode != GasInactive && !a8::HasBitFlag(status, HS_Fly) && !a8::HasBitFlag(status, HS_Jump) ) { obstacle->health = 0; obstacle->dead = obstacle->health <= 0.01f; obstacle->dead_frameno = room->frame_no; if (obstacle->dead) { #if 0 if (obstacle->meta->i->damage_dia() > 0.01f && obstacle->meta->i->damage() > 0.01f) { obstacle->Explosion(this); } #endif room->ScatterDrop(obstacle->GetPos(), obstacle->meta->i->drop()); } obstacle->BroadcastFullState(); } else { Global::last_collider = collider; return true; } } } } break; case ET_Building: { if (!collider->owner->dead && TestCollision((ColliderComponent*)collider)) { if (last_collision_door != collider->owner) { Global::last_collider = collider; return true; } } } break; default: break; } } return false; } void Human::FindPathInMapService() { a8::Vec2 old_pos = GetPos(); ColliderComponent* last_collider = Global::last_collider; if (last_collider) { switch (last_collider->type) { case CT_Aabb: { } break; case CT_Circle: { a8::Vec2 extend_dir = GetPos() - last_collider->owner->GetPos(); if (std::abs(extend_dir.x) > FLT_EPSILON || std::abs(extend_dir.y) > FLT_EPSILON) { extend_dir.Normalize(); { #if 0 a8::Vec2 extend_dir_inverse(extend_dir.y, extend_dir.x); float angle = extend_dir_inverse.CalcAngle(move_dir); if (angle > 0.001f) { extend_dir.Rotate(-1/180.0f); } else { extend_dir.Rotate(1/180.0f); } #endif extend_dir.Rotate(1/180.0f); } float distance = ((CircleCollider*)last_collider)->rad + meta->i->radius(); SetPos(last_collider->owner->GetPos() + extend_dir * (distance + 1)); if (IsCollisionInMapService()) { SetPos(old_pos); } } return; } break; default: break; } } { float up_dot = a8::Vec2::UP.Dot(move_dir); bool at_left_side = a8::Vec2::LEFT.Dot(move_dir) > 0.0001f; if (std::abs(up_dot) <= 0.001f) { //相互垂直 //向上 SetPos(old_pos + a8::Vec2::UP); if (!IsCollisionInMapService()) { return; } else { //向下 SetPos(old_pos + a8::Vec2::DOWN); if (!IsCollisionInMapService()) { return; } } } else if (up_dot > 0.001f) { //基本相同 SetPos(old_pos + (at_left_side ? a8::Vec2::LEFT : a8::Vec2::RIGHT)); if (!IsCollisionInMapService()) { return; } else { //向上 SetPos(old_pos + a8::Vec2::UP); if (!IsCollisionInMapService()) { return; } } } else if (up_dot < 0.001f) { //基本相反 SetPos(old_pos + (at_left_side ? a8::Vec2::LEFT : a8::Vec2::RIGHT)); if (!IsCollisionInMapService()) { return; } else { //向下 SetPos(old_pos + a8::Vec2::DOWN); if (!IsCollisionInMapService()) { return; } } } } SetPos(old_pos); } float Human::GetRadius() { return meta->i->radius(); } float Human::GetHP() { return ability.hp; } float Human::GetMaxHP() { return ability.max_hp; } void Human::UpdateSkill() { if (skill_meta_) { if (curr_skill_phase < skill_meta_->phases.size()) { MetaData::SkillPhase* phase = &skill_meta_->phases[curr_skill_phase]; if (phase->time_offset >= (int)((room->frame_no - last_use_skill_frameno_) * FRAME_RATE_MS)) { ProcSkillPhase(phase); ++curr_skill_phase; } } else { playing_skill = false; } } } void Human::UpdatePoisoning() { if (dead) { return; } bool need_notify = poisoning_time > 1000; while (poisoning_time > 1000) { if (room->gas_data.is_last_gas) { DecHP(room->gas_data.new_area_meta->i->hurt(), VP_SafeArea, "毒圈", VW_SafeArea); } else { DecHP(room->gas_data.old_area_meta->i->hurt(), VP_SafeArea, "毒圈", VW_SafeArea); } if (dead) { poisoning_time = 0; break; } poisoning_time -= 1000; } if (need_notify && entity_subtype == EST_Player) { SyncAroundPlayers(__FILE__, __LINE__, __func__); } } void Human::SyncAroundPlayers(const char* file, int line, const char* func) { for (auto& cell : grid_list) { for (Human* hum : cell->human_list) { hum->AddToNewObjects(this); assert(hum->part_objects.find(this) != hum->part_objects.end()); if (hum->part_objects.find(this) == hum->part_objects.end()) { if (a8::XGetTickCount() - room->last_debugout_tick > 1000 * 10) { room->last_debugout_tick = a8::XGetTickCount(); a8::UdpLog::Instance()->Warning("SyncAroundPlayers error file:%s line:%d func:%s", { file, line, func }); } } } } } void Human::AutoLoadingBullet(bool manual) { if (curr_weapon->weapon_idx != 0 && (curr_weapon->ammo <= 0 || (manual && curr_weapon->ammo < curr_weapon->GetClipVolume())) ) { MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(curr_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) { StartAction(AT_Reload, curr_weapon->meta->i->reload_time(), curr_weapon->weapon_id, curr_weapon->weapon_idx); } } return; } } void Human::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; } this->action_type = action_type; this->action_frameno = room->frame_no; this->action_duration = action_duration; this->action_item_id = item_id; this->action_target_id = target_id; need_sync_active_player = true; } void Human::CancelAction() { if (action_type == AT_Relive) { Entity* entity = room->GetEntityByUniId(action_target_id); if (entity->entity_type != ET_Player) { Human* hum = (Human*)entity; if (hum->action_type == AT_Rescue) { hum->CancelAction(); } } } ResetAction(); } void Human::ResetAction() { action_type = AT_None; action_duration = 0; action_frameno = 0; action_item_id = 0; action_target_id = 0; need_sync_active_player = true; } void Human::FillSMGameOver(cs::SMGameOver& msg) { if (stats.rank <= 0) { std::vector human_list; room->TouchHumanList(a8::XParams(), [&human_list] (Human* hum, a8::XParams& param) -> bool { if (hum->leave_frameno_ == 0 || hum->leave_frameno_ > hum->room->battle_start_frameno_) { human_list.push_back(hum); } return true; }); std::sort(human_list.begin(), human_list.end(), [] (Human* a, Human* b ) { if (a->dead_frameno == b->dead_frameno) { return a->entity_uniid < b->entity_uniid; } else { return a->dead_frameno == 0 || (b->dead_frameno != 0 && a->dead_frameno > b->dead_frameno); } }); int rank = human_list.size(); for (size_t i = 0; i < human_list.size(); ++i) { if (human_list[i] == this) { rank = i + 1; break; } } if (room->GetAliveTeamNum() == 1) { std::set* alive_team = room->GetAliveTeam(); if (alive_team == team_members) { rank = 1; } } stats.rank = rank; } msg.set_team_id(team_id); msg.set_team_rank(stats.rank); msg.set_team_allcnt(1); msg.set_game_over(room->game_over); msg.set_victory(!dead); msg.set_room_uuid(a8::XValue(room->room_uuid)); cs::MFPlayerStats* p = msg.add_player_stats(); FillMFPlayerStats(p); } void Human::BeKill(int killer_id, const std::string& killer_name, int weapon_id) { if (!dead && !room->game_over && !real_dead) { lethal_weapon = weapon_id; Entity* hum = room->GetEntityByUniId(killer_id); if (hum && hum->entity_type == ET_Player) { if (killer_id == entity_uniid) { std::string msg = a8::Format("%s 自杀", { killer_name, }); SendRollMsg(msg); } else { ((Human*)hum)->stats.kills++; ((Human*)hum)->kill_humans.insert(this); ((Human*)hum)->SyncAroundPlayers(__FILE__, __LINE__, __func__); if (weapon_id == VW_Tank) { std::string msg = a8::Format("%s 使用 %s 干掉了 %s", { killer_name, "载具", name }); SendRollMsg(msg); } else { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(weapon_id); if (equip_meta) { std::string msg = a8::Format("%s 使用 %s 干掉了 %s", { killer_name, equip_meta->i->name(), name }); SendRollMsg(msg); } } } } else { switch (weapon_id) { case VW_SafeArea: { std::string msg = a8::Format("%s 被毒圈干掉", { name }); SendRollMsg(msg); } break; case VW_Spectate: { std::string msg = a8::Format("%s 自杀", { name }); SendRollMsg(msg); } break; case VW_SelfDetonate: { std::string msg = a8::Format("%s 被炸死", { name }); SendRollMsg(msg); } break; case VW_Mine: { std::string msg = a8::Format("%s 被地雷炸死", { name }); SendRollMsg(msg); } break; } } stats.killer_id = killer_id; stats.killer_name = killer_name; stats.weapon_id = weapon_id; dead = true; downed = false; ability.hp = 0.0f; dead_frameno = room->frame_no; ++dead_times; int max_revive_times = MetaMgr::Instance()->GetSysParamAsInt("max_revive_times", 1); #if 1 ++max_revive_times; #else if (today_enter_times == 0) { ++max_revive_times; } #endif if (weapon_id != VW_Spectate && dead_times <= max_revive_times && room->AliveCount() > 2 && entity_subtype == EST_Player) { Revive(); } else { real_dead = true; OnDie(); } } } void Human::DecHP(float dec_hp, int killer_id, const std::string& killer_name, int weapon_id) { auto downed_func = [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); if (!hum->downed) { hum->FreeDownedTimer(); return; } if (hum->dead) { hum->FreeDownedTimer(); return; } if (!hum->HasLiveTeammate()) { hum->FreeDownedTimer(); hum->BeKill(param.param1, param.param2, param.param3); return; } int dec_hp = MetaMgr::Instance()->GetSysParamAsInt("downed_dec_hp"); hum->DecHP(dec_hp, param.param1, param.param2, param.param3); }; if (energy_shield > 0.001f) { energy_shield = std::max(0.0f, energy_shield - dec_hp); SyncAroundPlayers(__FILE__, __LINE__, __func__); } else { float old_health = GetHP(); ability.hp = std::max(0.0f, GetHP() - dec_hp); if (GetHP() - old_health > 0.001f) { stats.damage_amount_in += GetHP() - old_health; } if (GetHP() <= 0.0001f && !dead) { if (downed) { if (downed_timer) { room->xtimer.DeleteTimer(downed_timer); } downed = false; downed_timer = nullptr; BeKill(killer_id, killer_name, weapon_id); SyncAroundPlayers(__FILE__, __LINE__, __func__); } else { if (HasNoDownedTeammate()) { ability.hp = MetaMgr::Instance()->GetSysParamAsInt("downed_recover_hp"); downed = true; downed_timer = room->xtimer.AddRepeatTimerAndAttach( SERVER_FRAME_RATE, a8::XParams() .SetSender(this) .SetParam1(killer_id) .SetParam2(killer_name) .SetParam3(weapon_id), downed_func, &xtimer_attacher.timer_list_ ); SyncAroundPlayers(__FILE__, __LINE__, __func__); } else { BeKill(killer_id, killer_name, weapon_id); } } } room->frame_event.AddHpChg(this); } } void Human::AddToNewObjects(Entity* entity) { new_objects.insert(entity); } void Human::AddToPartObjects(Entity* entity) { part_objects.insert(entity); } void Human::RemovePartObjects(Entity* entity) { part_objects.erase(entity); } void Human::RemoveObjects(Entity* entity) { del_objects.insert(entity->entity_uniid); } void Human::AddOutObjects(Entity* entity) { out_objects.insert(entity->entity_uniid); } void Human::RemoveOutObjects(Entity* entity) { out_objects.erase(entity->entity_uniid); } bool Human::HasLiveTeammate() { if (team_members) { for (auto& hum : *team_members) { if (hum != this && !hum->dead) { return true; } } } return false; } bool Human::HasNoDownedTeammate() { if (team_members) { for (auto& hum : *team_members) { if (hum != this && !hum->dead && !hum->downed) { return true; } } } return false; } void Human::Land() { a8::UnSetBitFlag(status, HS_Jump); if (App::Instance()->HasFlag(8)) { MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(a8::RandEx(12103, 12122)); if (weapon_meta) { weapons[GUN_SLOT1].weapon_idx = GUN_SLOT1; weapons[GUN_SLOT1].weapon_id = weapon_meta->i->id(); weapons[GUN_SLOT1].weapon_lv = 1; weapons[GUN_SLOT1].ammo = 0; weapons[GUN_SLOT1].meta = weapon_meta; weapons[GUN_SLOT1].Recalc(); curr_weapon = &weapons[GUN_SLOT1]; } } FindLocation(); SyncAroundPlayers(__FILE__, __LINE__, __func__); } void Human::DoJump() { if (a8::HasBitFlag(status, HS_Fly)) { a8::UnSetBitFlag(status, HS_Fly); a8::SetBitFlag(status, HS_Jump); jump_frameno = room->frame_no; SyncAroundPlayers(__FILE__, __LINE__, __func__); room->xtimer.AddDeadLineTimerAndAttach(MetaMgr::Instance()->jump_time * SERVER_FRAME_RATE, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->Land(); }, &xtimer_attacher.timer_list_); } } bool Human::CanUseSkill() { if (downed) { return false; } if (!skill_meta_) { return false; } if (GetSkillLeftTime() > 0) { return false; } return true; } void Human::DoSkill() { if (CanUseSkill()) { use_skill = false; curr_skill_phase = 0; skill_dir = a8::Vec2(); skill_target_pos = a8::Vec2(); skill_param1 = 0.0f; playing_skill = true; last_use_skill_frameno_ = room->frame_no; #if 1 skill_target_id = entity_uniid; #endif Entity* entity = room->GetEntityByUniId(skill_target_id); if (entity && entity->entity_type == ET_Player) { Human* hum = (Human*)entity; std::set target_list; skill_target_pos = hum->GetPos(); SelectSkillTargets(hum->GetPos(), target_list); TriggerBuff(target_list, kBTT_UseSkill); if (!skill_meta_->phases.empty() && skill_meta_->phases[0].time_offset <= 0) { UpdateSkill(); } } else { playing_skill = false; } ++stats.use_skill_times; OnAttack(); #if 1 room->frame_event.AddSkillCdChg(this); #else need_sync_active_player = true; #endif } use_skill = false; } void Human::DoGetDown() { if (skin_tank.skin_id != 0) { int entity_uniid = room->CreateLoot(skin_tank.skin_id, GetPos(), 1, 1); Entity* loot_entity = room->GetEntityByUniId(entity_uniid); if (loot_entity && loot_entity->entity_type == ET_Loot) { ((Loot*)loot_entity)->bullet_num = tank_weapon.ammo; ((Loot*)loot_entity)->param1 = tank_oil_value; ((Loot*)loot_entity)->param2 = tank_oil_max; } room->UpdateCarObject(skin_tank.tank_uniid, entity_uniid, GetPos()); room->TakeOffCarObject(entity_uniid, GetPos()); ResetTankSkin(); SyncAroundPlayers(__FILE__, __LINE__, __func__); room->NotifyUiUpdate(); need_sync_active_player = true; } } void Human::FindLocation() { Entity* target = nullptr; for (auto& grid : grid_list) { for (Entity* entity : grid->entity_list) { switch (entity->entity_type) { case ET_Obstacle: { if (!target) { if (TestCollision(entity)) { target = entity; } } } break; case ET_Building: { if (!target || target->entity_type != ET_Building) { AabbCollider aabb_box; entity->GetAabbBox(aabb_box); if (TestCollision(&aabb_box)) { target = entity; } } } break; default: break; } } } if (target) { FindLocationWithTarget(target); } } void Human::RefreshView() { for (auto& cell : grid_list) { for (Human* hum : cell->human_list) { hum->AddToNewObjects(this); hum->AddToPartObjects(this); AddToNewObjects(hum); AddToPartObjects(hum); } for (Entity* entity : cell->entity_list) { switch (entity->entity_type) { case ET_Building: case ET_Obstacle: case ET_Hero: case ET_Loot: { AddToNewObjects(entity); } break; default: { } break; } } } } void Human::OnGridListChange(std::set& old_grid_list, std::set& inc_grid_list, std::set& dec_grid_list ) { for (GridCell* cell : inc_grid_list) { for (Human* hum : cell->human_list) { if (!room->grid_service.HumanInGridList(hum, old_grid_list)) { hum->AddToNewObjects(this); hum->AddToPartObjects(this); hum->RemoveOutObjects(this); AddToNewObjects(hum); AddToPartObjects(hum); RemoveOutObjects(hum); } } for (Entity* entity : cell->entity_list) { if (!room->grid_service.EntityInGridList(entity, old_grid_list)) { switch (entity->entity_type) { case ET_Building: case ET_Obstacle: case ET_Hero: case ET_Loot: { AddToNewObjects(entity); RemoveOutObjects(entity); } break; default: { } break; } } } } for (GridCell* cell : dec_grid_list) { for (Human* hum : cell->human_list) { if (!room->grid_service.HumanInGridList(hum, grid_list)) { AddOutObjects(hum); hum->AddOutObjects(this); } } for (Entity* entity : cell->entity_list) { if (!room->grid_service.EntityInGridList(entity, grid_list)) { switch (entity->entity_type) { case ET_Building: case ET_Obstacle: case ET_Hero: case ET_Loot: { AddOutObjects(entity); } break; default: { } break; } } } } } void Human::FillMFActivePlayerData(cs::MFActivePlayerData* player_data) { { player_data->set_action_type(action_type); if (action_type != AT_None) { int duration = std::max(0, action_duration - (int)((room->frame_no - action_frameno) * 1.0f / SERVER_FRAME_RATE) * 1000 ); player_data->set_action_item_id(action_item_id); player_data->set_action_duration(duration); player_data->set_action_target_id(action_target_id); } } #if 1 for (auto itr : skins) { auto skin = player_data->add_skin(); itr.ToPB(skin); } #else skin.ToPB(player_data->mutable_skin()); #endif player_data->set_backpack(backpack); player_data->set_helmet(helmet); player_data->set_chest(chest); player_data->set_health(GetHP()); player_data->set_max_health(GetMaxHP()); player_data->set_cur_weapon_idx(curr_weapon->weapon_idx); player_data->set_cur_scope(curr_scope_idx); for (auto& weapon : weapons) { auto p = player_data->add_weapons(); weapon.ToPB(p); } for (auto& num : inventory_) { player_data->add_inventory(num); } player_data->set_energy_shield(energy_shield); #if 1 { player_data->set_max_energy_shield(max_energy_shield); } #endif #if 0 if (skill_meta) { if (last_use_skill_frameno_ == 0) { player_data->set_skill_left_time(0); player_data->set_skill_cd_time(skill_meta->i->cd_time() * 1000); } else { int passed_time = (room->frame_no - last_use_skill_frameno_) * FRAME_RATE_MS; int skill_left_time = std::max(0, skill_meta->i->cd_time() * 1000 - passed_time); player_data->set_skill_left_time(skill_left_time); player_data->set_skill_cd_time(skill_meta->i->cd_time() * 1000); } } #endif FillBodyState(player_data->mutable_states()); } void Human::FillMFGasData(cs::MFGasData* gas_data) { gas_data->set_mode(room->gas_data.gas_mode); if (room->gas_data.gas_mode == GasInactive) { long long duration = MetaMgr::Instance()->gas_inactive_time * SERVER_FRAME_RATE - (room->frame_no - room->gas_data.gas_start_frameno); gas_data->set_duration(std::max(duration * 50, (long long)1000) / 1000); } else if (room->gas_data.gas_mode == GasJump) { gas_data->set_duration(0); } else if (room->gas_data.gas_mode == GasMoving) { if (room->gas_data.new_area_meta->i->shrink_speed() > 0.01f) { long long duration = (room->gas_data.old_area_meta->i->rad() - room->gas_data.new_area_meta->i->rad()) / room->gas_data.new_area_meta->i->shrink_speed(); ++duration; gas_data->set_duration(++duration); } else { gas_data->set_duration(0); } } else { if (room->gas_data.old_area_meta->i->wait_time() <= 0) { gas_data->set_duration(0); } else { long long duration = room->gas_data.old_area_meta->i->wait_time() * 20 - (room->frame_no - room->gas_data.gas_start_frameno); gas_data->set_duration(std::max(duration * 50, (long long)1000) / 1000); } } TypeConvert::ToPb(room->gas_data.pos_old, gas_data->mutable_pos_old()); TypeConvert::ToPb(room->gas_data.pos_new, gas_data->mutable_pos_new()); gas_data->set_rad_old(room->gas_data.rad_old); gas_data->set_rad_new(room->gas_data.rad_new); } bool Human::CanSee(const Human* hum) const { return room->grid_service.InView(grid_id, hum->grid_id); } void Human::RecalcVolume() { MetaData::Equip* backpack_meta = MetaMgr::Instance()->GetEquip(backpack); if (backpack_meta) { for (size_t i = 0; i < backpack_meta->volume.size(); ++i) { volume_[i] = meta->volume[i] + backpack_meta->volume[i]; } } } void Human::RecalcBaseAttr() { ability.def = meta->i->def(); MetaData::Equip* chest_meta = MetaMgr::Instance()->GetEquip(chest); if (chest_meta) { ability.def += chest_meta->i->def(); } MetaData::Equip* helmet_meta = MetaMgr::Instance()->GetEquip(helmet); if (helmet_meta) { ability.def += helmet_meta->i->def(); } ability.hp = meta->i->health(); ability.max_hp = ability.hp; } int Human::GetInventory(int slot_id) { if (!IsValidSlotId(slot_id)) { abort(); } return inventory_[slot_id]; } void Human::AddInventory(int slot_id, int num) { assert(num > 0); if (!IsValidSlotId(slot_id)) { abort(); } inventory_[slot_id] += num; } void Human::DecInventory(int slot_id, int num) { assert(num > 0); if (!IsValidSlotId(slot_id)) { abort(); } inventory_[slot_id] -= num; } int Human::GetVolume(int slot_id) { if (!IsValidSlotId(slot_id)) { abort(); } return volume_[slot_id]; } void Human::RecoverHp(int inc_hp) { if (!dead) { ability.hp += inc_hp; ability.hp = std::max(GetHP(), GetMaxHP()); } } void Human::FillBodyState(::google::protobuf::RepeatedPtrField<::cs::MFBodyState>* states) { if (pain_killer_timer) { int passed_time = (room->frame_no - pain_killer_frameno) * FRAME_RATE_MS; int left_time = std::max(0, pain_killer_lastingtime * 1000 - passed_time); int anodyne_max_time = MetaMgr::Instance()->GetSysParamAsInt("anodyne_max_time"); left_time = std::min(left_time, anodyne_max_time * 1000); cs::MFBodyState* state = states->Add(); state->set_state_type(1); state->set_left_time(left_time); state->set_lasting_time(anodyne_max_time * 1000); } if (a8::HasBitFlag(status, HS_Fly)) { cs::MFBodyState* state = states->Add(); state->set_state_type(2); } if (a8::HasBitFlag(status, HS_Jump)) { int passed_time = (room->frame_no - jump_frameno) * FRAME_RATE_MS; cs::MFBodyState* state = states->Add(); state->set_state_type(3); state->set_left_time(std::max(0, MetaMgr::Instance()->jump_time * 1000 - passed_time)); state->set_lasting_time(MetaMgr::Instance()->jump_time * 1000); } #if 0 if (a8::HasBitFlag(status, HS_Hide) && skill_meta) { int passed_time = (room->frame_no - hide_frameno_) * FRAME_RATE_MS; cs::MFBodyState* state = states->Add(); state->set_state_type(HS_Hide); state->set_left_time(std::max(0, skill_meta->last_time * 1000 - passed_time)); state->set_lasting_time(skill_meta->last_time * 1000); } if (a8::HasBitFlag(status, HS_Accelerate) && skill_meta) { int passed_time = (room->frame_no - accelerate_frameno_) * FRAME_RATE_MS; cs::MFBodyState* state = states->Add(); state->set_state_type(HS_Accelerate); state->set_left_time(std::max(0, skill_meta->last_time * 1000 - passed_time)); state->set_lasting_time(skill_meta->last_time * 1000); } if (a8::HasBitFlag(status, HS_DamageAdd) && skill_meta) { int passed_time = (room->frame_no - damageadd_frameno_) * FRAME_RATE_MS; cs::MFBodyState* state = states->Add(); state->set_state_type(HS_DamageAdd); state->set_left_time(std::max(0, skill_meta->last_time * 1000 - passed_time)); state->set_lasting_time(skill_meta->last_time * 1000); } if (a8::HasBitFlag(status, HS_DefAdd) && skill_meta) { int passed_time = (room->frame_no - defadd_frameno_) * FRAME_RATE_MS; cs::MFBodyState* state = states->Add(); state->set_state_type(HS_DefAdd); state->set_left_time(std::max(0, skill_meta->last_time * 1000 - passed_time)); state->set_lasting_time(skill_meta->last_time * 1000); } if (a8::HasBitFlag(status, HS_RecoverHP) && skill_meta) { int passed_time = (room->frame_no - recover_hp_frameno_) * FRAME_RATE_MS; cs::MFBodyState* state = states->Add(); state->set_state_type(HS_RecoverHP); state->set_left_time(std::max(0, skill_meta->last_time * 1000 - passed_time)); state->set_lasting_time(skill_meta->last_time * 1000); } if (a8::HasBitFlag(status, HS_ReflectDamage) && skill_meta) { int passed_time = (room->frame_no - reflect_damage_frameno_) * FRAME_RATE_MS; cs::MFBodyState* state = states->Add(); state->set_state_type(HS_ReflectDamage); state->set_left_time(std::max(0, skill_meta->last_time * 1000 - passed_time)); state->set_lasting_time(skill_meta->last_time * 1000); } if (a8::HasBitFlag(status, HS_SummonHero) && skill_meta) { int passed_time = (room->frame_no - summon_hero_frameno_) * FRAME_RATE_MS; cs::MFBodyState* state = states->Add(); state->set_state_type(HS_SummonHero); state->set_left_time(std::max(0, skill_meta->last_time * 1000 - passed_time)); state->set_lasting_time(skill_meta->last_time * 1000); } #endif if (a8::HasBitFlag(status, HS_AtkAdd)) { cs::MFBodyState* state = states->Add(); state->set_state_type(HS_AtkAdd); state->set_left_time(999 * 1000); state->set_lasting_time(999 * 1000); } } void Human::SummonHero() { #if 0 Hero* hero = room->CreateHero(this); if (hero) { summon_hero_frameno_ = room->frame_no; a8::SetBitFlag(status, HS_SummonHero); room->xtimer.AddDeadLineTimerAndAttach(skill_meta->last_time * SERVER_FRAME_RATE, a8::XParams() .SetSender(this) .SetParam1(hero->entity_uniid), [] (const a8::XParams& param) { }, &skill_xtimer_attacher_.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); Entity* hero = hum->room->GetEntityByUniId(param.param1); if (hero && hero->entity_type == ET_Hero) { hum->room->RemoveObjectLater(hero); } a8::UnSetBitFlag(hum->status, HS_SummonHero); hum->need_sync_active_player = true; hum->BroadcastFullState(); } ); need_sync_active_player = true; BroadcastFullState(); } #endif } void Human::AddObserver(Human* observer) { observers_.insert(observer); } void Human::RemoveObserver(Human* observer) { observers_.erase(observer); } void Human::SendUpdateMsg() { if (!follow_target_) { if (send_msg_times == 0) { room->FetchBuilding(this); } cs::MFActivePlayerData* active_player_data_pb = nullptr; if (send_msg_times == 0 || need_sync_active_player) { active_player_data_pb = new cs::MFActivePlayerData(); FillMFActivePlayerData(active_player_data_pb); need_sync_active_player = false; } cs::SMUpdate* msg = room->frame_maker.MakeUpdateMsg(this); if (send_msg_times == 0 || last_sync_gas_frameno < room->gas_data.gas_start_frameno) { last_sync_gas_frameno = room->gas_data.gas_start_frameno; FillMFGasData(msg->mutable_gas_data()); } bool refreshed_view = false; std::set view_objects; for (Human* observer : observers_) { msg->clear_team_data(); if (observer->team_members) { for (auto& itr : *observer->team_members) { if (itr != observer) { itr->FillMFTeamData(msg->add_team_data()); } } } if (observer != this && !observer->follow_synced_active_player) { msg->set_active_player_id(entity_uniid); FillMFActivePlayerData(msg->mutable_active_player_data()); if (!refreshed_view) { for (auto& cell : grid_list) { for (Human* hum : cell->human_list) { view_objects.insert(hum); } for (Entity* entity : cell->entity_list) { switch (entity->entity_type) { case ET_Building: case ET_Obstacle: case ET_Hero: case ET_Loot: { view_objects.insert(entity); } break; default: { } break; } } } for (Entity* entity : view_objects) { if (new_objects.find(entity) == new_objects.end()) { entity->FillMFObjectFull(msg->add_full_objects()); } } refreshed_view = true; } observer->follow_synced_active_player = true; } else { if (active_player_data_pb) { msg->set_active_player_id(entity_uniid); *msg->mutable_active_player_data() = *active_player_data_pb; } else { msg->clear_active_player_id(); msg->clear_active_player_data(); } } observer->SendNotifyMsg(*msg); } delete msg; if (active_player_data_pb) { delete active_player_data_pb; } ++send_msg_times; } ClearFrameData(); } void Human::SendGameOver() { if (entity_subtype == EST_Player) { //!!!必须要在SendNotifyMsg之前注意哦 if (!sent_battlereport_) { SendBattleReport(); sent_battlereport_ = true; GameLog::Instance()->GameEnd((Player*)this); } { cs::SMGameOver msg; FillSMGameOver(msg); SendNotifyMsg(msg); } } } void Human::FollowTarget(Human* target) { if (target == this) { return; } if (follow_target_) { follow_target_->RemoveObserver(this); } target->AddObserver(this); follow_target_ = target; follow_synced_active_player = false; } void Human::SendDebugMsg(const std::string& debug_msg) { cs::SMDebugMsg notify_msg; notify_msg.set_debug_msg(debug_msg); SendNotifyMsg(notify_msg); } void Human::SendRollMsg(const std::string& roll_msg) { room->xtimer.AddDeadLineTimerAndAttach( 0, a8::XParams() .SetSender(this) .SetParam1(roll_msg), [] (const a8::XParams& param) { Human* target = (Human*)param.sender.GetUserData(); std::string roll_msg = param.param1; target->room->TouchHumanList(a8::XParams(), [target, roll_msg] (Human* hum, a8::XParams& param) -> bool { if (target != hum) { cs::SMRollMsg msg; msg.set_msg(roll_msg); hum->SendNotifyMsg(msg); } return true; }); }, &xtimer_attacher.timer_list_ ); } void Human::UpdateAction() { int duration = std::max(0, action_duration - (int)((room->frame_no - action_frameno) * 1.0f / SERVER_FRAME_RATE) * 1000 ); if (duration <= 0) { switch (action_type) { case AT_Reload: { if (curr_weapon->weapon_idx == action_target_id && curr_weapon->weapon_id == action_item_id && curr_weapon->weapon_idx != 0) { MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(curr_weapon->meta->i->use_bullet()); if (bullet_meta) { int ammo = curr_weapon->ammo; if (ammo < curr_weapon->GetClipVolume()) { if (bullet_meta->i->_inventory_slot() >= 0 && bullet_meta->i->_inventory_slot() < IS_END) { if (GetInventory(bullet_meta->i->_inventory_slot()) > 0) { int add_num = 0; if (GetInventory(bullet_meta->i->_inventory_slot()) <= curr_weapon->GetClipVolume() - ammo) { add_num = GetInventory(bullet_meta->i->_inventory_slot()); DecInventory(bullet_meta->i->_inventory_slot(), add_num); } else { add_num = curr_weapon->GetClipVolume() - ammo; DecInventory(bullet_meta->i->_inventory_slot(), add_num); } curr_weapon->ammo += add_num; need_sync_active_player = true;; } } } } } } break; case AT_UseItem: { switch (action_item_id) { case IS_HEALTHKIT: { MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquipBySlotId(action_item_id); if (item_meta){ if (GetInventory(item_meta->i->_inventory_slot()) > 0) { float old_health = GetHP(); ability.hp += item_meta->i->heal(); ability.hp = std::min(GetHP(), GetMaxHP()); stats.heal_amount += GetHP() - old_health; DecInventory(item_meta->i->_inventory_slot(), 1); need_sync_active_player = true; SyncAroundPlayers(__FILE__, __LINE__, __func__); } } } break; case IS_PAIN_KILLER: { MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquipBySlotId(action_item_id); if (item_meta){ if (GetInventory(item_meta->i->_inventory_slot()) > 0) { if (pain_killer_timer) { int passed_time = (room->frame_no - pain_killer_frameno) * FRAME_RATE_MS; int left_time = std::max(0, pain_killer_lastingtime * 1000 - passed_time); int anodyne_max_time = MetaMgr::Instance()->GetSysParamAsInt("anodyne_max_time"); left_time = std::min(left_time, anodyne_max_time * 1000); pain_killer_lastingtime += std::min(item_meta->i->time() * 1000, anodyne_max_time * 1000 - left_time) / 1000; need_sync_active_player = true; } else { pain_killer_frameno = room->frame_no; pain_killer_lastingtime = item_meta->i->time(); pain_killer_timer = room->xtimer.AddRepeatTimerAndAttach( SERVER_FRAME_RATE, a8::XParams() .SetSender(this) .SetParam1(item_meta->i->heal()), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); float old_health = hum->GetHP(); hum->ability.hp += param.param1.GetDouble(); hum->ability.hp = std::min(hum->GetHP(), hum->GetMaxHP()); hum->stats.heal_amount += hum->GetHP() - old_health; hum->SyncAroundPlayers(__FILE__, __LINE__, __func__); if (hum->room->frame_no - hum->pain_killer_frameno > hum->pain_killer_lastingtime * SERVER_FRAME_RATE) { hum->room->xtimer.DeleteTimer(hum->pain_killer_timer); hum->pain_killer_timer = nullptr; } }, &xtimer_attacher.timer_list_ ); } DecInventory(item_meta->i->_inventory_slot(), 1); need_sync_active_player = true; } } } break; default: { } break; } } break; case AT_Relive: { Entity* entity = room->GetEntityByUniId(action_target_id); if (entity->entity_type != ET_Player) { return; } Human* hum = (Human*)entity; if (hum->action_type == AT_Rescue) { hum->CancelAction(); return; } if (!hum->dead && hum->downed) { hum->ability.hp = MetaMgr::Instance()->GetSysParamAsInt("downed_relive_recover_hp"); hum->downed = false; if (hum->downed_timer) { room->xtimer.DeleteTimer(hum->downed_timer); hum->downed_timer = nullptr; } ++hum->stats.rescue_member; } hum->SyncAroundPlayers(__FILE__, __LINE__, __func__); } break; default: break; } ResetAction(); } } void Human::SendUIUpdate() { cs::SMUiUpdate notifymsg; notifymsg.set_alive_count(room->AliveCount()); notifymsg.set_kill_count(stats.kills); room->FillSMUiUpdate(notifymsg); SendNotifyMsg(notifymsg); } void Human::SendWxVoip() { cs::SMWxVoip notifymsg; if (!team_uuid.empty()) { notifymsg.set_group_id(a8::XValue(room->room_uuid).GetString() + "_" + a8::XValue(team_id).GetString()); } SendNotifyMsg(notifymsg); } int Human::GetWeaponConfigLv(int weapon_id) { auto itr = weapon_configs.find(weapon_id); return itr != weapon_configs.end() ? itr->second : 0; } int Human::GetSkinConfigLv(int skin_id) { auto itr = skin_configs.find(skin_id); return itr != skin_configs.end() ? itr->second : 0; } void Human::CheckSkinTank() { auto dechp_func = [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); if (hum->dead || !hum->last_tank_attacker || hum->last_tank_attacker->dead || hum->last_tank_attacker->skin_tank.skin_id == 0 || hum->last_tank_attack_idx != param.param1.GetInt64() ) { hum->last_tank_attacker = nullptr; hum->last_tank_attack_idx = 0; hum->room->xtimer.DeleteTimer(hum->room->xtimer.GetRunningTimer()); return; } float old_rad = hum->last_tank_attacker->self_collider_->rad; if (!hum->TestCollision(hum->last_tank_attacker)) { hum->last_tank_attacker->self_collider_->rad = old_rad; hum->last_tank_attacker = nullptr; hum->last_tank_attack_idx = 0; hum->room->xtimer.DeleteTimer(hum->room->xtimer.GetRunningTimer()); return; } hum->last_tank_attacker->self_collider_->rad = old_rad; #if 1 float def = hum->ability.def; #else float def = hum->def + hum->buff.def_add; #endif float finaly_dmg = MetaMgr::Instance()->tank_damage2 * (1 - def/MetaMgr::Instance()->K); hum->DecHP(finaly_dmg, hum->last_tank_attacker->entity_uniid, hum->last_tank_attacker->name, VW_Tank); }; if (tank_weapon.meta) { float old_rad = self_collider_->rad; self_collider_->rad = skin_tank_meta->i->rad2(); std::set objects; for (auto& grid : grid_list) { for (Human* hum: grid->human_list) { if (hum != this && !hum->dead && !hum->tank_weapon.meta && hum->last_tank_attacker != this && (hum->team_id == 0 || team_id != hum->team_id)) { if (TestCollision(hum)) { objects.insert(hum); } } } }//end for for (Human* hum : objects) { hum->last_tank_attacker = this; hum->last_tank_attack_idx = App::Instance()->NewUuid(); #if 1 float def = hum->ability.def; #else float def = hum->def + hum->buff.def_add; #endif float finaly_dmg = MetaMgr::Instance()->tank_damage1 * (1 - def/MetaMgr::Instance()->K); hum->DecHP(finaly_dmg, entity_uniid, name, VW_Tank); room->xtimer.AddRepeatTimerAndAttach( SERVER_FRAME_RATE * MetaMgr::Instance()->tank_damage_interval, a8::XParams() .SetSender(hum) .SetParam1(hum->last_tank_attack_idx), dechp_func, &hum->xtimer_attacher.timer_list_ ); } self_collider_->rad = old_rad; } } void Human::_UpdateMove(int speed) { for (int i = 0; i < speed; ++i) { a8::Vec2 old_pos = GetPos(); SetPos(GetPos() + move_dir); if (IsCollisionInMapService()) { SetPos(old_pos); FindPathInMapService(); #if 0 if (rand() % 3 == 0) { i += 1; } #endif } room->grid_service.MoveHuman(this); } } void Human::ClearFrameData() { if (!new_objects.empty()) { new_objects.clear(); } if (!del_objects.empty()) { for (auto& itr : del_objects) { Entity* entity = room->GetEntityByUniId(itr); if (entity) { RemovePartObjects(entity); } } del_objects.clear(); } if (!out_objects.empty()) { for (auto& itr : out_objects) { Entity* entity = room->GetEntityByUniId(itr); if (entity) { RemovePartObjects(entity); } } out_objects.clear(); } if (!shots_.empty()) { shots_.clear(); } if (!bullets_.empty()) { bullets_.clear(); } if (!explosions_.empty()) { explosions_.clear(); } if (!smokes_.empty()) { smokes_.clear(); } if (!emotes_.empty()) { emotes_.clear(); } if (!chged_bullet_nums_.empty()) { chged_bullet_nums_.clear(); } if (!chged_tank_bullet_nums_.empty()) { chged_tank_bullet_nums_.clear(); } if (!chged_tank_oil_value_.empty()){ chged_tank_oil_value_.clear(); } if (!chged_tank_oil_max_.empty()){ chged_tank_oil_max_.clear(); } if (!chged_hps_.empty()) { chged_hps_.clear(); } if (!chged_buffs_.empty()) { chged_buffs_.clear(); } if (!chged_skillcds_.empty()) { chged_skillcds_.clear(); } } void Human::GenBattleReportData(a8::MutableXObject* params) { int rank = 0; { std::vector human_list; room->TouchHumanList(a8::XParams(), [&human_list] (Human* hum, a8::XParams& param) -> bool { human_list.push_back(hum); return true; }); std::sort(human_list.begin(), human_list.end(), [] (Human* a, Human* b ) { if (a->dead_frameno == b->dead_frameno) { return a->entity_uniid < b->entity_uniid; } else { return a->dead_frameno == 0 || (b->dead_frameno != 0 && a->dead_frameno > b->dead_frameno); } }); rank = human_list.size(); for (size_t i = 0; i < human_list.size(); ++i) { if (human_list[i] == this) { rank = i + 1; break; } } if (room->GetAliveTeamNum() == 1) { std::set* alive_team = room->GetAliveTeam(); if (alive_team == team_members) { rank = 1; } } } stats.rank = rank; params->SetVal("account_id", account_id); params->SetVal("map_id", room->map_meta->i->map_id()); params->SetVal("map_name", room->map_meta->i->map_name()); params->SetVal("map_tpl_name", room->map_tpl_name); params->SetVal("game_time", time(nullptr)); params->SetVal("hurt", stats.damage_amount_in); params->SetVal("rank", rank); params->SetVal("kills", stats.kills); params->SetVal("harm", stats.damage_amount_out); params->SetVal("add_HP", stats.heal_amount); if (!dead) { params->SetVal("alive_time", room->frame_no * 1000.0f / SERVER_FRAME_RATE); } else { params->SetVal("alive_time", dead_frameno * 1000.0f / SERVER_FRAME_RATE); } params->SetVal("team_status", team_members && team_members->size() > 1 ? 1 : 0); params->SetVal("room_uuid", room->room_uuid); int snipe_kill = 0; int rifle_kill = 0; int pistol_kill = 0; int submachine_kill = 0; for (Human* hum : kill_humans) { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(hum->lethal_weapon); if (equip_meta) { switch (equip_meta->i->equip_subtype()) { case 7: { //狙击枪 ++snipe_kill; } break; case 5: { //步枪 ++rifle_kill; } break; case 2: { //手枪 ++pistol_kill; } break; case 3: { //冲锋枪 ++submachine_kill; } break; } } } params->SetVal("snipe_kill", snipe_kill); params->SetVal("rifle_kill", rifle_kill); params->SetVal("pistol_kill", pistol_kill); params->SetVal("submachine_kill", submachine_kill); params->SetVal("rescue_member", stats.rescue_member); { float rank_param = MetaMgr::Instance()->GetRankRewardParam(rank); float kill_param = MetaMgr::Instance()->GetKillRewardParam(stats.kills); int coin_num = (rank_param * MetaMgr::Instance()->rank_param) + (kill_param * MetaMgr::Instance()->kill_param); stats.gold = coin_num; params->SetVal("coin_num", coin_num); } { stats.pass_score = MetaMgr::Instance()->GetKillPointParam1(stats.kills); stats.pass_score += MetaMgr::Instance()->GetRankPointParam1(rank); stats.rank_score = MetaMgr::Instance()->GetKillPointParam2(stats.kills); stats.rank_score += MetaMgr::Instance()->GetRankPointParam2(rank); } params->SetVal("score", 0); params->SetVal("pass_score", has_pass ? stats.pass_score * 2 : stats.pass_score); params->SetVal("rank_score", stats.rank_score); } void Human::DeadDrop() { for (auto& weapon : weapons) { if (weapon.weapon_id != 0 && weapon.weapon_id != default_weapon.weapon_id) { a8::Vec2 drop_pos = GetPos(); room->DropItem(drop_pos, weapon.weapon_id, 1, weapon.weapon_lv); } } for (size_t slot = 0; slot < inventory_.size(); ++slot) { if (inventory_[slot] > 0) { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquipBySlotId(slot); if (equip_meta) { if (equip_meta->i->equip_type() == 2 && MetaMgr::Instance()->fighting_mode) { return; } a8::Vec2 drop_pos = GetPos(); room->DropItem(drop_pos, equip_meta->i->id(), inventory_[slot], 1); } } } } void Human::SendBattleReport() { a8::MutableXObject* params = a8::MutableXObject::NewObject(); GenBattleReportData(params); auto on_ok = [] (a8::XParams& param, a8::XObject& data) { }; auto on_error = [] (a8::XParams& param, const std::string& response) { a8::UdpLog::Instance()->Error("battleReport http error params: %s response: %s", { param.param2, response }); }; std::string url; if (!f8::IsOnlineEnv()) { if (App::Instance()->HasFlag(3)) { url = "http://192.168.100.41/webapp/index.php?c=Role&a=battleReport"; } else { url = "https://game2001api-test.kingsome.cn/webapp/index.php?c=Role&a=battleReport"; } } else { #if 1 url = "http://game2001api.kingsome.cn/webapp/index.php?c=Role&a=battleReport"; #else url = "https://game2001api.kingsome.cn/webapp/index.php?c=Role&a=battleReport"; #endif } std::string data; params->ToUrlEncodeStr(data); f8::HttpClientPool::Instance()->HttpGet( a8::XParams() .SetSender(room->room_uuid) .SetParam1(entity_uniid) .SetParam2(data), on_ok, on_error, url.c_str(), *params, MAX_SYS_HTTP_NUM + (entity_uniid % MAX_USER_HTTP_NUM) ); delete params; } void Human::ProcLootSkin(Loot* entity, MetaData::Equip* item_meta) { auto oil_sync_func = [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); if (hum->dead || hum->skin_tank.tank_uniid != param.param1.GetInt()) { hum->room->xtimer.DeleteTimer(hum->room->xtimer.GetRunningTimer()); return; } a8::XParams* mutable_param = hum->room->xtimer.MutableParams(hum->room->xtimer.GetRunningTimer()); if (std::abs(mutable_param->param2.GetDouble() - (double)hum->tank_oil_value) > 0.1f) { hum->room->frame_event.AddTankOilValueChg(hum); mutable_param->param2 = hum->tank_oil_value; } }; if (item_meta->i->is_luck()) { if (item_meta->i->is_luck() == 2) { if (skin_tank.skin_id != 0) { int entity_uniid = room->CreateLoot(skin_tank.skin_id, GetPos(), 1, 1); Entity* loot_entity = room->GetEntityByUniId(entity_uniid); if (loot_entity && loot_entity->entity_type == ET_Loot) { ((Loot*)loot_entity)->bullet_num = tank_weapon.ammo; ((Loot*)loot_entity)->param1 = tank_oil_value; ((Loot*)loot_entity)->param2 = tank_oil_max; room->UpdateCarObject(skin_tank.tank_uniid, loot_entity->entity_uniid, loot_entity->GetPos()); } } skin_tank.tank_uniid = entity->entity_uniid; skin_tank.skin_id = item_meta->i->id(); skin_tank.skin_lv = std::max(1, GetSkinConfigLv(skin_tank.skin_id)); skin_tank_meta = item_meta; tank_oil_value = entity->param1; tank_oil_max = entity->param2; { tank_weapon = Weapon(); tank_weapon.weapon_idx = 100; tank_weapon.weapon_id = item_meta->i->use_bullet(); tank_weapon.weapon_lv = 1; tank_weapon.ammo = entity->bullet_num; tank_weapon.meta = MetaMgr::Instance()->GetEquip(item_meta->i->use_bullet()); tank_weapon.Recalc(); } SetPos(entity->GetPos()); RecalcSelfCollider(); RecalcBuffAttr(); SyncAroundPlayers(__FILE__, __LINE__, __func__); room->TakeOnCarObject(skin_tank.tank_uniid); room->NotifyUiUpdate(); room->frame_event.AddTankBulletNumChg(this); room->frame_event.AddTankOilMaxChg(this); room->frame_event.AddTankOilValueChg(this); room->xtimer.AddRepeatTimerAndAttach( SERVER_FRAME_RATE, a8::XParams() .SetSender(this) .SetParam1(skin_tank.tank_uniid) .SetParam2(tank_oil_value), oil_sync_func, &xtimer_attacher.timer_list_ ); } else { if (skin_jlf.skin_id != 0) { room->DropItem(GetPos(), skin_jlf.skin_id, 1, skin_jlf.skin_lv); } skin_jlf.skin_id = item_meta->i->id(); skin_jlf.skin_lv = std::max(1, GetSkinConfigLv(skin_jlf.skin_id)); skin_jlf_meta = MetaMgr::Instance()->GetDress(skin_jlf.skin_id); #if 0 if (skin_jlf_meta) { skill_meta = MetaMgr::Instance()->GetSkill(skin_jlf_meta->i->skill_id()); } else { skill_meta = nullptr; } #endif RecalcBuffAttr(); SyncAroundPlayers(__FILE__, __LINE__, __func__); } } } void Human::FindLocationWithTarget(Entity* target) { a8::Vec2 old_pos = GetPos(); a8::Vec2 new_pos = GetPos(); AabbCollider a_collider; GetAabbBox(a_collider); AabbCollider target_collider; target->GetAabbBox(target_collider); { bool ret = a_collider.CalcSafePoint(&target_collider, new_pos); if (!ret) { abort(); } } a8::Vec2 new_pos_dir = new_pos - old_pos; new_pos_dir.Normalize(); float distance = (new_pos - old_pos).Norm(); for (int i = distance; i < 10000000; i += 5) { SetPos(old_pos + new_pos_dir * i); room->grid_service.MoveHuman(this); Entity* building = nullptr; std::set new_grid_list; room->grid_service.GetAllCellsByXy(GetX(), GetY(), new_grid_list); for (auto& grid : new_grid_list) { for (Entity* entity : grid->entity_list) { switch (entity->entity_type) { case ET_Building: { if (TestCollision(entity)) { building = entity; } } break; default: break; } if (building) { break; } } if (building) { break; } } if (!building) { bool is_collision = false; std::set colliders; room->map_service.GetColliders(GetX(), GetY(), colliders); for (ColliderComponent* collider : colliders) { if (TestCollision(collider)) { is_collision = true; break; } } if (!is_collision) { break; } } } } void Human::OnDie() { room->OnHumanDie(this); SyncAroundPlayers(__FILE__, __LINE__, __func__); if (team_members) { for (auto& hum : *team_members) { if (hum != this && hum->action_type == AT_Relive && hum->action_target_id == entity_uniid) { hum->CancelAction(); } } } { std::set over_humans; if (!leave_) { if (!HasNoDownedTeammate()) { if (team_members) { for (auto& member : *team_members) { if (member->real_dead) { over_humans.insert(member); } } } else { over_humans.insert(this); } } else { over_humans.insert(this); } } if (room->GetAliveTeamNum() == 1) { std::set* alive_team = room->GetAliveTeam(); if (alive_team) { for (Human* member : *alive_team) { over_humans.insert(member); } } } for (Human* hum : over_humans) { hum->SendGameOver(); } } DeadDrop(); } void Human::FreeDownedTimer() { if (downed_timer) { room->xtimer.DeleteTimer(downed_timer); downed_timer = nullptr; } } void Human::FreeReviveTimer() { if (revive_timer) { room->xtimer.DeleteTimer(revive_timer); revive_timer = nullptr; } } void Human::RandSkin() { std::vector ids = {1, 2, 3, 4, 5, 6}; for (int i = 0; i < 3; ++i) { int rand_idx = rand() % ids.size(); Skin& skin = a8::FastAppend(skins); skin.skin_id = ids[rand_idx]; skin.skin_lv = 1; ids.erase(ids.begin() + rand_idx); } } void Human::AddSkin(int skin_id) { if (skins.size() < 3) { Skin& skin = a8::FastAppend(skins); skin.skin_id = skin_id; skin.skin_lv = 1; } } MetaData::Skill* Human::CurrentSkillMeta() { return skill_meta_; } int Human::GetSkillLeftTime() { if (skill_meta_) { if (last_use_skill_frameno_ == 0) { return 0; } else { int passed_time = (room->frame_no - last_use_skill_frameno_) * FRAME_RATE_MS; int skill_left_time = std::max(0, GetSkillCd() - passed_time); return skill_left_time; } } return 0; } int Human::GetSkillCd() { if (!skill_meta_) { return 0; } if (GetLevel() <= 1) { return skill_meta_->i->skill_cd() * 1000; } else { return skill_meta_->i->skill_cd() * 1000 - (GetLevel() - 1) * skill_meta_->i->cold_time_up(); } } int Human::GetSkillLv() { return GetLevel(); } float Human::GetSkillAtkAdd(int skill_id) { if (!skill_meta_) { return 0.0f; } if (skill_meta_->i->skill_id() != skill_id) { return 0.0f; } if (GetSkillLv() > 0) { return skill_meta_->value_up * GetSkillLv(); } else { return 0.0f; } } void Human::TriggerOneObjectBuff(Entity* target, BuffTriggerType_e trigger_type) { if (target->entity_type != ET_Player) { return; } if (target->dead) { return; } Human* hum = (Human*)target; 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 (hum == this) { hum->AddBuff(buff_meta, GetSkillLv()); } } break; case kBuffTargetFriendly: //友军 { if (hum->team_id == team_id) { hum->AddBuff(buff_meta, GetSkillLv()); } } break; case kBuffTargetEnemy: //敌军 { if (hum->team_id != team_id) { hum->AddBuff(buff_meta, GetSkillLv()); } } break; default: break; } } } } void Human::TriggerBuff(std::set& target_list, BuffTriggerType_e trigger_type) { for (Entity* entity : target_list) { TriggerOneObjectBuff(entity, trigger_type); } } void Human::AddBuff(MetaData::Buff* buff_meta, int skill_lv) { if (GetBuffById(buff_meta->i->buff_id())) { 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->add_frameno = room->frame_no; buff->xtimer_attacher.xtimer = &room->xtimer; buff_effect_[buff->meta->i->buff_effect()] = buff; room->frame_event.AddBuff(this, 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) { Human* hum = (Human*)param.sender.GetUserData(); hum->RemoveBuffById(param.param1); }, &buff->xtimer_attacher.timer_list_ ); } ProcBuffEffect(buff); } void Human::RemoveBuffById(int buff_id) { for (auto itr = buff_list_.begin(); itr != buff_list_.end(); ++itr) { if (itr->meta->i->buff_id() == buff_id) { if (buff_effect_[itr->meta->i->buff_effect()] == &(*itr)) { buff_effect_[itr->meta->i->buff_effect()] = nullptr; } buff_list_.erase(itr); room->frame_event.RemoveBuff(this, buff_id); break; } } RecalcBuffAttr(); } void Human::RemoveBuffByEffectId(int buff_effect_id) { Buff* buff = GetBuffByEffectId(buff_effect_id); if (buff) { RemoveBuffById(buff->meta->i->buff_id()); } } void Human::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; } #if 0 switch (itr->meta->i->buff_effect()) { default: break; } #endif room->frame_event.RemoveBuff(this, itr->meta->i->buff_id()); } buff_list_.clear(); buff_effect_ = {}; buff_attr_abs_ = {}; buff_attr_rate_ = {}; RecalcBuffAttr(); } bool Human::HasBuffEffect(int buff_effect_id) { return GetBuffByEffectId(buff_effect_id) != nullptr; } void Human::ProcBuffEffect(Buff* buff) { switch (buff->meta->i->buff_effect()) { case kBET_ChgAttr: { RecalcBuffAttr(); } break; case kBET_Jump: { if (curr_skill_phase < skill_meta_->phases.size()) { MetaData::SkillPhase* phase = &skill_meta_->phases[curr_skill_phase]; if (phase->time_offset >= (int)((room->frame_no - last_use_skill_frameno_) * FRAME_RATE_MS)) { _UpdateMove(phase->param1); } } } break; default: break; } } void Human::RecalcBuffAttr() { buff_attr_abs_ = {}; buff_attr_rate_ = {}; for (auto& buff : buff_list_) { if (buff.meta->i->buff_effect() == kBET_ChgAttr) { 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 Human::Revive() { auto callback = [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->dead = true; hum->real_dead = true; hum->downed = false; hum->OnDie(); }; int wait_revive_time = MetaMgr::Instance()->GetSysParamAsInt("revive_time", 25) + kReviveTimeAdd; revive_timer = room->xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * wait_revive_time, a8::XParams() .SetSender(this), callback, &xtimer_attacher.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->revive_timer = nullptr; }); SyncAroundPlayers(__FILE__, __LINE__, __func__); } void Human::ResetTankSkin() { skin_tank = Skin(); skin_tank_meta = nullptr; tank_weapon = Weapon(); tank_oil_value = 0.0f; tank_oil_max = 0.0f; RecalcSelfCollider(); } Buff* Human::GetBuffById(int buff_id) { for (Buff& buff : buff_list_) { if (buff.meta->i->buff_id() == buff_id) { return &buff; } } return nullptr; } Buff* Human::GetBuffByEffectId(int effect_id) { return IsValidBuffEffect(effect_id) ? buff_effect_[effect_id] : nullptr; } void Human::SelectSkillTargets(const a8::Vec2& target_pos, std::set& target_list) { switch (skill_meta_->i->skill_target()) { case kST_All: { for (auto& cell : grid_list) { for (Human* hum : cell->human_list) { if (hum->GetPos().Distance(target_pos) < skill_meta_->i->skill_distance()) { target_list.insert(hum); } } } } break; case kST_Self: { target_list.insert(this); } break; case kST_FriendlyIncludeSelf: { target_list.insert(this); for (auto& cell : grid_list) { for (Human* hum : cell->human_list) { if ((hum == this || hum->team_id == team_id) && hum->GetPos().Distance(target_pos) < skill_meta_->i->skill_distance()) { target_list.insert(hum); } } } } break; case kST_FriendlyExcludeSelf: { for (auto& cell : grid_list) { for (Human* hum : cell->human_list) { if ((hum != this && hum->team_id == team_id) && hum->GetPos().Distance(target_pos) < skill_meta_->i->skill_distance()) { target_list.insert(hum); } } } } break; case kST_EnemySingle: { Entity* entity = room->GetEntityByUniId(skill_target_id); if (entity && entity->entity_type == ET_Player) { Human* hum = (Human*)entity; if (hum->team_id != team_id) { target_list.insert(hum); } } } break; case kST_EnemyGroup: { for (auto& cell : grid_list) { for (Human* hum : cell->human_list) { if ((hum->team_id != team_id) && hum->GetPos().Distance(target_pos) < skill_meta_->i->skill_distance()) { target_list.insert(hum); } } } } break; case kST_EnemyAndObject: { for (auto& cell : grid_list) { for (Human* hum : cell->human_list) { if ((hum->team_id != team_id) && hum->GetPos().Distance(target_pos) < skill_meta_->i->skill_distance()) { target_list.insert(hum); } } } } break; case kST_EnemyAndSelf: { for (auto& cell : grid_list) { for (Human* hum : cell->human_list) { if ((hum == this || hum->team_id != team_id) && hum->GetPos().Distance(target_pos) < skill_meta_->i->skill_distance()) { target_list.insert(hum); } } } } break; case kST_SingleEnemyAndSelf: { Entity* entity = room->GetEntityByUniId(skill_target_id); if (entity && entity->entity_type == ET_Player) { Human* hum = (Human*)entity; if (hum->team_id != team_id) { target_list.insert(hum); } } target_list.insert(this); } break; default: break; } } void Human::ProcSkillPhase(MetaData::SkillPhase* phase) { switch (phase->func_id) { case kSkill_Jump: { } break; default: break; } }