Siege des Capitales E8 : dirigeant comme objectif, et correction du recrutement

E8 - dirigeant de la capitale. Au declenchement le module le retrouve par son
creature.guid (nouvelles cles siege_*_boss_spawn), force le chargement de sa
grille si aucun joueur n est present, retire les drapeaux bloquants reellement
presents, applique niveau et points de vie, et le designe comme objectif du
commandant. Sa mort est detectee a chaque tick et vaut victoire immediate. En
fin d evenement tout est restaure : drapeaux exactement ceux qui avaient ete
retires, niveau, points de vie, faction, et sortie de combat. Rien n est ecrit
en base, un arret du worldserver laisse donc la creature intacte.

Le reglage du dirigeant est absolu et par ville (siege_*_boss_level,
siege_*_boss_health, siege_*_boss_faction, 0 = ne pas toucher) et non plus un
multiplicateur global : les deux chefs n ont rien de comparable, Anduin depasse
le milliard de points de vie quand Vol jin, reste au niveau 83, en a quelques
milliers. Un multiplicateur de 50 sur Anduin donnait 69 milliards.

AcceptCommand() ne pilote que le deplacement : une cible d attaque prioritaire
(SetSiegeTarget) a ete ajoutee, sinon les bots atteignaient le trone et
passaient leur temps sur la garde rapprochee.

Correction du recrutement, defaut signale en jeu : des bots Alliance se
retrouvaient enroles dans l assaut sur Hurlevent. La cause est dans le core,
PlayerBotBaseInfo::GetRandomCharacterByFuction() se termine par
 return characters.begin()->second  et renvoie donc le premier personnage du
compte, faction ignoree, quand il n en trouve aucun de la faction demandee. Le
module choisit desormais lui-meme le personnage et le connecte par
BGSType_Online_GUID, avec verification de la faction apres connexion et
garde-fou dans CommandSiege::AddBot. Le quota est rendu sur tout refus, sinon
la horde arrivait incomplete. Verifie : 15 bots recrutes, 15 horde, 15 au
niveau 110.

.siege status affiche l etat du dirigeant en direct.

