Siege des Capitales E1-E3 : ordonnanceur, persistance, commandes GM

Nouveau module d evenement quotidien : une horde de playerbots assaille la
capitale adverse et tente d abattre son dirigeant. Cette premiere tranche
livre le squelette, la persistance et l ordonnancement ; le spawner, l IA
d assaut et l eveil du dirigeant suivent (E4-E8).

Le serveur tourne sur DestinyCore, sans systeme de modules : le module prend
donc la forme d un script module TrinityCore (decouverte CMake automatique
via AddCapitalSiegeScripts()), avec le gestionnaire dans la bibliotheque game
comme PlayerBotMgr, puisque l IA d assaut devra y acceder depuis BotBGAI.

game/CapitalSiege/CapitalSiegeMgr : tirage quotidien d une heure aleatoire
dans une plage configurable, alternance stricte de la faction attaquante,
machine a etats et arret d urgence si le diff monde depasse le seuil.
Le jour est consomme au declenchement et non a la fin, donc un redemarrage
du worldserver en plein evenement ne peut pas le rejouer.

scripts/CapitalSiege : branchement par WorldScript (aucune modification de
World.cpp), et commandes GM .siege status/start/stop/history sous la
permission RBAC_PERM_COMMAND_EVENT existante.

Persistance dans dc_characters et non dc_world : il s agit d etat mutable
ecrit par le serveur, qu une reimportation de la base de contenu ne doit pas
effacer. Six requetes preparees ajoutees a CharacterDatabase. Toute ligne
d historique restee ouverte est refermee au demarrage du core.

