#include "precompile.h" #include #include "playermgr.h" #include "player.h" #include "cs_proto.pb.h" #include "room.h" #include "metamgr.h" #include "movement.h" #include "bullet.h" #include "obstacle.h" #include "building.h" #include "loot.h" Player::Player() { entity_type = ET_Player; entity_subtype = EST_Player; update_msg = new cs::SMUpdate(); } Player::~Player() { delete update_msg; update_msg = nullptr; } void Player::Initialize() { health = meta->i->health(); RecalcSelfCollider(); } void Player::Update(int delta_time) { if (poisoning) { poisoning_time += delta_time; } movement->Update(delta_time); if (moving) { UpdateMove(); } if (room->frame_no % 2 == 0) { if (shot_start || shot_hold) { UpdateShot(); } if (interaction_objids.size() > 0) { ProcInteraction(); } if (poisoning) { UpdatePoisoning(); } if (select_weapon) { UpdateSelectWeapon(); } if (drop_weapon) { UpdateDropWeapon(); } if (use_scope) { UpdateUseScope(); } if (reload) { UpdateReload(); } if (cancel_action) { UpdateCancelAction(); } if (use_item) { UpdateUseItemIdx(); } if (action_type != AT_None) { UpdateAction(); } if (spectate) { UpdateSpectate(); } MakeUpdateMsg(); SendNotifyMsg(*update_msg); { if (!new_objects.empty()) { new_objects.clear(); } } } } void Player::UpdateMove() { ++moved_frames; if (moved_frames > 4) { moving = false; moved_frames = 0; return; } int speed = std::max(1, (int)GetSpeed()); for (int i = 0; i < speed; ++i) { Vector2D old_pos = pos; pos = pos + move_dir; if (IsCollision()) { pos = old_pos; if (i == 0) { FindPath(); } break; } } if (last_collision_door && !TestCollision(last_collision_door)) { last_collision_door = nullptr; } } void Player::UpdateShot() { if (shot_start) { shot_start = false; Shot(); return; } if (shot_hold) { ++series_shot_frames; if (series_shot_frames > 4) { shot_hold = false; series_shot_frames = 0; Shot(); } } } void Player::UpdateSelectWeapon() { if (selected_weapon_idx >= 0 && selected_weapon_idx < weapons.size()) { Weapon* weapon = &weapons[selected_weapon_idx]; if (weapon->weapon_id != 0) { curr_weapon = weapon; ResetAction(); need_sync_active_player = true; for (auto& pair : room->human_hash_) { pair.second->new_objects.insert(this); } AutoLoadingBullet(); } } select_weapon = false; selected_weapon_idx = 0; } void Player::UpdateAction() { int duration = std::max(0, action_duration - (int)((room->frame_no - action_frameno) * 1.0f / SERVER_FRAME_RATE) * 1000 ); if (duration <= 0) { switch (action_type) { case AT_Reload: { if (curr_weapon->weapon_idx == action_target_id && curr_weapon->weapon_id == action_item_id && curr_weapon->weapon_idx != 0) { MetaData::Equip* bullet_meta = MetaMgr::Instance()->GetEquip(curr_weapon->meta->i->use_bullet()); if (bullet_meta) { int ammo = curr_weapon->ammo; if (ammo < curr_weapon->meta->i->clip_volume()) { if (bullet_meta->i->_inventory_slot() >= 0 && bullet_meta->i->_inventory_slot() < MAX_INVENTORY_NUM) { if (inventory[bullet_meta->i->_inventory_slot()] > 0) { int add_num = 0; if (inventory[bullet_meta->i->_inventory_slot()] <= curr_weapon->meta->i->clip_volume() - ammo) { add_num = inventory[bullet_meta->i->_inventory_slot()]; inventory[bullet_meta->i->_inventory_slot()] -= add_num; } else { add_num = curr_weapon->meta->i->clip_volume() - ammo; inventory[bullet_meta->i->_inventory_slot()] -= add_num; } curr_weapon->ammo += add_num; need_sync_active_player = true;; } } } } } } break; case AT_UseItem: { switch (action_item_id) { case IS_HEALTHKIT: { MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquipBySlotId(action_item_id); if (item_meta){ if (inventory[item_meta->i->_inventory_slot()] > 0) { health += item_meta->i->heal(); health = std::max(100.0f, health); --inventory[item_meta->i->_inventory_slot()]; need_sync_active_player = true; SyncAroundPlayers(); } } } break; default: { } break; } } break; } ResetAction(); } } void Player::UpdateReload() { AutoLoadingBullet(true); reload = false; } void Player::UpdateCancelAction() { CancelAction(); cancel_action = false; } void Player::UpdateUseItemIdx() { if (use_item_idx >= 0 && use_item_idx < MAX_INVENTORY_NUM) { switch (use_item_idx) { case IS_HEALTHKIT: { MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquipBySlotId(use_item_idx); if (item_meta) { StartAction( AT_UseItem, item_meta->i->use_time(), use_item_idx, 0 ); } } break; default: { } break; } } use_item_idx = 0; use_item = false; } void Player::UpdateSpectate() { spectate = false; } void Player::Shot() { if (!curr_weapon->meta) { return; } if (curr_weapon->weapon_idx != 0 && curr_weapon->ammo <= 0) { AutoLoadingBullet(); return; } Vector2D bullet_born_offset = Vector2D(0, 12.0f); #if 0 Vector2D bullet_born_dir = Vector2D(0, 12.0f); bullet_born_dir.Normalize(); bullet_born_offset = bullet_born_dir * bullet_born_offset.Norm(); #else bullet_born_offset.Rotate2(attack_dir.Angle(Vector2D::UP)); #endif Vector2D bullet_born_pos = pos + bullet_born_offset; { cs::MFShot* shot = room->frame_data.shots.Add(); shot->set_player_id(entity_uniid); curr_weapon->ToPB(shot->mutable_weapon()); shot->set_offhand(true); shot->set_bullskin(10001); } { cs::MFBullet* bullet = room->frame_data.bullets.Add(); bullet->set_player_id(entity_uniid); bullet->set_bullet_id(curr_weapon->meta->i->use_bullet()); bullet_born_pos.ToPB(bullet->mutable_pos()); attack_dir.ToPB(bullet->mutable_dir()); bullet->set_bulletskin(10001); bullet->set_gun_id(curr_weapon->meta->i->id()); bullet->set_fly_distance(fly_distance); } { Bullet* bullet = new Bullet(); bullet->player = this; bullet->room = room; bullet->gun_meta = curr_weapon->meta; bullet->meta = MetaMgr::Instance()->GetEquip(curr_weapon->meta->i->use_bullet()); bullet->pos = bullet_born_pos; bullet->dir = attack_dir; bullet->born_pos = bullet_born_pos; bullet->born_dir = attack_dir; bullet->fly_distance = fly_distance; bullet->entity_uniid = bullet->room->AllocUniid(); bullet->Initialize(); room->AddBullet(bullet); } --curr_weapon->ammo; int slot_id = curr_weapon->meta->i->_inventory_slot(); switch (slot_id) { case 5: { //手雷 if (inventory[slot_id] > 0) { --inventory[slot_id]; ++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 (inventory[slot_id] > 0) { --inventory[slot_id]; ++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; } need_sync_active_player = true; } void Player::ProcInteraction() { for (auto obj_id : interaction_objids) { Entity* entity = room->GetEntityByUniId(obj_id); if (entity) { switch (entity->entity_type) { case ET_Obstacle: { ObstacleInteraction((Obstacle*)entity); } break; case ET_Loot: { LootInteraction((Loot*)entity); } break; default: break; } } } interaction_objids.Clear(); } void Player::ObstacleInteraction(Obstacle* entity) { if (entity->is_door) { if (entity->door_state == DoorStateClose) { entity->door_state = DoorStateOpen; entity->pos = Vector2D(entity->building->pos.x + entity->door_state1->x() - entity->building->meta->i->tilewidth() / 2.0, entity->building->pos.y + entity->door_state1->y() - entity->building->meta->i->tileheight() / 2.0); } else { entity->door_state = DoorStateClose; entity->pos = Vector2D(entity->building->pos.x + entity->door_state0->x() - entity->building->meta->i->tilewidth() / 2.0, entity->building->pos.y + entity->door_state0->y() - entity->building->meta->i->tileheight() / 2.0); } entity->RecalcSelfCollider(); for (auto& pair : room->human_hash_) { pair.second->new_objects.insert(entity); pair.second->part_objects.insert(entity); if (entity->TestCollision(pair.second)) { pair.second->last_collision_door = entity; } else if (pair.second->last_collision_door == entity) { pair.second->last_collision_door = nullptr; } } } else { } } void Player::LootInteraction(Loot* entity) { if (entity->pickuped) { return; } MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquip(entity->item_id); if (!item_meta) { return; } switch (item_meta->i->equip_type()) { case 1: { //装备 if (item_meta->i->equip_subtype() == 1) { //近战 if (default_weapon.weapon_id != weapons[0].weapon_id) { cs::SMPickup notifymsg; notifymsg.set_error_code(2); SendNotifyMsg(notifymsg); return; } else { weapons[0].weapon_idx = 0; weapons[0].weapon_id = entity->item_id; weapons[0].weapon_lv = 1; weapons[0].ammo = 0; need_sync_active_player = true; } need_sync_active_player = true; for (auto& pair : room->human_hash_) { pair.second->new_objects.insert(this); } } else { Weapon* weapon = nullptr; if (weapons[GUN_SLOT1].weapon_id == 0) { weapon = &weapons[GUN_SLOT1]; weapon->weapon_idx = GUN_SLOT1; if (curr_weapon != &weapons[GUN_SLOT2]) { curr_weapon = &weapons[GUN_SLOT1]; } } else if (weapons[GUN_SLOT2].weapon_id == 0) { weapon = &weapons[GUN_SLOT2]; weapon->weapon_idx = GUN_SLOT2; if (curr_weapon != &weapons[GUN_SLOT1]) { curr_weapon = &weapons[GUN_SLOT2]; } } if (!weapon) { cs::SMPickup notifymsg; notifymsg.set_error_code(2); SendNotifyMsg(notifymsg); return; } weapon->weapon_id = entity->item_id; weapon->weapon_lv = 1; weapon->ammo = 0; weapon->meta = item_meta; AutoLoadingBullet(); need_sync_active_player = true; for (auto& pair : room->human_hash_) { pair.second->new_objects.insert(this); } } } break; default: { if (item_meta->i->_inventory_slot() >= 0 && item_meta->i->_inventory_slot() < MAX_INVENTORY_NUM) { inventory[item_meta->i->_inventory_slot()] += entity->count; if (item_meta->i->_inventory_slot() == 5 || item_meta->i->_inventory_slot() == 6) { Weapon* weapon = &weapons[3 + (item_meta->i->_inventory_slot() - 5)]; if (weapon->weapon_id == 0) { weapon->weapon_id = entity->item_id; weapon->weapon_lv = 1; weapon->ammo += entity->count; weapon->meta = item_meta; inventory[item_meta->i->_inventory_slot()] -= entity->count; } } if (item_meta->i->_inventory_slot() > 12) { if (item_meta->i->_inventory_slot() - 12 > curr_scope_idx) { curr_scope_idx = item_meta->i->_inventory_slot() - 12; } } } else { switch (item_meta->i->equip_type()) { case 7: { //背包 MetaData::Equip* old_item_meta = MetaMgr::Instance()->GetEquip(backpack); if (old_item_meta) { if (old_item_meta->i->equip_lv() >= item_meta->i->equip_lv()) { return; } DropItem(old_item_meta->i->id()); } backpack = item_meta->i->id(); } break; case 4: { //防具 if (item_meta->i->equip_subtype() == 1) { //盔甲 MetaData::Equip* old_item_meta = MetaMgr::Instance()->GetEquip(chest); if (old_item_meta) { if (old_item_meta->i->equip_lv() >= item_meta->i->equip_lv()) { return; } DropItem(old_item_meta->i->id()); } chest = item_meta->i->id(); } else if (item_meta->i->equip_subtype() == 2) { //头盔 MetaData::Equip* old_item_meta = MetaMgr::Instance()->GetEquip(helmet); if (old_item_meta) { if (old_item_meta->i->equip_lv() >= item_meta->i->equip_lv()) { return; } DropItem(old_item_meta->i->id()); } helmet = item_meta->i->id(); } } break; } } need_sync_active_player = true; SyncAroundPlayers(); } break; } entity->pickuped = true; room->AddDeletedObject(entity->entity_uniid); } void Player::_CMMove(f8::MsgHdr& hdr, const cs::CMMove& msg) { bool has_move_dir = msg.has_move_dir(); moving = false; if (msg.has_move_dir()) { if (std::abs(msg.move_dir().x()) > FLT_EPSILON || std::abs(msg.move_dir().y()) > FLT_EPSILON ) { move_dir.FromPB(&msg.move_dir()); move_dir.Normalize(); moving = true; } } assert(!isnan(move_dir.x) && !isnan(move_dir.y)); #if 0 moving = msg.has_move_dir(); #endif if (msg.has_attack_dir()) { attack_dir.FromPB(&msg.attack_dir()); attack_dir.Normalize(); } else { if (moving) { attack_dir = move_dir; } } if (moving) { moved_frames = 0; } shot_start = msg.shot_start(); shot_hold = msg.shot_hold(); fly_distance = std::min(200.0f, msg.fly_distance()); if (!shot_hold) { series_shot_frames = 0; } if (msg.has_interaction()) { interaction_objids = msg.interaction_objids(); } last_seq_id = msg.seq(); if (msg.has_select_weapon()) { select_weapon = true; selected_weapon_idx = msg.select_weapon(); } if (msg.has_drop_weapon()) { drop_weapon = true; drop_weapon_idx = msg.drop_weapon(); } if (msg.has_use_scope()) { use_scope = true; use_scope_idx = msg.use_scope(); } if (msg.has_reload()) { reload = msg.reload(); } if (msg.has_cancel_action()) { cancel_action = msg.cancel_action(); } if (msg.has_use_item_idx()) { use_item = true; use_item_idx = msg.use_item_idx(); } if (msg.has_spectate()) { spectate = true; } } void Player::UpdateDropWeapon() { if (drop_weapon_idx >= 0 && drop_weapon_idx < weapons.size()) { bool drop_ok = false; Weapon* weapon = &weapons[drop_weapon_idx]; int weapon_id = weapon->weapon_id; int weapon_ammo = weapon->ammo; if (weapon->weapon_id != 0) { if (weapon->weapon_idx == 0) { if (weapon->weapon_id != default_weapon.weapon_id) { drop_ok = true; *weapon = default_weapon; } } else if (weapon->weapon_idx == GUN_SLOT1) { drop_ok = true; *weapon = Weapon(); weapon->weapon_idx = drop_weapon_idx; if (curr_weapon == weapon) { if (weapons[GUN_SLOT2].weapon_id != 0) { curr_weapon = &weapons[GUN_SLOT2]; } else { curr_weapon = &weapons[0]; } } } else if (weapon->weapon_idx == GUN_SLOT2) { drop_ok = true; *weapon = Weapon(); weapon->weapon_idx = drop_weapon_idx; if (curr_weapon == weapon) { if (weapons[GUN_SLOT1].weapon_id != 0) { curr_weapon = &weapons[GUN_SLOT1]; } else { curr_weapon = &weapons[0]; } } } else if (weapon->weapon_idx == FRAG_SLOT) { drop_ok = true; *weapon = Weapon(); weapon->weapon_idx = drop_weapon_idx; if (curr_weapon == weapon) { if (weapons[GUN_SLOT1].weapon_id != 0) { curr_weapon = &weapons[GUN_SLOT1]; } else if (weapons[GUN_SLOT2].weapon_id != 0) { curr_weapon = &weapons[GUN_SLOT2]; } else { curr_weapon = &weapons[0]; } } } else if (weapon->weapon_idx == SMOKE_SLOT) { drop_ok = true; *weapon = Weapon(); weapon->weapon_idx = drop_weapon_idx; if (curr_weapon == weapon) { if (weapons[GUN_SLOT1].weapon_id != 0) { curr_weapon = &weapons[GUN_SLOT1]; } else if (weapons[GUN_SLOT2].weapon_id != 0) { curr_weapon = &weapons[GUN_SLOT2]; } else { curr_weapon = &weapons[0]; } } } if (drop_ok) { #if 1 { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(weapon_id); if (equip_meta) { Loot* entity = new Loot(); entity->room = room; entity->meta = equip_meta; entity->entity_uniid = room->AllocUniid(); { Vector2D dir = Vector2D::UP; dir.Rotate(a8::RandAngle()); entity->pos = pos + dir * (25 + rand() % 50); } entity->item_id = weapon_id; entity->count = std::max(1, weapon_ammo); entity->Initialize(); room->uniid_hash_[entity->entity_uniid] = entity; for (auto& pair : room->human_hash_) { pair.second->new_objects.insert(entity); pair.second->part_objects.insert(entity); } } } #endif need_sync_active_player = true; for (auto& pair : room->human_hash_) { pair.second->new_objects.insert(this); } } } } ResetAction(); AutoLoadingBullet(); drop_weapon = false; drop_weapon_idx = 0; } void Player::UpdateUseScope() { if (12 + use_scope_idx >= 12 && 12 + use_scope_idx < 16) { if (inventory[12 + use_scope_idx] > 0) { curr_scope_idx = use_scope_idx; need_sync_active_player = true; } } use_scope = false; use_scope_idx = 0; } void Player::_CMEmote(f8::MsgHdr& hdr, const cs::CMEmote& msg) { } void Player::_CMVoice(f8::MsgHdr& hdr, const cs::CMVoice& msg) { cs::SMVoiceNotify notifymsg; notifymsg.set_mode(msg.mode()); notifymsg.set_account_id(account_id); notifymsg.set_msg(msg.msg()); auto send_func = [] (Player* hum, a8::XParams& param) { cs::SMVoiceNotify* msg = (cs::SMVoiceNotify*)param.sender.GetUserData(); hum->SendNotifyMsg(*msg); }; room->TouchPlayerList(a8::XParams() .SetSender(¬ifymsg), send_func); } void Player::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_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); } } void Player::FillMFGasData(cs::MFGasData* gas_data) { gas_data->set_mode(room->gas_data.gas_mode); if (room->gas_data.gas_mode == GasInactive) { long long duration = GAS_INACTIVE_TIME * 20 - (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.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); } void Player::MakeUpdateMsg() { update_msg->Clear(); { update_msg->set_ack(last_seq_id); for (auto& obj_uniid : room->frame_data.deleted_objects) { update_msg->add_del_objids(obj_uniid); } *update_msg->mutable_shots() = room->frame_data.shots; *update_msg->mutable_bullets() = room->frame_data.bullets; { for (auto& pair : room->frame_data.explosions_hash) { if (pair.first <= room->frame_no) { for (auto& itr : pair.second) { *update_msg->add_explosions() = itr; } } } for (auto& pair : room->frame_data.smokes_hash) { if (pair.first <= room->frame_no) { for (auto& itr : pair.second) { *update_msg->add_smokes() = itr; } } } } if (updated_times == 0) { for (auto& pair : room->uniid_hash_) { if (pair.second->entity_type == ET_Building) { new_objects.insert(pair.second); } } } for (auto& itr : new_objects) { itr->FillMFObjectFull(update_msg->add_full_objects()); } for (auto& itr : part_objects) { itr->FillMFObjectPart(update_msg->add_part_objects()); } if (updated_times == 0 || need_sync_active_player) { update_msg->set_active_player_id(entity_uniid); FillMFActivePlayerData(update_msg->mutable_active_player_data()); need_sync_active_player = false; } if (updated_times == 0 || last_sync_gas_frameno < room->gas_data.gas_start_frameno) { last_sync_gas_frameno = room->gas_data.gas_start_frameno; FillMFGasData(update_msg->mutable_gas_data()); } if (room->gas_data.gas_mode == GasMoving) { update_msg->set_gas_progress(room->gas_data.gas_progress); room->gas_data.pos_old.ToPB(update_msg->mutable_gas_pos_old()); } update_msg->set_alive_count(room->AliveCount()); } }