#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 "app.h" #include "roommgr.h" #include "android.h" #include "gamelog.h" #include "typeconvert.h" #include "obstacle.h" #include "roomobstacle.h" #include "player.h" #include "buff.h" #include "car.h" #include "roomobstacle.h" #include "aicomponent.h" #include "jsondatamgr.h" #include "skill.h" #include "incubator.h" #include "framework/cpp/utils.h" #include "framework/cpp/httpclientpool.h" const int kReviveTimeAdd = 12; const int kSkinNum = 4; const int kREVIVE_BUFF_ID = 0; Human::Human():Creature() { default_weapon.weapon_idx = 0; default_weapon.weapon_id = 30101; default_weapon.weapon_lv = 1; default_weapon.ammo = 1; default_weapon.meta = MetaMgr::Instance()->GetEquip(default_weapon.weapon_id); default_weapon.Recalc(); for (int i = 0; i < kSkinNum; ++i) { Skin& skin = a8::FastAppend(skins); skin.skin_id = 0; skin.skin_lv = 1; } weapons[0] = default_weapon; SetCurrWeapon(&weapons[0]); } Human::~Human() { } void Human::Initialize() { Creature::Initialize(); RecalcSelfCollider(); volume_ = meta->volume; observers_.insert(this); ability.hp = meta->i->health(); for (auto& weapon : spec_weapons) { if (weapon.meta) { ability.hp += weapon.meta ? weapon.GetAttrValue(kHAT_MaxHp) : 0; } } } float Human::GetSpeed() { { Buff* buff = GetBuffByEffectId(kBET_JumpTo); if (buff) { return buff->meta->param2; } } { Buff* buff = GetBuffByEffectId(kBET_BePull); if (buff) { return buff->meta->param2; } } if (downed) { return meta->i->move_speed3(); } else { if (shot_hold) { if (GetCurrWeapon()->weapon_idx == GUN_SLOT1 || GetCurrWeapon()->weapon_idx == GUN_SLOT2) { if (action_type != AT_Reload) { return meta->i->shot_speed(); } } } else if (aiming) { return std::max(1, meta->i->aiming_speed()); } else if (action_type != AT_None) { return std::max(1, meta->i->reload_speed()); } float speed = meta->i->move_speed(); speed = (speed + GetBuffAttrAbs(kHAT_Speed)) * (1 + GetBuffAttrRate(kHAT_Speed)); if (a8::HasBitFlag(cell_flags_, kColliderTag_Water)) { speed *= MetaMgr::Instance()->water_move_coefficient; } return std::max(speed, 1.0f); } } float Human::GetSpeed4() { return meta->i->move_speed4(); } void Human::FillMFObjectPart(Room* room, Human* hum, cs::MFObjectPart* part_data) { part_data->set_object_type(ET_Player); cs::MFPlayerPart* p = part_data->mutable_union_obj_1(); p->set_obj_uniid(GetEntityUniId()); p->set_speed(GetSpeed() * SERVER_FRAME_RATE); TypeConvert::ToPb(GetPos(), p->mutable_pos()); TypeConvert::ToPb(GetAttackDir(), p->mutable_dir()); } void Human::FillMFObjectLess(Room* room, Human* hum, cs::MFPlayerFull* full_data) { cs::MFPlayerFull* p = full_data; p->set_obj_uniid(GetEntityUniId()); TypeConvert::ToPb(GetPos(), p->mutable_pos()); TypeConvert::ToPb(GetAttackDir(), 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); for (auto itr : skins) { auto skin = p->add_skin(); itr.ToPB(skin); } p->set_backpack(backpack); p->set_helmet(helmet); p->set_chest(chest); p->set_shoot_offset_x(shoot_offset.x); p->set_shoot_offset_y(shoot_offset.y); GetCurrWeapon()->ToPB(p->mutable_weapon()); } void Human::FillMFObjectFull(Room* room, Human* hum, cs::MFObjectFull* full_data) { full_data->set_object_type(ET_Player); cs::MFPlayerFull* p = full_data->mutable_union_obj_1(); p->set_obj_uniid(GetEntityUniId()); TypeConvert::ToPb(GetPos(), p->mutable_pos()); TypeConvert::ToPb(GetAttackDir(), 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); p->set_speed(GetSpeed() * SERVER_FRAME_RATE); for (auto itr : skins) { auto skin = p->add_skin(); itr.ToPB(skin); } p->set_backpack(backpack); p->set_helmet(helmet); p->set_chest(chest); GetCurrWeapon()->ToPB(p->mutable_weapon()); p->set_energy_shield(energy_shield); p->set_shoot_offset_x(shoot_offset.x); p->set_shoot_offset_y(shoot_offset.y); #if 1 { p->set_max_energy_shield(max_energy_shield); } #endif if (guild_id != 0) { p->set_guild_id(guild_id); } if (vip_lv != 0) { p->set_vip_lv(vip_lv); } if (head != 0) { p->set_head(head); } if (sex != 0) { p->set_sex(sex); } if (!user_data.empty()) { p->set_user_data(user_data); } 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()); FillBuffList(hum, p->mutable_buff_list()); FillSkillList(p->mutable_skill_list()); 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 " "dead_frameno:%d dead_times:%d alive_count:%d " "room.frameno:%d", { dead_frameno, dead_times, room->AliveCount(), room->GetFrameNo() }); } countdown = std::max(0, countdown - kReviveTimeAdd); p->set_revive_countdown(countdown); } } if (room->GetRoomMode() == kZombieMode) { p->set_charid(meta->i->id()); } if (GetCar()) { p->set_car_uniid(GetCar()->car_uniid); p->set_car_seat(GetSeat()); } else { p->set_car_uniid(0); p->set_car_seat(0); } } void Human::FillMFPlayerStats(cs::MFPlayerStats* stats_pb) { stats_pb->set_player_id(GetEntityUniId()); stats_pb->set_player_avatar_url(avatar_url); stats_pb->set_charid(meta->i->id()); stats_pb->set_team_id(team_id); stats_pb->set_nickname(name); if (!dead) { stats_pb->set_time_alive(room->GetFrameNo() * 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); if (guild_id != 0) { stats_pb->set_guild_id(guild_id); } stats_pb->set_rescue_guild_member(stats.rescue_guild_member); for (auto& pair : stats.items) { auto p = stats_pb->add_items(); p->set_key(pair.first); p->set_value(pair.second); } for (auto& pair : stats.extra_drop) { auto p = stats_pb->add_extra_drop(); p->set_key(pair.first); p->set_value(pair.second); } if (vip_lv != 0) { stats_pb->set_vip_lv(vip_lv); } if (head != 0) { stats_pb->set_head(head); } if (sex != 0) { stats_pb->set_sex(sex); } { for (auto& skin : skins) { auto pb_skin = stats_pb->add_skin(); skin.ToPB(pb_skin); } } } void Human::GetAabbBox(AabbCollider& aabb_box) { if (!meta) { abort(); } aabb_box.active = true; aabb_box.owner = this; if (GetCar() && GetCar()->IsDriver(this)) { aabb_box._min.x = -GetCar()->meta->i->rad(); aabb_box._min.y = -GetCar()->meta->i->rad(); aabb_box._max.x = GetCar()->meta->i->rad(); aabb_box._max.y = GetCar()->meta->i->rad(); } else { 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(); } } bool Human::IsDead(Room * room) { return dead; } long long Human::GetDeadFrameNo(Room* room) { return dead_frameno; } void Human::FillItemList(::google::protobuf::RepeatedPtrField<::cs::MFPair>* pb_item_list) { for (auto& pair : items_) { auto p = pb_item_list->Add(); p->set_key(pair.first); p->set_value(pair.second); } } long long Human::GetRealDeadFrameNo(Room* room) { return real_dead_frameno; } void Human::FillMFTeamData(cs::MFTeamData* team_data, bool is_game_over) { { last_sync_teamdata_frameno_ = room->GetFrameNo(); team_data->set_player_id(GetEntityUniId()); if (is_game_over || !real_dead || room->GetFrameNo() - GetRealDeadFrameNo(room) < 4) { TypeConvert::ToPb(GetPos(), team_data->mutable_pos()); TypeConvert::ToPb(GetAttackDir(), team_data->mutable_dir()); team_data->set_health(GetHP()); team_data->set_max_health(GetMaxHP()); team_data->set_dead(dead); team_data->set_downed(downed); } if (is_game_over || room->GetGasData().gas_mode == GasInactive) { team_data->set_name(name); } if (room->GetGasData().gas_mode != GasInactive && room->GetFrameNo() - room->GetBattleStartFrameNo() < 4) { team_data->set_name(name); team_data->set_disconnected(socket_handle == 0); } if (is_game_over) { team_data->set_account_id(account_id); team_data->set_avatar_url(avatar_url); team_data->set_user_value1(user_value1); team_data->set_user_value2(user_value2); team_data->set_user_value3(user_value3); if (guild_id != 0) { team_data->set_guild_id(guild_id); } } if (vip_lv != 0) { team_data->set_vip_lv(vip_lv); } if (head != 0) { team_data->set_head(head); } if (sex != 0) { team_data->set_sex(sex); } if (room->GetGasData().gas_mode == GasInactive || room->GetFrameNo() - room->GetBattleStartFrameNo() < 4) { team_data->set_user_data(user_data); } } } void Human::CarShot(const a8::Vec2& target_dir) { if (!second_weapon.meta) { return; } if (second_weapon.weapon_idx != 0 && second_weapon.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(); } InternalShot(this, second_weapon.meta, second_weapon.GetUpgradeMeta(), second_weapon.bullet_meta, second_weapon.weapon_lv, 0, 5, false); --second_weapon.ammo; last_shot_frameno_ = room->GetFrameNo(); } void Human::RecalcSelfCollider() { if (!self_collider_) { self_collider_ = new CircleCollider(); self_collider_->owner = this; AddEntityCollider(self_collider_); } self_collider_->pos = a8::Vec2(); self_collider_->rad = meta->i->radius(); Buff* buff = GetBuffByEffectId(kBET_Car); if (buff) { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(buff->meta->param4); if (equip_meta && equip_meta->i->rad() > 1 && equip_meta->i->equip_type() == EQUIP_TYPE_CAR ) { self_collider_->rad = equip_meta->i->rad(); } } } bool Human::IsCollisionInMapService() { Global::last_collider = nullptr; if (room->OverBorder(GetPos(), meta->i->radius())){ return true; } if (HasBuffEffect(kBET_ThroughWall) || HasBuffEffect(kBET_Fly)) { return false; } std::set colliders; room->map_service->GetColliders(room, GetX(), GetY(), colliders); AabbCollider aabb_box; GetAabbBox(aabb_box); for (ColliderComponent* collider : colliders) { switch (collider->owner->GetEntityType()) { case ET_Obstacle: { Obstacle* obstacle = (Obstacle*)collider->owner; if (!obstacle->IsDead(room) && ( (collider->type == CT_Aabb && aabb_box.Intersect((ColliderComponent*)collider)) || (collider->type == CT_Circle && self_collider_->Intersect((ColliderComponent*)collider)) ) && !obstacle->CanThroughable(this)) { if (last_collision_door_ != collider->owner) { if (!obstacle->IsDead(room) && obstacle->Attackable() && obstacle->meta->i->drop() != 0 && room->GetGasData().gas_mode != GasInactive && (!obstacle->IsTerminatorAirDropBox(room) || GetRace() == kHumanRace) ) { obstacle->Die(room); if (obstacle->IsDead(room)) { #if 0 if (obstacle->meta->i->damage_dia() > 0.01f && obstacle->meta->i->damage() > 0.01f) { obstacle->Explosion(this); } #endif DropItems(obstacle); } obstacle->BroadcastFullState(room); if (obstacle->IsTerminatorAirDropBox(room) && GetRace() == kHumanRace && !HasBuffEffect(kBET_Terminator)) { MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(TERMINATOR_BUFF_ID); if (buff_meta) { AddBuff(this, buff_meta, 1); } } } else { Global::last_collider = collider; return true; } } } } break; case ET_Building: { #if 1 if ( ( (collider->type == CT_Aabb && aabb_box.Intersect((ColliderComponent*)collider)) || (collider->type == CT_Circle && self_collider_->Intersect((ColliderComponent*)collider)) ) ) { #else if (!collider->owner->dead && TestCollision((ColliderComponent*)collider)) { #endif 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(GetMoveDir()); bool at_left_side = a8::Vec2::LEFT.Dot(GetMoveDir()) > 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); } void Human::FillSMGameOver(cs::SMGameOver& msg) { if (stats.rank <= 0) { std::vector human_list; room->TraverseHumanList(a8::XParams(), [&human_list] (Human* hum, a8::XParams& param) -> bool { if (hum->leave_frameno_ == 0 || hum->leave_frameno_ > hum->room->GetBattleStartFrameNo()) { human_list.push_back(hum); } return true; }); std::sort(human_list.begin(), human_list.end(), [] (Human* a, Human* b ) { if (a->real_dead && b->real_dead) { if (a->dead_frameno == b->dead_frameno) { return a->GetEntityUniId() < b->GetEntityUniId(); } else { return a->dead_frameno == 0 || (b->dead_frameno != 0 && a->dead_frameno > b->dead_frameno); } } else { if (a->real_dead) { return false; } if (b->real_dead) { return true; } return a->GetEntityUniId() < b->GetEntityUniId(); } }); 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->IsGameOver()); msg.set_victory(!dead); msg.set_room_uuid(a8::XValue(room->GetRoomUuid())); msg.set_total_human_num(room->GetHumanNum()); msg.set_alive_human_num(room->AliveCount()); { for (auto& itr : *team_members) { if (itr != this) { itr->FillMFTeamData(msg.add_team_data(), true); } } } { cs::MFPlayerStats* p = msg.add_player_stats(); FillMFPlayerStats(p); } { for (auto& pair : spoils_items) { auto p = msg.add_spoils_items(); p->add_values(pair.first); p->add_values(pair.second); } } } void Human::BeKill(int killer_id, const std::string& killer_name, int weapon_id) { #ifdef DEBUG room->CheckPartObjects(); #endif if (!dead && !room->IsGameOver() && !real_dead) { lethal_weapon = weapon_id; Entity* hum = room->GetEntityByUniId(killer_id); if (hum && hum->IsEntityType(ET_Player)) { Creature* killer = (Creature*)hum; if (killer_id == GetEntityUniId()) { std::string msg = a8::Format(TEXT("battle_server_dead_specate", "%s 自杀").c_str(), { killer_name, }); SendRollMsg(msg, killer->GetEntityUniId(), killer->team_id); } else { ((Human*)hum)->stats.kills++; ((Human*)hum)->stats.last_kill_frameno = room->GetFrameNo(); ((Human*)hum)->kill_humans.insert(this); ((Human*)hum)->SyncAroundPlayers(__FILE__, __LINE__, __func__); if (weapon_id == VW_Tank) { std::string msg = a8::Format(TEXT("battle_server_dead_car", "%s 使用 载具 干掉了 %s").c_str(), { killer_name, name }); SendRollMsg(msg, killer->GetEntityUniId(), killer->team_id); } else { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(weapon_id); if (equip_meta) { std::string msg = a8::Format(TEXT("battle_server_dead_weapon", "%s 使用 %s 干掉了 %s").c_str(), { killer_name, equip_meta->i->name(), name }); SendRollMsg(msg, killer->GetEntityUniId(), killer->team_id); } } } } else { switch (weapon_id) { case VW_SafeArea: { std::string msg = a8::Format(TEXT("battle_server_dead_gas", "%s 被毒圈干掉").c_str(), { name }); SendRollMsg(msg, 0, 0); } break; case VW_Spectate: { std::string msg = a8::Format(TEXT("battle_server_dead_specate", "%s 自杀").c_str(), { name }); SendRollMsg(msg, 0, 0); } break; case VW_SelfDetonate: { std::string msg = a8::Format(TEXT("battle_server_dead_self_detonate", "%s 被炸死").c_str(), { name }); SendRollMsg(msg, 0, 0); } break; case VW_Mine: { std::string msg = a8::Format(TEXT("battle_server_dead_mine", "%s 被地雷炸死").c_str(), { name }); SendRollMsg(msg, 0, 0); } break; } } ++stats.dead_times; stats.killer_id = killer_id; stats.killer_name = killer_name; stats.weapon_id = weapon_id; if (room->GetRoomMode() == kZombieMode) { dead = true; downed = false; real_dead = true; dead_frameno = room->GetFrameNo(); DoGetDown(); if (HasBuffEffect(kBET_Camouflage)) { RemoveBuffByEffectId(kBET_Camouflage); } ClearLordMode(); ClearBuffList(); room->frame_event.AddDead(GetWeakPtrRef(), meta->i->revive_time() * 1000); SyncAroundPlayers(__FILE__, __LINE__, __func__); room->xtimer.AddDeadLineTimerAndAttach (meta->i->revive_time() * SERVER_FRAME_RATE, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->Revive(); }, &xtimer_attacher.timer_list_ ); room->OnHumanDie(this); } else { dead = true; downed = false; ability.hp = 0.0f; dead_frameno = room->GetFrameNo(); ++dead_times; if (HasBuffEffect(kBET_Camouflage)) { RemoveBuffByEffectId(kBET_Camouflage); } ClearLordMode(); room->frame_event.AddDead(GetWeakPtrRef(), 0); #ifdef DEBUG room->CheckPartObjects(); #endif #if 1 real_dead = true; OnDie(); #else int max_revive_times = MetaMgr::Instance()->GetSysParamAsInt("max_revive_times", 1); if (weapon_id != VW_Spectate && dead_times <= max_revive_times && room->AliveCount() >= 5 && IsEntitySubType(EST_Player)) { Revive(); } else { real_dead = true; OnDie(); } #endif DoGetDown(); } } } void Human::DecHP(float dec_hp, int killer_id, const std::string& killer_name, int weapon_id) { #ifdef DEBUG #if 0 if (IsPlayer()) { return; } #endif #endif { Buff* buff = GetBuffByEffectId(kBET_Shield); if (buff) { dec_hp = std::max((float)1.0, dec_hp - buff->meta->param2); } } 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(); float new_health = std::max(0.0f, GetHP() - dec_hp); AdjustDecHp(old_health, new_health); ability.hp = std::max(0.0f, new_health); 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; if (HasBuffEffect(kBET_Camouflage)) { RemoveBuffByEffectId(kBET_Camouflage); } CancelAction(); DoGetDown(); 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__); if (GetNearbyTeammateNum(MetaMgr::Instance()->refresh_ai_downed_nearby_range) < MetaMgr::Instance()->refresh_ai_downed_nearby_teammate_num) { #if 0 room->GetIncubator()->AllocAndroid(this, 1 + rand() % 2); #endif } } else { BeKill(killer_id, killer_name, weapon_id); } } } room->frame_event.AddHpChg(GetWeakPtrRef()); } } void Human::AddToNewObjects(Entity* entity) { new_objects.insert(entity); } void Human::AddToPartObjects(Entity* entity) { PartObject part_obj; part_obj.entity_uniid = entity->GetEntityUniId(); part_obj.entity_type = entity->GetEntityType(); part_obj.entity_subtype = entity->GetEntitySubType(); part_obj.add_frameno = room->GetFrameNo(); part_objects[entity] = part_obj; entity->OnAddToTargetPartObject(this); } void Human::RemovePartObjects(Entity* entity) { part_objects.erase(entity); entity->OnRemoveFromTargetPartObject(this); } void Human::ClearPartObjects() { part_objects.clear(); } int Human::GetPartObjectsCount() { return part_objects.size(); } bool Human::InNewObjects(Entity* target) { return new_objects.find(target) != new_objects.end(); } bool Human::InPartObjects(Entity* target) { return part_objects.find(target) != part_objects.end(); } void Human::RemoveObjects(Entity* entity) { del_objects.insert(entity->GetEntityUniId()); } void Human::AddOutObjects(Entity* entity) { out_objects.insert(entity->GetEntityUniId()); } void Human::RemoveOutObjects(Entity* entity) { out_objects.erase(entity->GetEntityUniId()); } bool Human::HasLiveTeammate() { if (room->GetRoomMode() == kZombieMode) { return true; } if (team_members) { for (auto& hum : *team_members) { if (hum != this && !hum->dead) { return true; } } } return false; } bool Human::HasNoDownedTeammate() { if (room->GetRoomMode() == kZombieMode) { if (GetRace() == kHumanRace) { bool has_now_downed_teammate = false; Human* myself = this; room->TraverseHumanList ( a8::XParams(), [myself, &has_now_downed_teammate] (Human* hum, const a8::XParams& param) { if (myself != hum && !hum->dead && !hum->downed && hum->GetRace() == kHumanRace) { has_now_downed_teammate = true; return false; } return true; } ); return has_now_downed_teammate; } else { return false; } } if (team_members) { for (auto& hum : *team_members) { if (hum != this && !hum->dead && !hum->downed) { return true; } } } return false; } int Human::GetNearbyTeammateNum(float range) { int num = 0; if (team_members) { for (auto& hum : *team_members) { if (hum != this && !hum->dead && !hum->downed) { if (hum->GetPos().Distance(GetPos()) <= range) { ++num; } } } } return num; } bool Human::CanUseSkill(int skill_id) { return !downed && Creature::CanUseSkill(skill_id); } void Human::DoJump() { if (HasBuffEffect(kBET_Fly)) { a8::UnSetBitFlag(status, HS_DisableAttack); RemoveBuffByEffectId(kBET_Fly); MustBeAddBuff(this, kThroughWall_BUFFID); MustBeAddBuff(this, JUMP_BUFFID); jump_frameno_ = room->GetFrameNo(); SyncAroundPlayers(__FILE__, __LINE__, __func__); } } Skill* Human::SelectSkill() { return nullptr; } void Human::DoGetOn(int obj_uniid) { if (room->GetGasData().gas_mode == GasInactive) { return; } Entity* entity = room->GetEntityByUniId(obj_uniid); if (!entity) { return; } if (GetPos().Distance(entity->GetPos()) > MetaMgr::Instance()->max_mount_horse_distance) { return; } if (downed) { return; } switch (entity->GetEntityType()) { case ET_Loot: { DoGetOnWithLoot((Loot*)entity); } break; case ET_Car: { DoGetOnWithCar((Car*)entity); } break; default: break; } } void Human::FindLocation() { Entity* target = nullptr; TraverseAllLayerEntityList ( [this, &target] (Entity* entity, bool& stop) { switch (entity->GetEntityType()) { case ET_Obstacle: { if (!target) { if (TestCollision(room, entity)) { target = entity; } } } break; case ET_Building: { if (!target || !target->IsEntityType(ET_Building)) { AabbCollider aabb_box; entity->GetAabbBox(aabb_box); if (TestCollision(room, &aabb_box)) { target = entity; } } } break; default: { } break; } }); if (target) { FindLocationWithTarget(target); } } void Human::RefreshView() { TraverseAllLayerHumanList ( [this] (Human* hum, bool& stop) { hum->AddToNewObjects(this); hum->AddToPartObjects(this); AddToNewObjects(hum); AddToPartObjects(hum); }); TraverseAllLayerEntityList ( [this] (Entity* entity, bool& stop) { switch (entity->GetEntityType()) { case ET_Building: case ET_Obstacle: case ET_Loot: { AddToNewObjects(entity); } break; default: { } break; } }); } void Human::OnGridListChange(std::set& old_grids, std::set& inc_grids, std::set& dec_grids ) { ProcIncGridList(old_grids, inc_grids, dec_grids); ProcDecGridList(old_grids, inc_grids, dec_grids); if (IsPlayer() && on_grid_chg) { on_grid_chg(this); } } void Human::SyncAroundPlayers(const char* file, int line, const char* func) { #if 0 if (a8::HasBitFlag(status, HS_Disable)) { return; } #endif #ifdef DEBUG #if 0 room->CheckPartObjects(); a8::UdpLog::Instance()->Debug("room_idx:%d syncaround begin %s %d %s", { room->GetRoomIdx(), file, line, func }); #endif #endif TraverseAllLayerHumanList ( [this, file, line, func] (Human* hum, bool& stop) { hum->AddToNewObjects(this); #ifdef DEBUG #if 0 { std::string objs_str; for (auto& obj : hum->part_objects) { objs_str += a8::Format("%d ", {(long long)obj}); } a8::UdpLog::Instance()->Debug("hum1 %d %s", {(long long)hum, objs_str}); } { std::string objs_str; for (auto& obj : part_objects) { objs_str += a8::Format("%d ", {(long long)obj}); } a8::UdpLog::Instance()->Debug("hum2 %d %s", {(long long)this, objs_str}); } room->CheckPartObjects(hum, this); hum->InPartObjects(this); #endif #endif assert(hum->InPartObjects(this)); if (!hum->InPartObjects(this)) { static long long last_debugout_tick = 0; if (a8::XGetTickCount() - last_debugout_tick > 1000 * 10) { last_debugout_tick = a8::XGetTickCount(); a8::UdpLog::Instance()->Warning ("SyncAroundPlayers error room_idx:%d, file:%s line:%d func:%s", { room->GetRoomIdx(), file, line, func }); } } }); #ifdef DEBUG #if 0 room->CheckPartObjects(); a8::UdpLog::Instance()->Debug("syncaround end %s %d %s", { file, line, func }); #endif #endif } 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->GetFrameNo() - 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(GetCurrWeapon()->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 : GetInventoryData()) { player_data->add_inventory(num); } player_data->set_energy_shield(energy_shield); #if 1 { player_data->set_max_energy_shield(max_energy_shield); } #endif FillBodyState(player_data->mutable_states()); FillItemList(player_data->mutable_items()); FillSkillList(player_data->mutable_skill_list()); player_data->set_shoot_offset_x(shoot_offset.x); player_data->set_shoot_offset_y(shoot_offset.y); } void Human::FillMFGasData(cs::MFGasData* gas_data) { gas_data->set_mode(room->GetGasData().gas_mode); if (room->GetGasData().gas_mode == GasInactive) { long long duration = room->GetGasInactiveTime() * SERVER_FRAME_RATE - (room->GetFrameNo() - room->GetGasData().gas_start_frameno); gas_data->set_duration(std::max(duration * 50, (long long)1000) / 1000); } else if (room->GetGasData().gas_mode == GasJump) { gas_data->set_duration(0); } else if (room->GetGasData().gas_mode == GasMoving) { if (room->GetGasData().new_area_meta->i->shrink_speed() > 0.01f) { long long duration = (room->GetGasData().old_area_meta->i->rad() - room->GetGasData().new_area_meta->i->rad()) / room->GetGasData().new_area_meta->i->shrink_speed(); ++duration; gas_data->set_duration(++duration); } else { gas_data->set_duration(0); } } else { if (room->GetGasData().old_area_meta->i->wait_time() <= 0) { gas_data->set_duration(0); } else { long long duration = room->GetGasData().old_area_meta->i->wait_time() * 20 - (room->GetFrameNo() - room->GetGasData().gas_start_frameno); gas_data->set_duration(std::max(duration * 50, (long long)1000) / 1000); } } TypeConvert::ToPb(room->GetGasData().pos_old, gas_data->mutable_pos_old()); TypeConvert::ToPb(room->GetGasData().pos_new, gas_data->mutable_pos_new()); gas_data->set_rad_old(room->GetGasData().rad_old); gas_data->set_rad_new(room->GetGasData().rad_new); } 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.max_hp = std::max(ability.hp, ability.max_hp); } 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->GetFrameNo() - 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); } } void Human::AddObserver(Human* observer) { observers_.insert(observer); } void Human::RemoveObserver(Human* observer) { observers_.erase(observer); } void Human::SendUpdateMsg() { if (!follow_target_ && !a8::HasBitFlag(status, HS_Disable) && IsPlayer()) { #ifdef DEBUG long long begin_tick = a8::XGetTickCount(); long long end_tick = a8::XGetTickCount(); #endif 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; } #ifdef DEBUG end_tick = a8::XGetTickCount(); if (a8::XGetTickCount() - begin_tick > 1000) { abort(); } begin_tick = a8::XGetTickCount(); #endif cs::SMUpdate* msg = room->frame_maker.MakeUpdateMsg(this); if (send_msg_times == 0 || last_sync_gas_frameno < room->GetGasData().gas_start_frameno) { last_sync_gas_frameno = room->GetGasData().gas_start_frameno; FillMFGasData(msg->mutable_gas_data()); } #ifdef DEBUG end_tick = a8::XGetTickCount(); if (a8::XGetTickCount() - begin_tick > 1000) { abort(); } begin_tick = a8::XGetTickCount(); #endif NotifyObservers(msg, active_player_data_pb); delete msg; if (active_player_data_pb) { delete active_player_data_pb; } ++send_msg_times; } else { need_sync_active_player = false; } ClearFrameData(); } void Human::SendGameOver() { if (IsEntitySubType(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& msg, int killer_id, bool killer_team_id) { room->xtimer.AddDeadLineTimerAndAttach ( 0, a8::XParams() .SetSender(this) .SetParam1(killer_id) .SetParam2(killer_team_id) .SetParam3(msg), [] (const a8::XParams& param) { Human* target = (Human*)param.sender.GetUserData(); int killer_id = param.param1; int killer_team_id = param.param2; std::string text = param.param3.GetString(); target->room->TraversePlayerList (a8::XParams(), [target, killer_id, killer_team_id, text] (Human* hum, a8::XParams& param) -> bool { cs::SMRollMsg msg; auto element = msg.add_elements(); element->set_element_type(1); element->mutable_union_obj_1()->set_text(text); if (killer_id == hum->GetEntityUniId()){ element->mutable_union_obj_1()->set_color(MetaMgr::Instance()->self_kill_color); } else if (killer_team_id == hum->team_id) { element->mutable_union_obj_1()->set_color(MetaMgr::Instance()->teammate_kill_color); } else if (target == hum) { element->mutable_union_obj_1()->set_color(MetaMgr::Instance()->self_bekill_color); } else if (target->team_id == hum->team_id) { element->mutable_union_obj_1()->set_color(MetaMgr::Instance()->teammate_bekill_color); } hum->SendNotifyMsg(msg); return true; }); }, &xtimer_attacher.timer_list_, [] (const a8::XParams& param) { } ); } void Human::UpdateAction() { int passed_time = (room->GetFrameNo() - action_frameno) * FRAME_RATE_MS; int left_time = std::max(0, action_duration - passed_time); if (left_time <= 0) { switch (action_type) { case AT_Reload: { ProcReloadAction(); } break; case AT_UseItem: { ProcUseItemAction(); } break; case AT_Relive: { ProcReliveAction(); } 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->GetRoomUuid()).GetString() + "_" + a8::XValue(team_id).GetString()); } SendNotifyMsg(notifymsg); } void Human::SendSysPiaoMsg(const std::string& msg, int color, int duration) { cs::SMSysPiaoMsg notifymsg; notifymsg.set_msg(msg); notifymsg.set_color(color); notifymsg.set_duration(duration); SendNotifyMsg(notifymsg); } void Human::SendShowCountdown(const std::string& msg, int countdown) { cs::SMShowCountdown notifymsg; notifymsg.set_msg(msg); notifymsg.set_countdown(countdown); 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; } bool Human::HasSpecMove() { return GetBuffByEffectId(kBET_JumpTo) || GetBuffByEffectId(kBET_BePull); } void Human::_UpdateSpecMove() { if (!HasSpecMove()) { return; } bool move_end = false; float target_distance = target_pos.Distance(GetPos()); if (target_distance <= 0.000001f) { move_end = true; } else { a8::Vec2 old_move_dir = GetMoveDir(); a8::Vec2 move_dir = target_pos - GetPos(); move_dir.Normalize(); SetMoveDir(move_dir); bool is_collision = false; std::function old_on_move_collision = on_move_collision; on_move_collision = [&is_collision] () { is_collision = true; return false; }; _UpdateMove(std::min((int)target_distance, (int)GetSpeed())); on_move_collision = old_on_move_collision; move_dir = old_move_dir; target_distance = target_pos.Distance(GetPos()); if (is_collision || target_distance <= 1.0001f) { move_end = true; } } if (move_end) { Buff* buff = GetBuffByEffectId(kBET_JumpTo); if (buff) { if (CurrentSkill() && buff->skill_meta == CurrentSkill()->meta && !CurrentSkill()->meta->phases.empty()) { std::set target_list; metatable::Skill* mutable_skill_meta = (metatable::Skill*)CurrentSkill()->meta->i; float old_skill_distance = CurrentSkill()->meta->i->skill_distance(); mutable_skill_meta->set_skill_distance(CurrentSkill()->meta->phases[0].param1.GetDouble()); SelectSkillTargets(CurrentSkill(), GetPos(), target_list); mutable_skill_meta->set_skill_distance(old_skill_distance); TriggerBuff(CurrentSkill(), target_list, kBTT_SkillHit); } RemoveBuffByEffectId(kBET_JumpTo); } buff = GetBuffByEffectId(kBET_BePull); if (buff) { MetaData::Buff* new_buff = MetaMgr::Instance()->GetBuff(1039); if (new_buff) { AddBuff(this, new_buff, 1); } RemoveBuffByEffectId(kBET_BePull); } } } void Human::_UpdateMove(int speed) { if (!HasBuffEffect(kBET_Vertigo)) { do { int distance = std::min(5, speed); _InternalUpdateMove(distance); speed -= distance; } while (speed > 0); CheckSpecObject(); } } void Human::ChangeToRace(RaceType_e race, int level) { if (race != kHumanRace && race != kZombieRace) { abort(); } if (race_ != race && IsAndroid()) { Android* android = (Android*)this; android->ai->Reset(); if (race == kHumanRace) { android->ai->SetAiMode(kHumanAiMode); } else if (race == kZombieRace) { android->ai->SetAiMode(kZombieAiMode); } } race_ = race; level_ = level; if (race_ == kHumanRace) { meta = MetaMgr::Instance()->GetPlayer(HUMAN_RACE_META_START_ID + level_ - 1); if (!meta) { abort(); } exp_ = meta->i->exp(); OnMetaChange(); } else if (race_ == kZombieRace) { DoGetDown(); meta = MetaMgr::Instance()->GetPlayer(ZOMBIE_RACE_META_START_ID + level_ - 1); if (!meta) { abort(); } exp_ = meta->i->exp(); OnMetaChange(); } else { abort(); } } void Human::ChangeToRaceAndNotify(RaceType_e race, int level) { ChangeToRace(race, level); room->frame_event.AddZombieIdChg(this); } void Human::WinExp(Human* sender, int exp) { if (race_ != kHumanRace && race_ != kZombieRace) { abort(); } if (room->debug_trace) { a8::UdpLog::Instance()->Debug ( "WinExp before sender_id:%d uniid:%d race:%d level:%d exp:%d add_exp:%d ", { sender->GetEntityUniId(), GetEntityUniId(), GetRace(), GetLevel(), GetExp(), exp } ); } exp_ += exp; MetaData::Player* old_meta = meta; int start_meta_id = race_ == kHumanRace ? HUMAN_RACE_META_START_ID : ZOMBIE_RACE_META_START_ID; ClearSkill(); do { MetaData::Player* tmp_meta = MetaMgr::Instance()->GetPlayer(start_meta_id + level_); if (!tmp_meta) { break; } if (exp_ >= tmp_meta->i->exp()) { meta = tmp_meta; AddSkill(meta->i->active_skill()); ResetSkill(); ++level_; } else { break; } } while (true); if (old_meta != meta) { room->frame_event.AddZombieIdChg(this); OnMetaChange(); if (GetRace() == kZombieRace && meta->i->level() == 3) { room->OnZombieAppear(this); } } if (room->debug_trace) { a8::UdpLog::Instance()->Debug ( "WinExp after sender_id:%d uniid:%d race:%d level:%d exp:%d add_exp:%d ", { sender->GetEntityUniId(), GetEntityUniId(), GetRace(), GetLevel(), GetExp(), exp } ); } } void Human::_InternalUpdateMove(float speed) { float nx = GetMoveDir().x * speed; float ny = GetMoveDir().y * speed; a8::Vec2 old_pos = GetPos(); #if 1 SetPos(old_pos + a8::Vec2(nx, ny)); if (!IsCollisionInMapService()) { room->grid_service->MoveCreature(this); return; } else { if (Global::last_collider && Global::last_collider->type == CT_Circle) { SetPos(old_pos + a8::Vec2(nx, ny)); if (self_collider_->Intersect(Global::last_collider)) { CircleCollider* circle_collider = (CircleCollider*)Global::last_collider; a8::Vec2 tmp_dir = GetPos() - (circle_collider->owner->GetPos() + circle_collider->pos); float len = circle_collider->rad + self_collider_->rad + 1; float rate = len - tmp_dir.Norm(); tmp_dir.Normalize(); a8::Vec2 new_dir = tmp_dir * rate; SetPos(GetPos() + new_dir); } if (!IsCollisionInMapService()) { room->grid_service->MoveCreature(this); return; } } } #endif SetPos(old_pos + a8::Vec2(nx, 0)); if (IsCollisionInMapService()) { if (on_move_collision && !on_move_collision()) { SetPos(old_pos); return; } nx = 0; } SetPos(old_pos + a8::Vec2(nx, ny)); if (IsCollisionInMapService()) { if (on_move_collision && !on_move_collision()) { SetPos(old_pos); return; } ny = 0; } SetPos(old_pos + a8::Vec2(nx, ny)); room->grid_service->MoveCreature(this); } void Human::ClearFrameData() { if (!new_objects.empty()) { new_objects.clear(); } if (!del_objects.empty()) { if (!a8::HasBitFlag(status, HS_Disable)) { for (auto& itr : del_objects) { Entity* entity = room->GetEntityByUniId(itr); if (entity) { RemovePartObjects(entity); if (entity->IsEntityType(ET_Player)) { Human* hum = (Human*)entity; hum->RemovePartObjects(this); } #ifdef DEBUG1 room->CheckPartObjects(); #endif } } } del_objects.clear(); } if (!out_objects.empty()) { if (!a8::HasBitFlag(status, HS_Disable)) { for (auto& itr : out_objects) { Entity* entity = room->GetEntityByUniId(itr); if (entity) { RemovePartObjects(entity); if (entity->IsEntityType(ET_Player)) { Human* hum = (Human*)entity; hum->RemovePartObjects(this); } } #ifdef DEBUG1 room->CheckPartObjects(); #endif } } out_objects.clear(); } if (!shots_.empty()) { shots_.clear(); } if (!bullets_.empty()) { bullets_.clear(); } if (!explosions_.empty()) { explosions_.clear(); } if (!smokes_.empty()) { smokes_.clear(); } if (!play_skills_.empty()) { play_skills_.clear(); } if (!emotes_.empty()) { emotes_.clear(); } if (!chged_bullet_nums_.empty()) { chged_bullet_nums_.clear(); } if (!chged_hps_.empty()) { chged_hps_.clear(); } if (!chged_buffs_.empty()) { chged_buffs_.clear(); } if (!chged_skillcds_.empty()) { chged_skillcds_.clear(); } if (!chged_skill_curr_times_.empty()) { chged_skill_curr_times_.clear(); } if (!chged_items_.empty()) { chged_items_.clear(); } if (!chged_weapon_ammo_.empty()) { chged_weapon_ammo_.clear(); } if (!chged_level_.empty()) { chged_level_.clear(); } if (!chged_exp_.empty()) { chged_exp_.clear(); } if (!chged_race_.empty()) { chged_race_.clear(); } if (!chged_zombieid_.empty()) { chged_zombieid_.clear(); } if (!dead_alive_objs_.empty()) { dead_alive_objs_.clear(); } if (!chged_cars_.empty()) { chged_cars_.clear(); } } void Human::GenBattleReportData(a8::MutableXObject* params) { params->SetVal("room_mode", room->GetRoomMode()); int rank = 0; { std::vector human_list; room->TraverseHumanList(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->real_dead && b->real_dead) { if (a->dead_frameno == b->dead_frameno) { return a->GetEntityUniId() < b->GetEntityUniId(); } else { return a->dead_frameno == 0 || (b->dead_frameno != 0 && a->dead_frameno > b->dead_frameno); } } else { if (a->real_dead) { return false; } if (b->real_dead) { return true; } return a->GetEntityUniId() < b->GetEntityUniId(); } }); 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("session_id", session_id); params->SetVal("map_id", room->GetMapMeta()->i->map_id()); params->SetVal("map_name", room->GetMapMeta()->i->map_name()); params->SetVal("map_tpl_name", room->GetMapTplName()); 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->GetFrameNo() * 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->GetRoomUuid()); 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); params->SetVal("rescue_guild_member", stats.rescue_guild_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); } { std::string items_str; MetaData::RankReward* rank_reward_meta = MetaMgr::Instance()->GetRankReward(rank); #ifdef DEBUG1 { #else if (rank_reward_meta && rank_reward_meta->i->drop() > 0) { #endif #ifdef DEBUG1 { #else if (rand() % 100 < rank_reward_meta->i->drop()) { #endif MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquip(grow_weapon.weapon_id); if (item_meta) { MetaData::Drop* drop_meta = MetaMgr::Instance()->GetDrop(item_meta->i->drop_id()); if (drop_meta) { std::vector> drop_items; drop_meta->RandItems(drop_items); for (auto& item : drop_items) { int item_id = std::get<0>(item); int item_num = std::get<1>(item); stats.items.push_back(std::make_pair( item_id, item_num )); items_str += a8::Format("%d:%d|", {item_id, item_num}); #ifdef DEBUG SendDebugMsg (a8::Format("drop weapon_id:%d drop_id:%d item_id:%d item_num:%d", { grow_weapon.weapon_id, item_meta->i->drop_id(), item_id, item_num })); #endif } } } } }//end if rank_reward_meta if (!items_str.empty() && items_str[items_str.size() - 1] == '|') { items_str.erase(items_str.begin() + items_str.size() - 1); } params->SetVal("items", items_str); } { stats.pass_score = MetaMgr::Instance()->GetKillPointParam1(stats.kills); stats.pass_score += MetaMgr::Instance()->GetRankPointParam1(rank); stats.rank_score = MetaMgr::Instance()->GetKillPointParam2(stats.kills); if (room->IsMiniRoom()) { stats.rank_score += MetaMgr::Instance()->GetRankPointParam3(rank); } else { 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::GenZbModeBattleReportData(a8::MutableXObject* params) { params->SetVal("room_mode", room->GetRoomMode()); int rank = 0; { std::vector human_list; room->TraverseHumanList(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->stats.kills > b->stats.kills) { return true; } if (a->stats.kills < b->stats.kills) { return false; } if (a->stats.last_kill_frameno < b->stats.last_kill_frameno) { return true; } return a->GetEntityUniId() < b->GetEntityUniId(); }); rank = human_list.size(); for (size_t i = 0; i < human_list.size(); ++i) { if (human_list[i] == this) { rank = i + 1; break; } } } stats.rank = rank; params->SetVal("account_id", account_id); params->SetVal("session_id", session_id); params->SetVal("map_id", room->GetMapMeta()->i->map_id()); params->SetVal("game_time", time(nullptr)); params->SetVal("rank", rank); params->SetVal("kills", stats.kills); params->SetVal("room_uuid", room->GetRoomUuid()); } void Human::DeadDrop() { auto SkinCanDrop = [this] (Skin* skin) -> bool { #ifdef DEBUG return false; #else if (JsonDataMgr::Instance()->channel == kTouTiaoChannelId) { return false; } else { return skin->skin_id != 0; } #endif }; if (GetRace() == kHumanRace && !HasBuffEffect(kBET_Terminator) && !(HasBuffEffect(kBET_Become) && GetBuffByEffectId(kBET_Become)->FreezeOperate())) { 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); weapon.Clear(); } } { weapons[0] = default_weapon; SetCurrWeapon(&weapons[0]); } } { Skin* old_skin = GetSkinByIdx(kSkinSlot_HAT); if (old_skin && old_skin->skin_id != 0 && SkinCanDrop(old_skin)) { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); room->CreateLoot(old_skin->skin_id, GetPos() + dir * (40 + rand() % 50), 1, 1); *old_skin = Skin(); } } { Skin* old_skin = GetSkinByIdx(kSkinSlot_CLOTH); if (old_skin && old_skin->skin_id != 0 && SkinCanDrop(old_skin)) { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); room->CreateLoot(old_skin->skin_id, GetPos() + dir * (40 + rand() % 50), 1, 1); *old_skin = Skin(); } } { //头盔 if (helmet != 0) { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); room->CreateLoot(helmet, GetPos() + dir * (40 + rand() % 50), 1, 1); helmet = 0; } //衣服 if (chest != 0) { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); room->CreateLoot(chest, GetPos() + dir * (40 + rand() % 50), 1, 1); chest = 0; } //背包 if (backpack != 0) { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); room->CreateLoot(backpack, GetPos() + dir * (40 + rand() % 50), 1, 1); backpack = 0; } } for (size_t slot = 0; slot < GetInventoryData().size(); ++slot) { if (GetInventoryData()[slot] > 0) { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquipBySlotId(slot); if (equip_meta) { if (equip_meta->i->equip_type() == EQUIP_TYPE_BULLET) { switch (equip_meta->i->_inventory_slot()) { case IS_FRAG: case IS_SMOKE: case IS_POSION_GAS_BOMB: case IS_MOLOTOR_COCKTAIL: case IS_TRAP: case IS_MINE: { a8::Vec2 drop_pos = GetPos(); room->DropItem(drop_pos, equip_meta->i->id(), GetInventoryData()[slot], 1); DecInventory(slot, GetInventoryData()[slot]); } break; default: break; } } else { a8::Vec2 drop_pos = GetPos(); room->DropItem(drop_pos, equip_meta->i->id(), GetInventoryData()[slot], 1); } } } } need_sync_active_player = true; } void Human::SendBattleReport() { a8::MutableXObject* params = a8::MutableXObject::NewObject(); if (room->GetRoomMode() == kZombieMode) { GenZbModeBattleReportData(params); } else { 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 (JsonDataMgr::Instance()->channel != 0) { url = a8::Format("http://game2005api-test.kingsome.cn/%d/webapp/index.php?c=Role&a=battleReport", { JsonDataMgr::Instance()->channel }); } else { url = "https://game2005api-test.kingsome.cn/webapp/index.php?c=Role&a=battleReport"; } } else { if (JsonDataMgr::Instance()->channel != 0) { if (kTouTiaoChannelId == JsonDataMgr::Instance()->channel) { url = a8::Format("http://game2005api-al.kingsome.cn/%d/webapp/index.php?c=Role&a=battleReport", { JsonDataMgr::Instance()->channel }); } else { url = a8::Format("http://game2005api.kingsome.cn/%d/webapp/index.php?c=Role&a=battleReport", { JsonDataMgr::Instance()->channel }); } } else { url = "http://game2005api.kingsome.cn/webapp/index.php?c=Role&a=battleReport"; } } JsonDataMgr::Instance()->GetBattleReportUrl(url); std::string data; params->ToUrlEncodeStr(data); f8::HttpClientPool::Instance()->HttpGet( a8::XParams() .SetSender(room->GetRoomUuid()) .SetParam1(GetEntityUniId()) .SetParam2(data), on_ok, on_error, url.c_str(), *params, MAX_SYS_HTTP_NUM + (GetEntityUniId() % MAX_USER_HTTP_NUM) ); delete params; } void Human::ProcLootSkin(Loot* entity, MetaData::Equip* item_meta) { switch (item_meta->i->equip_subtype()) { case 11: { //装饰 Skin* old_skin = GetSkinByIdx(kSkinSlot_HAT); if (old_skin) { if (old_skin->skin_id != 0) { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); room->CreateLoot(old_skin->skin_id, GetPos() + dir * (40 + rand() % 50), 1, 1); } *old_skin = Skin(); old_skin->skin_id = item_meta->i->id(); old_skin->skin_lv = 1; SyncAroundPlayers(__FILE__, __LINE__, __func__); } } break; case 12: { //衣服 Skin* old_skin = GetSkinByIdx(kSkinSlot_CLOTH); if (old_skin) { if (old_skin->skin_id != 0) { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); room->CreateLoot(old_skin->skin_id, GetPos() + dir * (40 + rand() % 50), 1, 1); } *old_skin = Skin(); old_skin->skin_id = item_meta->i->id(); old_skin->skin_lv = 1; SyncAroundPlayers(__FILE__, __LINE__, __func__); } } break; } } void Human::ProcLootCar(Loot* entity, MetaData::Equip* item_meta) { DoGetOn(entity->GetEntityUniId()); } void Human::ProcCamoutflage(Loot* entity, MetaData::Equip* item_meta) { AddItem(item_meta->i->id(), 1); } void Human::ProcSpoils(Loot* entity, MetaData::Equip* item_meta) { if (spoils_items.find(item_meta->i->id()) != spoils_items.end()) { spoils_items[item_meta->i->id()] += entity->count; } else { spoils_items[item_meta->i->id()] = entity->count; } } 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->MoveCreature(this); Entity* building = nullptr; std::set tmp_grids; room->grid_service->GetAllCellsByXy(room, GetX(), GetY(), tmp_grids); room->grid_service->TraverseAllLayerEntityList ( room->GetRoomIdx(), tmp_grids, [this, &building] (Entity* entity, bool& stop) { switch (entity->GetEntityType()) { case ET_Building: { if (TestCollision(room, entity)) { building = entity; stop = true; } } break; default: break; } }); if (!building) { bool is_collision = false; std::set colliders; room->map_service->GetColliders(room, GetX(), GetY(), colliders); for (ColliderComponent* collider : colliders) { if (TestCollision(room, collider)) { is_collision = true; break; } } if (!is_collision) { break; } } } } void Human::OnDie() { real_dead_frameno = room->GetFrameNo(); 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 == GetEntityUniId()) { 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::SetSkin(int idx, int skin_id) { if (idx < kSkinNum) { Skin& skin = skins[idx]; skin.skin_id = skin_id; skin.skin_lv = 1; } } Skin* Human::GetSkinByIdx(int idx) { int i = 0; for (auto& skin : skins) { if (i == idx) { return &skin; } ++i; } return nullptr; } void Human::AddBuffPostProc(Creature* caster, Buff* buff) { } int Human::GetItemNum(int item_id) { auto itr = items_.find(item_id); return itr != items_.end() ? itr->second : 0; } void Human::AddItem(int item_id, int item_num) { auto itr = items_.find(item_id); if (itr != items_.end()) { itr->second += item_num; } else { items_[item_id] = item_num; } if (room && room->GetFrameNo() > join_frameno) { room->frame_event.AddItemChg(this, item_id, items_[item_id]); } else { if (item_num <= 0) { battling_items_.insert(item_id); } } } void Human::DecItem(int item_id, int item_num) { auto itr = items_.find(item_id); if (itr != items_.end()) { itr->second -= item_num; room->frame_event.AddItemChg(this, item_id, std::max(0, itr->second)); if (itr->second <= 0) { if (battling_items_.find(item_id) == battling_items_.end()) { items_.erase(itr); } } } } void Human::DropItems(Obstacle* obstacle) { bool is_treasure_box = false; if (obstacle->IsEntitySubType(EST_RoomObstacle)) { is_treasure_box = ((RoomObstacle*)obstacle)->is_treasure_box; } int drop_id = obstacle->meta->i->drop(); if (drop_id == 0) { return; } if (room->GetRoomType() == RT_NewBrid && IsPlayer()) { if (is_treasure_box) { if (box_drop_times_ < MetaMgr::Instance()->newbie_airdrop.size()) { drop_id = MetaMgr::Instance()->newbie_airdrop[box_drop_times_]; } } else { if (normal_drop_times_ < MetaMgr::Instance()->newbie_drop.size()) { if (normal_drop_times_ == 0) { } drop_id = MetaMgr::Instance()->newbie_drop[normal_drop_times_]; } } } if (drop_id == 0) { drop_id = obstacle->meta->i->drop(); } room->ScatterDrop(obstacle->GetPos(), drop_id); if (is_treasure_box) { ++box_drop_times_; } else { ++normal_drop_times_; } { if (behavior.curr_start_destory_box_frameno <= 0 || behavior.curr_destory_box_times >= MetaMgr::Instance()->refresh_ai_destory_box_times || (room->GetFrameNo() - behavior.curr_start_destory_box_frameno > MetaMgr::Instance()->refresh_ai_destory_box_time * SERVER_FRAME_RATE)){ behavior.curr_start_destory_box_frameno = room->GetFrameNo(); behavior.curr_destory_box_times = 0; } behavior.curr_destory_box_times++; behavior.total_destory_box_times++; if (behavior.curr_destory_box_times >= MetaMgr::Instance()->refresh_ai_destory_box_times) { #if 0 room->GetIncubator()->AllocAndroid(this, 1 + rand() % 2); #endif } } #ifdef DEBUG #if 0 a8::UdpLog::Instance()->Debug("DropItems normal:%d box:%d drop_id:%d is_treasure_box:%d", { normal_drop_times_, box_drop_times_, drop_id, is_treasure_box ? 1 : 0 }); #endif #endif } void Human::Revive() { if (room->GetRoomMode() == kZombieMode) { if (GetRace() == kHumanRace) { ChangeToRaceAndNotify(kZombieRace, 1); } dead = false; downed = false; real_dead = false; ability.hp = GetMaxHP(); ClearBuffList(); { MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(kREVIVE_BUFF_ID); if (buff_meta) { AddBuff(this, buff_meta, 1); } } room->frame_event.AddRevive(GetWeakPtrRef()); room->OnHumanRevive(this); ResetSkill(); SyncAroundPlayers(__FILE__, __LINE__, __func__); } else { 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::CancelRevive() { if (dead && revive_timer && !real_dead) { dead = true; real_dead = true; downed = false; FreeDownedTimer(); OnDie(); FreeReviveTimer(); } } void Human::ProcNewBieLogic() { if (a8::HasBitFlag(status, HS_AlreadyProcNewBieLogic)) { return; } a8::SetBitFlag(status, HS_AlreadyProcNewBieLogic); //刷一个机器人 room->xtimer.AddDeadLineTimerAndAttach (SERVER_FRAME_RATE * MetaMgr::Instance()->newbie_first_robot_appeartime, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->room->ShuaGuideAndroid(hum); }, &xtimer_attacher.timer_list_ ); //6秒后出现空投 room->xtimer.AddDeadLineTimerAndAttach (SERVER_FRAME_RATE * MetaMgr::Instance()->newbie_airdrop_appeartime, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->room->InitAirDrop(); }, &xtimer_attacher.timer_list_ ); } void Human::ClearLordMode() { Buff* buff = GetBuffByEffectId(kBET_LordMode); if (buff) { std::vector strings; a8::Split(buff->meta->i->buff_param1(), strings, ':'); for (auto& str : strings) { int buff_id = a8::XValue(str); RemoveBuffById(buff_id); } RemoveBuffByEffectId(kBET_LordMode); } } void Human::AdjustDecHp(float old_health, float& new_health) { Buff* buff = GetBuffByEffectId(kBET_NewProtect); if (buff) { if (new_health < GetMaxHP() * buff->meta->param1) { new_health = std::max(GetMaxHP() * buff->meta->param1, (float)0); } } } void Human::OnEnable() { a8::UnSetBitFlag(status, HS_Disable); enable_frameno = room->GetFrameNo(); room->grid_service->MoveCreature(this); FindLocation(); RefreshView(); } void Human::OnDisable() { a8::SetBitFlag(status, HS_Disable); RemoveFromScene(); ClearFrameData(); ClearPartObjects(); } void Human::GetViewObjects(std::set& view_objects) { TraverseAllLayerHumanList ( [&view_objects] (Human* hum, bool& stop) { view_objects.insert(hum); }); TraverseAllLayerEntityList ( [&view_objects] (Entity* entity, bool& stop) { switch (entity->GetEntityType()) { case ET_Building: case ET_Obstacle: case ET_Loot: { view_objects.insert(entity); } break; default: { } break; } }); } void Human::NotifyObservers(cs::SMUpdate* msg, cs::MFActivePlayerData* active_player_data_pb) { bool refreshed_view = false; 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(), false); } } } if (observer != this && !observer->follow_synced_active_player) { msg->set_active_player_id(GetEntityUniId()); FillMFActivePlayerData(msg->mutable_active_player_data()); if (!refreshed_view) { std::set view_objects; GetViewObjects(view_objects); for (Entity* entity : view_objects) { if (new_objects.find(entity) == new_objects.end()) { entity->FillMFObjectFull(room, this, msg->add_full_objects()); } } refreshed_view = true; } observer->follow_synced_active_player = true; } else { if (active_player_data_pb) { msg->set_active_player_id(GetEntityUniId()); *msg->mutable_active_player_data() = *active_player_data_pb; } else { msg->clear_active_player_id(); msg->clear_active_player_data(); } } observer->SendNotifyMsg(*msg); } } void Human::ProcIncGridList(std::set& old_grids, std::set& inc_grids, std::set& dec_grids) { room->grid_service->TraverseCreatures ( room->GetRoomIdx(), inc_grids, [this, &old_grids] (Creature* c, bool& stop) { if (!room->grid_service->CreatureInGridList(c, old_grids)) { c->AddToNewObjects(this); c->AddToPartObjects(this); c->RemoveOutObjects(this); AddToNewObjects(c); AddToPartObjects(c); RemoveOutObjects(c); } }); room->grid_service->TraverseAllLayerEntityList ( room->GetRoomIdx(), inc_grids, [this] (Entity* entity, bool& stop) { switch (entity->GetEntityType()) { case ET_Building: case ET_Obstacle: case ET_Loot: { AddToNewObjects(entity); RemoveOutObjects(entity); } break; default: { } break; } }); } void Human::ProcDecGridList(std::set& old_grids, std::set& inc_grids, std::set& dec_grids) { room->grid_service->TraverseCreatures ( room->GetRoomIdx(), dec_grids, [this] (Creature* c, bool& stop) { if (!room->grid_service->CreatureInGridList(c, GetGridList())) { AddOutObjects(c); c->AddOutObjects(this); #ifdef DEBUG #if 0 a8::UdpLog::Instance()->Debug("addoutobjects %d %d", { (long long)c, c->GetEntityUniId() }); #endif #endif } }); room->grid_service->TraverseAllLayerEntityList ( room->GetRoomIdx(), dec_grids, [this] (Entity* entity, bool& stop) { if (!room->grid_service->EntityInGridList(room, entity, GetGridList())) { switch (entity->GetEntityType()) { case ET_Building: case ET_Obstacle: case ET_Loot: { AddOutObjects(entity); } break; default: { } break; } } }); } void Human::RemoveFromScene() { room->grid_service->DeatchHuman(this); room->TraverseHumanList ( a8::XParams() .SetSender(this), [] (Human* hum, a8::XParams& param) { Human* target = (Human*)param.sender.GetUserData(); hum->RemovePartObjects(target); return true; }); last_collision_door_ = nullptr; } ObjectSyncFlags* Human::GetObjectSyncFlags(int obj_uniid) { if ((size_t)obj_uniid < fixed_object_sync_flags_.size()) { return &fixed_object_sync_flags_[obj_uniid]; } else { return nullptr; } } void Human::OnMetaChange() { if (GetRace() == kZombieRace) { MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(meta->i->normal_skill()); if (weapon_meta) { default_weapon.weapon_idx = 0; default_weapon.weapon_id = weapon_meta->i->id(); default_weapon.weapon_lv = 1; default_weapon.ammo = 1; default_weapon.meta = weapon_meta; default_weapon.Recalc(); weapons[0] = default_weapon; SetCurrWeapon(&weapons[0]); } } ability.hp = meta->i->health(); for (auto& weapon : spec_weapons) { if (weapon.meta) { ability.hp += weapon.meta ? weapon.GetAttrValue(kHAT_MaxHp) : 0; } } room->frame_event.AddHpChg(GetWeakPtrRef()); RecalcBaseAttr(); ClearSkill(); AddSkill(meta->i->active_skill()); ResetSkill(); ClearPassiveSkill(); AddPassiveSkill(meta->i->passive_skill()); } void Human::OnChgToTerminator() { Buff* buff = GetBuffByEffectId(kBET_Terminator); if (buff) { std::vector strings; a8::Split(buff->meta->i->buff_param4(), strings, ':'); if (strings.size() >= 3) { for (size_t i = 0; i < strings.size(); ++i) { int weapon_id = a8::XValue(strings[i]); MetaData::Equip* weapon_meta = MetaMgr::Instance()->GetEquip(weapon_id); if (weapon_meta) { Weapon* weapon = nullptr; switch (i) { case 0: { weapon = &weapons[0]; } break; case 1: { weapon = &weapons[GUN_SLOT1]; } break; case 2: { weapon = &weapons[GUN_SLOT2]; } break; default: { } break; } weapon->weapon_idx = i; weapon->weapon_id = weapon_meta->i->id(); weapon->weapon_lv = 1; weapon->meta = weapon_meta; weapon->Recalc(); weapon->ammo = weapon->GetClipVolume(); if (i == 0) { default_weapon = *weapon; } } } }//end if need_sync_active_player = true; SyncAroundPlayers(__FILE__, __LINE__, __func__); } } void Human::ProcReloadAction() { Weapon* p_weapon = GetCurrWeapon(); if (second_weapon.meta) { p_weapon = &second_weapon; } if (p_weapon->weapon_idx == action_target_id && p_weapon->weapon_id == action_item_id && p_weapon->weapon_idx != 0) { MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(p_weapon->meta->i->use_bullet()); if (bullet_meta) { int ammo = p_weapon->ammo; if (ammo < p_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()) <= p_weapon->GetClipVolume() - ammo) { add_num = GetInventory(bullet_meta->i->_inventory_slot()); if (p_weapon->meta->i->reloadtype() == 1) { add_num = 1; } DecInventory(bullet_meta->i->_inventory_slot(), add_num); } else { add_num = p_weapon->GetClipVolume() - ammo; if (p_weapon->meta->i->reloadtype() == 1) { add_num = 1; } DecInventory(bullet_meta->i->_inventory_slot(), add_num); } p_weapon->ammo += add_num; need_sync_active_player = true; if (p_weapon->meta->i->reloadtype() == 1) { room->xtimer.AddDeadLineTimerAndAttach (1, a8::XParams() .SetSender(this) .SetParam1(p_weapon->weapon_idx) .SetParam2(p_weapon->weapon_id), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->NextReload(param.param2, param.param1); }, &xtimer_attacher.timer_list_ ); } } } } } } } void Human::ProcUseItemAction() { MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquipBySlotId(action_item_id); if (!item_meta) { return; } if (GetInventory(item_meta->i->_inventory_slot()) <= 0) { return; } switch (action_item_id) { case IS_HEALTHKIT: { AddHp(item_meta->i->heal()); DecInventory(item_meta->i->_inventory_slot(), 1); } break; case IS_PAIN_KILLER: { if (pain_killer_timer) { int passed_time = (room->GetFrameNo() - 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; } else { pain_killer_frameno = room->GetFrameNo(); pain_killer_lastingtime = item_meta->i->time(); if (pain_killer_lastingtime > 0) { 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(); hum->AddHp(param.param1.GetDouble()); if (hum->room->GetFrameNo() - hum->pain_killer_frameno > hum->pain_killer_lastingtime * SERVER_FRAME_RATE) { hum->room->xtimer.DeleteTimer(hum->pain_killer_timer); } }, &xtimer_attacher.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->pain_killer_timer = nullptr; }); } AddHp(item_meta->i->heal()); } DecInventory(item_meta->i->_inventory_slot(), 1); need_sync_active_player = true; } break; default: { } break; } } void Human::ProcReliveAction() { Entity* entity = room->GetEntityByUniId(action_target_id); if (!entity->IsEntityType(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; if (hum->guild_id != 0 && hum->guild_id == guild_id) { ++hum->stats.rescue_guild_member; } } hum->SyncAroundPlayers(__FILE__, __LINE__, __func__); } void Human::OnBuffRemove(Buff& buff) { switch (buff.meta->i->buff_effect()) { case kBET_Jump: { //跳伞结束 room->xtimer.AddDeadLineTimerAndAttach (1, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->OnLand(); }, &xtimer_attacher.timer_list_ ); } break; case kBET_Become: { buff.ProcRemoveBecome(this); } break; case kBET_Driver: { buff.ProcRemoveDriver(this); } break; case kBET_Passenger: { buff.ProcRemovePassenger(this); } break; default: break; } if (!buff.meta->i->only_server()) { room->frame_event.RemoveBuff(GetWeakPtrRef(), buff.meta->i->buff_id()); } } void Human::OnLand() { //着陆 RemoveBuffByEffectId(kBET_Jump); RemoveBuffByEffectId(kBET_ThroughWall); if (IsAndroid() && team_uuid.empty()) { MustBeAddBuff(this, kBeRecycleBuffId); } if (IsPlayer()) { refresh_view_timer_ = room->xtimer.AddRepeatTimerAndAttach ( SERVER_FRAME_RATE * MetaMgr::Instance()->refresh_view_time, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->UpdateViewObjects(); }, &xtimer_attacher.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->refresh_view_timer_ = nullptr; }); } if (IsCollisionInMapService()) { a8::Vec2 old_pos = GetPos(); std::vector dirs; { dirs.push_back(a8::Vec2::UP); dirs.push_back(a8::Vec2::DOWN); dirs.push_back(a8::Vec2::LEFT); dirs.push_back(a8::Vec2::RIGHT); } for (int i = 0; i < 10000000; i += 10) { for (const a8::Vec2& dir : dirs) { SetPos(old_pos + dir * i); if (!IsCollisionInMapService()) { room->grid_service->MoveCreature(this); return; } } } SetPos(old_pos); } } void Human::NextReload(int prev_weapon_id, int prev_weapon_idx) { if (action_type != AT_None) { return; } Weapon* p_weapon = GetCurrWeapon(); if (second_weapon.meta) { p_weapon = &second_weapon; } if (p_weapon && p_weapon->weapon_id == prev_weapon_id && p_weapon->weapon_idx == prev_weapon_idx) { AutoLoadingBullet(true); } } void Human::DoGetDown() { if (GetCar()) { GetCar()->GetDown(this); } } void Human::DoGetOnWithLoot(Loot* entity) { MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquip(entity->item_id); if (!item_meta) { return; } if (GetCar()) { GetCar()->GetDown(this); } Car* car = room->CreateCar( this, entity->GetEntityUniId(), item_meta, entity->GetPos(), team_id ); car->GetOn(this); room->TakeOnCarObject(entity->GetEntityUniId()); room->NotifyUiUpdate(); #ifdef DEBUG a8::XPrintf("DoGetOnWithLoot uniid:%d car_uniid:%d\n", {car->GetEntityUniId(), car->car_uniid}); #endif } void Human::DoGetOnWithCar(Car* car) { car->GetOn(this); } void Human::OnEnterSpecMapArea(int tag, SpecMapObject& map_obj) { #ifdef DEBUG SendDebugMsg("进入特殊区域"); #endif ClearSpecMapAreaTimer(map_obj); map_obj.enter_timer = room->xtimer.AddDeadLineTimerAndAttach (MetaMgr::Instance()->GetSpecMapAreaEnterTime(tag) * SERVER_FRAME_RATE, a8::XParams() .SetSender(this) .SetParam1(&map_obj) .SetParam2(tag), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); int buff_id = MetaMgr::Instance()->GetSpecMapAreaBuffId(param.param2); hum->MustBeAddBuff(hum, buff_id); }, &xtimer_attacher.timer_list_, [] (const a8::XParams& param) { SpecMapObject* map_obj = (SpecMapObject*)param.param1.GetUserData(); map_obj->enter_timer = nullptr; }); } void Human::OnLeaveSpecMapArea(int tag, SpecMapObject& map_obj) { #ifdef DEBUG SendDebugMsg("离开特殊区域"); #endif ClearSpecMapAreaTimer(map_obj); map_obj.leave_timer = room->xtimer.AddDeadLineTimerAndAttach (MetaMgr::Instance()->GetSpecMapAreaLeaveTime(tag) * SERVER_FRAME_RATE, a8::XParams() .SetSender(this) .SetParam1(&map_obj) .SetParam2(tag), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->RemoveBuffById((MetaMgr::Instance()->GetSpecMapAreaBuffId(param.param2))); }, &xtimer_attacher.timer_list_, [] (const a8::XParams& param) { SpecMapObject* map_obj = (SpecMapObject*)param.param1.GetUserData(); map_obj->leave_timer = nullptr; }); } void Human::ClearSpecMapAreaTimer(SpecMapObject& map_obj) { if (map_obj.leave_timer) { room->xtimer.DeleteTimer(map_obj.leave_timer); map_obj.leave_timer = nullptr; } if (map_obj.enter_timer) { room->xtimer.DeleteTimer(map_obj.enter_timer); map_obj.enter_timer = nullptr; } } void Human::DoSkillPreProc(int skill_id, int target_id, const a8::Vec2& target_pos) { if (action_type == AT_Reload || action_type == AT_UseItem ) { CancelAction(); } } void Human::DoSkillPostProc(bool used, int skill_id, int target_id, const a8::Vec2& target_pos) { if (used) { ++stats.skill_times; Skill* skill = GetSkill(skill_id); if (skill) { skill->DecTimes(); room->frame_event.AddSkillCdChg(AllocWeakPtr(), skill_id, skill->GetLeftTime()); room->frame_event.AddSkillCurrTimesChg(AllocWeakPtr(), skill_id, skill->GetCurrTimes()); } OnAttack(); } } float Human::GetRadius() { return meta->i->radius(); } float Human::GetHitRadius() { return meta->i->hit_radius(); } void Human::GetHitAabbBox(AabbCollider& aabb_box) { if (!meta) { abort(); } aabb_box.active = true; aabb_box.owner = this; if (GetCar() && GetCar()->IsDriver(this)) { aabb_box._min.x = -GetCar()->meta->i->rad(); aabb_box._min.y = -GetCar()->meta->i->rad(); aabb_box._max.x = GetCar()->meta->i->rad(); aabb_box._max.y = GetCar()->meta->i->rad(); } else { aabb_box._min.x = -GetHitRadius(); aabb_box._min.y = -GetHitRadius(); aabb_box._max.x = GetHitRadius(); aabb_box._max.y = GetHitRadius(); } } void Human::UpdateViewObjects() { if (view_objects_.size() >= 2) { std::vector deleted_humans; for (Human* hum : view_objects_) { if (hum->dead || hum->GetPos().ManhattanDistance(GetPos()) > MetaMgr::Instance()->view_objects_out_distance) { deleted_humans.push_back(hum); } } for (Human* hum : deleted_humans) { view_objects_.erase(hum); } } if (view_objects_.size() < 2) { TraverseAllLayerHumanList ( [this] (Human* hum, bool& stop) { if (hum->IsAndroid() && !hum->dead && view_objects_.find(hum) == view_objects_.end()) { if (hum->GetPos().ManhattanDistance(GetPos()) < MetaMgr::Instance()->view_objects_in_distance) { view_objects_.insert(hum); } if (view_objects_.size() >= 2) { stop = true; return; } } }); } if (view_objects_.size() < 2) { room->GetIncubator()->AllocAndroid(this, 1 + rand() % 2); if (refresh_view_timer_) { room->xtimer.ModifyTimer(refresh_view_timer_, SERVER_FRAME_RATE * (MetaMgr::Instance()->refresh_view_time + (rand() % 3)) ); } } } void Human::GMAddItem(int item_id, int item_num) { MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquip(item_id); if (!item_meta) { return; } if (item_meta->i->equip_type() == EQUIP_TYPE_WEAPON) { if (item_meta->i->equip_subtype() == 1) { if (default_weapon.weapon_id != weapons[0].weapon_id) { } else { weapons[0].weapon_idx = 0; weapons[0].weapon_id = item_id; weapons[0].weapon_lv = std::max(1, 1); weapons[0].ammo = 0; weapons[0].meta = item_meta; weapons[0].Recalc(); } } else { Weapon* weapon = nullptr; if (weapons[GUN_SLOT1].weapon_id == 0) { weapon = &weapons[GUN_SLOT1]; weapon->weapon_idx = GUN_SLOT1; if (GetCurrWeapon() != &weapons[GUN_SLOT2] && !FreezeOperate()) { SetCurrWeapon(&weapons[GUN_SLOT1]); } } else if (weapons[GUN_SLOT2].weapon_id == 0) { weapon = &weapons[GUN_SLOT2]; weapon->weapon_idx = GUN_SLOT2; if (GetCurrWeapon() != &weapons[GUN_SLOT1] && !FreezeOperate()) { SetCurrWeapon(&weapons[GUN_SLOT2]); } } if (weapon) { weapon->weapon_id = item_id; weapon->weapon_lv = std::max(1, 1); weapon->ammo = 0; weapon->meta = item_meta; weapon->Recalc(); } } } else { if (item_meta->i->_inventory_slot() >= 0 && item_meta->i->_inventory_slot() < IS_END) { if (GetInventory(item_meta->i->_inventory_slot()) >= GetVolume(item_meta->i->_inventory_slot()) ) { /* cs::SMPickup notifymsg; notifymsg.set_error_code(1); SendNotifyMsg(notifymsg); */ return; } int add_num = GetVolume(item_meta->i->_inventory_slot()) - GetInventory(item_meta->i->_inventory_slot()); add_num = std::min(item_num, add_num); AddInventory(item_meta->i->_inventory_slot(), add_num); switch (item_meta->i->_inventory_slot()) { case IS_FRAG: case IS_SMOKE: { Weapon* weapon = &weapons[SPEC1_SLOT_BEGIN + (item_meta->i->_inventory_slot() - SPEC1_IS_BEGIN) ]; weapon->weapon_id = item_id; weapon->weapon_lv = 1; weapon->ammo += item_num; weapon->meta = item_meta; weapon->Recalc(); DecInventory(item_meta->i->_inventory_slot(), add_num); } break; case IS_1XSCOPE: case IS_2XSCOPE: case IS_4XSCOPE: case IS_8XSCOPE: case IS_15XSCOPE: { if (item_meta->i->_inventory_slot() - IS_1XSCOPE > curr_scope_idx) { curr_scope_idx = item_meta->i->_inventory_slot() - IS_1XSCOPE; } } break; case IS_POSION_GAS_BOMB: case IS_MOLOTOR_COCKTAIL: case IS_TRAP: case IS_MINE: { Weapon* weapon = &weapons[SPEC2_SLOT_BEGIN + (item_meta->i->_inventory_slot() - SPEC2_IS_BEGIN) ]; weapon->weapon_id = item_id; weapon->weapon_lv = 1; weapon->ammo += item_num; weapon->meta = item_meta; weapon->Recalc(); DecInventory(item_meta->i->_inventory_slot(), add_num); } break; default: { #if 0 abort(); #endif } break; } } } need_sync_active_player = true; SyncAroundPlayers(__FILE__, __LINE__, __func__); } void Human::OnBulletHit(Bullet* bullet) { if (IsInvincible()) { return; } if (bullet->sender.Get()->room->GetRoomMode() == kZombieMode && bullet->sender.Get()->GetRace() == GetRace()) { return; } if (!dead && (bullet->IsBomb() || bullet->sender.Get()->team_id != team_id)) { float dmg = bullet->GetAtk() * (1 + bullet->sender.Get()->GetAttrRate(kHAT_Atk)) + bullet->sender.Get()->GetAttrAbs(kHAT_Atk); float def = ability.def * (1 + GetAttrRate(kHAT_Def)) + GetAttrAbs(kHAT_Def); float finaly_dmg = dmg * (1 - def/MetaMgr::Instance()->K); finaly_dmg = std::max(finaly_dmg, 0.0f); #if 0 sender->stats.damage_amount_out += finaly_dmg; #endif #ifdef DEBUG if (App::Instance()->HasFlag(1) && IsPlayer()) { return; } #endif if (bullet->meta->buff_meta) { MustBeAddBuff(this, bullet->meta->i->buffid()); } DecHP(finaly_dmg, bullet->sender.Get()->GetEntityUniId(), bullet->sender.Get()->GetName(), bullet->gun_meta->i->id()); #ifdef DEBUG bullet->sender.Get()->SendDebugMsg (a8::Format("bullet weapon_id:%d atk:%f", { bullet->gun_meta->i->id(), bullet->GetAtk() }) ); #endif } }