game2006/server/gameserver/incubator.cc
2023-11-06 10:58:36 +00:00

631 lines
22 KiB
C++

#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 "killmgr.h"
#include "movement.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"
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_);
}
},
&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<Human*>* androids)
{
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 (hold_humans_.size() <= 6) {
if (room->GetGasData().new_area_meta->GetSmallRingCount() < 4) {
ShowHand();
}
return;
}
#ifdef DEBUG
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);
Global::Instance()->verify_set_pos = 1;
hum->GetMutablePos().FromGlmVec3(point);
Global::Instance()->verify_set_pos = 0;
if (!CanSee(hum, target)) {
room->EnableHuman(hum);
#ifdef DEBUG
#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 DEBUG
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 DEBUG
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 DEBUG
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 DEBUG
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 DEBUG
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()
{
switch (room->GetGasData().GetGasMode()) {
case GasWaiting:
case GasMoving:
{
if (!hold_humans_.empty() && hold_humans_.size() > 6) {
Human* hum = hold_humans_[0];
if (room->GetGasData().GetGasMode() == GasMoving &&
hold_humans_.size() > 1 &&
((rand() % 100) > 70)) {
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 {
KillMgr::Instance()->UnSetAutoDie();
return;
}
KillMgr::Instance()->UnSetAutoDie();
hold_humans_.erase(hold_humans_.begin());
room->xtimer.ModifyTime(alloc_timer_, SERVER_FRAME_RATE * (30 + rand() % 5));
}
}
break;
default:
{
}
break;
}
}
Human* Incubator::ActiveAndroid(Human* hum)
{
Human* target = nullptr;
std::vector<Human*> 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)) {
Global::Instance()->verify_set_pos = 1;
target->GetMutablePos().FromGlmVec3(out_pt);
Global::Instance()->verify_set_pos = 0;
} else {
Global::Instance()->verify_set_pos = 1;
target->GetMutablePos().FromGlmVec3(target->born_point->GetSrcPoint(room).ToGlmVec3());
Global::Instance()->verify_set_pos = 0;
}
room->EnableHuman(target);
hold_humans_.erase(hold_humans_.begin());
}
#ifdef DEBUG
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 DEBUG
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 DEBUG
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 DEBUG
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 DEBUG1
{
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 DEBUG
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)
{
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 DEBUG
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;
}
}
Global::Instance()->verify_set_pos = 1;
hum->GetMutablePos().FromGlmVec3(point);
Global::Instance()->verify_set_pos = 0;
room->EnableHuman(hum);
hum->MustBeAddBuff(hum, kTraceBuffId);
a8::SetBitFlag(hum->status, CS_CrazeMode);
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() < 6) {
return;
}
std::vector<int> teams2;
std::vector<int> teams3;
std::vector<int> teams4;
{
std::map<int, int> 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 DEBUG
{
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
}