#include "precompile.h" #include #include #include #include #include #include "playermgr.h" #include "player.h" #include "cs_proto.pb.h" #include "room.h" #include "android.h" #include "metamgr.h" #include "bullet.h" #include "collider.h" #include "obstacle.h" #include "roomobstacle.h" #include "building.h" #include "loot.h" #include "roommgr.h" #include "app.h" #include "gamelog.h" #include "typeconvert.h" const int ROOM_MAX_PLAYER_NUM = 40; static long long RoomXGetTickCount(void* context) { Room* room = (Room*)context; return room->frame_no; } Room::~Room() { } void Room::Init() { if (map_meta->i->map_width() < 1) { abort(); } if (map_meta->i->map_height() < 1) { abort(); } xtimer.Init(RoomXGetTickCount, this, 100, 100); xtimer_attacher_.xtimer = &xtimer; CreateSpawnPoints(); CreateLoots(); CreateDropObjs(); InitObstacleDatas(); ShuaAndroid(); } void Room::UnInit() { App::Instance()->perf.alive_count -= alive_count_; if (game_over_timer) { a8::Timer::Instance()->DeleteTimer(game_over_timer); game_over_timer = nullptr; } xtimer_attacher_.ClearTimerList(); for (auto& pair : accountid_hash_) { PlayerMgr::Instance()->RemovePlayerBySocket(pair.second->socket_handle); } for (auto& pair : uniid_hash_) { delete pair.second; } uniid_hash_.clear(); for (auto& pair : later_add_hash_) { delete pair.second; } later_add_hash_.clear(); for (auto& pair : removed_robot_hash_) { delete pair.second; } removed_robot_hash_.clear(); grid_service->ClearRoomData(this); } void Room::Update(int delta_time) { xtimer.Update(); if (game_over && frame_no - game_over_frameno > SERVER_FRAME_RATE * 20) { return; } elapsed_time_ += delta_time; while (elapsed_time_ >= 50) { if (frame_no % 2 == 0) { UpdateGas(); } for (auto& pair : moveable_hash_) { pair.second->Update(50); } if (frame_no % 2 == 0) { frame_event.Clear(); } ++frame_no; elapsed_time_ -= 50; } } int Room::GetPlayerNum() { return accountid_hash_.size(); } Player* Room::GetPlayerByAccountId(const std::string& accountid) { auto itr = accountid_hash_.find(accountid); return itr != accountid_hash_.end() ? itr->second : nullptr; } Player* Room::GetPlayerByUniId(int uniid) { Entity* entity = GetEntityByUniId(uniid); return entity && entity->entity_type == ET_Player && entity->entity_subtype == EST_Player ? (Player*)entity : nullptr; } Entity* Room::GetEntityByUniId(int uniid) { auto itr = uniid_hash_.find(uniid); return itr != uniid_hash_.end() ? itr->second : nullptr; } int Room::AliveCount() { return alive_count_; } void Room::AddPlayer(Player* hum) { assert(gas_data.gas_mode == GasInactive); hum->born_point = AllocBornPoint(hum); if (!hum->born_point) { hum->SetPos(a8::Vec2(DEFAULT_BORN_POINT_X + rand() % 100, DEFAULT_BORN_POINT_Y + rand() % 200) ); } else { hum->SetPos(hum->born_point->RandPoint()); } hum->attack_dir = hum->GetPos(); hum->attack_dir.Normalize(); hum->attack_dir.Rotate(a8::RandAngle()); hum->move_dir = hum->attack_dir; hum->entity_uniid = AllocUniid(); hum->room = this; hum->join_frameno = frame_no; hum->Initialize(); { cs::SMJoinedNotify notifymsg; notifymsg.set_error_code(0); FillSMJoinedNotify(hum, notifymsg); GGListener::Instance()->SendToClient(hum->socket_handle, 0, notifymsg); } { cs::SMMapInfo notifymsg; notifymsg.set_map_id(map_meta->i->map_id()); notifymsg.set_map_width(map_meta->i->map_width()); notifymsg.set_map_height(map_meta->i->map_height()); GGListener::Instance()->SendToClient(hum->socket_handle, 0, notifymsg); } uniid_hash_[hum->entity_uniid] = hum; moveable_hash_[hum->entity_uniid] = hum; accountid_hash_[hum->account_id] = hum; human_hash_[hum->entity_uniid] = hum; alive_human_hash_[hum->entity_uniid] = hum; last_add_player_tick = a8::XGetTickCount(); ++alive_count_; ++App::Instance()->perf.alive_count; grid_service->AddHuman(hum); hum->FindLocation(); hum->RefreshView(); MatchTeam(hum); if (room_type == RT_NewBrid) { if (force_shua_android_times_ < 1) { CreateAndroid(20 + rand() % 10); ++force_shua_android_times_; } xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * (1 + rand() % 3), a8::XParams() .SetSender(hum), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->room->ShowAndroid(hum, 1 + rand() % 2); }, &hum->xtimer_attacher.timer_list_); } if (room_type == RT_NewBrid || room_type == RT_MidBrid) { for (int buff_id : MetaMgr::Instance()->newbie_buff_list) { MetaData::Buff* buff_meta = MetaMgr::Instance()->GetBuff(buff_id); if (buff_meta) { hum->AddBuff(buff_meta, 1); } #ifdef DEBUG a8::UdpLog::Instance()->Debug("room_idx:%d 添加新手buff %s %s %d", { room_idx, hum->account_id, hum->name, buff_id }); #endif } } while (human_hash_.size() > ROOM_MAX_PLAYER_NUM) { RandRemoveAndroid(); } NotifyUiUpdate(); } int Room::AllocUniid() { while (GetEntityByUniId(++current_uniid_) || later_add_hash_.find(current_uniid_) != later_add_hash_.end() || current_uniid_ == 0) {} return current_uniid_; } void Room::ShuaAndroid() { if (gas_data.gas_mode != GasInactive) { return; } int robot_min_num = MetaMgr::Instance()->GetSysParamAsInt("refresh_robot_min_num", 5); int robot_max_num = MetaMgr::Instance()->GetSysParamAsInt("refresh_robot_max_num", 10); int refresh_min_time = MetaMgr::Instance()->GetSysParamAsInt("refresh_robot_min_time", 5); int refresh_max_time = MetaMgr::Instance()->GetSysParamAsInt("refresh_robot_max_time", 10); int robot_num = a8::RandEx(robot_min_num, robot_max_num); int refresh_time = a8::RandEx(refresh_min_time, refresh_max_time); if (robot_num > 0 && refresh_time > 0) { CreateAndroid(robot_num); xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * refresh_time, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->ShuaAndroid(); }, &xtimer_attacher_.timer_list_); NotifyUiUpdate(); } } void Room::ShowAndroid(Human* target, int num) { if (!target->born_point) { return; } int i = 0; for (auto& pair : human_hash_) { Human* hum = pair.second; if (a8::HasBitFlag(hum->status, HS_Disable)) { if (hum->born_point) { DecBornPointHumanNum(hum->born_point, hum); } hum->born_point = target->born_point; IncBornPointHumanNum(hum->born_point, hum); hum->SetPos(hum->born_point->RandPoint()); a8::SetBitFlag(hum->status, HS_AlreadyShow); EnableHuman(hum); ++i; if (i >= num) { break; } } } } void Room::CreateAndroid(int robot_num) { std::vector* robot_list = MetaMgr::Instance()->GetRobotList(); if (!robot_list || robot_list->empty()) { return; } if (robot_num <= 0) { return; } int rnd_skin_robot_idx = rand() % robot_num; for (int i = 0; i < robot_num; ++i) { if (human_hash_.size() >= ROOM_MAX_PLAYER_NUM) { return; } MetaData::Robot* robot_meta = nullptr; int try_count = 0; while (true) { MetaData::Robot& tmp_robot_meta = (*robot_list)[rand() % robot_list->size()]; if (refreshed_robot_set_.find(tmp_robot_meta.i->id()) == refreshed_robot_set_.end()) { robot_meta = &tmp_robot_meta; break; } ++try_count; if (try_count > 3000) { return; } } Android* hum = new Android(); hum->name = robot_meta->i->name(); hum->meta = MetaMgr::Instance()->android_meta; hum->robot_meta = robot_meta; hum->entity_uniid = AllocUniid(); hum->born_point = AllocBornPoint(hum); if (!hum->born_point) { hum->SetPos(a8::Vec2(DEFAULT_BORN_POINT_X + rand() % 1400, DEFAULT_BORN_POINT_Y + rand() % 1500) ); } else { hum->SetPos(hum->born_point->RandPoint()); } hum->attack_dir = hum->GetPos(); hum->attack_dir.Normalize(); hum->attack_dir.Rotate(a8::RandAngle()); hum->move_dir = hum->attack_dir; hum->room = this; hum->Initialize(); if (rnd_skin_robot_idx == i) { MetaData::Dress* skin_meta = MetaMgr::Instance()->RandDress(); if (skin_meta) { #if 0 hum->skin_meta = skin_meta; hum->SetSkinInfo(skin_meta->i->id()); #endif } } uniid_hash_[hum->entity_uniid] = hum; human_hash_[hum->entity_uniid] = hum; ++alive_count_; { hum->team_id = NewTeam(); hum->team_members = &team_hash_[hum->team_id]; hum->team_members->insert(hum); } ++App::Instance()->perf.alive_count; refreshed_robot_set_.insert(robot_meta->i->id()); if (room_type == RT_NewBrid) { a8::SetBitFlag(hum->status, HS_Disable); } else { alive_human_hash_[hum->entity_uniid] = hum; moveable_hash_[hum->entity_uniid] = hum; grid_service->AddHuman(hum); hum->FindLocation(); hum->RefreshView(); } } } Human* Room::FindEnemy(Human* hum) { if (a8::HasBitFlag(hum->status, HS_NewBieNpc) && frame_no - hum->enable_frameno < SERVER_FRAME_RATE * 8) { return first_newbie; } std::vector enemys; enemys.reserve(50); EntitySubType_e sub_type = EST_Player; if ((rand() % 10) < 2) { sub_type = EST_Android; } for (auto& cell : hum->grid_list) { for (Human* target : cell->human_list[room_idx]) { if (target->entity_subtype == sub_type && !target->dead) { if (hum->GetPos().Distance(target->GetPos()) < 300.0f) { if (target->team_id == 0 || target->team_id != hum->team_id ) { enemys.push_back(target); } } } } } std::sort(enemys.begin(), enemys.end(), [hum] (Human* a, Human *b) -> bool { return (hum->GetPos() - a->GetPos()).Norm() < (hum->GetPos() - b->GetPos()).Norm(); }); return !enemys.empty() ? enemys[0] : nullptr; } void Room::FillSMJoinedNotify(Player* self_hum, cs::SMJoinedNotify& msg) { msg.set_team_mode(msg.team_mode()); msg.set_player_id(self_hum->entity_uniid); msg.set_started(false); msg.set_room_uuid(a8::XValue(room_uuid).GetString()); } void Room::ScatterDrop(a8::Vec2 center, int drop_id) { MetaData::Drop* drop_meta = MetaMgr::Instance()->GetDrop(drop_id); if (drop_meta) { std::vector> drop_items; drop_meta->RandItems(drop_items); for (auto& item : drop_items) { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); DropItem(center + dir * (5 + rand() % 50), std::get<0>(item), std::get<1>(item), std::get<1>(item)); } } } void Room::DropItem(a8::Vec2 pos, int item_id, int item_count, int item_lv) { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(item_id); if (equip_meta && equip_meta->i->group_num() > 0 && item_count > 0) { int total_count = item_count; while (total_count > 0) { int drop_num = std::min(total_count, equip_meta->i->group_num()); { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); pos = pos + dir * (25 + rand() % 50); } CreateLoot(item_id, pos, drop_num, item_lv); total_count -= drop_num; } } } RoomObstacle* Room::CreateObstacle(int id, float x, float y) { RoomObstacle* entity = InternalCreateObstacle(id, x, y, [] (Obstacle*) { }); assert(entity); if (entity) { entity->BroadcastFullState(this); } return entity; } int Room::CreateLoot(int equip_id, a8::Vec2 pos, int count, int equip_lv) { MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(equip_id); if (equip_meta) { if (equip_meta->i->equip_type() == 2 && MetaMgr::Instance()->fighting_mode) { return 0; } Loot* entity = new Loot(); entity->room = this; entity->meta = equip_meta; entity->entity_uniid = AllocUniid(); entity->SetPos(pos); #if 1 { if (entity->GetX() >= map_meta->i->map_width()) { entity->SetX(map_meta->i->map_width() - 1); } if (entity->GetX() < 1.0f) { entity->SetX(1.0f); } if (entity->GetY() >= map_meta->i->map_height()) { entity->SetY(map_meta->i->map_height() - 1); } if (entity->GetY() < 1.0f) { entity->SetY(1.0f); } } #endif entity->item_id = equip_id; entity->count = count; entity->item_level = equip_lv; entity->Initialize(); uniid_hash_[entity->entity_uniid] = entity; grid_service->AddRoomEntity(this, entity); entity->BroadcastFullState(this); return entity->entity_uniid; } else { return 0; } } void Room::CreateBullet(Human* hum, Weapon* weapon, a8::Vec2 pos, a8::Vec2 dir, float fly_distance, bool is_tank_skin) { if (grid_service->CanAdd(pos.x, pos.y)) { Bullet* bullet = new Bullet(); bullet->player = hum; bullet->room = this; bullet->gun_meta = weapon->meta; bullet->gun_upgrade_meta = weapon->upgrade_meta; bullet->meta = MetaMgr::Instance()->GetEquip(weapon->meta->i->use_bullet()); bullet->SetPos(pos); bullet->dir = dir; bullet->born_pos = pos; bullet->born_dir = dir; bullet->fly_distance = fly_distance; bullet->is_tank_skin = is_tank_skin; bullet->entity_uniid = AllocUniid(); bullet->Initialize(); AddObjectLater(bullet); grid_service->AddBullet(bullet); } } void Room::RemoveObjectLater(RoomEntity* entity) { auto remove_func = [] (const a8::XParams& param) { RoomEntity* entity = (RoomEntity*)param.sender.GetUserData(); switch (entity->entity_type) { case ET_Bullet: { entity->room->moveable_hash_.erase(entity->entity_uniid); entity->room->grid_service->DelBullet((Bullet*)entity); } break; case ET_Loot: { entity->BroadcastDeleteState(entity->room); entity->room->grid_service->DelRoomEntity(entity->room, entity); } break; case ET_Player: { entity->room->moveable_hash_.erase(entity->entity_uniid); entity->room->human_hash_.erase(entity->entity_uniid); } break; default: { abort(); } break; } entity->room->uniid_hash_.erase(entity->entity_uniid); delete entity; }; xtimer.AddDeadLineTimerAndAttach(0, a8::XParams() .SetSender(entity), remove_func, &entity->xtimer_attacher.timer_list_); } void Room::OnHumanDie(Human* hum) { --alive_count_; --App::Instance()->perf.alive_count; alive_human_hash_.erase(hum->entity_uniid); NotifyUiUpdate(); } bool Room::OverBorder(const a8::Vec2 pos, float radius) { //检查x轴 { int left_x = pos.x - radius; if (left_x < 0.001f) { return true; } int right_x = pos.x + radius; if (right_x > map_meta->i->map_width()) { return true; } } //检查y轴 { int up_y = pos.y + radius; if (up_y > map_meta->i->map_height()) { return true; } int down_y = pos.y - radius; if (down_y < 0.001f) { return true; } } return false; } Human* Room::GetWatchWarTarget(Human* hum) { if (hum->team_members) { for (auto& member : *hum->team_members) { if (member != hum && !member->dead) { return member; } } } std::vector humans; for (auto& pair : human_hash_) { if (pair.first != hum->entity_uniid && !pair.second->dead) { humans.push_back(pair.second); } } if (!humans.empty()) { Human* target = humans[rand() % humans.size()]; return target; } return nullptr; } bool Room::BattleStarted() { return battle_start_frameno_ != 0; } int Room::GetAliveTeamNum() { int num = 0; for (auto& pair : team_hash_) { for (Human* hum : pair.second) { if (!hum->real_dead) { ++num; break; } } } return num; } bool Room::CanJoin(const std::string& accountid, RoomType_e self_room_type) { if (gas_data.gas_mode != GasInactive) { return false; } if (GetPlayerByAccountId(accountid)) { return false; } if (room_type == RT_NewBrid) { if (GetPlayerNum() > 0) { return false; } } if (room_type == RT_MidBrid) { if (GetPlayerNum() > 4) { return false; } } return GetPlayerNum() < ROOM_MAX_PLAYER_NUM; } void Room::OnPlayerOffline(Player* hum) { bool has_player = false; for (auto& pair : accountid_hash_) { if (pair.second->socket_handle != 0) { has_player = true; } } if (!has_player) { RoomMgr::Instance()->AddOverRoom(room_uuid); } } Entity* Room::FindFirstCollisonEntity(const a8::Vec2& aabb_pos, AabbCollider& aabb_box) { Entity* target = nullptr; std::set grid_list; grid_service->GetAllCellsByXy(this, aabb_pos.x, aabb_pos.y, grid_list); for (auto& grid : grid_list) { for (Entity* entity : grid->entity_list[0]) { switch (entity->entity_type) { case ET_Obstacle: { if (!target) { if (entity->TestCollisionEx(this, aabb_pos, aabb_box)) { target = entity; } } } break; case ET_Building: { if (!target || target->entity_type != ET_Building) { AabbCollider building_aabb_box; entity->GetAabbBox(building_aabb_box); if (building_aabb_box.IntersectEx(aabb_pos, &aabb_box)) { target = entity; } } } break; default: break; } } for (Entity* entity : grid->entity_list[room_idx]) { switch (entity->entity_type) { case ET_Obstacle: { if (!target) { if (entity->TestCollisionEx(this, aabb_pos, aabb_box)) { target = entity; } } } break; case ET_Building: { if (!target || target->entity_type != ET_Building) { AabbCollider building_aabb_box; entity->GetAabbBox(building_aabb_box); if (building_aabb_box.IntersectEx(aabb_pos, &aabb_box)) { target = entity; } } } break; default: break; } } } return target; } void Room::FindLocationWithAabb(Entity* target, const a8::Vec2& aabb_pos, AabbCollider* aabb_box, float& new_x, float& new_y) { a8::Vec2 old_pos = aabb_pos; a8::Vec2 new_pos = aabb_pos; AabbCollider target_collider; target->GetAabbBox(target_collider); { bool ret = aabb_box->CalcSafePointEx(aabb_pos, &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 < 100000; i += 5) { a8::Vec2 pos = old_pos + new_pos_dir * i; new_x = pos.x; new_y = pos.y; bool is_collision = false; std::set colliders; map_service->GetColliders(this, pos.x, pos.y, colliders); for (ColliderComponent* collider : colliders) { if (collider->IntersectEx(pos, aabb_box)) { is_collision = true; break; } } if (!is_collision) { break; } } } void Room::FillSMUiUpdate(cs::SMUiUpdate& msg) { for (auto& pair : car_hash_) { if (!pair.second.taken) { cs::MFCar* car = msg.add_car_list(); car->set_car_id(pair.second.car_id); TypeConvert::ToPb(pair.second.pos, car->mutable_pos()); } } } std::set* Room::GetAliveTeam() { for (auto& pair : team_hash_) { for (Human* hum : pair.second) { if (!hum->real_dead) { return &pair.second; } } } return nullptr; } int Room::NewTeam() { ++current_teamid_; team_hash_[current_teamid_] = std::set(); return current_teamid_; } void Room::TouchPlayerList(a8::XParams param, std::function func) { if (!func) { return; } for (auto& pair : accountid_hash_) { if (pair.second) { func(pair.second, param); } } } void Room::TouchHumanList(a8::XParams param, std::function func) { if (!func) { return; } for (auto& pair : human_hash_) { if (pair.second) { if (!func(pair.second, param)) { break; } } } } void Room::TouchEntityList(a8::XParams param, std::function func) { if (!func) { return; } for (auto& pair : uniid_hash_) { if (pair.second) { if (!func(pair.second, param)) { break; } } } } void Room::UpdateGas() { switch (gas_data.gas_mode) { case GasInactive: UpdateGasInactive(); break; case GasJump: abort(); break; case GasWaiting: UpdateGasWaiting(); break; case GasMoving: UpdateGasMoving(); break; } if (gas_data.gas_mode != GasInactive && gas_data.gas_mode != GasJump) { if (!game_over && alive_count_ <= MAX_TEAM_NUM && GetAliveTeamNum() <= 1) { game_over = true; game_over_frameno = frame_no; OnGameOver(); } for (auto& pair : human_hash_) { if (pair.second->real_dead) { continue; } bool b1 = a8::CircleContainCircle(gas_data.pos_old, gas_data.gas_progress, pair.second->GetPos(), pair.second->GetRadius() ); bool b2 = a8::CircleContainCircle(gas_data.pos_new, gas_data.rad_new, pair.second->GetPos(), pair.second->GetRadius() ); if (!b1 && !b2) { pair.second->poisoning = true; } else { pair.second->poisoning = false; pair.second->poisoning_time = false; } } } } void Room::UpdateGasInactive() { if (frame_no - gas_data.gas_start_frameno >= GetGasInactiveTime() * SERVER_FRAME_RATE) { gas_data.gas_mode = GasWaiting; gas_data.old_area_meta = MetaMgr::Instance()->GetSafeArea(30001); gas_data.new_area_meta = MetaMgr::Instance()->GetSafeArea(30002); gas_data.gas_progress = gas_data.old_area_meta->i->rad(); gas_data.gas_start_frameno = frame_no; gas_data.pos_old = a8::Vec2(map_meta->i->map_width() / 2.0f, map_meta->i->map_height() / 2.0f); gas_data.pos_old_bk = gas_data.pos_old; { bool gen_ok = GenSmallCircle(gas_data.pos_old, gas_data.old_area_meta->i->rad(), gas_data.new_area_meta->i->rad(), gas_data.pos_new); assert(gen_ok); } gas_data.rad_old = gas_data.old_area_meta->i->rad(); gas_data.rad_new = gas_data.new_area_meta->i->rad(); battle_start_frameno_ = frame_no; if (human_hash_.size() < ROOM_MAX_PLAYER_NUM) { CreateAndroid(ROOM_MAX_PLAYER_NUM - human_hash_.size()); NotifyUiUpdate(); } CombineTeam(); NotifyGameStart(); NotifyWxVoip(); if (room_type != RT_NewBrid) { InitAirDrop(); } RoomMgr::Instance()->ActiveRoom(room_uuid); } } void Room::UpdateGasWaiting() { if (frame_no - gas_data.gas_start_frameno >= gas_data.old_area_meta->i->wait_time() * SERVER_FRAME_RATE) { gas_data.gas_mode = GasMoving; gas_data.gas_start_frameno = frame_no;; } } void Room::UpdateGasMoving() { if (frame_no - gas_data.gas_start_frameno > 0 && !gas_data.is_last_gas) { float distance = gas_data.old_area_meta->i->shrink_speed() * ((frame_no - gas_data.gas_start_frameno) * (1.0 / SERVER_FRAME_RATE)); gas_data.gas_progress = std::max(gas_data.rad_old - distance, gas_data.rad_new); if (!(gas_data.pos_new == gas_data.pos_old)) { a8::Vec2 p1 = gas_data.pos_new - gas_data.pos_old_bk; gas_data.pre_pos_old = gas_data.pos_old; if (p1.Norm() - distance <= 0.01f) { gas_data.pos_old = gas_data.pos_new; } else { a8::Vec2 dir = p1; dir.Normalize(); gas_data.pos_old = gas_data.pos_old_bk + dir * distance; } if (gas_data.rad_old - distance <= gas_data.rad_new) { assert(gas_data.pos_new == gas_data.pos_old); } } if (std::abs(gas_data.gas_progress - gas_data.rad_new) <= 0.001f) { int pre_area_id = gas_data.new_area_meta->i->id(); a8::Vec2 pre_pos = gas_data.pos_new; if (!MetaMgr::Instance()->GetSafeArea(pre_area_id + 1)) { gas_data.is_last_gas = true; return; } gas_data.gas_mode = GasWaiting; gas_data.old_area_meta = MetaMgr::Instance()->GetSafeArea(pre_area_id); gas_data.new_area_meta = MetaMgr::Instance()->GetSafeArea(pre_area_id + 1); gas_data.gas_progress = gas_data.old_area_meta->i->rad(); gas_data.gas_start_frameno = frame_no; gas_data.pos_old = pre_pos; gas_data.pos_old_bk = gas_data.pos_old; { bool gen_ok = GenSmallCircle(gas_data.pos_old, gas_data.old_area_meta->i->rad(), gas_data.new_area_meta->i->rad(), gas_data.pos_new); assert(gen_ok); } gas_data.rad_old = gas_data.old_area_meta->i->rad(); gas_data.rad_new = gas_data.new_area_meta->i->rad(); } } } bool Room::GenSmallCircle(a8::Vec2 big_circle_pos, float big_circle_rad, float small_circle_rad, a8::Vec2& out_pos) { if (big_circle_rad <= small_circle_rad) { abort(); } a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); float rad = rand() % (int)(big_circle_rad - small_circle_rad); if (rad <= 0.001f){ rad = 0.001f; } out_pos = big_circle_pos + dir * rad; return true; } void Room::MatchTeam(Human* hum) { for (auto& pair : human_hash_) { if (pair.second != hum) { if (!hum->team_uuid.empty() && pair.second->team_uuid == hum->team_uuid) { if (!pair.second->team_members) { pair.second->team_id = NewTeam(); pair.second->team_members = &team_hash_[pair.second->team_id]; pair.second->team_members->insert(pair.second); } if (pair.second->team_members->size() < MAX_TEAM_NUM) { pair.second->team_members->insert(hum); hum->team_id = pair.second->team_id; hum->team_members = pair.second->team_members; } break; } } } if (hum->team_id == 0) { hum->team_id = NewTeam(); hum->team_members = &team_hash_[hum->team_id]; hum->team_members->insert(hum); } } void Room::CombineTeam() { std::map need_combine_teams; std::map need_combine_teams_copy; int first_team_id = 0; int total_count = 0; for (auto& pair : team_hash_) { for (Human* hum : pair.second) { if (hum->auto_fill && !hum->team_uuid.empty()) { if (first_team_id == 0) { first_team_id = pair.first; } need_combine_teams[pair.first] = pair.second.size(); total_count += pair.second.size(); break; } } } if (total_count <= 1) { return; } int first_team_num = 4; switch (total_count) { case 3: { first_team_num = 2; break; } case 4: { first_team_num = 2; break; } case 5: { first_team_num = 3; break; } default: { first_team_num = 4; break; } } need_combine_teams_copy = need_combine_teams; int try_count = 0; do { for (auto& pair1 : need_combine_teams) { bool combine_ok = false; for (auto& pair2 : need_combine_teams_copy) { int team_id1 = pair1.first; int team_id2 = pair2.first; if (team_id2 == first_team_id) { continue; } if (team_id1 == first_team_id) { if (pair1.second + pair2.second > first_team_num) { continue; } } if (team_id1 != team_id2) { if (pair1.second + pair2.second <= MAX_TEAM_NUM) { int new_team_num = pair1.second + pair2.second; { std::set& team1_members = team_hash_[team_id1]; std::set& team2_members = team_hash_[team_id2]; if (team1_members.size() + team2_members.size() > MAX_TEAM_NUM) { a8::UdpLog::Instance()->Warning("team_member > 4 :%d", { team1_members.size() + team2_members.size() }); } if (pair1.first == first_team_id || pair1.second >= pair2.second) { for (Human* hum : team2_members) { hum->team_id = team_id1; hum->team_members = &team1_members; team1_members.insert(hum); } team_hash_.erase(pair2.first); } else { for (Human* hum : team1_members) { hum->team_id = team_id2; hum->team_members = &team2_members; team2_members.insert(hum); } team_hash_.erase(pair1.first); } } if (first_team_id == team_id1) { if (pair1.second + pair2.second >= first_team_num) { need_combine_teams.erase(team_id1); need_combine_teams_copy.erase(team_id1); } else { pair1.second = new_team_num; if (need_combine_teams_copy.find(pair1.first) != need_combine_teams_copy.end()) { need_combine_teams_copy[pair1.first] = new_team_num; } } need_combine_teams.erase(team_id2); need_combine_teams_copy.erase(team_id2); } else { if (pair1.second >= pair2.second) { if (pair1.second + pair2.second >= MAX_TEAM_NUM) { need_combine_teams.erase(team_id1); need_combine_teams_copy.erase(team_id1); } else { pair1.second = new_team_num; if (need_combine_teams_copy.find(pair1.first) != need_combine_teams_copy.end()) { need_combine_teams_copy[pair1.first] = new_team_num; } } need_combine_teams.erase(team_id2); need_combine_teams_copy.erase(team_id2); } else { if (pair1.second + pair2.second >= MAX_TEAM_NUM) { need_combine_teams.erase(team_id2); need_combine_teams_copy.erase(team_id2); } else { pair2.second = new_team_num; if (need_combine_teams.find(pair2.first) != need_combine_teams.end()) { need_combine_teams[pair2.first] = new_team_num; } } need_combine_teams.erase(team_id1); need_combine_teams_copy.erase(team_id1); } } combine_ok = true; break; } } }//end for if (combine_ok) { break; } } } while (++try_count < 20); } void Room::InitAirDrop() { std::list& air_drops = MetaMgr::Instance()->GetAirDrops(); for (auto& air_drop : air_drops) { xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * air_drop.i->time(), a8::XParams() .SetSender(this) .SetParam1(air_drop.i->appear_time()) .SetParam2(air_drop.i->drop_id()) .SetParam3(air_drop.i->id()), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); if (!room->game_over) { room->AirDrop(param.param1, param.param2); } }, &xtimer_attacher_.timer_list_); } } void Room::AirDrop(int appear_time, int box_id) { MetaData::MapThing* thing_meta = MetaMgr::Instance()->GetMapThing(box_id); if (thing_meta && thing_meta->i->type() == 2) { a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); a8::Vec2 box_pos = gas_data.pos_new + dir * (500 + rand() % 300); if (box_pos.x < 1.0f) { box_pos.x = 1.0f; } if (box_pos.x >= map_meta->i->map_width()) { box_pos.x = map_meta->i->map_width() - 1; } if (box_pos.y < 1.0f) { box_pos.y = 1.0f; } if (box_pos.y >= map_meta->i->map_height()) { box_pos.y = map_meta->i->map_height() - 1; } AdjustAirDropPos(thing_meta, box_pos); AabbCollider air_drop_aabb_box; { air_drop_aabb_box._min.x = 0 - thing_meta->i->width()/2.0f; air_drop_aabb_box._min.y = 0 - thing_meta->i->height()/2.0f; air_drop_aabb_box._max.x = 0 + thing_meta->i->width()/2.0f; air_drop_aabb_box._max.y = 0 + thing_meta->i->height()/2.0f; } Entity* target = FindFirstCollisonEntity(box_pos, air_drop_aabb_box); if (target) { float new_x = box_pos.x; float new_y = box_pos.y; FindLocationWithAabb(target, box_pos, &air_drop_aabb_box, new_x, new_y); box_pos.x = new_x; box_pos.y = new_y; } if (box_pos.x + thing_meta->i->width()/2 + 2 > map_meta->i->map_width() || box_pos.x - thing_meta->i->width()/2 - 2 < 1 || box_pos.y + thing_meta->i->height()/2 + 2 > map_meta->i->map_height() || box_pos.y - thing_meta->i->height()/2 - 2 < 1 ) { return; } frame_event.AddAirDrop(appear_time, box_id, box_pos); xtimer. AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * appear_time / 1000.f, a8::XParams() .SetSender(this) .SetParam1(box_id) .SetParam2(box_pos.x) .SetParam3(box_pos.y), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); if (!room->game_over) { RoomObstacle* obstacle = room-> CreateObstacle(param.param1.GetInt(), param.param2.GetDouble(), param.param3.GetDouble()); obstacle->is_treasure_box = true; } }, &xtimer_attacher_.timer_list_); ++airdrop_times_; } } void Room::AdjustAirDropPos(MetaData::MapThing* thing_meta, a8::Vec2& box_pos) { if (room_type == RT_NewBrid) { if (airdrop_times_ < MetaMgr::Instance()->newbie_airdrop.size()) { for (auto& pair : accountid_hash_) { if (pair.second) { Human* hum = pair.second; a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); box_pos = hum->GetPos() + dir * (80 + rand() % 50); break; } } } } } RoomObstacle* Room::InternalCreateObstacle(int id, float x, float y, std::function on_precreate) { MetaData::MapThing* thing = MetaMgr::Instance()->GetMapThing(id); if (thing) { RoomObstacle* entity = new RoomObstacle(); entity->meta = thing; entity->room = this; entity->entity_uniid = AllocUniid(); entity->SetPos(a8::Vec2(x, y)); entity->Initialize(); if (on_precreate) { on_precreate(entity); } uniid_hash_[entity->entity_uniid] = entity; grid_service->AddRoomEntity(this, entity); return entity; } return nullptr; } void Room::AddObjectLater(RoomEntity* entity) { auto add_func = [] (const a8::XParams& param) { RoomEntity* entity = (RoomEntity*)param.sender.GetUserData(); if (entity->entity_type == ET_Bullet) { MoveableEntity* moveableentity = (MoveableEntity*)entity; entity->room->moveable_hash_[entity->entity_uniid] = moveableentity; } entity->room->uniid_hash_[entity->entity_uniid] = entity; entity->room->later_add_hash_.erase(entity->entity_uniid); }; later_add_hash_[entity->entity_uniid] = entity; xtimer.AddDeadLineTimerAndAttach(0, a8::XParams() .SetSender(entity), add_func, &entity->xtimer_attacher.timer_list_); } void Room::OnGameOver() { for (auto& pair : human_hash_) { if (game_over && game_over_frameno == frame_no) { pair.second->SendGameOver(); } } RoomMgr::Instance()->AddOverRoom(room_uuid); } void Room::RandRemoveAndroid() { Human* hum = nullptr; for (auto& pair : human_hash_) { if (pair.second->entity_subtype == EST_Android) { hum = pair.second; break; } } if (hum) { if (hum->team_id != 0) { team_hash_.erase(hum->team_id); } if (hum->born_point) { --hum->born_point->android_num; } for (auto& cell : hum->grid_list) { for (Human* target : cell->human_list[room_idx]) { target->AddOutObjects(hum); } cell->human_list[room_idx].erase(hum); } moveable_hash_.erase(hum->entity_uniid); uniid_hash_.erase(hum->entity_uniid); human_hash_.erase(hum->entity_uniid); removed_robot_hash_[hum->entity_uniid] = hum; --alive_count_; --App::Instance()->perf.alive_count; for (auto& pair : human_hash_) { pair.second->RemovePartObjects(hum); } } } void Room::NotifyUiUpdate() { xtimer.AddDeadLineTimerAndAttach(0, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->TouchPlayerList(a8::XParams(), [] (Player * hum, a8::XParams & param) { hum->SendUIUpdate(); }); }, &xtimer_attacher_.timer_list_); } void Room::UpdateCarObject(int old_uniid, int new_uniid, a8::Vec2 pos) { auto itr = car_hash_.find(old_uniid); if (itr != car_hash_.end()) { CarObject new_obj = itr->second; new_obj.pos = pos; car_hash_[new_uniid] = new_obj; car_hash_.erase(old_uniid); } else { assert(false); } } void Room::TakeOnCarObject(int car_uniid) { auto itr = car_hash_.find(car_uniid); if (itr != car_hash_.end()) { itr->second.taken = true; } } void Room::TakeOffCarObject(int car_uniid, a8::Vec2 pos) { auto itr = car_hash_.find(car_uniid); if (itr != car_hash_.end()) { itr->second.pos = pos; itr->second.taken = false; } } int Room::CreateAndTakeonCar(int car_id, a8::Vec2 pos) { MetaData::Equip* item_meta = MetaMgr::Instance()->GetEquip(car_id); if (!item_meta) { return -1; } int car_uniid = AllocUniid(); CarObject car; car.car_id = car_id; car.pos = pos; car.taken = true; car_hash_[car_uniid] = car; return car_uniid; } bool Room::HaveMyTeam(const std::string& team_uuid) { if (team_uuid.empty()) { return false; } for (auto& pair : accountid_hash_) { if (pair.second->team_uuid == team_uuid) { return true; } } return false; } void Room::NotifyWxVoip() { xtimer.AddDeadLineTimerAndAttach(0, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->TouchPlayerList(a8::XParams(), [] (Player * hum, a8::XParams & param) { hum->SendWxVoip(); }); }, &xtimer_attacher_.timer_list_); } BornPoint* Room::AllocBornPoint(Human* hum) { BornPoint* born_point = nullptr; if (hum->born_point) { std::vector point_list; std::vector free_point_list; BornPoint* pre_point = nullptr; for (auto& pair : born_point_hash_) { if (&pair.second != hum->born_point) { if (pair.second.player_num + pair.second.android_num < pair.second.thing_tpl->i->param1()) { point_list.push_back(&pair.second); free_point_list.push_back(&pair.second);; } } else { pre_point = &pair.second; } } if (pre_point) { DecBornPointHumanNum(pre_point, hum); } if (!free_point_list.empty()) { born_point = free_point_list[rand() % free_point_list.size()]; } else { born_point = !point_list.empty() ? point_list[rand() % point_list.size()] : nullptr; } } else { for (auto& pair : born_point_hash_) { if (pair.second.player_num + pair.second.android_num < pair.second.thing_tpl->i->param1()) { born_point = &pair.second; break; } } } if (born_point) { IncBornPointHumanNum(born_point, hum); } return born_point; } BornPoint* Room::GetBornPoint(int point_uniid) { auto itr = born_point_hash_.find(point_uniid); return itr != born_point_hash_.end() ? &itr->second : nullptr; } void Room::CreateSpawnPoints() { if (!spawn_points || spawn_points->empty()) { abort(); } for (auto& thing_tpl : *spawn_points) { int uniid = AllocUniid(); BornPoint born_point; born_point.thing_tpl = thing_tpl; born_point_hash_[uniid] = born_point; if (thing_tpl == newbie_born_point_meta) { newbie_born_point_uniid_ = uniid; } } } void Room::CreateLoots() { if (loots) { for (auto& thing_tpl : *loots) { int thing_id = thing_tpl->RandThing(); MetaData::Equip* equip_meta = MetaMgr::Instance()->GetEquip(thing_id); if (equip_meta) { int entity_uniid = CreateLoot( equip_meta->i->id(), a8::Vec2 ( thing_tpl->i->x(), thing_tpl->i->y() ), 1, 1 ); if (entity_uniid && equip_meta->i->equip_type() == EQUIP_TYPE_CAR) { Entity* loot_entity = GetEntityByUniId(entity_uniid); if (loot_entity && loot_entity->entity_type == ET_Loot) { ((Loot*)loot_entity)->bullet_num = equip_meta->i->clip_volume(); ((Loot*)loot_entity)->param1 = MetaMgr::Instance()->max_oil; ((Loot*)loot_entity)->param2 = MetaMgr::Instance()->max_oil; CarObject car; car.car_id = equip_meta->i->id(); car.pos = loot_entity->GetPos(); car_hash_[loot_entity->entity_uniid] = car; } } } } } } void Room::CreateDropObjs() { if (buildings) { for (auto& building : *buildings) { for (auto& obj : building->meta->i->dropobj()) { CreateLoot(obj.id(), a8::Vec2( building->GetX() + obj.x() - building->meta->i->tilewidth() / 2.0, building->GetY() + obj.y() - building->meta->i->tileheight() / 2.0 ), 1, 1 ); } } } } void Room::IncBornPointHumanNum(BornPoint* point, Human* hum) { switch (hum->entity_subtype) { case EST_Player: { ++point->player_num; } break; case EST_Android: { ++point->android_num; } break; default: { } break; } } void Room::DecBornPointHumanNum(BornPoint* point, Human* hum) { switch (hum->entity_subtype) { case EST_Player: { --point->player_num; } break; case EST_Android: { --point->android_num; } break; default: { } break; } } void Room::SecondRandPoint() { for (auto& pair : accountid_hash_) { Human* hum = pair.second; hum->born_point = AllocBornPoint(hum); } for (auto& pair : team_hash_) { Human* target = nullptr; for (Human* hum : pair.second) { if (!target) { target = hum; } else { if (target->born_point) { if (hum->born_point) { DecBornPointHumanNum(hum->born_point, hum); } hum->born_point = target->born_point; if (hum->born_point) { IncBornPointHumanNum(hum->born_point, hum); } } } //end if if (!hum->born_point) { hum->SetX(DEFAULT_BORN_POINT_X + rand() % 100); hum->SetY(DEFAULT_BORN_POINT_Y + rand() % 200); } else { hum->SetPos(hum->born_point->RandPoint()); } if (!a8::HasBitFlag(hum->status, HS_Disable)) { hum->FindLocation(); hum->RefreshView(); grid_service->MoveHuman(hum); } } } if (room_type == RT_NewBrid) { for (auto& pair : accountid_hash_) { Human* hum = pair.second; BornPoint* newbie_point = GetBornPoint(newbie_born_point_uniid_); if (newbie_point && hum->born_point != newbie_point) { if (hum->born_point) { DecBornPointHumanNum(hum->born_point, hum); } hum->born_point = newbie_point; if (hum->born_point) { IncBornPointHumanNum(hum->born_point, hum); hum->SetPos(hum->born_point->RandPoint()); } hum->FindLocation(); hum->RefreshView(); grid_service->MoveHuman(hum); } break; } } } void Room::NotifyGameStart() { if (room_type == RT_NewBrid) { for (auto& pair : human_hash_) { if (pair.second->entity_subtype == EST_Android && !a8::HasBitFlag(pair.second->status, HS_Disable)) { DisableHuman(pair.second); } } } cs::SMGameStart msg; for (auto& pair : accountid_hash_) { pair.second->SendNotifyMsg(msg); if (room_type == RT_NewBrid) { first_newbie = pair.second; } } xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * 1, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->SecondRandPoint(); }, &xtimer_attacher_.timer_list_); waiting_start = true; xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * 2, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->waiting_start = false; }, &xtimer_attacher_.timer_list_); if (room_type == RT_MidBrid) { ShuaAndroidTimerFunc(); DieAndroidTimerFunc(); } } void Room::InitObstacleDatas() { obstacle_datas_.resize(FIXED_OBJECT_MAXID); memset(&obstacle_datas_[0], 0, sizeof(ObstacleData) * FIXED_OBJECT_MAXID); } ObstacleData* Room::GetPermanentObstacleData(int entity_uniid) { if (entity_uniid >= 0 && entity_uniid < (int)obstacle_datas_.size()) { return &obstacle_datas_[entity_uniid]; } else { return nullptr; } } long long Room::GetGasInactiveTime() { if (room_type == RT_NewBrid) { return MetaMgr::Instance()->newbie_gas_inactive_time; } else { return MetaMgr::Instance()->gas_inactive_time; } } void Room::EnableHuman(Human* target) { if (a8::HasBitFlag(target->status, HS_Disable)) { a8::UnSetBitFlag(target->status, HS_Disable); target->enable_frameno = frame_no; moveable_hash_[target->entity_uniid] = target; grid_service->AddHuman(target); target->FindLocation(); target->RefreshView(); if (!target->real_dead) { alive_human_hash_[target->entity_uniid] = target; } } } void Room::DisableHuman(Human* target) { if (!a8::HasBitFlag(target->status, HS_Disable)) { a8::SetBitFlag(target->status, HS_Disable); moveable_hash_.erase(target->entity_uniid); alive_human_hash_.erase(target->entity_uniid); for (auto& cell : target->grid_list) { bool has_target = false; for (Human* hum : cell->human_list[room_idx]) { if (hum == target) { has_target = true; } else { hum->RemoveOutObjects(target); } } if (has_target) { cell->human_list[room_idx].erase(target); } } } } void Room::ShuaNewBieAndroid(Human* target) { #ifdef DEBUG a8::UdpLog::Instance()->Debug("room_idx:%d ShuaNewBieAndroid %s %s", { room_idx, target->account_id, target->name }); #endif for (auto& pair : human_hash_) { if (pair.second->entity_subtype == EST_Android && a8::HasBitFlag(pair.second->status, HS_Disable) && !a8::HasBitFlag(pair.second->status, HS_AlreadyShow) ) { Android* hum = (Android*)pair.second; a8::Vec2 pos = target->GetPos(); pos.x -= MetaMgr::Instance()->newbie_first_robot_distance; if (OverBorder(pos, hum->GetRadius())) { a8::Vec2 pos = target->GetPos(); pos.x += MetaMgr::Instance()->newbie_first_robot_distance; if (OverBorder(pos, hum->GetRadius())) { pos = target->GetPos(); } } hum->SetPos(pos); a8::SetBitFlag(hum->status, HS_NewBieNpc); EnableHuman(hum); break; } } { int shua_time = a8::RandEx( MetaMgr::Instance()->level0room_shua_robot_min_time, MetaMgr::Instance()->level0room_shua_robot_max_time ); xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * (shua_time), a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->ShuaAndroidTimerFunc(); }, &xtimer_attacher_.timer_list_); } { int die_time = a8::RandEx( MetaMgr::Instance()->level0room_die_robot_min_time, MetaMgr::Instance()->level0room_die_robot_max_time ); xtimer.AddDeadLineTimerAndAttach(SERVER_FRAME_RATE * (die_time), a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->DieAndroidTimerFunc(); }, &xtimer_attacher_.timer_list_); } } void Room::ShuaAndroidTimerFunc() { if (room_type == RT_NewBrid || room_type == RT_MidBrid) { int shua_time = a8::RandEx( MetaMgr::Instance()->level0room_shua_robot_min_time, MetaMgr::Instance()->level0room_shua_robot_max_time ); int shua_num = a8::RandEx( MetaMgr::Instance()->level0room_shua_robot_min_num, MetaMgr::Instance()->level0room_shua_robot_max_num ); if (room_type == RT_MidBrid) { shua_time = a8::RandEx( MetaMgr::Instance()->level1room_shua_robot_min_time, MetaMgr::Instance()->level1room_shua_robot_max_time ); shua_num = a8::RandEx( MetaMgr::Instance()->level1room_shua_robot_min_num, MetaMgr::Instance()->level1room_shua_robot_max_num ); } shua_android_timer = xtimer.AddDeadLineTimerAndAttach (SERVER_FRAME_RATE * shua_time, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->ShuaAndroidTimerFunc(); }, &xtimer_attacher_.timer_list_, [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->shua_android_timer = nullptr; } ); if (shua_time > 0 && shua_num > 0) { ProcShuaAndroid(shua_time, shua_num); } } } void Room::DieAndroidTimerFunc() { if (room_type == RT_NewBrid || room_type == RT_MidBrid) { int die_time = a8::RandEx( MetaMgr::Instance()->level0room_die_robot_min_time, MetaMgr::Instance()->level0room_die_robot_max_time ); int die_num = a8::RandEx( MetaMgr::Instance()->level0room_die_robot_min_num, MetaMgr::Instance()->level0room_die_robot_max_num ); if (room_type == RT_MidBrid) { die_time = a8::RandEx( MetaMgr::Instance()->level1room_die_robot_min_time, MetaMgr::Instance()->level1room_die_robot_max_time ); die_num = a8::RandEx( MetaMgr::Instance()->level1room_die_robot_min_num, MetaMgr::Instance()->level1room_die_robot_max_num ); } die_android_timer = xtimer.AddDeadLineTimerAndAttach (SERVER_FRAME_RATE * die_time, a8::XParams() .SetSender(this), [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->DieAndroidTimerFunc(); }, &xtimer_attacher_.timer_list_, [] (const a8::XParams& param) { Room* room = (Room*)param.sender.GetUserData(); room->die_android_timer = nullptr; } ); if (die_time > 0 && die_num > 0) { ProcDieAndroid(die_time, die_num); } } } void Room::ProcShuaAndroid(int shua_time, int shua_num) { Human* target = nullptr; for (auto& pair : accountid_hash_) { if (!pair.second->real_dead) { target = pair.second; break; } } if (!target) { return; } int real_shua_num = shua_num; int autodie_time = 10; int autodie_distance = 500; if (room_type == RT_NewBrid) { real_shua_num = std::max(0, MetaMgr::Instance()->level0room_robot_water - RealAliveCount() + (int)accountid_hash_.size()); autodie_time = MetaMgr::Instance()->level0room_robot_autodie_time; autodie_distance = MetaMgr::Instance()->level0room_robot_autodie_distance; } else if (room_type == RT_MidBrid) { real_shua_num = std::max(0, MetaMgr::Instance()->level1room_robot_water - RealAliveCount() + (int)accountid_hash_.size()); autodie_time = MetaMgr::Instance()->level1room_robot_autodie_time; autodie_distance = MetaMgr::Instance()->level1room_robot_autodie_distance; } if (real_shua_num <= 0) { return; } #ifdef DEBUG a8::UdpLog::Instance()->Debug("ProcShuaAndroid room_idx:%d shua_time:%d shua_num%d real_shua_num:%d", { room_idx, shua_time, shua_num, real_shua_num }); #endif int refreshed_num = 0; for (auto& pair : human_hash_) { if (pair.second->entity_subtype == EST_Android && !pair.second->real_dead && a8::HasBitFlag(pair.second->status, HS_Disable) ) { Android* hum = (Android*)pair.second; a8::Vec2 pos = target->GetPos(); a8::Vec2 dir = a8::Vec2::UP; dir.Rotate(a8::RandAngle()); pos = pos + dir * 400; if (OverBorder(pos, hum->GetRadius())) { pos.x = target->GetPos().x; if (OverBorder(pos, hum->GetRadius())) { break; } } hum->SetPos(pos); #if 0 a8::SetBitFlag(hum->status, HS_NewBieNpc); #endif EnableHuman(hum); xtimer.AddDeadLineTimerAndAttach ( SERVER_FRAME_RATE * autodie_time, a8::XParams() .SetSender(hum) .SetParam1(autodie_time) .SetParam2(autodie_distance) .SetParam3(0), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->room->CheckAutoDie( hum, param.param1, param.param2, param.param3 ); }, &hum->xtimer_attacher.timer_list_); ++refreshed_num; if (refreshed_num >= real_shua_num) { break; } } } } void Room::ProcDieAndroid(int die_time, int die_num) { #ifdef DEBUG a8::UdpLog::Instance()->Debug("ProcDieAndroid room_idx:%d die_time:%d die_num:%d", { room_idx, die_time, die_num }); #endif std::vector alive_humans; alive_humans.reserve(human_hash_.size()); for (auto& pair : human_hash_) { if (pair.second->entity_subtype == EST_Android && !pair.second->real_dead && a8::HasBitFlag(pair.second->status, HS_Disable)) { Android* hum = (Android*)pair.second; alive_humans.push_back(hum); } } int dead_num = 0; int try_count = 0; std::vector alive_humans_copy = alive_humans; do { for (size_t i = 0; i < alive_humans.size(); ++i) { Android* hum = alive_humans[i]; Android* killer = nullptr; for (size_t ii = i + 1; ii < alive_humans_copy.size(); ++ii) { killer = alive_humans_copy[ii]; alive_humans.erase(alive_humans.begin() + ii); alive_humans.erase(alive_humans.begin() + i); alive_humans_copy.erase(alive_humans_copy.begin() + ii); alive_humans_copy.erase(alive_humans_copy.begin() + i); break; } if (killer && (rand() % 100 < 70)) { a8::UnSetBitFlag(hum->status, HS_Disable); hum->BeKill(killer->entity_uniid, killer->name, killer->curr_weapon->weapon_id); } else { a8::UnSetBitFlag(hum->status, HS_Disable); hum->BeKill(VP_SafeArea, "毒圈", VW_SafeArea); if (!alive_humans.empty()) { alive_humans.erase(alive_humans.begin() + i); alive_humans_copy.erase(alive_humans_copy.begin() + i); } } ++dead_num; break; } } while(++try_count < 40 && dead_num < die_num && !alive_humans.empty()); } void Room::CheckAutoDie(Human* target, int autodie_time, int autodie_distance, int check_times) { bool nodie = false; for (auto& grid : target->grid_list) { for (Human* hum: grid->human_list[room_idx]) { if (!hum->real_dead && hum->entity_uniid != target->entity_uniid && hum->IsPlayer()) { if (std::fabs(hum->GetPos().x - target->GetPos().x) <= autodie_distance && std::fabs(hum->GetPos().y - target->GetPos().y) <= autodie_distance) { nodie = true; break; } } } if (nodie) { break; } } if (nodie) { xtimer.AddDeadLineTimerAndAttach ( SERVER_FRAME_RATE * (2 + rand() % 3), a8::XParams() .SetSender(target) .SetParam1(autodie_time) .SetParam2(autodie_distance) .SetParam3(check_times + 1), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->room->CheckAutoDie( hum, param.param1, param.param2, param.param3 ); }, &target->xtimer_attacher.timer_list_); } else { for (auto& pair : human_hash_) { if (pair.second->entity_uniid != target->entity_uniid && pair.second->entity_subtype == EST_Android && !pair.second->real_dead && a8::HasBitFlag(pair.second->status, HS_Disable)) { Android* hum = (Android*)pair.second; { target->BeKill(hum->entity_uniid, hum->name, hum->curr_weapon->weapon_id); } #if 0 hum->SetPos(target->GetPos()); EnableHuman(hum); xtimer.AddDeadLineTimerAndAttach ( SERVER_FRAME_RATE * autodie_time, a8::XParams() .SetSender(hum) .SetParam1(autodie_time) .SetParam2(autodie_distance) .SetParam3(0), [] (const a8::XParams& param) { Human* hum = (Human*)param.sender.GetUserData(); hum->room->CheckAutoDie( hum, param.param1, param.param2, param.param3 ); }, &hum->xtimer_attacher.timer_list_); #endif break; } } } }