Reste ouvert : aucun assaut n a encore entame les points de vie d Anduin. La
horde entierement horde se fait laminer par la garde avant le trone, la ou les
essais precedents progressaient plus loin uniquement parce que des bots
Alliance traversaient la ville sans etre inquietes. A retester avec l effectif
complet de 50.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
SylvaniaCore deploy
2026-08-15 13:01:06 +02:00
parent 6349ea7c21
commit 7971b57cb4
7 changed files with 364 additions and 34 deletions
+17 -1
View File
@@ -44,6 +44,7 @@
#include "CellImpl.h"
#include "GridNotifiersImpl.h"
#include "CapitalSiegeMgr.h"
#include "ObjectAccessor.h"
//#define THREAD_PATHFINDING
#ifdef _DEBUG
@@ -138,7 +139,8 @@ BotBGAI::BotBGAI(Player* player) :
m_InitRndPos(),
m_lastClearCtrlTick(0),
m_SiegeMode(false),
m_SiegeAdvanceUntil(0)
m_SiegeAdvanceUntil(0),
m_SiegeTarget(ObjectGuid::Empty)
{
m_FilterCreatureEntrys.clear();
m_FilterCreatureEntrys.insert(14848);
@@ -1759,6 +1761,19 @@ void BotBGAI::UpdateBotAI(uint32 diff)
m_SiegeAdvanceUntil = 0;
}
// Module Siege des Capitales : le dirigeant prime sur tout le reste des
// qu il est a portee. Sans cette priorite les bots atteignent le trone
// mais passent leur temps sur sa garde rapprochee.
if (m_SiegeMode && !m_SiegeTarget.IsEmpty())
{
if (Unit* siegeBoss = ObjectAccessor::GetUnit(*me, m_SiegeTarget))
{
if (siegeBoss->IsAlive() && me->IsValidAttackTarget(siegeBoss)
&& me->GetDistance(siegeBoss) < searchRange * 2.0f)
me->SetSelection(m_SiegeTarget);
}
}
Unit* pSelect = GetBotAIValidSelectedUnit();
if (pSelect && TargetIsStealth(pSelect->ToPlayer()))
{
@@ -1859,6 +1874,7 @@ void BotBGAI::SetSiegeMode(bool enable)
{
m_SiegeMode = enable;
m_SiegeAdvanceUntil = 0;
m_SiegeTarget = ObjectGuid::Empty;
if (enable)
m_AIBGStateType = BotAIBGState::AIBGState_Start;
}
+4
View File
@@ -75,6 +75,9 @@ public:
// Fenetre de marche forcee : pendant cette duree le bot ignore les
// cibles et avance vers son objectif.
void PushSiegeAdvance(uint32 durationMs);
// Cible prioritaire du siege : le dirigeant. AcceptCommand() ne pilote que
// le deplacement, la cible d attaque reste choisie par SearchEnemy().
void SetSiegeTarget(ObjectGuid guid) { m_SiegeTarget = guid; }
void PushFinishQueue(PathParameter* pathParam);
bool CanUseBGObject();
NearObjectList SearchGameObject(float range);
@@ -223,6 +226,7 @@ protected:
uint32 m_lastClearCtrlTick;
bool m_SiegeMode;
uint32 m_SiegeAdvanceUntil;
ObjectGuid m_SiegeTarget;
};
#endif // !_BOT_AI_H_
+255 -12
View File
@@ -20,6 +20,9 @@
#include "Config.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "Creature.h"
#include "Map.h"
#include "MapManager.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "PlayerBotMgr.h"
@@ -42,24 +45,29 @@ namespace
// Au-dela, une recrue qui n a pas rejoint la horde est abandonnee : compte
// bloque en chargement, personnage d une classe non geree, teleport perdu.
constexpr uint32 SIEGE_RECRUIT_TIMEOUT_SECONDS = 90;
// Intervalle entre deux tentatives d eveil du dirigeant.
constexpr uint32 SIEGE_BOSS_RETRY_MS = 5 * IN_MILLISECONDS;
}
CapitalSiegeMgr::CapitalSiegeMgr() :
m_enabled(false), m_hourMin(18), m_hourMax(24), m_botCount(50), m_botLevel(110),
m_duration(HOUR), m_spawnRate(5), m_pvpMode(1), m_engageRange(18.0f), m_stallTimeout(20), m_advanceWindow(12),
m_bossLevel(112), m_bossHealthMult(50),
m_maxDiff(400), m_announce(true),
m_lastEventDay(0), m_lastAttackerTeam(TEAM_NEUTRAL), m_scheduledDay(0),
m_scheduledTime(0), m_scheduledTeam(TEAM_NEUTRAL),
m_status(SIEGE_STATUS_IDLE), m_attackerTeam(TEAM_NEUTRAL), m_target(nullptr),
m_commander(nullptr), m_totalRecruited(0),
m_commander(nullptr),
m_bossGuid(ObjectGuid::Empty), m_bossClearedFlags(0), m_bossOriginalLevel(0), m_bossOriginalFaction(0),
m_bossOriginalMaxHealth(0), m_bossAwakened(false), m_bossLookupFailed(false),
m_bossRetryTimer(0), m_totalRecruited(0),
m_elapsed(0), m_updateTimer(0), m_overloadTimer(0),
m_botsSpawned(0), m_botsLost(0), m_startTime(0)
{
// Les valeurs sont ecrasees par LoadConfig(). Elles ne servent que si le
// fichier de configuration est muet sur une cle.
m_targets[TEAM_ALLIANCE] = { 1, 42283, Position(), Position(), 2000, 2099, "Orgrimmar" };
m_targets[TEAM_HORDE] = { 0, 107574, Position(), Position(), 1000, 1099, "Hurlevent" };
m_targets[TEAM_ALLIANCE] = { 1, 42283, Position(), Position(), 2000, 2099, 110864, 110, 0, 0, "Orgrimmar" };
m_targets[TEAM_HORDE] = { 0, 107574, Position(), Position(), 1000, 1099, 20555650, 0, 0, 0, "Hurlevent" };
}
CapitalSiegeMgr::~CapitalSiegeMgr() { }
@@ -111,8 +119,6 @@ void CapitalSiegeMgr::LoadConfig()
m_engageRange = sConfigMgr->GetFloatDefault("siege_engage_range", 18.0f);
m_stallTimeout = sConfigMgr->GetIntDefault("siege_stall_timeout", 20);
m_advanceWindow = sConfigMgr->GetIntDefault("siege_advance_window", 12);
m_bossLevel = sConfigMgr->GetIntDefault("siege_boss_level", 112);
m_bossHealthMult = sConfigMgr->GetIntDefault("siege_boss_hp_mult", 50);
m_maxDiff = sConfigMgr->GetIntDefault("siege_maxdiff", 400);
m_announce = sConfigMgr->GetBoolDefault("siege_announce", true);
@@ -161,6 +167,10 @@ void CapitalSiegeMgr::LoadConfig()
sConfigMgr->GetFloatDefault("siege_horde_staging_z", 16.0f));
m_targets[TEAM_ALLIANCE].routeFirst = sConfigMgr->GetIntDefault("siege_horde_route_first", 2000);
m_targets[TEAM_ALLIANCE].routeLast = sConfigMgr->GetIntDefault("siege_horde_route_last", 2099);
m_targets[TEAM_ALLIANCE].bossSpawnId = sConfigMgr->GetIntDefault("siege_horde_boss_spawn", 110864);
m_targets[TEAM_ALLIANCE].bossLevel = sConfigMgr->GetIntDefault("siege_horde_boss_level", 110);
m_targets[TEAM_ALLIANCE].bossHealth = uint64(sConfigMgr->GetIntDefault("siege_horde_boss_health", 0));
m_targets[TEAM_ALLIANCE].bossFaction = sConfigMgr->GetIntDefault("siege_horde_boss_faction", 0);
m_targets[TEAM_ALLIANCE].cityName = "Orgrimmar";
// Cible assaillie par la Horde : la capitale de l Alliance.
@@ -176,6 +186,10 @@ void CapitalSiegeMgr::LoadConfig()
sConfigMgr->GetFloatDefault("siege_alliance_staging_z", 99.5f));
m_targets[TEAM_HORDE].routeFirst = sConfigMgr->GetIntDefault("siege_alliance_route_first", 1000);
m_targets[TEAM_HORDE].routeLast = sConfigMgr->GetIntDefault("siege_alliance_route_last", 1099);
m_targets[TEAM_HORDE].bossSpawnId = sConfigMgr->GetIntDefault("siege_alliance_boss_spawn", 20555650);
m_targets[TEAM_HORDE].bossLevel = sConfigMgr->GetIntDefault("siege_alliance_boss_level", 0);
m_targets[TEAM_HORDE].bossHealth = uint64(sConfigMgr->GetIntDefault("siege_alliance_boss_health", 0));
m_targets[TEAM_HORDE].bossFaction = sConfigMgr->GetIntDefault("siege_alliance_boss_faction", 0);
m_targets[TEAM_HORDE].cityName = "Hurlevent";
TC_LOG_INFO("server.loading", "Siege des Capitales: %s (plage %02uh-%02uh, %u bots niveau %u, duree max %u min).",
@@ -333,6 +347,8 @@ bool CapitalSiegeMgr::StartSiege(TeamId attacker, std::string const& trigger)
m_recruits.clear();
m_engagedAccounts.clear();
m_totalRecruited = 0;
m_bossLookupFailed = false;
m_bossRetryTimer = 0;
m_attackerTeam = attacker;
m_target = target;
@@ -351,6 +367,8 @@ bool CapitalSiegeMgr::StartSiege(TeamId attacker, std::string const& trigger)
SaveState();
OpenHistoryRow(trigger);
AwakenBoss();
TC_LOG_INFO("server.worldserver", "Siege des Capitales: debut de l assaut %s sur %s (declencheur: %s).",
GetTeamName(attacker), m_target->cityName, trigger.c_str());
@@ -383,7 +401,8 @@ void CapitalSiegeMgr::StopSiege(CapitalSiegeOutcome outcome)
delete m_commander;
m_commander = nullptr;
// E8 : restauration des drapeaux du dirigeant et purge des aggro de ville.
// Le dirigeant retrouve ses drapeaux, son niveau et ses points de vie.
RestoreBoss();
CloseHistoryRow(outcome);
@@ -424,7 +443,171 @@ void CapitalSiegeMgr::UpdateRunningSiege(uint32 diff)
m_botsSpawned = m_commander->GetBotCount();
m_botsLost = m_commander->GetLostCount();
// E8 : detection de la mort du dirigeant, condition de victoire immediate.
// Le dirigeant peut n avoir pas ete trouve au declenchement (grille non
// chargee, respawn en cours). On retente tant qu il manque a l appel.
if (!m_bossAwakened)
{
m_bossRetryTimer += diff;
if (m_bossRetryTimer >= SIEGE_BOSS_RETRY_MS)
{
m_bossRetryTimer = 0;
AwakenBoss();
}
}
// Victoire immediate : le dirigeant est tombe.
if (IsBossDead())
{
StopSiege(SIEGE_OUTCOME_VICTORY);
return;
}
}
/*******************************************************************************
* Dirigeant de la capitale
******************************************************************************/
Creature* CapitalSiegeMgr::FindBoss() const
{
if (!m_target || !m_target->bossSpawnId)
return nullptr;
Map* map = sMapMgr->FindMap(m_target->mapId, 0);
if (!map)
return nullptr;
// Sans joueur dans la salle du trone la grille n est pas chargee et la
// creature n existe pas encore en memoire. On la fait charger.
if (!map->IsGridLoaded(m_target->bossPos.GetPositionX(), m_target->bossPos.GetPositionY()))
map->LoadGrid(m_target->bossPos.GetPositionX(), m_target->bossPos.GetPositionY());
auto bounds = map->GetCreatureBySpawnIdStore().equal_range(m_target->bossSpawnId);
if (bounds.first == bounds.second)
return nullptr;
return bounds.first->second;
}
void CapitalSiegeMgr::AwakenBoss()
{
m_bossAwakened = false;
m_bossGuid = ObjectGuid::Empty;
m_bossClearedFlags = 0;
m_bossOriginalLevel = 0;
m_bossOriginalFaction = 0;
m_bossOriginalMaxHealth = 0;
Creature* boss = FindBoss();
if (!boss)
{
if (!m_bossLookupFailed)
{
m_bossLookupFailed = true;
TC_LOG_ERROR("server.worldserver", "Siege des Capitales: dirigeant introuvable (spawn %u sur la carte %u), nouvelle tentative dans %u s.",
m_target->bossSpawnId, m_target->mapId, SIEGE_BOSS_RETRY_MS / IN_MILLISECONDS);
}
return;
}
// Tout ce qui suit est applique en memoire uniquement. Rien n est ecrit en
// base : si le worldserver s arrete en plein siege, la creature est
// rechargee telle quelle depuis creature_template au redemarrage.
uint32 const blockingFlags = UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC
| UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED;
// On ne retire que les drapeaux reellement presents, et on ne restaurera
// que ceux-la : le module n a pas a supposer la configuration de la base.
m_bossClearedFlags = boss->GetUInt32Value(UNIT_FIELD_FLAGS) & blockingFlags;
if (m_bossClearedFlags)
boss->RemoveFlag(UNIT_FIELD_FLAGS, m_bossClearedFlags);
m_bossOriginalLevel = boss->getLevel();
m_bossOriginalMaxHealth = boss->GetMaxHealth();
m_bossOriginalFaction = boss->getFaction();
// Sans changement de faction le dirigeant reste intouchable : ni drapeau ni
// niveau ne le protegent, c est sa faction d origine qui interdit l attaque.
// Verifie en jeu : quinze bots au contact d Anduin pendant dix minutes ne lui
// ont pas retire un seul point de vie.
if (m_target->bossFaction)
boss->setFaction(m_target->bossFaction);
// Reglage absolu et par ville. Un multiplicateur serait ingerable : les deux
// dirigeants n ont rien de comparable, Anduin depasse le milliard de points
// de vie quand Vol jin, reste au niveau 83, en a quelques milliers.
if (m_target->bossLevel)
boss->SetLevel(uint8(m_target->bossLevel));
if (m_target->bossHealth)
{
boss->SetMaxHealth(m_target->bossHealth);
boss->SetHealth(m_target->bossHealth);
}
m_bossGuid = boss->GetGUID();
m_bossAwakened = true;
if (m_commander)
m_commander->SetBossGuid(m_bossGuid);
TC_LOG_INFO("server.worldserver", "Siege des Capitales: dirigeant eveille (%s, niveau %u -> %u, PV %s -> %s, faction %u -> %u, drapeaux retires 0x%X).",
boss->GetName().c_str(), m_bossOriginalLevel, uint32(boss->getLevel()),
std::to_string(m_bossOriginalMaxHealth).c_str(), std::to_string(boss->GetMaxHealth()).c_str(),
m_bossOriginalFaction, boss->getFaction(), m_bossClearedFlags);
}
bool CapitalSiegeMgr::IsBossDead() const
{
if (!m_bossAwakened)
return false;
Creature* boss = FindBoss();
if (!boss)
return false; // grille dechargee : on ne conclut pas a la victoire
return !boss->IsAlive();
}
void CapitalSiegeMgr::RestoreBoss()
{
if (!m_bossAwakened)
return;
Creature* boss = FindBoss();
// Si le dirigeant est mort il n y a rien a restaurer : il reapparaitra avec
// les valeurs de creature_template a l expiration de son respawn.
if (boss && boss->IsAlive())
{
if (m_bossClearedFlags)
boss->SetFlag(UNIT_FIELD_FLAGS, m_bossClearedFlags);
if (m_bossOriginalFaction)
boss->setFaction(m_bossOriginalFaction);
if (m_bossOriginalLevel)
boss->SetLevel(uint8(m_bossOriginalLevel));
if (m_bossOriginalMaxHealth)
{
boss->SetMaxHealth(m_bossOriginalMaxHealth);
if (boss->GetHealth() > m_bossOriginalMaxHealth)
boss->SetHealth(m_bossOriginalMaxHealth);
}
// La ville reprend son cours : plus personne ne doit rester en combat
// avec lui a cause de l evenement.
boss->CombatStop(true);
TC_LOG_INFO("server.worldserver", "Siege des Capitales: dirigeant restaure (%s).", boss->GetName().c_str());
}
m_bossAwakened = false;
m_bossGuid = ObjectGuid::Empty;
m_bossClearedFlags = 0;
m_bossOriginalLevel = 0;
m_bossOriginalFaction = 0;
m_bossOriginalMaxHealth = 0;
}
/*******************************************************************************
@@ -492,6 +675,8 @@ void CapitalSiegeMgr::ProcessPendingRecruits()
TC_LOG_ERROR("server.worldserver", "Siege des Capitales: recrue abandonnee, le compte bot %u n a pas rejoint la horde en %u s.",
it->accountId, SIEGE_RECRUIT_TIMEOUT_SECONDS);
m_engagedAccounts.erase(it->accountId);
if (m_totalRecruited)
--m_totalRecruited;
it = m_recruits.erase(it);
continue;
}
@@ -521,9 +706,26 @@ void CapitalSiegeMgr::ProcessPendingRecruits()
continue;
}
// Le tirage de personnage du core (GetRandomCharacterByFuction)
// retombe sur le premier personnage du compte quand il n en trouve
// aucun de la faction demandee. Sans ce controle, des Alliance se
// retrouvaient enroles dans l assaut sur Hurlevent.
if (player->GetTeamId() != m_attackerTeam)
{
TC_LOG_INFO("server.worldserver", "Siege des Capitales: %s est de la mauvaise faction, recrue relachee.", player->GetName().c_str());
sPlayerBotMgr->PlayerBotLogout(it->accountId);
m_engagedAccounts.erase(it->accountId);
if (m_totalRecruited)
--m_totalRecruited;
it = m_recruits.erase(it);
continue;
}
if (!IsSiegeCapableClass(player->getClass()))
{
m_engagedAccounts.erase(it->accountId);
if (m_totalRecruited)
--m_totalRecruited;
it = m_recruits.erase(it);
continue;
}
@@ -547,6 +749,8 @@ void CapitalSiegeMgr::ProcessPendingRecruits()
if (!m_commander->AddBot(player))
{
m_engagedAccounts.erase(it->accountId);
if (m_totalRecruited)
--m_totalRecruited;
it = m_recruits.erase(it);
continue;
}
@@ -614,10 +818,37 @@ bool CapitalSiegeMgr::RecruitOneBot()
if (session->GetPlayer())
continue;
// Le personnage est choisi ici, explicitement. Laisser le core le tirer
// (BGSType_Online + GetRandomCharacterByFuction) ne garantit rien : cette
// fonction retombe sur le premier personnage du compte quand elle n en
// trouve aucun de la faction demandee, ce qui enrolait des Alliance dans
// l assaut sur Hurlevent.
PlayerBotBaseInfo* accountInfo = sPlayerBotMgr->GetPlayerBotAccountInfo(session->GetAccountId());
if (!accountInfo)
accountInfo = sPlayerBotMgr->GetAccountBotAccountInfo(session->GetAccountId());
if (!accountInfo)
continue;
uint64 pickedGuid = 0;
for (PlayerBotBaseInfo::CharInfoMap::iterator itChar = accountInfo->characters.begin();
itChar != accountInfo->characters.end(); ++itChar)
{
PlayerBotCharBaseInfo& charInfo = itChar->second;
if (charInfo.GetCamp() != m_attackerTeam)
continue;
if (!IsSiegeCapableClass(uint8(charInfo.profession)))
continue;
pickedGuid = charInfo.guid;
break;
}
// Compte sans personnage utilisable pour cet assaut : on passe au suivant.
if (!pickedGuid)
continue;
uint32 const level = PlayerBotSetting::CheckMaxLevel(m_botLevel);
BotGlobleSchedule online(BotGlobleScheduleType::BGSType_Online, 0);
online.parameter1 = (m_attackerTeam == TEAM_ALLIANCE) ? 1 : 2;
BotGlobleSchedule online(BotGlobleScheduleType::BGSType_Online_GUID, pickedGuid);
session->PushScheduleToQueue(online);
BotGlobleSchedule setting(BotGlobleScheduleType::BGSType_Settting, 0);
@@ -751,14 +982,26 @@ std::string CapitalSiegeMgr::GetStatusText() const
default: break;
}
// Etat du dirigeant, pour savoir d un coup d oeil si l assaut le touche.
char bossText[96] = "dirigeant non eveille";
if (m_bossAwakened)
{
if (Creature* boss = FindBoss())
snprintf(bossText, sizeof(bossText), "%s a %u%% (%s PV)", boss->GetName().c_str(),
uint32(boss->GetHealthPct()), std::to_string(boss->GetHealth()).c_str());
else
snprintf(bossText, sizeof(bossText), "dirigeant hors grille");
}
snprintf(buffer, sizeof(buffer),
"Siege en cours [%s]: %s attaque %s. Ecoule %u s, restant %u s, bots %u engages / %u vivants / %u perdus / %u en route (objectif %u). Progression %u/%u.",
"Siege en cours [%s]: %s attaque %s. Ecoule %u s, restant %u s, bots %u engages / %u vivants / %u perdus / %u en route (objectif %u). Progression %u/%u. Objectif: %s.",
statusName, GetTeamName(m_attackerTeam), m_target ? m_target->cityName : "?",
GetElapsedSeconds(), GetRemainingSeconds(), m_botsSpawned,
m_commander ? m_commander->GetAliveCount() : 0, m_botsLost,
uint32(m_recruits.size()), m_botCount,
m_commander ? m_commander->GetVanguardProgress() + 1 : 0,
m_commander ? m_commander->GetRouteSize() : 0);
m_commander ? m_commander->GetRouteSize() : 0,
bossText);
return buffer;
}
+23 -4
View File
@@ -30,6 +30,7 @@
#define __CAPITALSIEGEMGR_H__
#include "Common.h"
#include "ObjectGuid.h"
#include "Position.h"
#include "SharedDefines.h"
#include <list>
@@ -37,6 +38,7 @@
#include <string>
class CommandSiege;
class Creature;
class Player;
enum CapitalSiegeStatus
@@ -67,6 +69,10 @@ struct CapitalSiegeTarget
Position stagingPos; // point de rassemblement de la horde d invasion
uint32 routeFirst; // premiere entree aiwaypoints de la route d assaut
uint32 routeLast; // derniere entree aiwaypoints de la route d assaut
uint32 bossSpawnId; // creature.guid du dirigeant, pour le retrouver sur la carte
uint32 bossLevel; // niveau impose pendant l evenement, 0 = ne pas toucher
uint64 bossHealth; // PV imposes pendant l evenement, 0 = ne pas toucher
uint32 bossFaction; // faction imposee pendant l evenement, 0 = ne pas toucher
char const* cityName;
};
@@ -115,8 +121,6 @@ public:
float GetEngageRange() const { return m_engageRange; }
uint32 GetStallTimeout() const { return m_stallTimeout; }
uint32 GetAdvanceWindow() const { return m_advanceWindow; }
uint32 GetBossLevel() const { return m_bossLevel; }
uint32 GetBossHealthMult() const { return m_bossHealthMult; }
static char const* GetTeamName(TeamId team);
static char const* GetOutcomeName(CapitalSiegeOutcome outcome);
@@ -131,6 +135,12 @@ private:
void UpdateRunningSiege(uint32 diff);
bool IsServerOverloaded(uint32 diff);
// Dirigeant --------------------------------------------------------------
Creature* FindBoss() const;
void AwakenBoss();
void RestoreBoss();
bool IsBossDead() const;
// Recrutement ------------------------------------------------------------
void UpdateRecruitment();
void ProcessPendingRecruits();
@@ -164,8 +174,6 @@ private:
float m_engageRange; // yards, portee d engagement en mode siege
uint32 m_stallTimeout; // secondes sans progresser avant de lacher la cible
uint32 m_advanceWindow; // secondes de marche forcee apres un enlisement
uint32 m_bossLevel;
uint32 m_bossHealthMult;
uint32 m_maxDiff; // ms, seuil d arret d urgence (0 = desactive)
bool m_announce;
CapitalSiegeTarget m_targets[2]; // indexe par TeamId de l attaquant
@@ -194,6 +202,17 @@ private:
TeamId m_attackerTeam;
CapitalSiegeTarget const* m_target;
CommandSiege* m_commander;
// Dirigeant : etat d origine, restaure en fin d evenement. Rien n est
// ecrit en base, un arret du worldserver laisse donc la creature intacte.
ObjectGuid m_bossGuid;
uint32 m_bossClearedFlags;
uint32 m_bossOriginalLevel;
uint32 m_bossOriginalFaction;
uint64 m_bossOriginalMaxHealth;
bool m_bossAwakened;
bool m_bossLookupFailed;
uint32 m_bossRetryTimer;
std::list<SiegeRecruit> m_recruits;
std::set<uint32> m_engagedAccounts;
// Total engage depuis le debut de l evenement. Plafonne le recrutement :
+26 -6
View File
@@ -107,6 +107,14 @@ bool CommandSiege::AddBot(Player* player)
if (m_bots.find(player->GetGUID()) != m_bots.end())
return false;
// Dernier filet : jamais un bot de la faction assiegee dans la horde
// d invasion. Le tirage de personnage du core peut se tromper de faction.
if (player->GetTeamId() != m_attackerTeam)
{
TC_LOG_ERROR("server.worldserver", "Siege des Capitales: %s n est pas de la faction attaquante, enrolement refuse.", player->GetName().c_str());
return false;
}
BotBGAI* botAI = dynamic_cast<BotBGAI*>(player->GetAI());
if (!botAI)
{
@@ -270,15 +278,27 @@ void CommandSiege::CommandBot(ObjectGuid guid, SiegeBot& state, Player* player)
botAI->PushSiegeAdvance(sCapitalSiegeMgr->GetAdvanceWindow() * IN_MILLISECONDS);
}
// Arrive au dernier point de la route, le bot bascule sur le dirigeant.
// L ordre est marque prioritaire pour qu un ordre de deplacement residuel
// ne le detourne pas de sa cible.
if (state.waypointIndex + 1 == m_route.size() && !m_bossGuid.IsEmpty())
// Le dirigeant prime des qu il est a portee, sans attendre que le bot ait
// formellement franchi tous les points de la route : les derniers metres se
// font sous le feu de la garde et l index progresse mal. L ordre est marque
// prioritaire pour qu un ordre de deplacement residuel ne le detourne pas.
if (!m_bossGuid.IsEmpty())
{
botAI->GetAIMovement()->AcceptCommand(m_bossGuid, true);
return;
bool const lastLeg = (state.waypointIndex + 1 == m_route.size());
if (lastLeg || player->GetDistance(GetObjectivePosition()) < COMMANDSIEGE_BOSS_RANGE)
{
// Deux ordres distincts : l un pour marcher jusqu a lui, l autre
// pour le prendre pour cible. AcceptCommand ne fait que le premier.
botAI->GetAIMovement()->AcceptCommand(m_bossGuid, true);
botAI->SetSiegeTarget(m_bossGuid);
return;
}
}
// Hors de portee de l objectif, aucune cible prioritaire : le bot combat ce
// qu il rencontre.
botAI->SetSiegeTarget(ObjectGuid::Empty);
botAI->GetAIMovement()->AcceptCommand(target);
}
@@ -47,6 +47,10 @@ class Player;
// Distance a laquelle un point de la route est considere atteint.
#define COMMANDSIEGE_WAYPOINT_REACH 12.0f
// Distance a l objectif final au-dela de laquelle le dirigeant devient la cible
// prioritaire, sans attendre le franchissement du dernier point de la route.
#define COMMANDSIEGE_BOSS_RANGE 60.0f
class TC_GAME_API CommandSiege
{
public:
+35 -11
View File
@@ -4731,17 +4731,6 @@ siege_stall_timeout = 20
siege_advance_window = 12
#
# siege_boss_level / siege_boss_hp_mult
# Description: Ajustement applique au dirigeant pendant l evenement, en memoire
# uniquement (jamais ecrit en base : un arret du serveur pendant le
# siege laisse la creature intacte). Le multiplicateur de points de
# vie est un pourcentage applique aux PV de base.
# Default: 112 / 50 - niveau 112, PV x50
#
siege_boss_level = 112
siege_boss_hp_mult = 50
#
# siege_maxdiff
@@ -4773,6 +4762,24 @@ siege_announce = 1
siege_alliance_map = 0
siege_alliance_boss_entry = 107574
# siege_alliance_boss_spawn : creature.guid du dirigeant, sert a le
# retrouver sur la carte. 20555650 = Anduin Wrynn dans le donjon.
siege_alliance_boss_spawn = 20555650
# siege_alliance_boss_level : niveau impose au dirigeant pendant l evenement.
# siege_alliance_boss_health : points de vie imposes.
# 0 = ne pas toucher. Reglage absolu et par ville : un multiplicateur
# serait ingerable, Anduin depasse le milliard de points de vie quand
# Vol jin, reste au niveau 83, en a quelques milliers.
siege_alliance_boss_level = 0
siege_alliance_boss_health = 0
# siege_alliance_boss_faction : faction imposee au dirigeant pendant l
# evenement, 0 = ne pas toucher. Indispensable cote Alliance : Anduin ne
# porte aucun drapeau bloquant mais sa faction d origine (1733) interdit
# l attaque. Verifie en jeu, quinze bots au contact pendant dix minutes
# ne lui retiraient pas un point de vie. 12 = Stormwind, la faction des
# civils de la ville, hostile a la Horde et attaquable.
siege_alliance_boss_faction = 0
siege_alliance_boss_x = -8363.3
siege_alliance_boss_y = 232.5
siege_alliance_boss_z = 157.1
@@ -4797,6 +4804,23 @@ siege_alliance_route_last = 1099
siege_horde_map = 1
siege_horde_boss_entry = 42283
# siege_horde_boss_spawn : 110864 = Vol'jin dans l enclos de Grommash.
siege_horde_boss_spawn = 110864
# siege_horde_boss_level : niveau impose au dirigeant pendant l evenement.
# siege_horde_boss_health : points de vie imposes.
# 0 = ne pas toucher. Reglage absolu et par ville : un multiplicateur
# serait ingerable, Anduin depasse le milliard de points de vie quand
# Vol jin, reste au niveau 83, en a quelques milliers.
siege_horde_boss_level = 110
siege_horde_boss_health = 0
# siege_horde_boss_faction : faction imposee au dirigeant pendant l
# evenement, 0 = ne pas toucher. Indispensable cote Alliance : Anduin ne
# porte aucun drapeau bloquant mais sa faction d origine (1733) interdit
# l attaque. Verifie en jeu, quinze bots au contact pendant dix minutes
# ne lui retiraient pas un point de vie. 12 = Stormwind, la faction des
# civils de la ville, hostile a la Horde et attaquable.
siege_horde_boss_faction = 0
siege_horde_boss_x = 1924.4
siege_horde_boss_y = -4144.1
siege_horde_boss_z = 40.6