#include "precompile.h" #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 "collision.h" #include "building.h" #include "hero.h" #include "app.h" #include "roommgr.h" #include "framework/cpp/utils.h" #include "framework/cpp/httpclientpool.h" 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); 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; } } Human::~Human() { } void Human::Initialize() { Entity::Initialize(); RecalcSelfCollider(); volume_ = meta->volume; observers_.insert(this); } float Human::GetSpeed() { if (a8::HasBitFlag(status, HS_Jump)) { return meta->i->jump_speed() + buff.speed; } if (downed) { return meta->i->move_speed3() + buff.speed; } else { return meta->i->move_speed() + buff.speed; } } 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); pos.ToPB(p->mutable_pos()); attack_dir.ToPB(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); pos.ToPB(p->mutable_pos()); attack_dir.ToPB(p->mutable_dir()); p->set_health(health); 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_skin(skin); 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); FillBodyState(p->mutable_states()); } ColliderComponent* Human::GetBoxBound() { CircleCollider* collider = new CircleCollider(); collider->owner = this; collider->pos = Vector2D(); collider->rad = GetRadius(); return collider; } 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_dead(dead); stats_pb->set_killer_id(stats.killer_id); stats_pb->set_killer_name(stats.killer_name); stats_pb->set_account_id(account_id); } void Human::FillMFTeamData(cs::MFTeamData* team_data) { team_data->set_player_id(entity_uniid); team_data->set_name(name); pos.ToPB(team_data->mutable_pos()); attack_dir.ToPB(team_data->mutable_dir()); team_data->set_health(health); team_data->set_max_health(GetMaxHP()); team_data->set_disconnected(false); team_data->set_dead(dead); team_data->set_downed(downed); } void Human::Shot(Vector2D& 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) { Vector2D bullet_born_offset = Vector2D(std::get<0>(tuple), std::get<1>(tuple)); bullet_born_offset.Rotate(attack_dir.CalcAngle(Vector2D::UP)); Vector2D bullet_born_pos = pos + 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) { Vector2D bullet_born_offset = Vector2D(std::get<0>(tuple), std::get<1>(tuple)); bullet_born_offset.Rotate(attack_dir.CalcAngle(Vector2D::UP)); Vector2D bullet_born_pos = pos + bullet_born_offset; room->frame_event.AddBullet(this, bullet_born_pos, attack_dir, fly_distance); room->CreateBullet(this, curr_weapon->meta, 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(); } 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(); } break; } last_shot_frameno_ = room->frame_no; need_sync_active_player = true; } void Human::RecalcSelfCollider() { if (!self_collider_) { self_collider_ = new CircleCollider(); self_collider_->owner = this; colliders.push_back(self_collider_); } self_collider_->pos = Vector2D(); self_collider_->rad = meta->i->radius(); } bool Human::IsCollision() { if (room->OverBorder(pos, meta->i->radius())){ return true; } if (a8::HasBitFlag(status, HS_Jump)) { return false; } std::vector objects; for (auto& grid : grid_list) { for (Entity* entity : grid->entity_list) { switch (entity->entity_type) { case ET_Obstacle: { if ( (last_collision_door == nullptr || last_collision_door != entity) && TestCollision(entity) ){ objects.push_back(entity); } } break; case ET_Building: { if (TestCollision(entity)) { objects.push_back(entity); } } break; default: { } break; } } } return !objects.empty(); } void Human::FindPath() { Vector2D old_pos = pos; { float up_dot = Vector2D::UP.Dot(move_dir); bool at_left_side = Vector2D::LEFT.Dot(move_dir) > 0.0001f; if (std::abs(up_dot) <= 0.001f) { //相互垂直 //向上 pos = old_pos + Vector2D::UP; if (!IsCollision()) { return; } else { //向下 pos = old_pos + Vector2D::DOWN; if (!IsCollision()) { return; } } } else if (up_dot > 0.001f) { //基本相同 pos = old_pos + (at_left_side ? Vector2D::LEFT : Vector2D::RIGHT); if (!IsCollision()) { return; } else { //向上 pos = old_pos + Vector2D::UP; if (!IsCollision()) { return; } } } else if (up_dot < 0.001f) { //基本相反 pos = old_pos + (at_left_side ? Vector2D::LEFT : Vector2D::RIGHT); if (!IsCollision()) { return; } else { //向下 pos = old_pos + Vector2D::DOWN; if (!IsCollision()) { return; } } } } pos = old_pos; } float Human::GetRadius() { return meta->i->radius(); } float Human::GetMaxHP() { return meta->i->health(); } 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(); } } void Human::SyncAroundPlayers() { room->TouchHumanList(a8::XParams(), [this] (Human* hum, a8::XParams& param) -> bool { hum->new_objects.insert(this); return true; }); } void Human::AutoLoadingBullet(bool manual) { if (curr_weapon->weapon_idx != 0 && (curr_weapon->ammo <= 0 || (manual && curr_weapon->ammo < curr_weapon->meta->i->clip_volume())) ) { 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() < 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) { 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; } } msg.set_team_id(team_id); msg.set_team_rank(rank); msg.set_team_allcnt(1); msg.set_game_over(room->game_over); msg.set_victory(!dead); 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) { Entity* hum = room->GetEntityByUniId(killer_id); if (hum && hum->entity_type == ET_Player) { ((Human*)hum)->stats.kills++; ((Human*)hum)->kill_humans.insert(this); 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; health = 0.0f; dead_frameno = room->frame_no; room->OnHumanDie(this); SyncAroundPlayers(); if (team_members) { for (auto& hum : *team_members) { if (hum != this && hum->action_type == AT_Relive && hum->action_target_id == entity_uniid) { hum->CancelAction(); } } } if (!HasNoDownedTeammate() && !leave_) { if (team_members) { for (auto& member : *team_members) { if (member == this) { member->SendGameOver(); } else { if (member->dead) { member->SendGameOver(); } else if (member->downed) { a8::XParams& timer_param = room->xtimer.GetTimerXParams(member->downed_timer); member->stats.killer_id = timer_param.param1; member->stats.killer_name = timer_param.param2.GetString(); member->stats.weapon_id = timer_param.param2; member->dead = true; member->health = 0.0f; member->dead_frameno = room->frame_no; member->room->OnHumanDie(this); member->SyncAroundPlayers(); member->SendGameOver(); room->xtimer.DeleteTimer(member->downed_timer); member->downed_timer = nullptr; } } } } else { SendGameOver(); } } if (room->GetAliveTeamNum() == 1) { std::set* members = room->GetAliveTeam(); if (members) { for (Human* member : *members) { member->SendGameOver(); } } } } } 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->room->xtimer.DeleteTimer(hum->downed_timer); return; } if (hum->dead) { return; } if (!hum->HasLiveTeammate()) { 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); } else { float old_health = health; health = std::max(0.0f, health - dec_hp); if (health - old_health > 0.001f) { stats.damage_amount_in += health - old_health; } if (health <= 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); } else { if (HasNoDownedTeammate()) { health = 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_ ); } else { BeKill(killer_id, killer_name, weapon_id); } } } } SyncAroundPlayers(); } void Human::AddToNewObjects(Entity* entity) { new_objects.insert(entity); } void Human::AddToPartObjects(Entity* entity) { part_objects.insert(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); FindLocation(); SyncAroundPlayers(); } 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(); 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_); } } void Human::DoSkill() { if (skill_meta && skill_meta->i->condition() == SC_Active) { int passed_time = (room->frame_no - last_use_skill_frameno_) * FRAME_RATE_MS; int skill_left_time = std::max(0, skill_meta->i->last_time() * 1000 - passed_time); if (skill_left_time <= 0 || last_use_skill_frameno_ == 0) { skill_xtimer_attacher_.ClearTimerList(); switch (skill_meta->i->type()) { case ST_Hide: { hide_frameno_ = room->frame_no; a8::SetBitFlag(status, HS_Hide); room->xtimer.AddDeadLineTimerAndAttach(skill_meta->i->last_time() * SERVER_FRAME_RATE, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { }, &skill_xtimer_attacher_.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); a8::UnSetBitFlag(hum->status, HS_Hide); hum->need_sync_active_player = true; hum->BroadcastFullState(); } ); need_sync_active_player = true; BroadcastFullState(); } break; case ST_SummonHero: { SummonHero(); } break; case ST_Accelerate: { accelerate_frameno_ = room->frame_no; a8::SetBitFlag(status, HS_Accelerate); buff.speed += skill_meta->i->value1(); room->xtimer.AddDeadLineTimerAndAttach(skill_meta->i->last_time() * SERVER_FRAME_RATE, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { }, &skill_xtimer_attacher_.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); a8::UnSetBitFlag(hum->status, HS_Accelerate); hum->buff.speed = 0.0f; hum->need_sync_active_player = true; hum->BroadcastFullState(); } ); need_sync_active_player = true; BroadcastFullState(); } break; case ST_DamageAdd: { damageadd_frameno_ = room->frame_no; a8::SetBitFlag(status, HS_DamageAdd); room->xtimer.AddDeadLineTimerAndAttach(skill_meta->i->last_time() * SERVER_FRAME_RATE, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { }, &skill_xtimer_attacher_.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); a8::UnSetBitFlag(hum->status, HS_DamageAdd); hum->need_sync_active_player = true; hum->BroadcastFullState(); } ); need_sync_active_player = true; BroadcastFullState(); } break; case ST_DefAdd: { defadd_frameno_ = room->frame_no; a8::SetBitFlag(status, HS_DefAdd); room->xtimer.AddDeadLineTimerAndAttach(skill_meta->i->last_time() * SERVER_FRAME_RATE, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { }, &skill_xtimer_attacher_.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); a8::UnSetBitFlag(hum->status, HS_DefAdd); hum->need_sync_active_player = true; hum->BroadcastFullState(); } ); need_sync_active_player = true; BroadcastFullState(); } break; case ST_RecoverHP: { recover_hp_frameno_ = room->frame_no; a8::SetBitFlag(status, HS_RecoverHP); room->xtimer.AddRepeatTimerAndAttach(SERVER_FRAME_RATE, a8::XParams() .SetSender(this) .SetParam1(skill_meta->i->value1()), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); if (a8::HasBitFlag(hum->status, HS_RecoverHP)) { hum->RecoverHp(param.param1); hum->need_sync_active_player = true; } }, &skill_xtimer_attacher_.timer_list_ ); room->xtimer.AddDeadLineTimerAndAttach(skill_meta->i->last_time() * SERVER_FRAME_RATE, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { }, &skill_xtimer_attacher_.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); a8::UnSetBitFlag(hum->status, HS_RecoverHP); hum->need_sync_active_player = true; hum->BroadcastFullState(); } ); need_sync_active_player = true; BroadcastFullState(); } break; case ST_ReflectDamage: { reflect_damage_frameno_ = room->frame_no; a8::SetBitFlag(status, HS_ReflectDamage); room->xtimer.AddDeadLineTimerAndAttach(skill_meta->i->last_time() * SERVER_FRAME_RATE, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { }, &skill_xtimer_attacher_.timer_list_, [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); a8::UnSetBitFlag(hum->status, HS_ReflectDamage); hum->need_sync_active_player = true; hum->BroadcastFullState(); } ); need_sync_active_player = true; BroadcastFullState(); } break; default: { } break; } last_use_skill_frameno_ = room->frame_no; } } } void Human::FindLocation() { { std::vector objects; room->BuildingBoxBoundCollisionDetection(this, objects); if (objects.size() > 1) { abort(); } if (!objects.empty()) { Building* building = (Building*)objects[0]; Vector2D b_min = Vector2D( building->pos.x - building->meta->i->tilewidth()/2.0, building->pos.y - building->meta->i->tileheight()/2.0 ); Vector2D b_max = Vector2D( building->pos.x + building->meta->i->tilewidth()/2.0, building->pos.y + building->meta->i->tileheight()/2.0 ); Vector2D new_pos; bool ret = CalcCircleAabbSafePoint( pos, GetRadius(), b_min, b_max, new_pos ); if (!ret) { abort(); } if (ret) { pos = new_pos; room->grid_service.MoveHuman(this); return; } } } { std::vector objects; for (auto& grid : grid_list) { for (Entity* entity : grid->entity_list) { switch (entity->entity_type) { case ET_Obstacle: { if (TestCollision(entity)){ objects.push_back(entity); } } break; default: { } break; } } } if (objects.empty()) { return; } std::sort(objects.begin(), objects.end(), [this] (Entity* a, Entity *b) -> bool { return (this->pos - a->pos).Norm() < (this->pos - b->pos).Norm(); }); Entity* target = objects[0]; 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: { 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: { 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: { 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); } } player_data->set_skin(skin); player_data->set_backpack(backpack); player_data->set_helmet(helmet); player_data->set_chest(chest); player_data->set_health(health); 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 1 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.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); } } room->gas_data.pos_old.ToPB(gas_data->mutable_pos_old()); room->gas_data.pos_new.ToPB(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::RecalcAttr() { def = meta->i->def(); MetaData::Equip* chest_meta = MetaMgr::Instance()->GetEquip(chest); if (chest_meta) { def += chest_meta->i->def(); } MetaData::Equip* helmet_meta = MetaMgr::Instance()->GetEquip(helmet); if (helmet_meta) { def += helmet_meta->i->def(); } } 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::RecalcBuff() { buff = HumanAbility(); if (skin_meta) { } } 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) { health += inc_hp; health = std::max(health, 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 (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->i->last_time() * 1000 - passed_time)); state->set_lasting_time(skill_meta->i->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->i->last_time() * 1000 - passed_time)); state->set_lasting_time(skill_meta->i->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->i->last_time() * 1000 - passed_time)); state->set_lasting_time(skill_meta->i->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->i->last_time() * 1000 - passed_time)); state->set_lasting_time(skill_meta->i->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->i->last_time() * 1000 - passed_time)); state->set_lasting_time(skill_meta->i->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->i->last_time() * 1000 - passed_time)); state->set_lasting_time(skill_meta->i->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->i->last_time() * 1000 - passed_time)); state->set_lasting_time(skill_meta->i->last_time() * 1000); } } void Human::SummonHero() { Hero* hero = room->CreateHero(this); if (hero) { summon_hero_frameno_ = room->frame_no; a8::SetBitFlag(status, HS_SummonHero); room->xtimer.AddDeadLineTimerAndAttach(skill_meta->i->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(); } } 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: { 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 (!sending_gameover_) { InternalSendGameOver(); } } 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::ClearFrameData() { if (!new_objects.empty()) { new_objects.clear(); } if (!del_objects.empty()) { for (auto& itr : del_objects) { Entity* entity = room->GetEntityByUniId(itr); if (entity) { part_objects.erase(entity); } } del_objects.clear(); } if (!out_objects.empty()) { for (auto& itr : out_objects) { Entity* entity = room->GetEntityByUniId(itr); if (entity) { part_objects.erase(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(); } } 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; } } } params->SetVal("account_id", account_id); params->SetVal("map_name", room->map_meta->i->map_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_id != 0); 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); { int coin_num = (rank * MetaMgr::Instance()->rank_param) + (stats.kills * MetaMgr::Instance()->kill_param); stats.gold = coin_num; params->SetVal("coin_num", coin_num); } params->SetVal("score", 0); } void Human::InternalSendGameOver() { if (already_report_battle_) { cs::SMGameOver msg; FillSMGameOver(msg); SendNotifyMsg(msg); return; } a8::MutableXObject* params = a8::MutableXObject::NewObject(); GenBattleReportData(params); auto on_ok = [] (a8::XParams& param, a8::XObject& data) { long long room_uuid = param.sender; int hum_uniid = param.param1; Room* room = RoomMgr::Instance()->GetRoomByUuid(room_uuid); if (room) { Entity* entity = room->GetEntityByUniId(hum_uniid); if (entity && entity->entity_type == ET_Player) { Human* hum = (Human*)entity; hum->sending_gameover_ = false; hum->already_report_battle_ = true; hum->stats.history_time_alive = data.Get("alive_time_his"); hum->stats.history_kills = data.Get("kill_his"); hum->stats.history_damage_amount = data.Get("harm_his"); hum->stats.history_heal_amount = data.Get("add_HP_his"); cs::SMGameOver msg; hum->FillSMGameOver(msg); hum->SendNotifyMsg(msg); } } }; auto on_error = [] (a8::XParams& param, const std::string& response) { long long room_uuid = param.sender; int hum_uniid = param.param1; Room* room = RoomMgr::Instance()->GetRoomByUuid(room_uuid); if (room) { Entity* entity = room->GetEntityByUniId(hum_uniid); if (entity && entity->entity_type == ET_Player) { Human* hum = (Human*)entity; hum->sending_gameover_ = false; ++hum->send_gameover_trycount_; if (hum->send_gameover_trycount_ < 10){ hum->SendGameOver(); } } } }; std::string url; if (!f8::IsOnlineEnv()) { if (App::Instance()->flags.find(3) != App::Instance()->flags.end()) { 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 { url = "https://game2001api.kingsome.cn/webapp/index.php?c=Role&a=battleReport"; } f8::HttpClientPool::Instance()->HttpGet( a8::XParams() .SetSender(room->room_uuid) .SetParam1(entity_uniid), on_ok, on_error, url.c_str(), *params, rand() ); delete params; sending_gameover_ = true; }