#include "precompile.h" #include "incubator.h" #include "room.h" #include "human.h" #include "hero.h" #include "player.h" #include "pbutils.h" #include "glmhelper.h" #include "mapinstance.h" #include "bornpoint.h" #include "movement.h" #include "app.h" #include "mt/Param.h" #include "mt/Map.h" #include "mt/Hero.h" #include "mt/PveGemini.h" #include "mt/PveGeminiMode.h" #include "mt/PveGeminiContent.h" #include "mt/Text.h" #include "mt/SafeArea.h" #include "mt/SafeAreaSafePoint.h" static const int CHIJI_HIDE_HUMANS = 6; void Incubator::Init() { #if 1 wait_alloc_time_ = room->GetGasInactiveTime() + 0 + mt::Param::s().wait_cloud_time; #else wait_alloc_time_ = room->GetGasInactiveTime() + 50 + mt::Param::s().wait_cloud_time; #endif xtimer_attacher_.SetOwner(&room->xtimer); if (!room->IsPveRoom()) { room->xtimer.SetTimeoutEx ( SERVER_FRAME_RATE * (wait_alloc_time_), [this] (int event, const a8::Args* args) { if (a8::TIMER_EXEC_EVENT == event) { alloc_timer_ = room->xtimer.SetIntervalWpEx ( SERVER_FRAME_RATE * (4 + rand() % 3), [this] (int event, const a8::Args* args) { if (a8::TIMER_EXEC_EVENT == event) { AutoAllocAndroid(); } }, &xtimer_attacher_); if (room->IsNewBieBattle()) { room->xtimer.SetTimeoutEx ( SERVER_FRAME_RATE * (50), [this] (int event, const a8::Args* args) { if (a8::TIMER_EXEC_EVENT == event) { StartNewBattleMode(); } }, &xtimer_attacher_); } } }, &xtimer_attacher_); } } void Incubator::InitPve() { if (room->IsPveRoom()) { room->pve_data.max_wave = room->pve_mode_meta->_mode_time.size(); int wave = 0; int total_time = 0; SpawnWaveMon(0); for (int time : room->pve_mode_meta->_mode_time) { total_time += time; auto timer = room->xtimer.SetTimeoutWpEx ( total_time * SERVER_FRAME_RATE, [this, wave] (int event, const a8::Args* args) { if (a8::TIMER_EXEC_EVENT == event) { OnEnterNewWave(wave); } }, &xtimer_attacher_); wave_timers_.push_back(timer); ++wave; } } } void Incubator::UnInit() { } void Incubator::AllocAndroid(Human* target, int num, std::vector* androids) { if (room->IsNewBieBattle() && target->IsAndroid()) { return; } if (!room->xtimer.IsRunning()) { A8_ABORT(); } if (target->room->IsGameOver() || target->room->IsForceOver()) { return; } if (room->GetFrameNo() < wait_alloc_time_ * SERVER_FRAME_RATE) { return; } if (hold_humans_.size() < 1) { return; } if (!room->IsNewBieBattle()) { if (hold_humans_.size() <= CHIJI_HIDE_HUMANS) { if (room->GetGasData().new_area_meta->GetSmallRingCount() < 4) { ShowHand(); } return; } } #ifdef MYDEBUG a8::XPrintf("SmallRingCount:%d \n", {room->GetGasData().new_area_meta->GetSmallRingCount()}); #endif #if 1 num = 1; #endif int try_count = 0; glm::vec3 dir = GlmHelper::UP; while (num > 0 && try_count < 8 && !hold_humans_.empty()) { GlmHelper::RotateY(dir, a8::RandAngle()); int rand_len = rand() % mt::Param::s().incubator_rand_length; Human* hum = hold_humans_.at(0); glm::vec3 center = target->GetPos().ToGlmVec3() + dir * (float)(mt::Param::s().incubator_base_length + rand_len); room->map_instance->Scale(center); glm::vec3 point; if (room->map_instance->FindRandomPointAroundCircle ( center, 10 * room->GetMapMeta()->scale(), point )) { room->map_instance->UnScale(point); App::Instance()->verify_set_pos = 1; hum->GetMutablePos().FromGlmVec3(point); App::Instance()->verify_set_pos = 0; if (!CanSee(hum, target)) { room->EnableHuman(hum); #ifdef MYDEBUG #if 0 if (!target->InNewObjects(hum)) { A8_ABORT(); } #endif if (hum->dead) { //A8_ABORT(); } #endif hum->MustBeAddBuff(hum, kTraceBuffId); hold_humans_.erase(hold_humans_.begin()); if (androids) { androids->push_back(hum); } --num; #ifdef MYDEBUG room->BroadcastDebugMsg(a8::Format("投放机器人 %d:%s pos:%d,%d pos1:%d,%d %d num:%d", {hum->GetUniId(), hum->name, hum->GetPos().GetX(), hum->GetPos().GetY(), target->GetPos().GetX(), target->GetPos().GetY(), hum->GetPos().Distance2D2(target->GetPos()), hold_humans_.size()})); #endif } } ++try_count; } #ifdef MYDEBUG if (num > 0) { room->BroadcastDebugMsg(a8::Format("投放机器人 分配失败 %d", {hold_humans_.size()})); } #endif } void Incubator::RecycleAndroid(Human* hum) { if (room->IsMiniMap() || room->IsNewerMap()) { return; } Human* nearest_hum = nullptr; Human* target = hum; float distance = 10000000; room->TraverseAlivePlayers ( [&nearest_hum, target, &distance] (Human* hum) -> bool { float tmp_distance = hum->GetPos().ManhattanDistance2D(target->GetPos()); if (tmp_distance < distance) { nearest_hum = hum; distance = tmp_distance; } return true; }); if (hum->dead) { #ifdef MYDEBUG room->BroadcastDebugMsg(a8::Format("回收机器人 %d:%s 角色已死亡", {hum->GetUniId(), hum->name})); #endif hum->RemoveBuffByEffectId(kBET_BeRecycle); return; } if (distance < mt::Param::s().incubator_canset_distance) { #ifdef MYDEBUG room->BroadcastDebugMsg(a8::Format("回收机器人 %d:%s 距离太近", {hum->GetUniId(), hum->name})); #endif hum->RemoveBuffByEffectId(kBET_BeRecycle); return; } if (distance > mt::Param::s().incubator_canset_distance + 100) { hum->RemoveBuffByEffectId(kBET_BeRecycle); hold_humans_.push_back(hum); room->DisableHuman(hum); Rearrangement(); #ifdef MYDEBUG room->BroadcastDebugMsg(a8::Format("回收机器人 %d:%s:%d 添加到回收列表", {hum->GetUniId(), hum->name, hold_humans_.size() })); #endif return; } } bool Incubator::CanSee(Human* hum, Human* exclude_hum) { Human* target = hum; bool can_see = false; room->TraverseAlivePlayers ( [target, exclude_hum, &can_see] (Human* hum) -> bool { #if 1 { #else if (hum != exclude_hum) { #endif if (target->GetPos().ManhattanDistance2D(hum->GetPos()) < mt::Param::s().incubator_canset_distance) { can_see = true; return false; } } return true; }); return can_see; } void Incubator::AutoAllocAndroid() { #ifdef MYDEBUG { a8::XPrintf("AutoAllocAndroid hold_humans.size:%d\n", {hold_humans_.size()}); } #endif switch (room->GetGasData().GetGasMode()) { case GasWaiting: case GasMoving: { if (!hold_humans_.empty() && hold_humans_.size() > 0) { int rnd_space = 70; if (room->IsNewBieBattle()) { rnd_space = 0; } Human* hum = hold_humans_[0]; if (room->GetGasData().GetGasMode() == GasWaiting && hold_humans_.size() > 1 && ((rand() % 100) > rnd_space)) { Human* killer = nullptr; if (hold_humans_.size() == 2) { killer = hold_humans_[1]; } else { killer = hold_humans_[1 + (rand() % (hold_humans_.size() - 1))]; } hum->BeKill(killer->GetUniId(), killer->name, killer->GetCurrWeapon()->weapon_id, killer->GetUniId(), killer->name); } else if (room->GetGasData().gas_count > 2) { hum->BeKill(VP_Gas, TEXT("battle_server_killer_gas", "毒圈"), VW_Gas, VP_Gas, TEXT("battle_server_killer_gas", "毒圈")); } else { return; } hold_humans_.erase(hold_humans_.begin()); if (!room->IsNewBieBattle()) { room->xtimer.ModifyTime(alloc_timer_, SERVER_FRAME_RATE * (30 + rand() % 5)); } } } break; default: { } break; } } Human* Incubator::ActiveAndroid(Human* hum) { Human* target = nullptr; std::vector androids; AllocAndroid(hum, 1, &androids); if (!androids.empty()) { target = androids[0]; } else if (!hold_humans_.empty()) { target = hold_humans_[0]; glm::vec3 center = glm::vec3(room->GetGasData().pos_new.x, 0.0f, room->GetGasData().pos_new.y); glm::vec3 out_pt; if (room->map_instance->GetNearestGrass(center, out_pt)) { App::Instance()->verify_set_pos = 1; target->GetMutablePos().FromGlmVec3(out_pt); App::Instance()->verify_set_pos = 0; } else { App::Instance()->verify_set_pos = 1; target->GetMutablePos().FromGlmVec3(target->GetBornPoint()->GetSrcPoint(room).ToGlmVec3()); App::Instance()->verify_set_pos = 0; } room->EnableHuman(target); hold_humans_.erase(hold_humans_.begin()); } #ifdef MYDEBUG if (target) { room->BroadcastDebugMsg(a8::Format("active android id:%d pos:%f,%f,%f", { target->GetUniId(), target->GetPos().GetX(), target->GetPos().GetY(), target->GetPos().GetZ(), })); } #endif return target; } void Incubator::OnEnterNewWave(int wave) { #ifdef MYDEBUG a8::XPrintf("OnEnterNewWave2 wave:%d \n", {wave + 1}); #endif if (room->IsGameOver()) { return; } if (timeout_) { return; } if ( room->pve_data.refreshed_mon > 0 && room->pve_data.mon_num > 0 ) { timeout_ = true; return; } room->pve_data.SetWave(wave + 1); room->OnEnterNewWave(wave + 1); if (wave < 0) { abort(); } if (wave >= room->pve_mode_meta->_waves.size()) { abort(); } if (wave + 1 < room->pve_mode_meta->_waves.size()) { SpawnWaveMon(wave + 1); } } void Incubator::SpawnWaveMon(int wave) { #ifdef MYDEBUG a8::XPrintf("SpawnWaveMon wave:%d \n", {wave}); #endif auto& mons = room->pve_mode_meta->_waves[wave]; for (const mt::PveGeminiContent* content : mons) { for (int i = 0; i < content->_enemys.size(); ++i) { int monIdx = i; room->xtimer.SetTimeoutEx ( i == 0 ? 0 : (1000 + room->pve_mode_meta->wave_prepare_time() * 1000) / FRAME_RATE_MS, [this, content, wave, monIdx] (int event, const a8::Args* args) { if (a8::TIMER_EXEC_EVENT == event) { #ifdef MYDEBUG a8::XPrintf("wave i:%d enemys_size:%d\n", {wave, content->_enemys.size()}); #endif if (monIdx < content->_enemys.size()) { int enemy_id = content->_enemys[monIdx]; const mt::Hero* hero_meta = mt::Hero::GetById(enemy_id); if (hero_meta) { Position hero_pos; hero_pos.FromGlmVec3(content->_spawn_point); #if 0 hero_pos.SetX(5801.422); hero_pos.SetY(6.000006); hero_pos.SetZ(5607.3); #endif int team_id = 666; Creature* master = nullptr; glm::vec3 dir = GlmHelper::UP; Hero* hero = room->CreateHero(master, hero_meta, hero_pos.ToGlmVec3(), dir, team_id); if (!hero) { A8_ABORT(); } #ifdef MYDEBUG1 { room->xtimer.SetTimeoutEx ( SERVER_FRAME_RATE * (rand() % 3), [hero] (int event, const a8::Args args) { if (a8::TIMER_EXEC_EVENT == event) { Human* hum = hero->room->GetOneAlivePlayer(); if (hum) { hum->room->pve_data.AddDamageInfo(hum->GetUniId(), hero->GetUniId(), 1); hero->BeKill(hum->GetUniId(), hum->name, hum->GetCurrWeapon()->weapon_id); } else { hero->BeKill(VP_Gas, TEXT("battle_server_killer_gas", "毒圈"), VW_Gas); } } }, &hero->xtimer_attacher.timer_list_); } #endif if (wave + 1 == room->pve_mode_meta->_waves.size()) { hero->is_pve_boss = true; room->pve_data.boss_state = 1; #ifdef DEBGU a8::XPrintf("pve_boss appear\n", {}); #endif } ++room->pve_data.mon_num; room->NotifyUiUpdate(); } } } }, &xtimer_attacher_); } room->pve_data.refreshed_mon += content->_enemys.size(); } } int Incubator::GetPveLeftTime() { if (room->pve_data.GetWave() >= wave_timers_.size() || room->pve_data.GetWave() < 0) { return 0; } auto timer = wave_timers_[room->pve_data.GetWave()]; if (timer.expired()) { return 0; } int remain_time = room->xtimer.GetRemainTime(timer); return remain_time * FRAME_RATE_MS; } void Incubator::NextWave() { if (room->pve_data.GetWave() < wave_timers_.size()) { #ifdef MYDEBUG a8::XPrintf("NextWave wait_time:%d\n", {room->pve_mode_meta->wave_prepare_time()}); #endif int acc_time = 0; { auto timer = wave_timers_[room->pve_data.GetWave()]; if (!timer.expired()) { int remain_time = room->xtimer.GetRemainTime(timer); room->xtimer.ModifyTime (timer, room->pve_mode_meta->wave_prepare_time() * SERVER_FRAME_RATE); acc_time = remain_time; } } acc_time -= room->pve_mode_meta->wave_prepare_time() * SERVER_FRAME_RATE; for (int i = room->pve_data.GetWave() + 1; i < wave_timers_.size(); ++i) { auto timer = wave_timers_[i]; int remain_time = room->xtimer.GetRemainTime(timer); room->xtimer.ModifyTime(timer, remain_time - acc_time); } #if 1 room->TraversePlayerList ( [this] (Player* hum) -> bool { int next_wave = hum->room->pve_data.GetWave() + 1 + 1; int max_wave = room->pve_data.max_wave; next_wave = std::min(next_wave, max_wave); int wait_time = hum->room->pve_mode_meta->wave_prepare_time(); PBUtils::Human_SendSMPvePassWave(hum, next_wave, max_wave, wait_time); return true; }); #endif } } void Incubator::ShowHand() { #ifdef MYDEBUG if (hold_humans_.empty()) { return; } a8::XPrintf("ShowHand hold_humans_.size:%d\n", {hold_humans_.size()}); #endif for (auto& hum : hold_humans_) { glm::vec3 point = glm::vec3(room->GetGasData().new_area_meta->GetLastArea()->x1(), 6.0f, room->GetGasData().new_area_meta->GetLastArea()->y1()); { glm::vec3* safe_point = mt::SafeAreaSafePoint::RandPoint(room->GetGasData().new_area_meta->type()); if (safe_point) { point = *safe_point; } } App::Instance()->verify_set_pos = 1; hum->GetMutablePos().FromGlmVec3(point); App::Instance()->verify_set_pos = 0; room->EnableHuman(hum); hum->MustBeAddBuff(hum, kTraceBuffId); a8::SetBitFlag(hum->status, CS_DisableAttackAndroid); } room->xtimer.SetIntervalEx ( SERVER_FRAME_RATE * 40, [room = room] (int event, const a8::Args* args) mutable { if (a8::TIMER_EXEC_EVENT == event) { if (!room->IsGameOver() && room->GetAlivePlayerCount() <= 0) { room->TraverseHumanList ( [] (Human* hum) { a8::UnSetBitFlag(hum->status, CS_DisableAttackAndroid); return true; }); room->xtimer.DeleteCurrentTimer(); } } }, &room->xtimer_attacher_); hold_humans_.clear(); } void Incubator::Clear(int save_num) { while (hold_humans_.size() > save_num) { Human* hum = hold_humans_.at(0); hum->BeKill(VP_Gas, TEXT("battle_server_killer_gas", "毒圈"), VW_Gas, VP_Gas, TEXT("battle_server_killer_gas", "毒圈")); hold_humans_.erase(hold_humans_.begin()); } } void Incubator::Rearrangement() { if (hold_humans_.size() < CHIJI_HIDE_HUMANS) { return; } std::vector teams2; std::vector teams3; std::vector teams4; { std::map team_num_hash; int i = 0; for (auto hum : hold_humans_) { if (team_num_hash.find(hum->team_id) != team_num_hash.end()) { ++team_num_hash[hum->team_id]; } else { team_num_hash[hum->team_id] = 1; } hum->sort_id = i; ++i; } for (auto& pair : team_num_hash) { if (pair.second > 1) { teams2.push_back(pair.first); } if (pair.second > 2) { teams3.push_back(pair.first); } if (pair.second > 3) { teams4.push_back(pair.first); } } } int team_id = 0; if (!teams3.empty() || !teams4.empty()) { if (a8::RandEx(1, 99) < 10 && !teams4.empty()) { team_id = teams4.at(rand() % teams4.size()); } else { if (!teams3.empty()) { team_id = teams3.at(rand() % teams3.size()); } else if (!teams4.empty()) { team_id = teams4.at(rand() % teams4.size()); } } } else if (!teams2.empty()) { team_id = teams2.at(rand() % teams2.size()); } if (team_id) { for (auto hum : hold_humans_) { if (hum->team_id == team_id) { hum->sort_id = 999999; } } std::sort(hold_humans_.begin(), hold_humans_.end(), [] (Human* a, Human* b) -> bool { return a->sort_id < b->sort_id; }); } #ifdef MYDEBUG { std::string data = "Rearrangement team_id:" + a8::XValue(team_id).GetString() + " "; for (auto hum : hold_humans_) { data += a8::XValue(hum->team_id).GetString() + ","; } a8::XPrintf("%s !%d %d %d\n", {data, teams2.size(), teams3.size(), teams4.size()}); } #endif } void Incubator::StartNewBattleMode() { Player* selected_player = nullptr; room->TraversePlayerList ( [&selected_player] (Player* hum) -> bool { selected_player = hum; return false; }); if (!selected_player) { return; } room->xtimer.SetIntervalWpEx ( SERVER_FRAME_RATE * (3 + rand() % 2), [this, selected_player] (int event, const a8::Args* args) { if (a8::TIMER_EXEC_EVENT == event) { if (room->IsGameOver()) { return; } room->TraverseAliveHumanList ( [selected_player] (Human* hum) -> bool { //selected_player = hum; return true; }); } }, &xtimer_attacher_); }