MMaps verifies pour les deux capitales : carte 0 (592 tuiles) et carte 1
(715 tuiles), tuiles de Hurlevent et d Orgrimmar presentes. La navigation
des bots passe par PathGenerator/MotionMaster, donc Detour, pas les VMaps.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
SylvaniaCore deploy
2026-08-14 18:03:06 +02:00
parent c2ae2c3ced
commit a8f8ba210b
9 changed files with 1193 additions and 1 deletions
+73
View File
@@ -0,0 +1,73 @@
--
-- SylvaniaCore - Module "Siege des Capitales"
-- Base : dc_characters (etat mutable ecrit par le worldserver, donc hors de la
-- base de contenu dc_world qui est reimportee a chaque mise a jour).
--
-- Deux tables :
-- capital_siege_state : ligne unique, l ordonnancement du module. Elle porte
-- le verrou anti-rejeu quotidien et l alternance de
-- faction, qui doivent survivre a un redemarrage.
-- capital_siege_history : une ligne par evenement, ouverte au declenchement et
-- refermee a la fin (ou au demarrage suivant du core
-- si le serveur s est arrete en plein siege).
--
-- Toutes les ecritures passent par des requetes preparees declarees dans
-- src/server/database/Database/Implementation/CharacterDatabase.{h,cpp}.
--
-- ---------------------------------------------------------------------------
-- Etat de l ordonnanceur (une seule ligne, id = 1)
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `capital_siege_state` (
`id` TINYINT(3) UNSIGNED NOT NULL DEFAULT 1
COMMENT 'Toujours 1 : la table ne contient qu une ligne',
`last_event_day` INT(10) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Jour serveur (epoch local / 86400) du dernier evenement consomme. Verrou anti-rejeu quotidien',
`last_attacker_team` TINYINT(4) NOT NULL DEFAULT -1
COMMENT 'Derniere faction attaquante : 0 = Alliance, 1 = Horde, -1 = aucune. Sert a l alternance stricte',
`scheduled_day` INT(10) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Jour serveur du tirage courant',
`scheduled_time` INT(10) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Horodatage unix du declenchement tire pour scheduled_day',
`scheduled_team` TINYINT(4) NOT NULL DEFAULT -1
COMMENT 'Faction attaquante tiree pour scheduled_day : 0 = Alliance, 1 = Horde, -1 = aucune',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
COMMENT='Siege des Capitales : ordonnancement persistant';
-- ---------------------------------------------------------------------------
-- Historique des evenements
-- ---------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `capital_siege_history` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`start_time` INT(10) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Horodatage unix du declenchement',
`end_time` INT(10) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Horodatage unix de la fin, 0 tant que l evenement est en cours',
`attacker_team` TINYINT(4) NOT NULL DEFAULT -1
COMMENT '0 = Alliance, 1 = Horde',
`target_map` SMALLINT(5) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Carte de la capitale assaillie (0 = Hurlevent, 1 = Orgrimmar)',
`boss_entry` INT(10) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'creature_template du dirigeant vise',
`outcome` TINYINT(4) NOT NULL DEFAULT 0
COMMENT '0 = en cours, 1 = victoire, 2 = temps ecoule, 3 = annule GM, 4 = arret d urgence (charge), 5 = interrompu (arret du serveur)',
`duration` INT(10) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Duree effective en secondes',
`bots_spawned` SMALLINT(5) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Nombre de bots effectivement deployes',
`bots_lost` SMALLINT(5) UNSIGNED NOT NULL DEFAULT 0
COMMENT 'Nombre de bots tues pendant l assaut',
`triggered_by` VARCHAR(32) NOT NULL DEFAULT 'auto'
COMMENT 'auto = ordonnanceur, sinon nom du GM declencheur',
PRIMARY KEY (`id`),
KEY `idx_outcome` (`outcome`),
KEY `idx_start_time` (`start_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
COMMENT='Siege des Capitales : historique des evenements';
-- Ligne d etat initiale. Aucune faction n a encore attaque : le premier
-- evenement sera donc mene par l Alliance.
INSERT IGNORE INTO `capital_siege_state`
(`id`, `last_event_day`, `last_attacker_team`, `scheduled_day`, `scheduled_time`, `scheduled_team`)
VALUES (1, 0, -1, 0, 0, -1);
@@ -806,6 +806,14 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_SPECIALIZATION, "UPDATE characters SET primarySpecialization = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHALLENGE_MEMBER, "DELETE FROM challenge_member WHERE member = ?", CONNECTION_ASYNC);
// Module Siege des Capitales (SylvaniaCore)
PrepareStatement(CHAR_SEL_CAPITAL_SIEGE_STATE, "SELECT last_event_day, last_attacker_team, scheduled_day, scheduled_time, scheduled_team FROM capital_siege_state WHERE id = 1", CONNECTION_SYNCH);
PrepareStatement(CHAR_REP_CAPITAL_SIEGE_STATE, "REPLACE INTO capital_siege_state (id, last_event_day, last_attacker_team, scheduled_day, scheduled_time, scheduled_team) VALUES (1, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CAPITAL_SIEGE_HISTORY, "INSERT INTO capital_siege_history (start_time, attacker_team, target_map, boss_entry, outcome, triggered_by) VALUES (?, ?, ?, ?, 0, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CAPITAL_SIEGE_HISTORY_CLOSE, "UPDATE capital_siege_history SET end_time = ?, duration = ?, outcome = ?, bots_spawned = ?, bots_lost = ? WHERE outcome = 0", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CAPITAL_SIEGE_HISTORY_ORPHAN, "UPDATE capital_siege_history SET end_time = ?, outcome = ? WHERE outcome = 0", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CAPITAL_SIEGE_HISTORY, "SELECT start_time, attacker_team, outcome, duration, bots_spawned, bots_lost, triggered_by FROM capital_siege_history ORDER BY id DESC LIMIT 10", CONNECTION_SYNCH);
}
CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo, ConnectionFlags connectionFlags) : MySQLConnection(connInfo, connectionFlags)
@@ -681,6 +681,14 @@ enum CharacterDatabaseStatements : uint32
CHAR_UPD_SPECIALIZATION,
// Module Siege des Capitales (SylvaniaCore)
CHAR_SEL_CAPITAL_SIEGE_STATE,
CHAR_REP_CAPITAL_SIEGE_STATE,
CHAR_INS_CAPITAL_SIEGE_HISTORY,
CHAR_UPD_CAPITAL_SIEGE_HISTORY_CLOSE,
CHAR_UPD_CAPITAL_SIEGE_HISTORY_ORPHAN,
CHAR_SEL_CAPITAL_SIEGE_HISTORY,
MAX_CHARACTERDATABASE_STATEMENTS
};
@@ -0,0 +1,513 @@
/*
* This file is part of the DestinyCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CapitalSiegeMgr.h"
#include "Config.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "Random.h"
#include "World.h"
#include <cstdarg>
#include <cstdio>
#include <ctime>
namespace
{
// Duree minimale passee au-dessus du seuil de charge avant l arret d urgence.
constexpr uint32 SIEGE_OVERLOAD_GRACE_MS = 10 * 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_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_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(), "Orgrimmar" };
m_targets[TEAM_HORDE] = { 0, 107574, Position(), Position(), "Hurlevent" };
}
CapitalSiegeMgr::~CapitalSiegeMgr() { }
CapitalSiegeMgr* CapitalSiegeMgr::instance()
{
static CapitalSiegeMgr instance;
return &instance;
}
char const* CapitalSiegeMgr::GetTeamName(TeamId team)
{
switch (team)
{
case TEAM_ALLIANCE: return "Alliance";
case TEAM_HORDE: return "Horde";
default: return "aucune";
}
}
char const* CapitalSiegeMgr::GetOutcomeName(CapitalSiegeOutcome outcome)
{
switch (outcome)
{
case SIEGE_OUTCOME_RUNNING: return "en cours";
case SIEGE_OUTCOME_VICTORY: return "victoire";
case SIEGE_OUTCOME_TIMEOUT: return "echec (temps ecoule)";
case SIEGE_OUTCOME_CANCELLED: return "annule";
case SIEGE_OUTCOME_OVERLOAD: return "arret d urgence (charge)";
case SIEGE_OUTCOME_INTERRUPTED: return "interrompu (arret du serveur)";
default: return "inconnu";
}
}
/*******************************************************************************
* Configuration
******************************************************************************/
void CapitalSiegeMgr::LoadConfig()
{
m_enabled = sConfigMgr->GetBoolDefault("siege_enable", false);
m_hourMin = sConfigMgr->GetIntDefault("siege_hour_min", 18);
m_hourMax = sConfigMgr->GetIntDefault("siege_hour_max", 24);
m_botCount = sConfigMgr->GetIntDefault("siege_botcount", 50);
m_botLevel = sConfigMgr->GetIntDefault("siege_botlevel", 110);
m_duration = sConfigMgr->GetIntDefault("siege_duration", HOUR);
m_spawnRate = sConfigMgr->GetIntDefault("siege_spawn_rate", 5);
m_pvpMode = sConfigMgr->GetIntDefault("siege_pvp", 1);
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);
// Garde-fous : une plage horaire vide ou inversee rendrait le tirage
// impossible, un nombre de bots absurde noierait le serveur.
if (m_hourMin > 23)
m_hourMin = 23;
if (m_hourMax > 24)
m_hourMax = 24;
if (m_hourMax <= m_hourMin)
{
TC_LOG_ERROR("server.worldserver", "Siege des Capitales: plage horaire invalide (%u-%u), retour a 18-24.", m_hourMin, m_hourMax);
m_hourMin = 18;
m_hourMax = 24;
}
if (m_botCount > 100)
{
TC_LOG_ERROR("server.worldserver", "Siege des Capitales: siege_botcount=%u plafonne a 100.", m_botCount);
m_botCount = 100;
}
if (m_spawnRate == 0)
m_spawnRate = 1;
if (m_duration < MINUTE)
m_duration = MINUTE;
if (m_pvpMode > 2)
m_pvpMode = 1;
// Cible assaillie par l Alliance : la capitale de la Horde.
m_targets[TEAM_ALLIANCE].mapId = sConfigMgr->GetIntDefault("siege_horde_map", 1);
m_targets[TEAM_ALLIANCE].bossEntry = sConfigMgr->GetIntDefault("siege_horde_boss_entry", 42283);
m_targets[TEAM_ALLIANCE].bossPos.Relocate(
sConfigMgr->GetFloatDefault("siege_horde_boss_x", 1924.4f),
sConfigMgr->GetFloatDefault("siege_horde_boss_y", -4144.1f),
sConfigMgr->GetFloatDefault("siege_horde_boss_z", 40.6f));
m_targets[TEAM_ALLIANCE].stagingPos.Relocate(
sConfigMgr->GetFloatDefault("siege_horde_staging_x", 1570.0f),
sConfigMgr->GetFloatDefault("siege_horde_staging_y", -4397.4f),
sConfigMgr->GetFloatDefault("siege_horde_staging_z", 16.0f));
m_targets[TEAM_ALLIANCE].cityName = "Orgrimmar";
// Cible assaillie par la Horde : la capitale de l Alliance.
m_targets[TEAM_HORDE].mapId = sConfigMgr->GetIntDefault("siege_alliance_map", 0);
m_targets[TEAM_HORDE].bossEntry = sConfigMgr->GetIntDefault("siege_alliance_boss_entry", 107574);
m_targets[TEAM_HORDE].bossPos.Relocate(
sConfigMgr->GetFloatDefault("siege_alliance_boss_x", -8363.3f),
sConfigMgr->GetFloatDefault("siege_alliance_boss_y", 232.5f),
sConfigMgr->GetFloatDefault("siege_alliance_boss_z", 157.1f));
m_targets[TEAM_HORDE].stagingPos.Relocate(
sConfigMgr->GetFloatDefault("siege_alliance_staging_x", -8833.1f),
sConfigMgr->GetFloatDefault("siege_alliance_staging_y", 622.8f),
sConfigMgr->GetFloatDefault("siege_alliance_staging_z", 93.9f));
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).",
m_enabled ? "actif" : "inactif", m_hourMin, m_hourMax, m_botCount, m_botLevel, m_duration / MINUTE);
}
/*******************************************************************************
* Cycle de vie
******************************************************************************/
void CapitalSiegeMgr::LoadState()
{
// Un evenement laisse ouvert signifie que le worldserver s est arrete en
// plein siege : la ligne d historique est refermee et les bots eventuels
// sont deja hors ligne (leurs sessions ne survivent pas a l arret).
CloseOrphanHistoryRows();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CAPITAL_SIEGE_STATE);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
{
TC_LOG_INFO("server.loading", "Siege des Capitales: aucun etat persiste, premier tirage au prochain tick.");
return;
}
Field* fields = result->Fetch();
m_lastEventDay = fields[0].GetUInt32();
m_lastAttackerTeam = TeamId(fields[1].GetInt8() < 0 ? TEAM_NEUTRAL : fields[1].GetInt8());
m_scheduledDay = fields[2].GetUInt32();
m_scheduledTime = time_t(fields[3].GetUInt32());
m_scheduledTeam = TeamId(fields[4].GetInt8() < 0 ? TEAM_NEUTRAL : fields[4].GetInt8());
TC_LOG_INFO("server.loading", "Siege des Capitales: etat recharge (derniere faction attaquante: %s, jour du dernier evenement: %u).",
GetTeamName(m_lastAttackerTeam), m_lastEventDay);
}
void CapitalSiegeMgr::Update(uint32 diff)
{
if (!m_enabled)
{
if (IsRunning())
{
TC_LOG_INFO("server.worldserver", "Siege des Capitales: module desactive en cours d evenement, arret.");
StopSiege(SIEGE_OUTCOME_CANCELLED);
}
return;
}
// La charge est surveillee a chaque tick monde, pas seulement a la seconde.
bool const overloaded = IsServerOverloaded(diff);
m_updateTimer += diff;
if (m_updateTimer < IN_MILLISECONDS)
return;
uint32 const elapsedMs = m_updateTimer;
m_updateTimer = 0;
if (IsRunning())
{
if (overloaded)
{
TC_LOG_ERROR("server.worldserver", "Siege des Capitales: arret d urgence, le monde tourne au-dessus de %u ms depuis %u s.",
m_maxDiff, SIEGE_OVERLOAD_GRACE_MS / IN_MILLISECONDS);
StopSiege(SIEGE_OUTCOME_OVERLOAD);
return;
}
UpdateRunningSiege(elapsedMs);
}
else
UpdateSchedule(sWorld->GetGameTime());
}
/*******************************************************************************
* Ordonnancement
******************************************************************************/
void CapitalSiegeMgr::UpdateSchedule(time_t now)
{
uint32 const today = GetServerDay(now);
if (m_scheduledDay != today)
DrawScheduleForDay(now);
if (m_lastEventDay == today) // deja joue aujourd hui
return;
if (now < m_scheduledTime)
return;
// Rattrapage. Si le worldserver etait arrete a l heure tiree, on ne
// declenche que tant qu on est encore dans la plage horaire configuree :
// sinon le siege se lancerait a 7h du matin apres une nuit d arret.
if (!IsInsideWindow(now))
{
TC_LOG_INFO("server.worldserver", "Siege des Capitales: creneau du jour manque (serveur arrete), report a demain.");
m_lastEventDay = today;
SaveState();
return;
}
StartSiege(m_scheduledTeam, "auto");
}
void CapitalSiegeMgr::DrawScheduleForDay(time_t now)
{
m_scheduledDay = GetServerDay(now);
// Alternance stricte : jamais deux fois la meme faction attaquante.
m_scheduledTeam = (m_lastAttackerTeam == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE;
uint32 const windowLength = (m_hourMax - m_hourMin) * HOUR;
m_scheduledTime = GetDayStart(now) + time_t(m_hourMin * HOUR) + time_t(urand(0, windowLength - 1));
SaveState();
tm scheduledTm;
time_t const scheduled = m_scheduledTime;
localtime_r(&scheduled, &scheduledTm);
TC_LOG_INFO("server.worldserver", "Siege des Capitales: tirage du jour -> %s a %02u:%02u:%02u.",
GetTeamName(m_scheduledTeam), uint32(scheduledTm.tm_hour), uint32(scheduledTm.tm_min), uint32(scheduledTm.tm_sec));
}
bool CapitalSiegeMgr::IsInsideWindow(time_t now) const
{
tm localTm;
localtime_r(&now, &localTm);
uint32 const hour = uint32(localTm.tm_hour);
return hour >= m_hourMin && hour < m_hourMax;
}
/*******************************************************************************
* Pilotage
******************************************************************************/
bool CapitalSiegeMgr::StartSiege(TeamId attacker, std::string const& trigger)
{
if (IsRunning())
return false;
if (attacker != TEAM_ALLIANCE && attacker != TEAM_HORDE)
return false;
m_attackerTeam = attacker;
m_target = &m_targets[attacker];
m_status = SIEGE_STATUS_SPAWNING;
m_elapsed = 0;
m_overloadTimer = 0;
m_botsSpawned = 0;
m_botsLost = 0;
m_startTime = sWorld->GetGameTime();
// Le jour est consomme des le declenchement, avant toute autre operation :
// un redemarrage du worldserver en plein evenement ne peut pas le rejouer.
m_lastEventDay = GetServerDay(m_startTime);
m_lastAttackerTeam = attacker;
SaveState();
OpenHistoryRow(trigger);
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());
if (m_announce)
Announce("|cffff2020[Siege des Capitales]|r Une armee de la %s marche sur %s !", GetTeamName(attacker), m_target->cityName);
// E7 : demarrage du spawn etale de la horde d invasion.
// E8 : eveil du dirigeant de la capitale.
return true;
}
void CapitalSiegeMgr::StopSiege(CapitalSiegeOutcome outcome)
{
if (!IsRunning())
return;
m_status = SIEGE_STATUS_ENDING;
// E7 : despawn des bots restants.
// E8 : restauration des drapeaux du dirigeant et purge des aggro de ville.
CloseHistoryRow(outcome);
TC_LOG_INFO("server.worldserver", "Siege des Capitales: fin de l assaut %s sur %s apres %u s -> %s.",
GetTeamName(m_attackerTeam), m_target ? m_target->cityName : "?", GetElapsedSeconds(), GetOutcomeName(outcome));
if (m_announce)
{
if (outcome == SIEGE_OUTCOME_VICTORY)
Announce("|cffff2020[Siege des Capitales]|r %s est tombee : le dirigeant a ete abattu par la %s !",
m_target ? m_target->cityName : "La capitale", GetTeamName(m_attackerTeam));
else if (outcome == SIEGE_OUTCOME_TIMEOUT)
Announce("|cff20ff20[Siege des Capitales]|r L assaut sur %s a ete repousse.", m_target ? m_target->cityName : "la capitale");
}
m_status = SIEGE_STATUS_IDLE;
m_attackerTeam = TEAM_NEUTRAL;
m_target = nullptr;
m_elapsed = 0;
}
void CapitalSiegeMgr::UpdateRunningSiege(uint32 diff)
{
m_elapsed += diff;
if (m_elapsed >= m_duration * IN_MILLISECONDS)
{
StopSiege(SIEGE_OUTCOME_TIMEOUT);
return;
}
// E5 : tick du commandant d assaut (progression, ciblage, morts de bots).
// E7 : suite du spawn etale tant que le quota n est pas atteint.
}
bool CapitalSiegeMgr::IsServerOverloaded(uint32 diff)
{
if (!m_maxDiff)
return false;
if (diff > m_maxDiff)
m_overloadTimer += diff;
else
m_overloadTimer = 0;
return m_overloadTimer >= SIEGE_OVERLOAD_GRACE_MS;
}
/*******************************************************************************
* Persistance
******************************************************************************/
void CapitalSiegeMgr::SaveState()
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CAPITAL_SIEGE_STATE);
stmt->setUInt32(0, m_lastEventDay);
stmt->setInt8(1, int8(m_lastAttackerTeam == TEAM_NEUTRAL ? -1 : int8(m_lastAttackerTeam)));
stmt->setUInt32(2, m_scheduledDay);
stmt->setUInt32(3, uint32(m_scheduledTime));
stmt->setInt8(4, int8(m_scheduledTeam == TEAM_NEUTRAL ? -1 : int8(m_scheduledTeam)));
CharacterDatabase.Execute(stmt);
}
void CapitalSiegeMgr::OpenHistoryRow(std::string const& trigger)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CAPITAL_SIEGE_HISTORY);
stmt->setUInt32(0, uint32(m_startTime));
stmt->setInt8(1, int8(m_attackerTeam));
stmt->setUInt16(2, uint16(m_target ? m_target->mapId : 0));
stmt->setUInt32(3, m_target ? m_target->bossEntry : 0);
stmt->setString(4, trigger);
CharacterDatabase.Execute(stmt);
}
void CapitalSiegeMgr::CloseHistoryRow(CapitalSiegeOutcome outcome)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CAPITAL_SIEGE_HISTORY_CLOSE);
stmt->setUInt32(0, uint32(sWorld->GetGameTime()));
stmt->setUInt32(1, GetElapsedSeconds());
stmt->setInt8(2, int8(outcome));
stmt->setUInt16(3, uint16(m_botsSpawned));
stmt->setUInt16(4, uint16(m_botsLost));
CharacterDatabase.Execute(stmt);
}
void CapitalSiegeMgr::CloseOrphanHistoryRows()
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CAPITAL_SIEGE_HISTORY_ORPHAN);
stmt->setUInt32(0, uint32(sWorld->GetGameTime()));
stmt->setInt8(1, int8(SIEGE_OUTCOME_INTERRUPTED));
CharacterDatabase.Execute(stmt);
}
/*******************************************************************************
* Consultation
******************************************************************************/
uint32 CapitalSiegeMgr::GetRemainingSeconds() const
{
if (!IsRunning())
return 0;
uint32 const elapsed = GetElapsedSeconds();
return elapsed >= m_duration ? 0 : m_duration - elapsed;
}
std::string CapitalSiegeMgr::GetStatusText() const
{
char buffer[256];
if (!m_enabled)
return "Siege des Capitales: module desactive (siege_enable = 0).";
if (!IsRunning())
return GetScheduleText();
char const* statusName = "?";
switch (m_status)
{
case SIEGE_STATUS_SPAWNING: statusName = "deploiement"; break;
case SIEGE_STATUS_ASSAULT: statusName = "assaut"; break;
case SIEGE_STATUS_ENDING: statusName = "nettoyage"; break;
default: break;
}
snprintf(buffer, sizeof(buffer),
"Siege en cours [%s]: %s attaque %s. Ecoule %u s, restant %u s, bots %u deployes / %u perdus.",
statusName, GetTeamName(m_attackerTeam), m_target ? m_target->cityName : "?",
GetElapsedSeconds(), GetRemainingSeconds(), m_botsSpawned, m_botsLost);
return buffer;
}
std::string CapitalSiegeMgr::GetScheduleText() const
{
char buffer[256];
time_t const now = sWorld->GetGameTime();
if (m_lastEventDay == GetServerDay(now))
{
snprintf(buffer, sizeof(buffer),
"Aucun siege en cours. Le creneau du jour est deja consomme (derniere faction attaquante: %s). Prochain tirage demain.",
GetTeamName(m_lastAttackerTeam));
return buffer;
}
if (!m_scheduledTime)
return "Aucun siege en cours. Tirage du jour pas encore effectue.";
tm scheduledTm;
time_t const scheduled = m_scheduledTime;
localtime_r(&scheduled, &scheduledTm);
snprintf(buffer, sizeof(buffer),
"Aucun siege en cours. Prochain assaut: %s a %02u:%02u:%02u (dans %d s).",
GetTeamName(m_scheduledTeam), uint32(scheduledTm.tm_hour), uint32(scheduledTm.tm_min), uint32(scheduledTm.tm_sec),
int32(m_scheduledTime - now));
return buffer;
}
/*******************************************************************************
* Utilitaires
******************************************************************************/
time_t CapitalSiegeMgr::GetDayStart(time_t t)
{
tm localTm;
localtime_r(&t, &localTm);
localTm.tm_hour = 0;
localTm.tm_min = 0;
localTm.tm_sec = 0;
localTm.tm_isdst = -1;
return mktime(&localTm);
}
uint32 CapitalSiegeMgr::GetServerDay(time_t t)
{
return uint32(GetDayStart(t) / DAY);
}
void CapitalSiegeMgr::Announce(char const* format, ...) const
{
char buffer[512];
va_list args;
va_start(args, format);
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
sWorld->SendGlobalText(buffer, nullptr);
}
@@ -0,0 +1,172 @@
/*
* This file is part of the DestinyCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// SylvaniaCore - Module "Siege des Capitales"
//
// Une fois par jour serveur, a une heure tiree au hasard dans une plage
// configurable, une horde de playerbots de haut niveau assaille la capitale de
// la faction adverse et tente d abattre son dirigeant. La faction attaquante
// alterne strictement d un jour a l autre.
//
// Ce gestionnaire porte l ordonnancement, la persistance et la machine a etats.
// L IA d assaut vit dans CommandSiege, le pilotage GM dans
// scripts/CapitalSiege/cs_capital_siege.cpp.
#ifndef __CAPITALSIEGEMGR_H__
#define __CAPITALSIEGEMGR_H__
#include "Common.h"
#include "Position.h"
#include "SharedDefines.h"
#include <string>
class Player;
enum CapitalSiegeStatus
{
SIEGE_STATUS_IDLE = 0, // aucun evenement en cours
SIEGE_STATUS_SPAWNING = 1, // vague en cours de connexion / equipement
SIEGE_STATUS_ASSAULT = 2, // progression vers le trone
SIEGE_STATUS_ENDING = 3 // nettoyage en cours
};
// Valeurs ecrites telles quelles dans capital_siege_history.outcome
enum CapitalSiegeOutcome
{
SIEGE_OUTCOME_RUNNING = 0, // ligne ouverte, evenement en cours
SIEGE_OUTCOME_VICTORY = 1, // dirigeant abattu
SIEGE_OUTCOME_TIMEOUT = 2, // duree maximale atteinte
SIEGE_OUTCOME_CANCELLED = 3, // annulation GM
SIEGE_OUTCOME_OVERLOAD = 4, // arret d urgence, serveur sous charge
SIEGE_OUTCOME_INTERRUPTED = 5 // arret/crash du worldserver pendant l evenement
};
// Cible d une invasion : la capitale de la faction qui subit l assaut.
struct CapitalSiegeTarget
{
uint32 mapId; // carte de la capitale
uint32 bossEntry; // creature_template du dirigeant
Position bossPos; // position du trone
Position stagingPos; // point de rassemblement de la horde d invasion
char const* cityName;
};
class TC_GAME_API CapitalSiegeMgr
{
private:
CapitalSiegeMgr();
~CapitalSiegeMgr();
public:
CapitalSiegeMgr(CapitalSiegeMgr const&) = delete;
CapitalSiegeMgr(CapitalSiegeMgr&&) = delete;
CapitalSiegeMgr& operator=(CapitalSiegeMgr const&) = delete;
CapitalSiegeMgr& operator=(CapitalSiegeMgr&&) = delete;
static CapitalSiegeMgr* instance();
// Cycle de vie -----------------------------------------------------------
void LoadConfig(); // (re)lecture de worldserver.conf
void LoadState(); // au demarrage du monde
void Update(uint32 diff); // tick monde
// Pilotage ---------------------------------------------------------------
bool StartSiege(TeamId attacker, std::string const& trigger);
void StopSiege(CapitalSiegeOutcome outcome);
// Consultation -----------------------------------------------------------
bool IsEnabled() const { return m_enabled; }
CapitalSiegeStatus GetStatus() const { return m_status; }
bool IsRunning() const { return m_status != SIEGE_STATUS_IDLE; }
TeamId GetAttackerTeam() const { return m_attackerTeam; }
TeamId GetScheduledTeam() const { return m_scheduledTeam; }
CapitalSiegeTarget const* GetTarget() const { return m_target; }
uint32 GetElapsedSeconds() const { return m_elapsed / IN_MILLISECONDS; }
uint32 GetRemainingSeconds() const;
std::string GetStatusText() const;
std::string GetScheduleText() const;
// Parametres exposes aux etapes suivantes (spawner, IA d assaut, boss)
uint32 GetBotCount() const { return m_botCount; }
uint32 GetBotLevel() const { return m_botLevel; }
uint32 GetSpawnRate() const { return m_spawnRate; }
uint32 GetPvpMode() const { return m_pvpMode; }
uint32 GetBossLevel() const { return m_bossLevel; }
uint32 GetBossHealthMult() const { return m_bossHealthMult; }
static char const* GetTeamName(TeamId team);
static char const* GetOutcomeName(CapitalSiegeOutcome outcome);
private:
// Ordonnancement ---------------------------------------------------------
void UpdateSchedule(time_t now);
void DrawScheduleForDay(time_t now);
bool IsInsideWindow(time_t now) const;
// Deroulement ------------------------------------------------------------
void UpdateRunningSiege(uint32 diff);
bool IsServerOverloaded(uint32 diff);
// Persistance ------------------------------------------------------------
void SaveState();
void OpenHistoryRow(std::string const& trigger);
void CloseHistoryRow(CapitalSiegeOutcome outcome);
void CloseOrphanHistoryRows();
// Utilitaires ------------------------------------------------------------
static time_t GetDayStart(time_t t);
static uint32 GetServerDay(time_t t);
void Announce(char const* format, ...) const ATTR_PRINTF(2, 3);
private:
// Configuration
bool m_enabled;
uint32 m_hourMin;
uint32 m_hourMax;
uint32 m_botCount;
uint32 m_botLevel;
uint32 m_duration; // secondes
uint32 m_spawnRate; // bots par seconde
uint32 m_pvpMode; // 0 = PNJ seuls, 1 = joueurs flagges PvP, 2 = tous
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
// Etat persiste
uint32 m_lastEventDay; // dernier jour ou un evenement a ete consomme
TeamId m_lastAttackerTeam; // derniere faction attaquante (alternance)
uint32 m_scheduledDay; // jour du tirage courant
time_t m_scheduledTime; // horodatage du declenchement tire
TeamId m_scheduledTeam;
// Etat volatil
CapitalSiegeStatus m_status;
TeamId m_attackerTeam;
CapitalSiegeTarget const* m_target;
uint32 m_elapsed; // ms depuis le debut de l evenement
uint32 m_updateTimer; // ms, cadence le tick a la seconde
uint32 m_overloadTimer; // ms passees au-dessus du seuil de charge
uint32 m_botsSpawned;
uint32 m_botsLost;
time_t m_startTime;
};
#define sCapitalSiegeMgr CapitalSiegeMgr::instance()
#endif // __CAPITALSIEGEMGR_H__
@@ -0,0 +1,62 @@
/*
* This file is part of the DestinyCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// SylvaniaCore - Module "Siege des Capitales"
//
// Branchement du gestionnaire sur la boucle du monde. Le module ne modifie pas
// World.cpp : tout passe par les hooks WorldScript, dans le sens autorise
// scripts -> game.
#include "CapitalSiegeMgr.h"
#include "ScriptMgr.h"
class capital_siege_world : public WorldScript
{
public:
capital_siege_world() : WorldScript("capital_siege_world") { }
// Lecture des cles de configuration, au demarrage comme sur .reload config.
void OnConfigLoad(bool /*reload*/) override
{
sCapitalSiegeMgr->LoadConfig();
}
// Rechargement de l ordonnancement persiste et fermeture d un evenement
// reste ouvert si le worldserver s est arrete en plein siege.
void OnStartup() override
{
sCapitalSiegeMgr->LoadState();
}
void OnUpdate(uint32 diff) override
{
sCapitalSiegeMgr->Update(diff);
}
// Arret propre : la ligne d historique est refermee tout de suite plutot
// que d attendre le prochain demarrage du core.
void OnShutdown() override
{
if (sCapitalSiegeMgr->IsRunning())
sCapitalSiegeMgr->StopSiege(SIEGE_OUTCOME_INTERRUPTED);
}
};
void AddSC_capital_siege_world()
{
new capital_siege_world();
}
@@ -0,0 +1,27 @@
/*
* This file is part of the DestinyCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// This is where scripts' loading functions should be declared:
void AddSC_capital_siege_world();
void AddSC_capital_siege_commandscript();
void AddCapitalSiegeScripts()
{
AddSC_capital_siege_world();
AddSC_capital_siege_commandscript();
}
@@ -0,0 +1,192 @@
/*
* This file is part of the DestinyCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// SylvaniaCore - Module "Siege des Capitales"
//
// Commandes GM :
// .siege status etat courant ou prochain declenchement
// .siege start [alliance|horde] declenchement manuel (faction par defaut :
// celle que l alternance a tiree pour aujourd hui)
// .siege stop annulation de l evenement en cours
// .siege history les dix derniers evenements
//
// Le declenchement manuel consomme le creneau du jour, exactement comme un
// declenchement automatique : pas de double siege dans la meme journee.
#include "CapitalSiegeMgr.h"
#include "Chat.h"
#include "DatabaseEnv.h"
#include "Player.h"
#include "RBAC.h"
#include "ScriptMgr.h"
#include "WorldSession.h"
#include <cstring>
#include <ctime>
namespace
{
std::string FormatTimestamp(uint32 unixTime)
{
if (!unixTime)
return "-";
time_t const t = time_t(unixTime);
tm localTm;
localtime_r(&t, &localTm);
char buffer[32];
strftime(buffer, sizeof(buffer), "%d/%m %H:%M", &localTm);
return buffer;
}
}
static bool HandleSiegeStatus(ChatHandler* handler, char const* /*args*/)
{
handler->SendSysMessage(sCapitalSiegeMgr->GetStatusText().c_str());
return true;
}
static bool HandleSiegeStart(ChatHandler* handler, char const* args)
{
if (!sCapitalSiegeMgr->IsEnabled())
{
handler->SendSysMessage("Le module Siege des Capitales est desactive (siege_enable = 0).");
handler->SetSentErrorMessage(true);
return false;
}
if (sCapitalSiegeMgr->IsRunning())
{
handler->SendSysMessage("Un siege est deja en cours. Utilisez .siege stop pour l interrompre.");
handler->SetSentErrorMessage(true);
return false;
}
TeamId attacker = TEAM_NEUTRAL;
if (args && *args)
{
if (!strncmp(args, "alliance", 8))
attacker = TEAM_ALLIANCE;
else if (!strncmp(args, "horde", 5))
attacker = TEAM_HORDE;
else
{
handler->SendSysMessage("Syntaxe : .siege start [alliance|horde]");
handler->SetSentErrorMessage(true);
return false;
}
}
else
{
// Sans argument, on respecte l alternance : la faction qui devait
// attaquer aujourd hui.
attacker = sCapitalSiegeMgr->GetScheduledTeam();
if (attacker == TEAM_NEUTRAL)
attacker = TEAM_ALLIANCE;
}
std::string trigger = "GM";
if (Player* gm = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr)
trigger = gm->GetName();
if (!sCapitalSiegeMgr->StartSiege(attacker, trigger))
{
handler->SendSysMessage("Impossible de declencher le siege.");
handler->SetSentErrorMessage(true);
return false;
}
handler->PSendSysMessage("Siege declenche : assaut %s.", CapitalSiegeMgr::GetTeamName(attacker));
return true;
}
static bool HandleSiegeStop(ChatHandler* handler, char const* /*args*/)
{
if (!sCapitalSiegeMgr->IsRunning())
{
handler->SendSysMessage("Aucun siege en cours.");
handler->SetSentErrorMessage(true);
return false;
}
sCapitalSiegeMgr->StopSiege(SIEGE_OUTCOME_CANCELLED);
handler->SendSysMessage("Siege annule et nettoye.");
return true;
}
static bool HandleSiegeHistory(ChatHandler* handler, char const* /*args*/)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CAPITAL_SIEGE_HISTORY);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
{
handler->SendSysMessage("Aucun siege dans l historique.");
return true;
}
handler->SendSysMessage("Derniers sieges (date, attaquant, issue, duree, bots) :");
do
{
Field* fields = result->Fetch();
uint32 const startTime = fields[0].GetUInt32();
int8 const attackerTeam = fields[1].GetInt8();
int8 const outcome = fields[2].GetInt8();
uint32 const duration = fields[3].GetUInt32();
uint16 const botsSpawned = fields[4].GetUInt16();
uint16 const botsLost = fields[5].GetUInt16();
std::string const trigger = fields[6].GetString();
handler->PSendSysMessage(" %s | %s | %s | %u s | %u deployes, %u perdus | %s",
FormatTimestamp(startTime).c_str(),
CapitalSiegeMgr::GetTeamName(attackerTeam < 0 ? TEAM_NEUTRAL : TeamId(attackerTeam)),
CapitalSiegeMgr::GetOutcomeName(CapitalSiegeOutcome(outcome)),
duration, uint32(botsSpawned), uint32(botsLost), trigger.c_str());
}
while (result->NextRow());
return true;
}
class capital_siege_commandscript : public CommandScript
{
public:
capital_siege_commandscript() : CommandScript("capital_siege_commandscript") { }
std::vector<ChatCommand> GetCommands() const override
{
static std::vector<ChatCommand> siegeCommandTable =
{
{ "status", rbac::RBAC_PERM_COMMAND_EVENT, true, &HandleSiegeStatus, "" },
{ "start", rbac::RBAC_PERM_COMMAND_EVENT, true, &HandleSiegeStart, "" },
{ "stop", rbac::RBAC_PERM_COMMAND_EVENT, true, &HandleSiegeStop, "" },
{ "history", rbac::RBAC_PERM_COMMAND_EVENT, true, &HandleSiegeHistory, "" },
};
static std::vector<ChatCommand> commandTable =
{
{ "siege", rbac::RBAC_PERM_COMMAND_EVENT, true, nullptr, "", siegeCommandTable },
};
return commandTable;
}
};
void AddSC_capital_siege_commandscript()
{
new capital_siege_commandscript();
}
+138 -1
View File
@@ -4621,4 +4621,141 @@ pbotbg_idlelogout = 300
pbotasl = 88
#
####################################################################################################
####################################################################################################
###################################################################################################
# MODULE SIEGE DES CAPITALES (SylvaniaCore)
#
# siege_enable
# Description: Evenement quotidien d invasion. Une fois par jour serveur, a une
# heure tiree au hasard dans la plage horaire ci-dessous, une horde
# de playerbots assaille la capitale de la faction adverse et tente
# d abattre son dirigeant. La faction attaquante alterne strictement
# d un jour a l autre.
# Default: 0 - Desactive
# 1 - Active
#
siege_enable = 0
#
# siege_hour_min / siege_hour_max
# Description: Plage horaire (heure locale du serveur) dans laquelle l heure de
# declenchement est tiree. Bornes en heures pleines, min inclus,
# max exclu. 18 / 24 = tirage entre 18h00m00s et 23h59m59s.
# Si le worldserver etait arrete a l heure tiree, l evenement n est
# rattrape que tant qu on est encore dans cette plage.
# Default: 18 / 24
#
siege_hour_min = 18
siege_hour_max = 24
#
# siege_botcount
# Description: Nombre de playerbots composant la horde d invasion.
# Plafonne a 100 par securite.
# Default: 50
#
siege_botcount = 50
#
# siege_botlevel
# Description: Niveau auquel les bots de l invasion sont ajustes.
# Default: 110
#
siege_botlevel = 110
#
# siege_duration
# Description: Duree maximale d un evenement, en secondes. Passe ce delai
# l assaut est declare repousse et les bots restants sont retires.
# Default: 3600 - une heure
#
siege_duration = 3600
#
# siege_spawn_rate
# Description: Nombre de bots connectes et deployes par seconde. Etale le cout
# du spawn pour eviter un pic de charge au declenchement.
# Default: 5
#
siege_spawn_rate = 5
#
# siege_pvp
# Description: Comportement des bots vis-a-vis des joueurs reels rencontres.
# Default: 1 - N engagent que les joueurs deja flagges PvP
# 0 - Ignorent les joueurs, n attaquent que gardes et PNJ
# 2 - Engagent tout joueur de la faction adverse
#
siege_pvp = 1
#
# 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
# Description: Arret d urgence. Si le temps de mise a jour du monde depasse cette
# valeur (en ms) pendant plus de dix secondes consecutives, l
# evenement en cours est interrompu et nettoye.
# 0 = surveillance desactivee.
# Default: 400
#
siege_maxdiff = 400
#
# siege_announce
# Description: Annonce serveur au declenchement et a la fin de l evenement.
# Default: 1 - Active
# 0 - Desactive
#
siege_announce = 1
#
# Cible assaillie par la Horde : la capitale de l Alliance (Hurlevent).
# siege_alliance_map carte de la capitale
# siege_alliance_boss_entry creature_template du dirigeant
# siege_alliance_boss_* position du trone
# siege_alliance_staging_* point de rassemblement de la horde d invasion
#
siege_alliance_map = 0
siege_alliance_boss_entry = 107574
siege_alliance_boss_x = -8363.3
siege_alliance_boss_y = 232.5
siege_alliance_boss_z = 157.1
siege_alliance_staging_x = -8833.1
siege_alliance_staging_y = 622.8
siege_alliance_staging_z = 93.9
#
# Cible assaillie par l Alliance : la capitale de la Horde (Orgrimmar).
#
siege_horde_map = 1
siege_horde_boss_entry = 42283
siege_horde_boss_x = 1924.4
siege_horde_boss_y = -4144.1
siege_horde_boss_z = 40.6
siege_horde_staging_x = 1570.0
siege_horde_staging_y = -4397.4
siege_horde_staging_z = 16.0
#
###################################################################################################