Merge branch 'master' into Playerbot
This commit is contained in:
@@ -679,6 +679,7 @@ Appender.Errors=2,5,0,Errors.log,w
|
||||
#
|
||||
|
||||
Logger.root=2,Console Server
|
||||
#Logger.metric=2,Console Server
|
||||
#Logger.commands.gm=4,Console GM
|
||||
Logger.diff=3,Console Server
|
||||
Logger.mmaps=4,Server
|
||||
@@ -813,6 +814,37 @@ Log.Async.Enable = 0
|
||||
|
||||
Metric.Enable = 0
|
||||
|
||||
#
|
||||
# Metric.InfluxDB
|
||||
# Description: Connection settings for InfluxDB.
|
||||
#
|
||||
# For InfluxDB v1:
|
||||
# Only fill in Metric.InfluxDB.Connection.
|
||||
#
|
||||
# Example:
|
||||
# Metric.InfluxDB.Connection = "hostname;port;database"
|
||||
#
|
||||
# For InfluxDB v2:
|
||||
# Fill in every field.
|
||||
#
|
||||
# NOTE: Currently, Grafana will not work with the provided json files to visualize
|
||||
# data from InfluxDB v2.
|
||||
#
|
||||
# Example:
|
||||
# Metric.InfluxDB.Connection = "hostname;port"
|
||||
# Metric.InfluxDB.v2 = 0 - (Disabled)
|
||||
# 1 - (Enabled)
|
||||
# Metric.InfluxDB.Org = "my-org"
|
||||
# Metric.InfluxDB.Bucket = "my-bucket"
|
||||
# Metric.InfluxDB.Token = "my-token"
|
||||
#
|
||||
|
||||
Metric.InfluxDB.Connection = "127.0.0.1;8086;worldserver"
|
||||
Metric.InfluxDB.v2 = 0
|
||||
Metric.InfluxDB.Org = ""
|
||||
Metric.InfluxDB.Bucket = ""
|
||||
Metric.InfluxDB.Token = ""
|
||||
|
||||
#
|
||||
# Metric.Interval
|
||||
# Description: Interval between every batch of data sent in seconds.
|
||||
@@ -823,15 +855,6 @@ Metric.Enable = 0
|
||||
|
||||
Metric.Interval = 1
|
||||
|
||||
#
|
||||
# Metric.ConnectionInfo
|
||||
# Description: Connection settings for metric database (currently InfluxDB).
|
||||
# Example: "hostname;port;database"
|
||||
# Default: "127.0.0.1;8086;worldserver"
|
||||
#
|
||||
|
||||
Metric.ConnectionInfo = "127.0.0.1;8086;worldserver"
|
||||
|
||||
#
|
||||
# Metric.OverallStatusInterval
|
||||
# Description: Interval between every gathering of overall worldserver status data in seconds
|
||||
@@ -1398,18 +1421,6 @@ CheckGameObjectLoS = 1
|
||||
|
||||
PreloadAllNonInstancedMapGrids = 0
|
||||
|
||||
#
|
||||
# SetAllCreaturesWithWaypointMovementActive
|
||||
# Description: Set all creatures with waypoint movement active. This means that they will start
|
||||
# movement once they are loaded (which happens on grid load) and keep moving even
|
||||
# when no player is near. This will increase CPU usage significantly and can be
|
||||
# used with enabled "PreloadAllNonInstancedMapGrids" to start waypoint movement on
|
||||
# server startup.
|
||||
# Default: 0 - (Disabled)
|
||||
# 1 - (Enabled)
|
||||
|
||||
SetAllCreaturesWithWaypointMovementActive = 0
|
||||
|
||||
#
|
||||
# DontCacheRandomMovementPaths
|
||||
# Description: Random movement paths (calculated using MoveMaps) can be cached to save cpu time,
|
||||
@@ -3835,11 +3846,18 @@ Arena.QueueAnnouncer.Detail = 3
|
||||
|
||||
#
|
||||
# Arena.ArenaStartRating
|
||||
# Description: Start rating for new arena teams.
|
||||
# Description: Start rating for new arena teams. (Applies to season 6 and higher)
|
||||
# Default: 0
|
||||
|
||||
Arena.ArenaStartRating = 0
|
||||
|
||||
#
|
||||
# Arena.LegacyArenaStartRating
|
||||
# Description: Start rating for new arena teams. (Only applies to season 1 - 5)
|
||||
# Default: 1500
|
||||
|
||||
Arena.LegacyArenaStartRating = 1500
|
||||
|
||||
#
|
||||
# Arena.ArenaStartPersonalRating
|
||||
# Description: Start personal rating when joining a team.
|
||||
|
||||
@@ -282,6 +282,14 @@ void PetAI::UpdateAI(uint32 diff)
|
||||
}
|
||||
}
|
||||
|
||||
if (spellInfo->HasEffect(SPELL_EFFECT_JUMP_DEST))
|
||||
{
|
||||
if (!spellUsed)
|
||||
delete spell;
|
||||
|
||||
continue; // Pets must only jump to target
|
||||
}
|
||||
|
||||
// No enemy, check friendly
|
||||
if (!spellUsed)
|
||||
{
|
||||
|
||||
@@ -36,7 +36,9 @@ ArenaTeam::ArenaTeam()
|
||||
Stats.WeekGames = 0;
|
||||
Stats.SeasonGames = 0;
|
||||
Stats.Rank = 0;
|
||||
Stats.Rating = sWorld->getIntConfig(CONFIG_ARENA_START_RATING);
|
||||
Stats.Rating = (sArenaSeasonMgr->GetCurrentSeason() < 6)
|
||||
? sWorld->getIntConfig(CONFIG_LEGACY_ARENA_START_RATING)
|
||||
: sWorld->getIntConfig(CONFIG_ARENA_START_RATING);
|
||||
Stats.WeekWins = 0;
|
||||
Stats.SeasonWins = 0;
|
||||
}
|
||||
|
||||
@@ -419,6 +419,9 @@ TeamId BattlegroundAB::GetPrematureWinner()
|
||||
|
||||
bool BattlegroundAB::SetupBattleground()
|
||||
{
|
||||
_honorTics = BattlegroundMgr::IsBGWeekend(GetBgTypeID(true)) ? BG_AB_HONOR_TICK_WEEKEND : BG_AB_HONOR_TICK_NORMAL;
|
||||
_reputationTics = BattlegroundMgr::IsBGWeekend(GetBgTypeID(true)) ? BG_AB_REP_TICK_WEEKEND : BG_AB_REP_TICK_NORMAL;
|
||||
|
||||
for (uint32 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
|
||||
{
|
||||
AddObject(BG_AB_OBJECT_BANNER_NEUTRAL + BG_AB_OBJECTS_PER_NODE * i, BG_AB_OBJECTID_NODE_BANNER_0 + i, BG_AB_NodePositions[i][0], BG_AB_NodePositions[i][1], BG_AB_NodePositions[i][2], BG_AB_NodePositions[i][3], 0, 0, std::sin(BG_AB_NodePositions[i][3] / 2), cos(BG_AB_NodePositions[i][3] / 2), RESPAWN_ONE_DAY);
|
||||
@@ -468,9 +471,6 @@ void BattlegroundAB::Init()
|
||||
|
||||
_bgEvents.Reset();
|
||||
|
||||
_honorTics = BattlegroundMgr::IsBGWeekend(GetBgTypeID(true)) ? BG_AB_HONOR_TICK_WEEKEND : BG_AB_HONOR_TICK_NORMAL;
|
||||
_reputationTics = BattlegroundMgr::IsBGWeekend(GetBgTypeID(true)) ? BG_AB_REP_TICK_WEEKEND : BG_AB_REP_TICK_NORMAL;
|
||||
|
||||
_capturePointInfo[BG_AB_NODE_STABLES]._iconNone = WORLD_STATE_BATTLEGROUND_AB_STABLE_ICON;
|
||||
_capturePointInfo[BG_AB_NODE_FARM]._iconNone = WORLD_STATE_BATTLEGROUND_AB_FARM_ICON;
|
||||
_capturePointInfo[BG_AB_NODE_BLACKSMITH]._iconNone = WORLD_STATE_BATTLEGROUND_AB_BLACKSMITH_ICON;
|
||||
|
||||
@@ -166,7 +166,7 @@ enum BG_AB_Misc
|
||||
BG_AB_HONOR_TICK_NORMAL = 260,
|
||||
BG_AB_HONOR_TICK_WEEKEND = 160,
|
||||
BG_AB_REP_TICK_NORMAL = 160,
|
||||
BG_AB_REP_TICK_WEEKEND = 120,
|
||||
BG_AB_REP_TICK_WEEKEND = 150,
|
||||
|
||||
BG_AB_WARNING_NEAR_VICTORY_SCORE = 1400,
|
||||
BG_AB_MAX_TEAM_SCORE = 1600,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "BattlegroundAV.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "CreatureTextMgr.h"
|
||||
#include "Formulas.h"
|
||||
#include "GameEventMgr.h"
|
||||
@@ -56,6 +57,14 @@ BattlegroundAV::BattlegroundAV()
|
||||
|
||||
m_Mine_Timer = 0;
|
||||
|
||||
_reputationTower = 0;
|
||||
_reputationCaptain = 0;
|
||||
_reputationBoss = 0;
|
||||
_reputationPerOwnedGraveyard = 0;
|
||||
_reputationSurvivingCaptain = 0;
|
||||
_reputationSurvivingTower = 0;
|
||||
_reputationPerOwnedMine = 0;
|
||||
|
||||
for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i)
|
||||
InitNode(i, TEAM_NEUTRAL, false);
|
||||
|
||||
@@ -93,7 +102,7 @@ void BattlegroundAV::HandleKillUnit(Creature* unit, Player* killer)
|
||||
if (entry == BG_AV_CreatureInfo[AV_NPC_A_BOSS])
|
||||
{
|
||||
CastSpellOnTeam(23658, TEAM_HORDE); //this is a spell which finishes a quest where a player has to kill the boss
|
||||
RewardReputationToTeam(729, BG_AV_REP_BOSS, TEAM_HORDE);
|
||||
RewardReputationToTeam(729, _reputationBoss, TEAM_HORDE);
|
||||
RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_BOSS), TEAM_HORDE);
|
||||
EndBattleground(TEAM_HORDE);
|
||||
DelCreature(AV_CPLACE_TRIGGER17);
|
||||
@@ -101,7 +110,7 @@ void BattlegroundAV::HandleKillUnit(Creature* unit, Player* killer)
|
||||
else if (entry == BG_AV_CreatureInfo[AV_NPC_H_BOSS])
|
||||
{
|
||||
CastSpellOnTeam(23658, TEAM_ALLIANCE); //this is a spell which finishes a quest where a player has to kill the boss
|
||||
RewardReputationToTeam(730, BG_AV_REP_BOSS, TEAM_ALLIANCE);
|
||||
RewardReputationToTeam(730, _reputationBoss, TEAM_ALLIANCE);
|
||||
RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_BOSS), TEAM_ALLIANCE);
|
||||
EndBattleground(TEAM_ALLIANCE);
|
||||
DelCreature(AV_CPLACE_TRIGGER19);
|
||||
@@ -114,7 +123,7 @@ void BattlegroundAV::HandleKillUnit(Creature* unit, Player* killer)
|
||||
return;
|
||||
}
|
||||
m_CaptainAlive[0] = false;
|
||||
RewardReputationToTeam(729, BG_AV_REP_CAPTAIN, TEAM_HORDE);
|
||||
RewardReputationToTeam(729, _reputationCaptain, TEAM_HORDE);
|
||||
RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_CAPTAIN), TEAM_HORDE);
|
||||
UpdateScore(TEAM_ALLIANCE, (-1)*BG_AV_RES_CAPTAIN);
|
||||
//spawn destroyed aura
|
||||
@@ -132,7 +141,7 @@ void BattlegroundAV::HandleKillUnit(Creature* unit, Player* killer)
|
||||
return;
|
||||
}
|
||||
m_CaptainAlive[1] = false;
|
||||
RewardReputationToTeam(730, BG_AV_REP_CAPTAIN, TEAM_ALLIANCE);
|
||||
RewardReputationToTeam(730, _reputationCaptain, TEAM_ALLIANCE);
|
||||
RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_CAPTAIN), TEAM_ALLIANCE);
|
||||
UpdateScore(TEAM_HORDE, (-1)*BG_AV_RES_CAPTAIN);
|
||||
//spawn destroyed aura
|
||||
@@ -174,7 +183,7 @@ void BattlegroundAV::HandleQuestComplete(uint32 questid, Player* player)
|
||||
{
|
||||
LOG_DEBUG("bg.battleground", "BG_AV Quest {} completed starting with unit upgrading..", questid);
|
||||
for (BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i)
|
||||
if (m_Nodes[i].OwnerId == player->GetTeamId() && m_Nodes[i].State == POINT_CONTROLED)
|
||||
if (m_Nodes[i].OwnerId == player->GetTeamId() && m_Nodes[i].State == POINT_CONTROLLED)
|
||||
{
|
||||
DePopulateNode(i);
|
||||
PopulateNode(i);
|
||||
@@ -503,29 +512,49 @@ void BattlegroundAV::EndBattleground(TeamId winnerTeamId)
|
||||
//first towers:
|
||||
uint8 kills[2] = {0, 0}; // 0 = Alliance 1 = Horde
|
||||
uint8 rep[2] = {0, 0}; // 0 = Alliance 1 = Horde
|
||||
|
||||
if (BattlegroundMgr::IsBGWeekend(GetBgTypeID(true)))
|
||||
kills[winnerTeamId] += 4;
|
||||
|
||||
for (BG_AV_Nodes i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i)
|
||||
{
|
||||
if (m_Nodes[i].State == POINT_CONTROLED)
|
||||
if (m_Nodes[i].State == POINT_CONTROLLED)
|
||||
{
|
||||
if (m_Nodes[i].OwnerId == TEAM_ALLIANCE)
|
||||
{
|
||||
rep[0] += BG_AV_REP_SURVIVING_TOWER;
|
||||
kills[0] += BG_AV_KILL_SURVIVING_TOWER;
|
||||
}
|
||||
if (m_Nodes[i].OwnerId == TEAM_NEUTRAL)
|
||||
continue;
|
||||
else
|
||||
{
|
||||
rep[0] += BG_AV_KILL_SURVIVING_TOWER;
|
||||
kills[1] += BG_AV_KILL_SURVIVING_TOWER;
|
||||
rep[m_Nodes[i].OwnerId] += _reputationSurvivingTower;
|
||||
kills[m_Nodes[i].OwnerId] += BG_AV_KILL_SURVIVING_TOWER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8 i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i)
|
||||
{
|
||||
if (m_Nodes[i].State == POINT_CONTROLLED)
|
||||
{
|
||||
if (m_Nodes[i].OwnerId == TEAM_NEUTRAL)
|
||||
continue;
|
||||
else
|
||||
rep[m_Nodes[i].OwnerId] += _reputationPerOwnedGraveyard;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8 mine = 0; mine < 2; mine++)
|
||||
{
|
||||
if (m_Mine_Owner[mine] == TEAM_NEUTRAL)
|
||||
continue;
|
||||
|
||||
rep[m_Mine_Owner[mine]] += _reputationPerOwnedMine;
|
||||
}
|
||||
|
||||
for (TeamId iTeamId = TEAM_ALLIANCE; iTeamId <= TEAM_HORDE; iTeamId = TeamId(iTeamId + 1))
|
||||
{
|
||||
if (m_CaptainAlive[iTeamId])
|
||||
{
|
||||
kills[iTeamId] += BG_AV_KILL_SURVIVING_CAPTAIN;
|
||||
rep[iTeamId] += BG_AV_REP_SURVIVING_CAPTAIN;
|
||||
rep[iTeamId] += _reputationSurvivingCaptain;
|
||||
}
|
||||
if (rep[iTeamId] != 0)
|
||||
RewardReputationToTeam(iTeamId == TEAM_ALLIANCE ? 730 : 729, rep[iTeamId], iTeamId);
|
||||
@@ -629,7 +658,7 @@ void BattlegroundAV::EventPlayerDestroyedPoint(BG_AV_Nodes node)
|
||||
SpawnBGObject(BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH + i + (tmp * 10), RESPAWN_IMMEDIATELY);
|
||||
|
||||
UpdateScore((ownerId == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE, -1 * BG_AV_RES_TOWER);
|
||||
RewardReputationToTeam(ownerId == TEAM_ALLIANCE ? 730 : 729, BG_AV_REP_TOWER, ownerId);
|
||||
RewardReputationToTeam(ownerId == TEAM_ALLIANCE ? 730 : 729, _reputationTower, ownerId);
|
||||
RewardHonorToTeam(GetBonusHonorFromKill(BG_AV_KILL_TOWER), ownerId);
|
||||
|
||||
SpawnBGObject(static_cast<uint8>(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH) + ownerId + (2 * tmp), RESPAWN_ONE_DAY);
|
||||
@@ -863,7 +892,7 @@ uint32 BattlegroundAV::GetObjectThroughNode(BG_AV_Nodes node)
|
||||
if (node >= BG_AV_NODES_ICEBLOOD_TOWER && node <= BG_AV_NODES_FROSTWOLF_WTOWER)
|
||||
return node + 7;
|
||||
}
|
||||
else if (m_Nodes[node].State == POINT_CONTROLED)
|
||||
else if (m_Nodes[node].State == POINT_CONTROLLED)
|
||||
if (node <= BG_AV_NODES_STONEHEART_BUNKER)
|
||||
return node;
|
||||
}
|
||||
@@ -874,7 +903,7 @@ uint32 BattlegroundAV::GetObjectThroughNode(BG_AV_Nodes node)
|
||||
if (node <= BG_AV_NODES_STONEHEART_BUNKER)
|
||||
return node + 22;
|
||||
}
|
||||
else if (m_Nodes[node].State == POINT_CONTROLED)
|
||||
else if (m_Nodes[node].State == POINT_CONTROLLED)
|
||||
{
|
||||
if (node <= BG_AV_NODES_FROSTWOLF_HUT)
|
||||
return node + 33;
|
||||
@@ -1010,7 +1039,7 @@ void BattlegroundAV::EventPlayerAssaultsPoint(Player* player, uint32 object)
|
||||
}
|
||||
else if (m_Nodes[node].TotalOwnerId == TEAM_NEUTRAL) //recapping, when no team owns this node realy
|
||||
{
|
||||
if (!(m_Nodes[node].State != POINT_CONTROLED))
|
||||
if (!(m_Nodes[node].State != POINT_CONTROLLED))
|
||||
return;
|
||||
|
||||
if (teamId == TEAM_ALLIANCE)
|
||||
@@ -1138,14 +1167,14 @@ uint8 BattlegroundAV::GetWorldStateType(uint8 state, TeamId teamId) //this is us
|
||||
//a_c a_a h_c h_a the positions in worldstate-array
|
||||
if (teamId == TEAM_ALLIANCE)
|
||||
{
|
||||
if (state == POINT_CONTROLED || state == POINT_DESTROYED)
|
||||
if (state == POINT_CONTROLLED || state == POINT_DESTROYED)
|
||||
return 0;
|
||||
if (state == POINT_ASSAULTED)
|
||||
return 1;
|
||||
}
|
||||
if (teamId == TEAM_HORDE)
|
||||
{
|
||||
if (state == POINT_DESTROYED || state == POINT_CONTROLED)
|
||||
if (state == POINT_DESTROYED || state == POINT_CONTROLLED)
|
||||
return 2;
|
||||
if (state == POINT_ASSAULTED)
|
||||
return 3;
|
||||
@@ -1193,7 +1222,7 @@ GraveyardStruct const* BattlegroundAV::GetClosestGraveyard(Player* player)
|
||||
minDist = (pGraveyard->x - x) * (pGraveyard->x - x) + (pGraveyard->y - y) * (pGraveyard->y - y);
|
||||
|
||||
for (uint8 i = BG_AV_NODES_FIRSTAID_STATION; i <= BG_AV_NODES_FROSTWOLF_HUT; ++i)
|
||||
if (m_Nodes[i].OwnerId == player->GetTeamId() && m_Nodes[i].State == POINT_CONTROLED)
|
||||
if (m_Nodes[i].OwnerId == player->GetTeamId() && m_Nodes[i].State == POINT_CONTROLLED)
|
||||
{
|
||||
entry = sGraveyard->GetGraveyard(BG_AV_GraveyardIds[i]);
|
||||
if (entry)
|
||||
@@ -1211,6 +1240,27 @@ GraveyardStruct const* BattlegroundAV::GetClosestGraveyard(Player* player)
|
||||
|
||||
bool BattlegroundAV::SetupBattleground()
|
||||
{
|
||||
if (sBattlegroundMgr->IsBGWeekend(GetBgTypeID(true)))
|
||||
{
|
||||
_reputationTower = 18;
|
||||
_reputationCaptain = 185;
|
||||
_reputationBoss = 525;
|
||||
_reputationPerOwnedGraveyard = 18;
|
||||
_reputationSurvivingCaptain = 175;
|
||||
_reputationSurvivingTower = 18;
|
||||
_reputationPerOwnedMine = 36;
|
||||
}
|
||||
else
|
||||
{
|
||||
_reputationTower = 12;
|
||||
_reputationCaptain = 125;
|
||||
_reputationBoss = sWorld->getIntConfig(CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH);
|
||||
_reputationPerOwnedGraveyard = 12;
|
||||
_reputationSurvivingCaptain = 125;
|
||||
_reputationSurvivingTower = 12;
|
||||
_reputationPerOwnedMine = 24;
|
||||
}
|
||||
|
||||
// Create starting objects
|
||||
|
||||
//spawn node-objects
|
||||
@@ -1735,7 +1785,7 @@ void BattlegroundAV::DestroyNode(BG_AV_Nodes node)
|
||||
m_Nodes[node].TotalOwnerId = m_Nodes[node].OwnerId;
|
||||
m_Nodes[node].PrevOwnerId = m_Nodes[node].OwnerId;
|
||||
m_Nodes[node].PrevState = m_Nodes[node].State;
|
||||
m_Nodes[node].State = (m_Nodes[node].Tower) ? POINT_DESTROYED : POINT_CONTROLED;
|
||||
m_Nodes[node].State = (m_Nodes[node].Tower) ? POINT_DESTROYED : POINT_CONTROLLED;
|
||||
m_Nodes[node].Timer = 0;
|
||||
}
|
||||
|
||||
@@ -1744,7 +1794,7 @@ void BattlegroundAV::InitNode(BG_AV_Nodes node, TeamId teamId, bool tower)
|
||||
m_Nodes[node].TotalOwnerId = teamId;
|
||||
m_Nodes[node].OwnerId = teamId;
|
||||
m_Nodes[node].PrevOwnerId = TEAM_NEUTRAL;
|
||||
m_Nodes[node].State = POINT_CONTROLED;
|
||||
m_Nodes[node].State = POINT_CONTROLLED;
|
||||
m_Nodes[node].PrevState = m_Nodes[node].State;
|
||||
m_Nodes[node].Timer = 0;
|
||||
m_Nodes[node].Tower = tower;
|
||||
@@ -1754,11 +1804,11 @@ void BattlegroundAV::DefendNode(BG_AV_Nodes node, TeamId teamId)
|
||||
{
|
||||
ASSERT(m_Nodes[node].TotalOwnerId == teamId);
|
||||
ASSERT(m_Nodes[node].OwnerId != teamId);
|
||||
ASSERT(m_Nodes[node].State != POINT_CONTROLED && m_Nodes[node].State != POINT_DESTROYED);
|
||||
ASSERT(m_Nodes[node].State != POINT_CONTROLLED && m_Nodes[node].State != POINT_DESTROYED);
|
||||
m_Nodes[node].PrevOwnerId = m_Nodes[node].OwnerId;
|
||||
m_Nodes[node].OwnerId = teamId;
|
||||
m_Nodes[node].PrevState = m_Nodes[node].State;
|
||||
m_Nodes[node].State = POINT_CONTROLED;
|
||||
m_Nodes[node].State = POINT_CONTROLLED;
|
||||
m_Nodes[node].Timer = 0;
|
||||
}
|
||||
|
||||
@@ -1805,7 +1855,7 @@ bool BattlegroundAV::IsAllTowersControlledAndCaptainAlive(TeamId teamId) const
|
||||
{
|
||||
for (BG_AV_Nodes i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_STONEHEART_BUNKER; ++i) // alliance towers controlled
|
||||
{
|
||||
if (m_Nodes[i].State == POINT_CONTROLED)
|
||||
if (m_Nodes[i].State == POINT_CONTROLLED)
|
||||
{
|
||||
if (m_Nodes[i].OwnerId != TEAM_ALLIANCE)
|
||||
return false;
|
||||
@@ -1824,7 +1874,7 @@ bool BattlegroundAV::IsAllTowersControlledAndCaptainAlive(TeamId teamId) const
|
||||
{
|
||||
for (BG_AV_Nodes i = BG_AV_NODES_ICEBLOOD_TOWER; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i) // horde towers controlled
|
||||
{
|
||||
if (m_Nodes[i].State == POINT_CONTROLED)
|
||||
if (m_Nodes[i].State == POINT_CONTROLLED)
|
||||
{
|
||||
if (m_Nodes[i].OwnerId != TEAM_HORDE)
|
||||
return false;
|
||||
|
||||
@@ -29,23 +29,18 @@
|
||||
#define SEND_MSG_NEAR_LOSE 120
|
||||
|
||||
#define BG_AV_KILL_BOSS 4
|
||||
#define BG_AV_REP_BOSS (sWorld->getIntConfig(CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH)) // Blizzlike default is 350
|
||||
|
||||
#define BG_AV_KILL_CAPTAIN 3
|
||||
#define BG_AV_REP_CAPTAIN 125
|
||||
#define BG_AV_RES_CAPTAIN 100
|
||||
|
||||
#define BG_AV_KILL_TOWER 3
|
||||
#define BG_AV_REP_TOWER 12
|
||||
#define BG_AV_RES_TOWER 75
|
||||
|
||||
#define BG_AV_GET_COMMANDER 1 //for a safely returned wingcommander
|
||||
//bonushonor at the end
|
||||
#define BG_AV_KILL_SURVIVING_TOWER 2
|
||||
#define BG_AV_REP_SURVIVING_TOWER 12
|
||||
|
||||
#define BG_AV_KILL_SURVIVING_CAPTAIN 2
|
||||
#define BG_AV_REP_SURVIVING_CAPTAIN 125
|
||||
|
||||
#define AV_EVENT_START_BATTLE 9166 // Achievement: The Alterac Blitz
|
||||
|
||||
@@ -1546,7 +1541,7 @@ enum BG_AV_States
|
||||
POINT_NEUTRAL = 0,
|
||||
POINT_ASSAULTED = 1,
|
||||
POINT_DESTROYED = 2,
|
||||
POINT_CONTROLED = 3
|
||||
POINT_CONTROLLED = 3
|
||||
};
|
||||
|
||||
//alliance_control neutral_control horde_control
|
||||
@@ -1848,6 +1843,14 @@ private:
|
||||
uint32 m_CaptainBuffTimer[2] {};
|
||||
bool m_CaptainAlive[2] {};
|
||||
|
||||
uint32 _reputationTower = 0; // 12, 18
|
||||
uint32 _reputationCaptain = 0; // 125, 185
|
||||
uint32 _reputationBoss = 0; // 350, 525
|
||||
uint32 _reputationPerOwnedGraveyard = 0; // 12, 18
|
||||
uint32 _reputationSurvivingCaptain = 0; // 125, 175
|
||||
uint32 _reputationSurvivingTower = 0; // 12, 18
|
||||
uint32 _reputationPerOwnedMine = 0; // 24, 36
|
||||
|
||||
bool m_IsInformedNearVictory[2] {};
|
||||
};
|
||||
|
||||
|
||||
@@ -263,6 +263,8 @@ void BattlegroundEY::HandleAreaTrigger(Player* player, uint32 trigger)
|
||||
|
||||
bool BattlegroundEY::SetupBattleground()
|
||||
{
|
||||
_honorTics = BattlegroundMgr::IsBGWeekend(GetBgTypeID(true)) ? BG_EY_HONOR_TICK_WEEKEND : BG_EY_HONOR_TICK_NORMAL;
|
||||
|
||||
// doors
|
||||
AddObject(BG_EY_OBJECT_DOOR_A, BG_OBJECT_A_DOOR_EY_ENTRY, 2527.6f, 1596.91f, 1262.13f, -3.12414f, -0.173642f, -0.001515f, 0.98477f, -0.008594f, RESPAWN_IMMEDIATELY);
|
||||
AddObject(BG_EY_OBJECT_DOOR_H, BG_OBJECT_H_DOOR_EY_ENTRY, 1803.21f, 1539.49f, 1261.09f, 3.14159f, 0.173648f, 0, 0.984808f, 0, RESPAWN_IMMEDIATELY);
|
||||
@@ -355,7 +357,6 @@ void BattlegroundEY::Init()
|
||||
Battleground::Init();
|
||||
|
||||
_bgEvents.Reset();
|
||||
_honorTics = BattlegroundMgr::IsBGWeekend(GetBgTypeID(true)) ? BG_EY_HONOR_TICK_WEEKEND : BG_EY_HONOR_TICK_NORMAL;
|
||||
_ownedPointsCount[TEAM_ALLIANCE] = 0;
|
||||
_ownedPointsCount[TEAM_HORDE] = 0;
|
||||
_flagKeeperGUID.Clear();
|
||||
|
||||
@@ -426,6 +426,19 @@ void BattlegroundWS::HandleAreaTrigger(Player* player, uint32 trigger)
|
||||
|
||||
bool BattlegroundWS::SetupBattleground()
|
||||
{
|
||||
if (sBattlegroundMgr->IsBGWeekend(GetBgTypeID(true)))
|
||||
{
|
||||
_reputationCapture = 45;
|
||||
_honorWinKills = 3;
|
||||
_honorEndKills = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
_reputationCapture = 35;
|
||||
_honorWinKills = 1;
|
||||
_honorEndKills = 2;
|
||||
}
|
||||
|
||||
// flags
|
||||
AddObject(BG_WS_OBJECT_A_FLAG, BG_OBJECT_A_FLAG_WS_ENTRY, 1540.423f, 1481.325f, 351.8284f, 3.089233f, 0, 0, 0.9996573f, 0.02617699f, RESPAWN_IMMEDIATELY);
|
||||
AddObject(BG_WS_OBJECT_H_FLAG, BG_OBJECT_H_FLAG_WS_ENTRY, 916.0226f, 1434.405f, 345.413f, 0.01745329f, 0, 0, 0.008726535f, 0.9999619f, RESPAWN_IMMEDIATELY);
|
||||
@@ -486,19 +499,6 @@ void BattlegroundWS::Init()
|
||||
_flagState[TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE;
|
||||
_lastFlagCaptureTeam = TEAM_NEUTRAL;
|
||||
|
||||
if (sBattlegroundMgr->IsBGWeekend(GetBgTypeID(true)))
|
||||
{
|
||||
_reputationCapture = 45;
|
||||
_honorWinKills = 3;
|
||||
_honorEndKills = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
_reputationCapture = 35;
|
||||
_honorWinKills = 1;
|
||||
_honorEndKills = 2;
|
||||
}
|
||||
|
||||
uint32 bgWarsongFlagsConfig = sWorld->getIntConfig(CONFIG_BATTLEGROUND_WARSONG_FLAGS);
|
||||
_configurableMaxTeamScore = bgWarsongFlagsConfig > 0
|
||||
? bgWarsongFlagsConfig
|
||||
|
||||
@@ -573,7 +573,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo)
|
||||
}
|
||||
case CONDITION_WORLD_SCRIPT:
|
||||
{
|
||||
condMeets = sWorldState->IsConditionFulfilled(static_cast<WorldStateCondition>(ConditionValue1), static_cast<WorldStateConditionState>(ConditionValue2));
|
||||
condMeets = sWorldState->IsConditionFulfilled(ConditionValue1, ConditionValue2);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1496,10 +1496,6 @@ namespace lfg
|
||||
{
|
||||
uint32 dungeonId = (it2->first & 0x00FFFFFF); // Compare dungeon ids
|
||||
|
||||
// Skip faction-specific locks if cross-faction is enabled
|
||||
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && (it2->second == LFG_LOCKSTATUS_QUEST_NOT_COMPLETED || it2->second == LFG_LOCKSTATUS_MISSING_ITEM))
|
||||
continue;
|
||||
|
||||
LfgDungeonSet::iterator itDungeon = dungeons.find(dungeonId);
|
||||
if (itDungeon != dungeons.end())
|
||||
{
|
||||
@@ -2687,24 +2683,17 @@ namespace lfg
|
||||
LFGQueue& LFGMgr::GetQueue(ObjectGuid guid)
|
||||
{
|
||||
uint8 queueId = 0;
|
||||
if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP))
|
||||
if (guid.IsGroup())
|
||||
{
|
||||
queueId = TEAM_ALLIANCE;
|
||||
LfgGuidSet const& players = GetPlayers(guid);
|
||||
ObjectGuid pguid = players.empty() ? ObjectGuid::Empty : (*players.begin());
|
||||
if (pguid)
|
||||
queueId = GetTeam(pguid);
|
||||
else
|
||||
queueId = GetTeam(GetLeader(guid));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (guid.IsGroup())
|
||||
{
|
||||
LfgGuidSet const& players = GetPlayers(guid);
|
||||
ObjectGuid pguid = players.empty() ? ObjectGuid::Empty : (*players.begin());
|
||||
if (pguid)
|
||||
queueId = GetTeam(pguid);
|
||||
else
|
||||
queueId = GetTeam(GetLeader(guid));
|
||||
}
|
||||
else
|
||||
queueId = GetTeam(guid);
|
||||
}
|
||||
queueId = GetTeam(guid);
|
||||
return QueuesStore[queueId];
|
||||
}
|
||||
|
||||
|
||||
@@ -2773,11 +2773,7 @@ bool Creature::LoadCreaturesAddon(bool reload)
|
||||
|
||||
//Load Path
|
||||
if (cainfo->path_id != 0)
|
||||
{
|
||||
if (sWorld->getBoolConfig(CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE))
|
||||
setActive(true);
|
||||
m_path_id = cainfo->path_id;
|
||||
}
|
||||
|
||||
if (!cainfo->auras.empty())
|
||||
{
|
||||
@@ -3897,3 +3893,27 @@ std::string Creature::GetDebugInfo() const
|
||||
<< " WaypointPath: " << GetWaypointPath() << " SpawnId: " << GetSpawnId();
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
// Note: This is called in a tight (heavy) loop, is it critical that all checks are FAST and are hopefully only simple conditionals.
|
||||
bool Creature::IsUpdateNeeded()
|
||||
{
|
||||
if (WorldObject::IsUpdateNeeded())
|
||||
return true;
|
||||
|
||||
if (GetMap()->isCellMarked(GetCurrentCell().GetCellCoord().GetId()))
|
||||
return true;
|
||||
|
||||
if (IsInCombat())
|
||||
return true;
|
||||
|
||||
if (IsVisibilityOverridden())
|
||||
return true;
|
||||
|
||||
if (GetMotionMaster()->HasMovementGeneratorType(WAYPOINT_MOTION_TYPE))
|
||||
return true;
|
||||
|
||||
if (HasUnitState(UNIT_STATE_EVADE))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class CreatureGroup;
|
||||
|
||||
#define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY
|
||||
|
||||
class Creature : public Unit, public GridObject<Creature>, public MovableMapObject
|
||||
class Creature : public Unit, public GridObject<Creature>, public MovableMapObject, public UpdatableMapObject
|
||||
{
|
||||
public:
|
||||
explicit Creature(bool isWorldObject = false);
|
||||
@@ -436,6 +436,8 @@ public:
|
||||
|
||||
std::string GetDebugInfo() const override;
|
||||
|
||||
bool IsUpdateNeeded() override;
|
||||
|
||||
protected:
|
||||
bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 Entry, uint32 vehId, const CreatureData* data = nullptr);
|
||||
bool InitEntry(uint32 entry, const CreatureData* data = nullptr);
|
||||
|
||||
@@ -31,7 +31,7 @@ enum DynamicObjectType
|
||||
DYNAMIC_OBJECT_FARSIGHT_FOCUS = 0x2,
|
||||
};
|
||||
|
||||
class DynamicObject : public WorldObject, public GridObject<DynamicObject>, public MovableMapObject
|
||||
class DynamicObject : public WorldObject, public GridObject<DynamicObject>, public MovableMapObject, public UpdatableMapObject
|
||||
{
|
||||
public:
|
||||
DynamicObject(bool isWorldObject);
|
||||
|
||||
@@ -3076,3 +3076,21 @@ std::string GameObject::GetDebugInfo() const
|
||||
<< "SpawnId: " << GetSpawnId() << " GoState: " << std::to_string(GetGoState()) << " ScriptId: " << GetScriptId() << " AIName: " << GetAIName();
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
// Note: This is called in a tight (heavy) loop, is it critical that all checks are FAST and are hopefully only simple conditionals.
|
||||
bool GameObject::IsUpdateNeeded()
|
||||
{
|
||||
if (WorldObject::IsUpdateNeeded())
|
||||
return true;
|
||||
|
||||
if (GetMap()->isCellMarked(GetCurrentCell().GetCellCoord().GetId()))
|
||||
return true;
|
||||
|
||||
if (IsVisibilityOverridden())
|
||||
return true;
|
||||
|
||||
if (IsTransport())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ enum LootState
|
||||
// 5 sec for bobber catch
|
||||
#define FISHING_BOBBER_READY_TIME 5
|
||||
|
||||
class GameObject : public WorldObject, public GridObject<GameObject>, public MovableMapObject
|
||||
class GameObject : public WorldObject, public GridObject<GameObject>, public MovableMapObject, public UpdatableMapObject
|
||||
{
|
||||
public:
|
||||
explicit GameObject();
|
||||
@@ -362,6 +362,8 @@ public:
|
||||
void SaveStateToDB();
|
||||
|
||||
std::string GetDebugInfo() const override;
|
||||
|
||||
bool IsUpdateNeeded() override;
|
||||
protected:
|
||||
bool AIM_Initialize();
|
||||
GameObjectModel* CreateModel();
|
||||
|
||||
@@ -1188,6 +1188,7 @@ void WorldObject::AddToWorld()
|
||||
{
|
||||
Object::AddToWorld();
|
||||
GetMap()->GetZoneAndAreaId(GetPhaseMask(), _zoneId, _areaId, GetPositionX(), GetPositionY(), GetPositionZ());
|
||||
GetMap()->AddObjectToPendingUpdateList(this);
|
||||
}
|
||||
|
||||
void WorldObject::RemoveFromWorld()
|
||||
@@ -2510,6 +2511,13 @@ void WorldObject::GetGameObjectListWithEntryInGrid(std::list<GameObject*>& gameo
|
||||
Cell::VisitGridObjects(this, searcher, maxSearchRange);
|
||||
}
|
||||
|
||||
void WorldObject::GetGameObjectListWithEntryInGrid(std::list<GameObject*>& gameobjectList, std::vector<uint32> const& entries, float maxSearchRange) const
|
||||
{
|
||||
Acore::AllGameObjectsMatchingOneEntryInRange check(this, entries, maxSearchRange);
|
||||
Acore::GameObjectListSearcher searcher(this, gameobjectList, check);
|
||||
Cell::VisitGridObjects(this, searcher, maxSearchRange);
|
||||
}
|
||||
|
||||
void WorldObject::GetCreatureListWithEntryInGrid(std::list<Creature*>& creatureList, uint32 entry, float maxSearchRange) const
|
||||
{
|
||||
Acore::AllCreaturesOfEntryInRange check(this, entry, maxSearchRange);
|
||||
@@ -2517,6 +2525,13 @@ void WorldObject::GetCreatureListWithEntryInGrid(std::list<Creature*>& creatureL
|
||||
Cell::VisitGridObjects(this, searcher, maxSearchRange);
|
||||
}
|
||||
|
||||
void WorldObject::GetCreatureListWithEntryInGrid(std::list<Creature*>& creatureList, std::vector<uint32> const& entries, float maxSearchRange) const
|
||||
{
|
||||
Acore::AllCreaturesMatchingOneEntryInRange check(this, entries, maxSearchRange);
|
||||
Acore::CreatureListSearcher searcher(this, creatureList, check);
|
||||
Cell::VisitGridObjects(this, searcher, maxSearchRange);
|
||||
}
|
||||
|
||||
void WorldObject::GetDeadCreatureListInGrid(std::list<Creature*>& creaturedeadList, float maxSearchRange, bool alive /*= false*/) const
|
||||
{
|
||||
Acore::AllDeadCreaturesInRange check(this, maxSearchRange, alive);
|
||||
@@ -3220,3 +3235,27 @@ void WorldObject::RemoveAllowedLooter(ObjectGuid guid)
|
||||
{
|
||||
_allowedLooters.erase(guid);
|
||||
}
|
||||
|
||||
bool WorldObject::IsUpdateNeeded()
|
||||
{
|
||||
if (isActiveObject())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WorldObject::CanBeAddedToMapUpdateList()
|
||||
{
|
||||
switch (GetTypeId())
|
||||
{
|
||||
case TYPEID_UNIT:
|
||||
return IsCreature();
|
||||
case TYPEID_DYNAMICOBJECT:
|
||||
case TYPEID_GAMEOBJECT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -230,6 +230,12 @@ public:
|
||||
|
||||
DataMap CustomData;
|
||||
|
||||
template<typename... T>
|
||||
[[nodiscard]] bool EntryEquals(T... entries) const
|
||||
{
|
||||
return ((GetEntry() == entries) || ...);
|
||||
}
|
||||
|
||||
protected:
|
||||
Object();
|
||||
|
||||
@@ -399,14 +405,58 @@ class MovableMapObject
|
||||
protected:
|
||||
MovableMapObject() = default;
|
||||
|
||||
private:
|
||||
[[nodiscard]] Cell const& GetCurrentCell() const { return _currentCell; }
|
||||
|
||||
private:
|
||||
void SetCurrentCell(Cell const& cell) { _currentCell = cell; }
|
||||
|
||||
Cell _currentCell;
|
||||
MapObjectCellMoveState _moveState{MAP_OBJECT_CELL_MOVE_NONE};
|
||||
};
|
||||
|
||||
class UpdatableMapObject
|
||||
{
|
||||
friend class Map;
|
||||
|
||||
public:
|
||||
enum UpdateState : uint8
|
||||
{
|
||||
NotUpdating,
|
||||
PendingAdd,
|
||||
Updating
|
||||
};
|
||||
|
||||
protected:
|
||||
UpdatableMapObject() : _mapUpdateListOffset(0), _mapUpdateState(NotUpdating) { }
|
||||
|
||||
private:
|
||||
void SetMapUpdateListOffset(std::size_t const offset)
|
||||
{
|
||||
ASSERT(_mapUpdateState == Updating, "Attempted to set update list offset when object is not in map update list");
|
||||
_mapUpdateListOffset = offset;
|
||||
}
|
||||
|
||||
size_t GetMapUpdateListOffset() const
|
||||
{
|
||||
ASSERT(_mapUpdateState == Updating, "Attempted to get update list offset when object is not in map update list");
|
||||
return _mapUpdateListOffset;
|
||||
}
|
||||
|
||||
void SetUpdateState(UpdateState state)
|
||||
{
|
||||
_mapUpdateState = state;
|
||||
}
|
||||
|
||||
UpdateState GetUpdateState() const
|
||||
{
|
||||
return _mapUpdateState;
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t _mapUpdateListOffset;
|
||||
UpdateState _mapUpdateState;
|
||||
};
|
||||
|
||||
class WorldObject : public Object, public WorldLocation
|
||||
{
|
||||
protected:
|
||||
@@ -554,7 +604,9 @@ public:
|
||||
|
||||
[[nodiscard]] Player* SelectNearestPlayer(float distance = 0) const;
|
||||
void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange) const;
|
||||
void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& gameobjectList, std::vector<uint32> const& entries, float maxSearchRange) const;
|
||||
void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange) const;
|
||||
void GetCreatureListWithEntryInGrid(std::list<Creature*>& creatureList, std::vector<uint32> const& entries, float maxSearchRange) const;
|
||||
void GetDeadCreatureListInGrid(std::list<Creature*>& lList, float maxSearchRange, bool alive = false) const;
|
||||
|
||||
void DestroyForNearbyPlayers();
|
||||
@@ -633,6 +685,9 @@ public:
|
||||
[[nodiscard]] GuidUnorderedSet const& GetAllowedLooters() const;
|
||||
void RemoveAllowedLooter(ObjectGuid guid);
|
||||
|
||||
virtual bool IsUpdateNeeded();
|
||||
bool CanBeAddedToMapUpdateList();
|
||||
|
||||
std::string GetDebugInfo() const override;
|
||||
|
||||
// Event handler
|
||||
|
||||
@@ -1684,6 +1684,8 @@ template <class T>
|
||||
void Player::UpdateVisibilityOf(T* target, UpdateData& data,
|
||||
std::vector<Unit*>& visibleNow)
|
||||
{
|
||||
GetMap()->AddObjectToPendingUpdateList(target);
|
||||
|
||||
if (HaveAtClient(target))
|
||||
{
|
||||
if (!CanSeeOrDetect(target, false, true))
|
||||
|
||||
@@ -5910,9 +5910,17 @@ uint32 Unit::GetDiseasesByCaster(ObjectGuid casterGUID, uint8 mode)
|
||||
else if (mode == 2)
|
||||
{
|
||||
Aura* aura = (*i)->GetBase();
|
||||
if (aura && !aura->IsRemoved() && aura->GetDuration() > 0)
|
||||
if ((aura->GetApplyTime() + aura->GetMaxDuration() / 1000 + 8) > (GameTime::GetGameTime().count() + aura->GetDuration() / 1000))
|
||||
aura->SetDuration(aura->GetDuration() + 3000);
|
||||
uint32 countMin = aura->GetMaxDuration();
|
||||
uint32 countMax = aura->GetSpellInfo()->GetMaxDuration() + 9000;
|
||||
|
||||
if (AuraEffect const* epidemic = (*i)->GetCaster()->GetAuraEffectOfRankedSpell(49036, EFFECT_0))
|
||||
countMax += epidemic->GetAmount();
|
||||
|
||||
if (countMin < countMax)
|
||||
{
|
||||
aura->SetDuration(uint32(aura->GetDuration() + 3000));
|
||||
aura->SetMaxDuration(countMin + 3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
++i;
|
||||
@@ -13739,7 +13747,7 @@ void Unit::CombatStart(Unit* victim, bool initialAggro)
|
||||
victim->SetInCombatWith(this);
|
||||
|
||||
// Update leash timer when attacking creatures
|
||||
if (victim->IsCreature())
|
||||
if (victim->IsCreature() && this != victim)
|
||||
victim->ToCreature()->UpdateLeashExtensionTime();
|
||||
|
||||
// Xinef: If pet started combat - put owner in combat
|
||||
|
||||
@@ -116,6 +116,9 @@ void GridObjectUnloader::Visit(GridRefMgr<T>& m)
|
||||
//Example: Flame Leviathan Turret 33139 is summoned when a creature is deleted
|
||||
//TODO: Check if that script has the correct logic. Do we really need to summons something before deleting?
|
||||
obj->CleanupsBeforeDelete();
|
||||
|
||||
obj->GetMap()->RemoveObjectFromMapUpdateList(obj);
|
||||
|
||||
///- object will get delinked from the manager when deleted
|
||||
delete obj;
|
||||
}
|
||||
|
||||
@@ -354,19 +354,6 @@ void MessageDistDelivererToHostile::Visit(DynamicObjectMapType& m)
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void ObjectUpdater::Visit(GridRefMgr<T>& m)
|
||||
{
|
||||
T* obj;
|
||||
for (typename GridRefMgr<T>::iterator iter = m.begin(); iter != m.end(); )
|
||||
{
|
||||
obj = iter->GetSource();
|
||||
++iter;
|
||||
if (obj->IsInWorld() && (i_largeOnly == obj->IsVisibilityOverridden()))
|
||||
obj->Update(i_timeDiff);
|
||||
}
|
||||
}
|
||||
|
||||
bool AnyDeadUnitObjectInRangeCheck::operator()(Player* u)
|
||||
{
|
||||
return !u->IsAlive() && !u->HasGhostAura() && i_searchObj->IsWithinDistInMap(u, i_range);
|
||||
@@ -396,7 +383,3 @@ bool AnyDeadUnitSpellTargetInRangeCheck::operator()(Creature* u)
|
||||
{
|
||||
return AnyDeadUnitObjectInRangeCheck::operator()(u) && i_check(u);
|
||||
}
|
||||
|
||||
template void ObjectUpdater::Visit<Creature>(CreatureMapType&);
|
||||
template void ObjectUpdater::Visit<GameObject>(GameObjectMapType&);
|
||||
template void ObjectUpdater::Visit<DynamicObject>(DynamicObjectMapType&);
|
||||
|
||||
@@ -154,16 +154,6 @@ namespace Acore
|
||||
}
|
||||
};
|
||||
|
||||
struct ObjectUpdater
|
||||
{
|
||||
uint32 i_timeDiff;
|
||||
bool i_largeOnly;
|
||||
explicit ObjectUpdater(const uint32 diff, bool largeOnly) : i_timeDiff(diff), i_largeOnly(largeOnly) {}
|
||||
template<class T> void Visit(GridRefMgr<T>& m);
|
||||
void Visit(PlayerMapType&) {}
|
||||
void Visit(CorpseMapType&) {}
|
||||
};
|
||||
|
||||
// SEARCHERS & LIST SEARCHERS & WORKERS
|
||||
|
||||
// WorldObject searchers & workers
|
||||
@@ -1457,6 +1447,24 @@ namespace Acore
|
||||
float m_fRange;
|
||||
};
|
||||
|
||||
class AllGameObjectsMatchingOneEntryInRange
|
||||
{
|
||||
public:
|
||||
AllGameObjectsMatchingOneEntryInRange(WorldObject const* object, std::vector<uint32> entries, float maxRange) : m_pObject(object), m_uiEntries(entries), m_fRange(maxRange) {}
|
||||
bool operator()(GameObject *go)
|
||||
{
|
||||
if (std::ranges::any_of( m_uiEntries, [go](uint32 entry) { return go->GetEntry() == entry; }) && m_pObject->IsWithinDist(go, m_fRange, false))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
WorldObject const* m_pObject;
|
||||
std::vector<uint32> m_uiEntries;
|
||||
float m_fRange;
|
||||
};
|
||||
|
||||
class AllCreaturesOfEntryInRange
|
||||
{
|
||||
public:
|
||||
@@ -1475,6 +1483,24 @@ namespace Acore
|
||||
float m_fRange;
|
||||
};
|
||||
|
||||
class AllCreaturesMatchingOneEntryInRange
|
||||
{
|
||||
public:
|
||||
AllCreaturesMatchingOneEntryInRange(WorldObject const* object, std::vector<uint32> entries, float maxRange) : m_pObject(object), m_uiEntries(entries), m_fRange(maxRange) {}
|
||||
bool operator()(Unit* unit)
|
||||
{
|
||||
if (std::ranges::any_of(m_uiEntries, [unit](uint32 entry) { return unit->GetEntry() == entry; }) && m_pObject->IsWithinDist(unit, m_fRange, false))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
WorldObject const* m_pObject;
|
||||
std::vector<uint32> m_uiEntries;
|
||||
float m_fRange;
|
||||
};
|
||||
|
||||
class MostHPMissingGroupInRange
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -51,7 +51,7 @@ void GroupMgr::InitGroupIds()
|
||||
}
|
||||
}
|
||||
|
||||
void GroupMgr::RegisterGroupId(uint32 groupId)
|
||||
void GroupMgr::RegisterGroupId(ObjectGuid::LowType groupId)
|
||||
{
|
||||
// Allocation was done in InitGroupIds()
|
||||
_groupIds[groupId] = true;
|
||||
|
||||
@@ -390,7 +390,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
|
||||
bool senderIsPlayer = AccountMgr::IsPlayerAccount(GetSecurity());
|
||||
bool receiverIsPlayer = AccountMgr::IsPlayerAccount(receiver ? receiver->GetSession()->GetSecurity() : SEC_PLAYER);
|
||||
|
||||
if (sender->GetLevel() < sWorld->getIntConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ) && receiver != sender)
|
||||
if (sender->GetLevel() < sWorld->getIntConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ) && receiver != sender && receiver && !receiver->IsGameMaster())
|
||||
{
|
||||
ChatHandler(this).SendNotification(LANG_WHISPER_REQ, sWorld->getIntConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ));
|
||||
return;
|
||||
|
||||
@@ -31,6 +31,7 @@ enum AreaTableIDs : uint32
|
||||
AREA_ELWYNN_FOREST = 12,
|
||||
AREA_DUROTAR = 14,
|
||||
AREA_DUSTWALLOW_MARSH = 15,
|
||||
AREA_AZSHARA = 16,
|
||||
AREA_THE_BARRENS = 17,
|
||||
AREA_WESTERN_PLAGUELANDS = 28,
|
||||
AREA_STRANGLETHORN_VALE = 33,
|
||||
|
||||
+125
-122
@@ -80,6 +80,7 @@ Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) :
|
||||
m_parentMap = (_parent ? _parent : this);
|
||||
|
||||
_zonePlayerCountMap.clear();
|
||||
_updatableObjectListRecheckTimer.SetInterval(UPDATABLE_OBJECT_LIST_RECHECK_TIMER);
|
||||
|
||||
//lets initialize visibility distance for map
|
||||
Map::InitVisibilityDistance();
|
||||
@@ -492,45 +493,7 @@ bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Map::VisitNearbyCellsOfPlayer(Player* player, TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& gridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& worldVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& largeGridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& largeWorldVisitor)
|
||||
{
|
||||
// check for valid position
|
||||
if (!player->IsPositionValid())
|
||||
return;
|
||||
|
||||
// check normal grid activation range of the player
|
||||
VisitNearbyCellsOf(player, gridVisitor, worldVisitor, largeGridVisitor, largeWorldVisitor);
|
||||
|
||||
// check maximum visibility distance for large creatures
|
||||
CellArea area = Cell::CalculateCellArea(player->GetPositionX(), player->GetPositionY(), MAX_VISIBILITY_DISTANCE);
|
||||
|
||||
for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x)
|
||||
{
|
||||
for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y)
|
||||
{
|
||||
// marked cells are those that have been visited
|
||||
// don't visit the same cell twice
|
||||
uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
|
||||
if (isCellMarkedLarge(cell_id))
|
||||
continue;
|
||||
|
||||
markCellLarge(cell_id);
|
||||
CellCoord pair(x, y);
|
||||
Cell cell(pair);
|
||||
|
||||
Visit(cell, largeGridVisitor);
|
||||
Visit(cell, largeWorldVisitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& gridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& worldVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& largeGridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& largeWorldVisitor)
|
||||
void Map::MarkNearbyCellsOf(WorldObject* obj)
|
||||
{
|
||||
// Check for valid position
|
||||
if (!obj->IsPositionValid())
|
||||
@@ -538,30 +501,13 @@ void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Acore::Objec
|
||||
|
||||
// Update mobs/objects in ALL visible cells around object!
|
||||
CellArea area = Cell::CalculateCellArea(obj->GetPositionX(), obj->GetPositionY(), obj->GetGridActivationRange());
|
||||
|
||||
for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x)
|
||||
{
|
||||
for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y)
|
||||
{
|
||||
// marked cells are those that have been visited
|
||||
// don't visit the same cell twice
|
||||
uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
|
||||
if (isCellMarked(cell_id))
|
||||
continue;
|
||||
|
||||
markCell(cell_id);
|
||||
CellCoord pair(x, y);
|
||||
Cell cell(pair);
|
||||
|
||||
Visit(cell, gridVisitor);
|
||||
Visit(cell, worldVisitor);
|
||||
|
||||
if (!isCellMarkedLarge(cell_id))
|
||||
{
|
||||
markCellLarge(cell_id);
|
||||
Visit(cell, largeGridVisitor);
|
||||
Visit(cell, largeWorldVisitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -613,39 +559,10 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
|
||||
return;
|
||||
}
|
||||
|
||||
/// update active cells around players and active objects
|
||||
_updatableObjectListRecheckTimer.Update(t_diff);
|
||||
resetMarkedCells();
|
||||
resetMarkedCellsLarge();
|
||||
|
||||
// Prepare object updaters
|
||||
Acore::ObjectUpdater updater(t_diff, false);
|
||||
|
||||
// For creature
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer> grid_object_update(updater);
|
||||
|
||||
// For pets
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer> world_object_update(updater);
|
||||
|
||||
// For large creatures
|
||||
Acore::ObjectUpdater largeObjectUpdater(t_diff, true);
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer> grid_large_object_update(largeObjectUpdater);
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer> world_large_object_update(largeObjectUpdater);
|
||||
|
||||
// pussywizard: container for far creatures in combat with players
|
||||
std::vector<Creature*> updateList;
|
||||
updateList.reserve(10);
|
||||
|
||||
// Update non-player active objects
|
||||
for (m_activeNonPlayersIter = m_activeNonPlayers.begin(); m_activeNonPlayersIter != m_activeNonPlayers.end();)
|
||||
{
|
||||
WorldObject* obj = *m_activeNonPlayersIter;
|
||||
++m_activeNonPlayersIter;
|
||||
|
||||
if (obj && obj->IsInWorld())
|
||||
VisitNearbyCellsOf(obj, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update);
|
||||
}
|
||||
|
||||
// Update players and their associated objects
|
||||
// Update players
|
||||
for (m_mapRefIter = m_mapRefMgr.begin(); m_mapRefIter != m_mapRefMgr.end(); ++m_mapRefIter)
|
||||
{
|
||||
Player* player = m_mapRefIter->GetSource();
|
||||
@@ -654,53 +571,37 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
|
||||
continue;
|
||||
|
||||
player->Update(s_diff);
|
||||
VisitNearbyCellsOfPlayer(player, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update);
|
||||
|
||||
// If player is using far sight, update viewpoint
|
||||
if (WorldObject* viewPoint = player->GetViewpoint())
|
||||
if (_updatableObjectListRecheckTimer.Passed())
|
||||
{
|
||||
if (Creature* viewCreature = viewPoint->ToCreature())
|
||||
{
|
||||
VisitNearbyCellsOf(viewCreature, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update);
|
||||
}
|
||||
else if (DynamicObject* viewObject = viewPoint->ToDynObject())
|
||||
{
|
||||
VisitNearbyCellsOf(viewObject, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update);
|
||||
}
|
||||
}
|
||||
MarkNearbyCellsOf(player);
|
||||
|
||||
// handle updates for creatures in combat with player and are more than X yards away
|
||||
if (player->IsInCombat())
|
||||
{
|
||||
updateList.clear();
|
||||
float rangeSq = player->GetGridActivationRange() - 1.0f;
|
||||
rangeSq *= rangeSq;
|
||||
|
||||
HostileReference* ref = player->getHostileRefMgr().getFirst();
|
||||
while (ref)
|
||||
// If player is using far sight, update viewpoint
|
||||
if (WorldObject* viewPoint = player->GetViewpoint())
|
||||
{
|
||||
if (Unit* unit = ref->GetSource()->GetOwner())
|
||||
if (Creature* cre = unit->ToCreature())
|
||||
if (cre->FindMap() == player->FindMap() && cre->GetExactDist2dSq(player) > rangeSq)
|
||||
updateList.push_back(cre);
|
||||
ref = ref->next();
|
||||
if (Creature* viewCreature = viewPoint->ToCreature())
|
||||
MarkNearbyCellsOf(viewCreature);
|
||||
else if (DynamicObject* viewObject = viewPoint->ToDynObject())
|
||||
MarkNearbyCellsOf(viewObject);
|
||||
}
|
||||
|
||||
for (Creature* cre : updateList)
|
||||
VisitNearbyCellsOf(cre, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update);
|
||||
}
|
||||
}
|
||||
|
||||
// Update transports - pussywizard: transports updated after VisitNearbyCellsOf, grids around are loaded, everything ok
|
||||
for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();)
|
||||
if (_updatableObjectListRecheckTimer.Passed())
|
||||
{
|
||||
MotionTransport* transport = *_transportsUpdateIter;
|
||||
++_transportsUpdateIter;
|
||||
// Mark all cells near active objects
|
||||
for (m_activeNonPlayersIter = m_activeNonPlayers.begin(); m_activeNonPlayersIter != m_activeNonPlayers.end(); ++m_activeNonPlayersIter)
|
||||
{
|
||||
WorldObject* obj = *m_activeNonPlayersIter;
|
||||
if (!obj || !obj->IsInWorld())
|
||||
continue;
|
||||
|
||||
if (transport->IsInWorld())
|
||||
transport->Update(t_diff);
|
||||
MarkNearbyCellsOf(obj);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateNonPlayerObjects(t_diff);
|
||||
|
||||
SendObjectUpdates();
|
||||
|
||||
///- Process necessary scripts
|
||||
@@ -728,6 +629,101 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
|
||||
METRIC_TAG("map_instanceid", std::to_string(GetInstanceId())));
|
||||
}
|
||||
|
||||
void Map::UpdateNonPlayerObjects(uint32 const diff)
|
||||
{
|
||||
for (WorldObject* obj : _pendingAddUpdatableObjectList)
|
||||
_AddObjectToUpdateList(obj);
|
||||
_pendingAddUpdatableObjectList.clear();
|
||||
|
||||
if (_updatableObjectListRecheckTimer.Passed())
|
||||
{
|
||||
for (uint32 i = 0; i < _updatableObjectList.size();)
|
||||
{
|
||||
WorldObject* obj = _updatableObjectList[i];
|
||||
if (!obj->IsInWorld())
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
obj->Update(diff);
|
||||
|
||||
if (!obj->IsUpdateNeeded())
|
||||
{
|
||||
_RemoveObjectFromUpdateList(obj);
|
||||
// Intentional no iteration here, obj is swapped with last element in
|
||||
// _updatableObjectList so next loop will update that object at the same index
|
||||
}
|
||||
else
|
||||
++i;
|
||||
}
|
||||
_updatableObjectListRecheckTimer.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint32 i = 0; i < _updatableObjectList.size(); ++i)
|
||||
{
|
||||
WorldObject* obj = _updatableObjectList[i];
|
||||
if (!obj->IsInWorld())
|
||||
continue;
|
||||
|
||||
obj->Update(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::AddObjectToPendingUpdateList(WorldObject* obj)
|
||||
{
|
||||
if (!obj->CanBeAddedToMapUpdateList())
|
||||
return;
|
||||
|
||||
UpdatableMapObject* mapUpdatableObject = dynamic_cast<UpdatableMapObject*>(obj);
|
||||
if (mapUpdatableObject->GetUpdateState() != UpdatableMapObject::UpdateState::NotUpdating)
|
||||
return;
|
||||
|
||||
_pendingAddUpdatableObjectList.insert(obj);
|
||||
mapUpdatableObject->SetUpdateState(UpdatableMapObject::UpdateState::PendingAdd);
|
||||
}
|
||||
|
||||
// Internal use only
|
||||
void Map::_AddObjectToUpdateList(WorldObject* obj)
|
||||
{
|
||||
UpdatableMapObject* mapUpdatableObject = dynamic_cast<UpdatableMapObject*>(obj);
|
||||
ASSERT(mapUpdatableObject && mapUpdatableObject->GetUpdateState() == UpdatableMapObject::UpdateState::PendingAdd);
|
||||
|
||||
mapUpdatableObject->SetUpdateState(UpdatableMapObject::UpdateState::Updating);
|
||||
mapUpdatableObject->SetMapUpdateListOffset(_updatableObjectList.size());
|
||||
_updatableObjectList.push_back(obj);
|
||||
}
|
||||
|
||||
// Internal use only
|
||||
void Map::_RemoveObjectFromUpdateList(WorldObject* obj)
|
||||
{
|
||||
UpdatableMapObject* mapUpdatableObject = dynamic_cast<UpdatableMapObject*>(obj);
|
||||
ASSERT(mapUpdatableObject && mapUpdatableObject->GetUpdateState() == UpdatableMapObject::UpdateState::Updating);
|
||||
|
||||
if (obj != _updatableObjectList.back())
|
||||
{
|
||||
dynamic_cast<UpdatableMapObject*>(_updatableObjectList.back())->SetMapUpdateListOffset(mapUpdatableObject->GetMapUpdateListOffset());
|
||||
std::swap(_updatableObjectList[mapUpdatableObject->GetMapUpdateListOffset()], _updatableObjectList.back());
|
||||
}
|
||||
|
||||
_updatableObjectList.pop_back();
|
||||
mapUpdatableObject->SetUpdateState(UpdatableMapObject::UpdateState::NotUpdating);
|
||||
}
|
||||
|
||||
void Map::RemoveObjectFromMapUpdateList(WorldObject* obj)
|
||||
{
|
||||
if (!obj->CanBeAddedToMapUpdateList())
|
||||
return;
|
||||
|
||||
UpdatableMapObject* mapUpdatableObject = dynamic_cast<UpdatableMapObject*>(obj);
|
||||
if (mapUpdatableObject->GetUpdateState() == UpdatableMapObject::UpdateState::PendingAdd)
|
||||
_pendingAddUpdatableObjectList.erase(obj);
|
||||
else if (mapUpdatableObject->GetUpdateState() == UpdatableMapObject::UpdateState::Updating)
|
||||
_RemoveObjectFromUpdateList(obj);
|
||||
}
|
||||
|
||||
void Map::HandleDelayedVisibility()
|
||||
{
|
||||
if (i_objectsForDelayedVisibility.empty())
|
||||
@@ -795,7 +791,10 @@ void Map::RemoveFromMap(T* obj, bool remove)
|
||||
obj->ResetMap();
|
||||
|
||||
if (remove)
|
||||
{
|
||||
RemoveObjectFromMapUpdateList(obj);
|
||||
DeleteFromWorld(obj);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -831,6 +830,10 @@ void Map::RemoveFromMap(MotionTransport* obj, bool remove)
|
||||
|
||||
obj->ResetMap();
|
||||
|
||||
// Transports are never actually deleted, but it *should* be safe to clear
|
||||
// from update list when removing from world
|
||||
RemoveObjectFromMapUpdateList(obj);
|
||||
|
||||
if (remove)
|
||||
{
|
||||
// if option set then object already saved at this moment
|
||||
|
||||
+18
-12
@@ -34,6 +34,7 @@
|
||||
#include "Position.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "Timer.h"
|
||||
#include "GridTerrainData.h"
|
||||
#include <bitset>
|
||||
#include <list>
|
||||
@@ -85,6 +86,7 @@ struct ScriptAction
|
||||
|
||||
#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations
|
||||
#define MIN_UNLOAD_DELAY 1 // immediate unload
|
||||
#define UPDATABLE_OBJECT_LIST_RECHECK_TIMER 30 * IN_MILLISECONDS // Time to recheck update object list
|
||||
|
||||
struct PositionFullTerrainStatus
|
||||
{
|
||||
@@ -181,14 +183,7 @@ public:
|
||||
template<class T> bool AddToMap(T*, bool checkTransport = false);
|
||||
template<class T> void RemoveFromMap(T*, bool);
|
||||
|
||||
void VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& gridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& worldVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& largeGridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& largeWorldVisitor);
|
||||
void VisitNearbyCellsOfPlayer(Player* player, TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& gridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& worldVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, GridTypeMapContainer>& largeGridVisitor,
|
||||
TypeContainerVisitor<Acore::ObjectUpdater, WorldTypeMapContainer>& largeWorldVisitor);
|
||||
void MarkNearbyCellsOf(WorldObject* obj);
|
||||
|
||||
virtual void Update(const uint32, const uint32, bool thread = true);
|
||||
|
||||
@@ -317,9 +312,6 @@ public:
|
||||
void resetMarkedCells() { marked_cells.reset(); }
|
||||
bool isCellMarked(uint32 pCellId) { return marked_cells.test(pCellId); }
|
||||
void markCell(uint32 pCellId) { marked_cells.set(pCellId); }
|
||||
void resetMarkedCellsLarge() { marked_cells_large.reset(); }
|
||||
bool isCellMarkedLarge(uint32 pCellId) { return marked_cells_large.test(pCellId); }
|
||||
void markCellLarge(uint32 pCellId) { marked_cells_large.set(pCellId); }
|
||||
|
||||
[[nodiscard]] bool HavePlayers() const { return !m_mapRefMgr.IsEmpty(); }
|
||||
[[nodiscard]] uint32 GetPlayersCountExceptGMs() const;
|
||||
@@ -512,6 +504,12 @@ public:
|
||||
uint32 GetCreatedCellsInGridCount(uint16 const x, uint16 const y);
|
||||
uint32 GetCreatedCellsInMapCount();
|
||||
|
||||
void AddObjectToPendingUpdateList(WorldObject* obj);
|
||||
void RemoveObjectFromMapUpdateList(WorldObject* obj);
|
||||
|
||||
typedef std::vector<WorldObject*> UpdatableObjectList;
|
||||
typedef std::unordered_set<WorldObject*> PendingAddUpdatableObjectList;
|
||||
|
||||
private:
|
||||
|
||||
template<class T> void InitializeObject(T* obj);
|
||||
@@ -576,7 +574,6 @@ private:
|
||||
Map* m_parentMap;
|
||||
|
||||
std::bitset<TOTAL_NUMBER_OF_CELLS_PER_MAP * TOTAL_NUMBER_OF_CELLS_PER_MAP> marked_cells;
|
||||
std::bitset<TOTAL_NUMBER_OF_CELLS_PER_MAP * TOTAL_NUMBER_OF_CELLS_PER_MAP> marked_cells_large;
|
||||
|
||||
bool i_scriptLock;
|
||||
std::unordered_set<WorldObject*> i_objectsToRemove;
|
||||
@@ -610,6 +607,11 @@ private:
|
||||
m_activeNonPlayers.erase(obj);
|
||||
}
|
||||
|
||||
void UpdateNonPlayerObjects(uint32 const diff);
|
||||
|
||||
void _AddObjectToUpdateList(WorldObject* obj);
|
||||
void _RemoveObjectFromUpdateList(WorldObject* obj);
|
||||
|
||||
std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _creatureRespawnTimes;
|
||||
std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _goRespawnTimes;
|
||||
|
||||
@@ -637,6 +639,10 @@ private:
|
||||
std::unordered_set<Corpse*> _corpseBones;
|
||||
|
||||
std::unordered_set<Object*> _updateObjects;
|
||||
|
||||
UpdatableObjectList _updatableObjectList;
|
||||
PendingAddUpdatableObjectList _pendingAddUpdatableObjectList;
|
||||
IntervalTimer _updatableObjectListRecheckTimer;
|
||||
};
|
||||
|
||||
enum InstanceResetMethod
|
||||
|
||||
@@ -969,6 +969,20 @@ MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
|
||||
return Impl[slot]->GetMovementGeneratorType();
|
||||
}
|
||||
|
||||
bool MotionMaster::HasMovementGeneratorType(MovementGeneratorType type) const
|
||||
{
|
||||
if (empty() && type == IDLE_MOTION_TYPE)
|
||||
return true;
|
||||
|
||||
for (int i = _top; i >= 0; --i)
|
||||
{
|
||||
if (Impl[i] && Impl[i]->GetMovementGeneratorType() == type)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Xinef: Escort system
|
||||
uint32 MotionMaster::GetCurrentSplineId() const
|
||||
{
|
||||
|
||||
@@ -243,6 +243,7 @@ public:
|
||||
#endif
|
||||
[[nodiscard]] MovementGeneratorType GetCurrentMovementGeneratorType() const;
|
||||
[[nodiscard]] MovementGeneratorType GetMotionSlotType(int slot) const;
|
||||
bool HasMovementGeneratorType(MovementGeneratorType type) const;
|
||||
[[nodiscard]] uint32 GetCurrentSplineId() const; // Xinef: Escort system
|
||||
|
||||
void propagateSpeedChange();
|
||||
|
||||
@@ -137,10 +137,16 @@ void FleeingMovementGenerator<T>::SetTargetLocation(T* owner)
|
||||
_path->Clear();
|
||||
}
|
||||
|
||||
if (owner->IsPlayer())
|
||||
_path->SetSlopeCheck(true);
|
||||
|
||||
_path->SetPathLengthLimit(30.0f);
|
||||
bool result = _path->CalculatePath(destination.GetPositionX(), destination.GetPositionY(), destination.GetPositionZ());
|
||||
if (!result || (_path->GetPathType() & PathType(PATHFIND_NOPATH | PATHFIND_SHORTCUT | PATHFIND_FARFROMPOLY)))
|
||||
if (!result || (_path->GetPathType() & PathType(PATHFIND_NOPATH | PATHFIND_SHORTCUT | PATHFIND_FARFROMPOLY | PATHFIND_NOT_USING_PATH)))
|
||||
{
|
||||
if (_fleeTargetGUID)
|
||||
++_invalidPathsCount;
|
||||
|
||||
_timer.Reset(100);
|
||||
return;
|
||||
}
|
||||
@@ -149,15 +155,13 @@ void FleeingMovementGenerator<T>::SetTargetLocation(T* owner)
|
||||
if (_path->getPathLength() < MIN_PATH_LENGTH)
|
||||
{
|
||||
if (_fleeTargetGUID)
|
||||
{
|
||||
++_shortPathsCount;
|
||||
}
|
||||
++_invalidPathsCount;
|
||||
|
||||
_timer.Reset(100);
|
||||
return;
|
||||
}
|
||||
|
||||
_shortPathsCount = 0;
|
||||
_invalidPathsCount = 0;
|
||||
|
||||
Movement::MoveSplineInit init(owner);
|
||||
init.MovebyPath(_path->GetPath());
|
||||
@@ -172,7 +176,7 @@ void FleeingMovementGenerator<T>::GetPoint(T* owner, Position& position)
|
||||
float casterDistance = 0.f;
|
||||
float casterAngle = 0.f;
|
||||
Unit* fleeTarget = nullptr;
|
||||
if (_shortPathsCount < 5)
|
||||
if (_invalidPathsCount < 5)
|
||||
fleeTarget = ObjectAccessor::GetUnit(*owner, _fleeTargetGUID);
|
||||
|
||||
if (fleeTarget)
|
||||
|
||||
@@ -26,7 +26,7 @@ template<class T>
|
||||
class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovementGenerator<T> >
|
||||
{
|
||||
public:
|
||||
explicit FleeingMovementGenerator(ObjectGuid fleeTargetGUID) : _path(nullptr), _fleeTargetGUID(fleeTargetGUID), _timer(0), _interrupt(false), _shortPathsCount(0) { }
|
||||
explicit FleeingMovementGenerator(ObjectGuid fleeTargetGUID) : _path(nullptr), _fleeTargetGUID(fleeTargetGUID), _timer(0), _interrupt(false), _invalidPathsCount(0) { }
|
||||
|
||||
MovementGeneratorType GetMovementGeneratorType() override { return FLEEING_MOTION_TYPE; }
|
||||
|
||||
@@ -43,7 +43,7 @@ class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovem
|
||||
ObjectGuid _fleeTargetGUID;
|
||||
TimeTracker _timer;
|
||||
bool _interrupt;
|
||||
uint8 _shortPathsCount;
|
||||
uint8 _invalidPathsCount;
|
||||
};
|
||||
|
||||
class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature>
|
||||
|
||||
@@ -556,7 +556,7 @@ void PathGenerator::BuildPointPath(const float* startPoint, const float* endPoin
|
||||
}
|
||||
|
||||
// Special case with start and end positions very close to each other
|
||||
if (_polyLength == 1 && pointCount == 1)
|
||||
if (_polyLength == 1 && pointCount == 1 && !(dtResult & DT_SLOPE_TOO_STEEP))
|
||||
{
|
||||
// First point is start position, append end position
|
||||
dtVcopy(&pathPoints[1 * VERTEX_SIZE], endPoint);
|
||||
@@ -564,6 +564,22 @@ void PathGenerator::BuildPointPath(const float* startPoint, const float* endPoin
|
||||
}
|
||||
else if (pointCount < 2 || dtStatusFailed(dtResult))
|
||||
{
|
||||
// If its too steep, just return incomplete path.
|
||||
if (pointCount > 0 && dtResult & DT_SLOPE_TOO_STEEP)
|
||||
{
|
||||
_pathPoints.resize(pointCount);
|
||||
for (uint32 i = 0; i < pointCount; ++i)
|
||||
_pathPoints[i] = G3D::Vector3(pathPoints[i * VERTEX_SIZE + 2], pathPoints[i * VERTEX_SIZE], pathPoints[i * VERTEX_SIZE + 1]);
|
||||
|
||||
NormalizePath();
|
||||
|
||||
// first point is always our current location - we need the next one
|
||||
SetActualEndPosition(_pathPoints[pointCount - 1]);
|
||||
|
||||
_type = PathType(_type | PATHFIND_INCOMPLETE);
|
||||
return;
|
||||
}
|
||||
|
||||
// only happens if pass bad data to findStraightPath or navmesh is broken
|
||||
// single point paths can be generated here
|
||||
/// @todo check the exact cases
|
||||
@@ -884,7 +900,9 @@ dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPo
|
||||
|
||||
if (canCheckSlope && !IsSwimmableSegment(iterPos, steerPos) && !IsWalkableClimb(iterPos, steerPos))
|
||||
{
|
||||
return DT_FAILURE;
|
||||
nsmoothPath--;
|
||||
*smoothPathSize = nsmoothPath;
|
||||
return DT_FAILURE | DT_SLOPE_TOO_STEEP;
|
||||
}
|
||||
|
||||
// Handle end of path and off-mesh links when close enough.
|
||||
|
||||
@@ -5405,7 +5405,7 @@ void Spell::TakePower()
|
||||
|
||||
void Spell::TakeAmmo()
|
||||
{
|
||||
if (m_attackType == RANGED_ATTACK && m_caster->IsPlayer())
|
||||
if (m_attackType == RANGED_ATTACK && m_caster->IsPlayer() && !m_spellInfo->HasAttribute(SPELL_ATTR6_DO_NOT_CONSUME_RESOURCES))
|
||||
{
|
||||
Item* pItem = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK);
|
||||
|
||||
|
||||
@@ -27,115 +27,115 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<uint32>(CONFIG_ENABLE_SINFO_LOGIN, "Server.LoginInfo", 0);
|
||||
|
||||
///- Read all rates from the config file
|
||||
SetConfigValue<float>(RATE_HEALTH, "Rate.Health", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0; }, "> 0");
|
||||
SetConfigValue<float>(RATE_POWER_MANA, "Rate.Mana", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0; }, "> 0");
|
||||
SetConfigValue<float>(RATE_POWER_RAGE_INCOME, "Rate.Rage.Income", 1);
|
||||
SetConfigValue<float>(RATE_POWER_RAGE_LOSS, "Rate.Rage.Loss", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0; }, "> 0");
|
||||
SetConfigValue<float>(RATE_POWER_RUNICPOWER_INCOME, "Rate.RunicPower.Income", 1);
|
||||
SetConfigValue<float>(RATE_POWER_RUNICPOWER_LOSS, "Rate.RunicPower.Loss", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0; }, "> 0");
|
||||
SetConfigValue<float>(RATE_POWER_FOCUS, "Rate.Focus", 1);
|
||||
SetConfigValue<float>(RATE_POWER_ENERGY, "Rate.Energy", 1);
|
||||
SetConfigValue<float>(RATE_HEALTH, "Rate.Health", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0.0f; }, "> 0");
|
||||
SetConfigValue<float>(RATE_POWER_MANA, "Rate.Mana", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0.0f; }, "> 0");
|
||||
SetConfigValue<float>(RATE_POWER_RAGE_INCOME, "Rate.Rage.Income", 1.0f);
|
||||
SetConfigValue<float>(RATE_POWER_RAGE_LOSS, "Rate.Rage.Loss", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0.0f; }, "> 0");
|
||||
SetConfigValue<float>(RATE_POWER_RUNICPOWER_INCOME, "Rate.RunicPower.Income", 1.0f);
|
||||
SetConfigValue<float>(RATE_POWER_RUNICPOWER_LOSS, "Rate.RunicPower.Loss", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0.0f; }, "> 0");
|
||||
SetConfigValue<float>(RATE_POWER_FOCUS, "Rate.Focus", 1.0f);
|
||||
SetConfigValue<float>(RATE_POWER_ENERGY, "Rate.Energy", 1.0f);
|
||||
|
||||
SetConfigValue<float>(RATE_SKILL_DISCOVERY, "Rate.Skill.Discovery", 1);
|
||||
SetConfigValue<float>(RATE_SKILL_DISCOVERY, "Rate.Skill.Discovery", 1.0f);
|
||||
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_POOR, "Rate.Drop.Item.Poor", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_NORMAL, "Rate.Drop.Item.Normal", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_UNCOMMON, "Rate.Drop.Item.Uncommon", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_RARE, "Rate.Drop.Item.Rare", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_EPIC, "Rate.Drop.Item.Epic", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_LEGENDARY, "Rate.Drop.Item.Legendary", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_ARTIFACT, "Rate.Drop.Item.Artifact", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_REFERENCED, "Rate.Drop.Item.Referenced", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_REFERENCED_AMOUNT, "Rate.Drop.Item.ReferencedAmount", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_GROUP_AMOUNT, "Rate.Drop.Item.GroupAmount", 1);
|
||||
SetConfigValue<float>(RATE_DROP_MONEY, "Rate.Drop.Money", 1);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_POOR, "Rate.Drop.Item.Poor", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_NORMAL, "Rate.Drop.Item.Normal", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_UNCOMMON, "Rate.Drop.Item.Uncommon", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_RARE, "Rate.Drop.Item.Rare", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_EPIC, "Rate.Drop.Item.Epic", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_LEGENDARY, "Rate.Drop.Item.Legendary", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_ARTIFACT, "Rate.Drop.Item.Artifact", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_REFERENCED, "Rate.Drop.Item.Referenced", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_REFERENCED_AMOUNT, "Rate.Drop.Item.ReferencedAmount", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_ITEM_GROUP_AMOUNT, "Rate.Drop.Item.GroupAmount", 1.0f);
|
||||
SetConfigValue<float>(RATE_DROP_MONEY, "Rate.Drop.Money", 1.0f);
|
||||
|
||||
SetConfigValue<float>(RATE_REWARD_QUEST_MONEY, "Rate.RewardQuestMoney", 1);
|
||||
SetConfigValue<float>(RATE_REWARD_BONUS_MONEY, "Rate.RewardBonusMoney", 1);
|
||||
SetConfigValue<float>(RATE_XP_KILL, "Rate.XP.Kill", 1);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_AV, "Rate.XP.BattlegroundKillAV", 1);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_WSG, "Rate.XP.BattlegroundKillWSG", 1);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_AB, "Rate.XP.BattlegroundKillAB", 1);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_EOTS, "Rate.XP.BattlegroundKillEOTS", 1);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_SOTA, "Rate.XP.BattlegroundKillSOTA", 1);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_IC, "Rate.XP.BattlegroundKillIC", 1);
|
||||
SetConfigValue<float>(RATE_XP_QUEST, "Rate.XP.Quest", 1);
|
||||
SetConfigValue<float>(RATE_XP_QUEST_DF, "Rate.XP.Quest.DF", 1);
|
||||
SetConfigValue<float>(RATE_XP_EXPLORE, "Rate.XP.Explore", 1);
|
||||
SetConfigValue<float>(RATE_XP_PET, "Rate.XP.Pet", 1);
|
||||
SetConfigValue<float>(RATE_XP_PET_NEXT_LEVEL, "Rate.Pet.LevelXP", 0);
|
||||
SetConfigValue<float>(RATE_REPAIRCOST, "Rate.RepairCost", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_REWARD_QUEST_MONEY, "Rate.RewardQuestMoney", 1.0f);
|
||||
SetConfigValue<float>(RATE_REWARD_BONUS_MONEY, "Rate.RewardBonusMoney", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_KILL, "Rate.XP.Kill", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_AV, "Rate.XP.BattlegroundKillAV", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_WSG, "Rate.XP.BattlegroundKillWSG", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_AB, "Rate.XP.BattlegroundKillAB", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_EOTS, "Rate.XP.BattlegroundKillEOTS", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_SOTA, "Rate.XP.BattlegroundKillSOTA", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_BG_KILL_IC, "Rate.XP.BattlegroundKillIC", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_QUEST, "Rate.XP.Quest", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_QUEST_DF, "Rate.XP.Quest.DF", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_EXPLORE, "Rate.XP.Explore", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_PET, "Rate.XP.Pet", 1.0f);
|
||||
SetConfigValue<float>(RATE_XP_PET_NEXT_LEVEL, "Rate.Pet.LevelXP", 0.05f);
|
||||
SetConfigValue<float>(RATE_REPAIRCOST, "Rate.RepairCost", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_POOR, "Rate.SellValue.Item.Poor", 1);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_NORMAL, "Rate.SellValue.Item.Normal", 1);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_UNCOMMON, "Rate.SellValue.Item.Uncommon", 1);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_RARE, "Rate.SellValue.Item.Rare", 1);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_EPIC, "Rate.SellValue.Item.Epic", 1);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_LEGENDARY, "Rate.SellValue.Item.Legendary", 1);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_ARTIFACT, "Rate.SellValue.Item.Artifact", 1);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_HEIRLOOM, "Rate.SellValue.Item.Heirloom", 1);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_POOR, "Rate.SellValue.Item.Poor", 1.0f);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_NORMAL, "Rate.SellValue.Item.Normal", 1.0f);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_UNCOMMON, "Rate.SellValue.Item.Uncommon", 1.0f);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_RARE, "Rate.SellValue.Item.Rare", 1.0f);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_EPIC, "Rate.SellValue.Item.Epic", 1.0f);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_LEGENDARY, "Rate.SellValue.Item.Legendary", 1.0f);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_ARTIFACT, "Rate.SellValue.Item.Artifact", 1.0f);
|
||||
SetConfigValue<float>(RATE_SELLVALUE_ITEM_HEIRLOOM, "Rate.SellValue.Item.Heirloom", 1.0f);
|
||||
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_POOR, "Rate.BuyValue.Item.Poor", 1);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_NORMAL, "Rate.BuyValue.Item.Normal", 1);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_UNCOMMON, "Rate.BuyValue.Item.Uncommon", 1);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_RARE, "Rate.BuyValue.Item.Rare", 1);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_EPIC, "Rate.BuyValue.Item.Epic", 1);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_LEGENDARY, "Rate.BuyValue.Item.Legendary", 1);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_ARTIFACT, "Rate.BuyValue.Item.Artifact", 1);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_HEIRLOOM, "Rate.BuyValue.Item.Heirloom", 1);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_POOR, "Rate.BuyValue.Item.Poor", 1.0f);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_NORMAL, "Rate.BuyValue.Item.Normal", 1.0f);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_UNCOMMON, "Rate.BuyValue.Item.Uncommon", 1.0f);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_RARE, "Rate.BuyValue.Item.Rare", 1.0f);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_EPIC, "Rate.BuyValue.Item.Epic", 1.0f);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_LEGENDARY, "Rate.BuyValue.Item.Legendary", 1.0f);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_ARTIFACT, "Rate.BuyValue.Item.Artifact", 1.0f);
|
||||
SetConfigValue<float>(RATE_BUYVALUE_ITEM_HEIRLOOM, "Rate.BuyValue.Item.Heirloom", 1.0f);
|
||||
|
||||
SetConfigValue<float>(RATE_REPUTATION_GAIN, "Rate.Reputation.Gain", 1);
|
||||
SetConfigValue<float>(RATE_REPUTATION_LOWLEVEL_KILL, "Rate.Reputation.LowLevel.Kill", 1);
|
||||
SetConfigValue<float>(RATE_REPUTATION_LOWLEVEL_QUEST, "Rate.Reputation.LowLevel.Quest", 1);
|
||||
SetConfigValue<float>(RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS, "Rate.Reputation.RecruitAFriendBonus", 0);
|
||||
SetConfigValue<float>(RATE_CREATURE_NORMAL_DAMAGE, "Rate.Creature.Normal.Damage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_ELITE_DAMAGE, "Rate.Creature.Elite.Elite.Damage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RAREELITE_DAMAGE, "Rate.Creature.Elite.RAREELITE.Damage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE, "Rate.Creature.Elite.WORLDBOSS.Damage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RARE_DAMAGE, "Rate.Creature.Elite.RARE.Damage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_NORMAL_HP, "Rate.Creature.Normal.HP", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_ELITE_HP, "Rate.Creature.Elite.Elite.HP", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RAREELITE_HP, "Rate.Creature.Elite.RAREELITE.HP", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_WORLDBOSS_HP, "Rate.Creature.Elite.WORLDBOSS.HP", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RARE_HP, "Rate.Creature.Elite.RARE.HP", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_NORMAL_SPELLDAMAGE, "Rate.Creature.Normal.SpellDamage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE, "Rate.Creature.Elite.Elite.SpellDamage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE, "Rate.Creature.Elite.RAREELITE.SpellDamage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE, "Rate.Creature.Elite.WORLDBOSS.SpellDamage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RARE_SPELLDAMAGE, "Rate.Creature.Elite.RARE.SpellDamage", 1);
|
||||
SetConfigValue<float>(RATE_CREATURE_AGGRO, "Rate.Creature.Aggro", 1);
|
||||
SetConfigValue<float>(RATE_REST_INGAME, "Rate.Rest.InGame", 1);
|
||||
SetConfigValue<float>(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY, "Rate.Rest.Offline.InTavernOrCity", 1);
|
||||
SetConfigValue<float>(RATE_REST_OFFLINE_IN_WILDERNESS, "Rate.Rest.Offline.InWilderness", 1);
|
||||
SetConfigValue<float>(RATE_REST_MAX_BONUS, "Rate.Rest.MaxBonus", 1);
|
||||
SetConfigValue<float>(RATE_DAMAGE_FALL, "Rate.Damage.Fall", 1);
|
||||
SetConfigValue<float>(RATE_AUCTION_TIME, "Rate.Auction.Time", 1);
|
||||
SetConfigValue<float>(RATE_AUCTION_DEPOSIT, "Rate.Auction.Deposit", 1);
|
||||
SetConfigValue<float>(RATE_AUCTION_CUT, "Rate.Auction.Cut", 1);
|
||||
SetConfigValue<float>(RATE_HONOR, "Rate.Honor", 1);
|
||||
SetConfigValue<float>(RATE_ARENA_POINTS, "Rate.ArenaPoints", 1);
|
||||
SetConfigValue<float>(RATE_INSTANCE_RESET_TIME, "Rate.InstanceResetTime", 1);
|
||||
SetConfigValue<float>(RATE_REPUTATION_GAIN, "Rate.Reputation.Gain", 1.0f);
|
||||
SetConfigValue<float>(RATE_REPUTATION_LOWLEVEL_KILL, "Rate.Reputation.LowLevel.Kill", 1.0f);
|
||||
SetConfigValue<float>(RATE_REPUTATION_LOWLEVEL_QUEST, "Rate.Reputation.LowLevel.Quest", 1.0f);
|
||||
SetConfigValue<float>(RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS, "Rate.Reputation.RecruitAFriendBonus", 0.1f);
|
||||
SetConfigValue<float>(RATE_CREATURE_NORMAL_DAMAGE, "Rate.Creature.Normal.Damage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_ELITE_DAMAGE, "Rate.Creature.Elite.Elite.Damage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RAREELITE_DAMAGE, "Rate.Creature.Elite.RAREELITE.Damage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE, "Rate.Creature.Elite.WORLDBOSS.Damage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RARE_DAMAGE, "Rate.Creature.Elite.RARE.Damage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_NORMAL_HP, "Rate.Creature.Normal.HP", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_ELITE_HP, "Rate.Creature.Elite.Elite.HP", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RAREELITE_HP, "Rate.Creature.Elite.RAREELITE.HP", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_WORLDBOSS_HP, "Rate.Creature.Elite.WORLDBOSS.HP", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RARE_HP, "Rate.Creature.Elite.RARE.HP", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_NORMAL_SPELLDAMAGE, "Rate.Creature.Normal.SpellDamage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE, "Rate.Creature.Elite.Elite.SpellDamage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE, "Rate.Creature.Elite.RAREELITE.SpellDamage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE, "Rate.Creature.Elite.WORLDBOSS.SpellDamage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_ELITE_RARE_SPELLDAMAGE, "Rate.Creature.Elite.RARE.SpellDamage", 1.0f);
|
||||
SetConfigValue<float>(RATE_CREATURE_AGGRO, "Rate.Creature.Aggro", 1.0f);
|
||||
SetConfigValue<float>(RATE_REST_INGAME, "Rate.Rest.InGame", 1.0f);
|
||||
SetConfigValue<float>(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY, "Rate.Rest.Offline.InTavernOrCity", 1.0f);
|
||||
SetConfigValue<float>(RATE_REST_OFFLINE_IN_WILDERNESS, "Rate.Rest.Offline.InWilderness", 1.0f);
|
||||
SetConfigValue<float>(RATE_REST_MAX_BONUS, "Rate.Rest.MaxBonus", 1.5f);
|
||||
SetConfigValue<float>(RATE_DAMAGE_FALL, "Rate.Damage.Fall", 1.0f);
|
||||
SetConfigValue<float>(RATE_AUCTION_TIME, "Rate.Auction.Time", 1.0f);
|
||||
SetConfigValue<float>(RATE_AUCTION_DEPOSIT, "Rate.Auction.Deposit", 1.0f);
|
||||
SetConfigValue<float>(RATE_AUCTION_CUT, "Rate.Auction.Cut", 1.0f);
|
||||
SetConfigValue<float>(RATE_HONOR, "Rate.Honor", 1.0f);
|
||||
SetConfigValue<float>(RATE_ARENA_POINTS, "Rate.ArenaPoints", 1.0f);
|
||||
SetConfigValue<float>(RATE_INSTANCE_RESET_TIME, "Rate.InstanceResetTime", 1.0f);
|
||||
|
||||
SetConfigValue<float>(RATE_MISS_CHANCE_MULTIPLIER_TARGET_CREATURE, "Rate.MissChanceMultiplier.TargetCreature", 11);
|
||||
SetConfigValue<float>(RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER, "Rate.MissChanceMultiplier.TargetPlayer", 7);
|
||||
SetConfigValue<float>(RATE_MISS_CHANCE_MULTIPLIER_TARGET_CREATURE, "Rate.MissChanceMultiplier.TargetCreature", 11.0f);
|
||||
SetConfigValue<float>(RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER, "Rate.MissChanceMultiplier.TargetPlayer", 7.0f);
|
||||
SetConfigValue<bool>(CONFIG_MISS_CHANCE_MULTIPLIER_ONLY_FOR_PLAYERS, "Rate.MissChanceMultiplier.OnlyAffectsPlayer", false);
|
||||
|
||||
SetConfigValue<float>(RATE_TALENT, "Rate.Talent", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_TALENT_PET, "Rate.Talent.Pet", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_TALENT, "Rate.Talent", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_TALENT_PET, "Rate.Talent.Pet", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
// Controls Player movespeed rate.
|
||||
SetConfigValue<float>(RATE_MOVESPEED_PLAYER, "Rate.MoveSpeed.Player", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_MOVESPEED_PLAYER, "Rate.MoveSpeed.Player", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
|
||||
// Controls all npc movespeed rate.
|
||||
SetConfigValue<float>(RATE_MOVESPEED_NPC, "Rate.MoveSpeed.NPC", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_MOVESPEED_NPC, "Rate.MoveSpeed.NPC", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
|
||||
SetConfigValue<float>(RATE_CORPSE_DECAY_LOOTED, "Rate.Corpse.Decay.Looted", 0);
|
||||
SetConfigValue<float>(RATE_CORPSE_DECAY_LOOTED, "Rate.Corpse.Decay.Looted", 0.5f);
|
||||
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_ON_DEATH, "DurabilityLoss.OnDeath", 10, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0 && value <= 100; }, ">= 0 && <= 100");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_ON_DEATH, "DurabilityLoss.OnDeath", 10.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 100.0f; }, ">= 0 && <= 100");
|
||||
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_DAMAGE, "DurabilityLossChance.Damage", 0, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_ABSORB, "DurabilityLossChance.Absorb", 0, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_PARRY, "DurabilityLossChance.Parry", 0, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_BLOCK, "DurabilityLossChance.Block", 0, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_DAMAGE, "DurabilityLossChance.Damage", 0.5f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_ABSORB, "DurabilityLossChance.Absorb", 0.5f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_PARRY, "DurabilityLossChance.Parry", 0.05f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
SetConfigValue<float>(RATE_DURABILITY_LOSS_BLOCK, "DurabilityLossChance.Block", 0.05f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f; }, ">= 0");
|
||||
|
||||
///- Read other configuration items from the config file
|
||||
|
||||
@@ -157,7 +157,7 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<bool>(CONFIG_ALLOW_PLAYER_COMMANDS, "AllowPlayerCommands", 1);
|
||||
SetConfigValue<bool>(CONFIG_PRESERVE_CUSTOM_CHANNELS, "PreserveCustomChannels", false);
|
||||
SetConfigValue<uint32>(CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION, "PreserveCustomChannelDuration", 14);
|
||||
SetConfigValue<uint32>(CONFIG_INTERVAL_SAVE, "PlayerSaveInterval", 15);
|
||||
SetConfigValue<uint32>(CONFIG_INTERVAL_SAVE, "PlayerSaveInterval", 900000);
|
||||
SetConfigValue<uint32>(CONFIG_INTERVAL_DISCONNECT_TOLERANCE, "DisconnectToleranceInterval", 0);
|
||||
SetConfigValue<bool>(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, "PlayerSave.Stats.SaveOnlyOnLogout", true);
|
||||
|
||||
@@ -165,7 +165,7 @@ void WorldConfig::BuildConfigCache()
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_INTERVAL_MAPUPDATE, "MapUpdateInterval", 10, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value >= MIN_MAP_UPDATE_DELAY; }, ">= MIN_MAP_UPDATE_DELAY");
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_INTERVAL_CHANGEWEATHER, "ChangeWeatherInterval", 10);
|
||||
SetConfigValue<uint32>(CONFIG_INTERVAL_CHANGEWEATHER, "ChangeWeatherInterval", 600000);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_PORT_WORLD, "WorldServerPort", 8085, ConfigValueCache::Reloadable::No);
|
||||
|
||||
@@ -174,10 +174,10 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<uint32>(CONFIG_SOCKET_TIMEOUTTIME_ACTIVE, "SocketTimeOutTimeActive", 60000);
|
||||
SetConfigValue<uint32>(CONFIG_SESSION_ADD_DELAY, "SessionAddDelay", 10000);
|
||||
|
||||
SetConfigValue<float>(CONFIG_GROUP_XP_DISTANCE, "MaxGroupXPDistance", 74);
|
||||
SetConfigValue<float>(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE, "MaxRecruitAFriendBonusDistance", 100);
|
||||
SetConfigValue<float>(CONFIG_GROUP_XP_DISTANCE, "MaxGroupXPDistance", 74.0f);
|
||||
SetConfigValue<float>(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE, "MaxRecruitAFriendBonusDistance", 100.0f);
|
||||
|
||||
SetConfigValue<float>(CONFIG_SIGHT_MONSTER, "MonsterSight", 50);
|
||||
SetConfigValue<float>(CONFIG_SIGHT_MONSTER, "MonsterSight", 50.0f);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_GAME_TYPE, "GameType", 0, ConfigValueCache::Reloadable::No);
|
||||
SetConfigValue<uint32>(CONFIG_REALM_ZONE, "RealmZone", REALM_ZONE_DEVELOPMENT, ConfigValueCache::Reloadable::No);
|
||||
@@ -258,11 +258,11 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<bool>(CONFIG_INSTANCE_IGNORE_LEVEL, "Instance.IgnoreLevel", false);
|
||||
SetConfigValue<bool>(CONFIG_INSTANCE_IGNORE_RAID, "Instance.IgnoreRaid", false);
|
||||
SetConfigValue<bool>(CONFIG_INSTANCE_GMSUMMON_PLAYER, "Instance.GMSummonPlayer", false);
|
||||
SetConfigValue<bool>(CONFIG_INSTANCE_SHARED_ID, "Instance.SharedNormalHeroicId", false);
|
||||
SetConfigValue<bool>(CONFIG_INSTANCE_SHARED_ID, "Instance.SharedNormalHeroicId", true);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_INSTANCE_RESET_TIME_HOUR, "Instance.ResetTimeHour", 4);
|
||||
SetConfigValue<uint32>(CONFIG_INSTANCE_RESET_TIME_RELATIVE_TIMESTAMP, "Instance.ResetTimeRelativeTimestamp", 1135814400);
|
||||
SetConfigValue<uint32>(CONFIG_INSTANCE_UNLOAD_DELAY, "Instance.UnloadDelay", 30);
|
||||
SetConfigValue<uint32>(CONFIG_INSTANCE_UNLOAD_DELAY, "Instance.UnloadDelay", 1800000);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_MAX_PRIMARY_TRADE_SKILL, "MaxPrimaryTradeSkill", 2);
|
||||
SetConfigValue<uint32>(CONFIG_MIN_PETITION_SIGNS, "MinPetitionSigns", 9, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 9; }, "<= 9");
|
||||
@@ -279,7 +279,7 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<bool>(CONFIG_ALLOW_GM_GROUP, "GM.AllowInvite", false);
|
||||
SetConfigValue<bool>(CONFIG_ALLOW_GM_FRIEND, "GM.AllowFriend", false);
|
||||
SetConfigValue<bool>(CONFIG_GM_LOWER_SECURITY, "GM.LowerSecurity", false);
|
||||
SetConfigValue<float>(CONFIG_CHANCE_OF_GM_SURVEY, "GM.TicketSystem.ChanceOfGMSurvey", 50);
|
||||
SetConfigValue<float>(CONFIG_CHANCE_OF_GM_SURVEY, "GM.TicketSystem.ChanceOfGMSurvey", 50.0f);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_GROUP_VISIBILITY, "Visibility.GroupMode", 1);
|
||||
|
||||
@@ -344,8 +344,8 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<uint32>(CONFIG_EVENT_ANNOUNCE, "Event.Announce", 0);
|
||||
|
||||
SetConfigValue<float>(CONFIG_CREATURE_LEASH_RADIUS, "CreatureLeashRadius", 30.0f);
|
||||
SetConfigValue<float>(CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS, "CreatureFamilyFleeAssistanceRadius", 30);
|
||||
SetConfigValue<float>(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS, "CreatureFamilyAssistanceRadius", 10);
|
||||
SetConfigValue<float>(CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS, "CreatureFamilyFleeAssistanceRadius", 30.0f);
|
||||
SetConfigValue<float>(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS, "CreatureFamilyAssistanceRadius", 10.0f);
|
||||
SetConfigValue<uint32>(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY, "CreatureFamilyAssistanceDelay", 2000);
|
||||
SetConfigValue<uint32>(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD, "CreatureFamilyAssistancePeriod", 3000);
|
||||
SetConfigValue<uint32>(CONFIG_CREATURE_FAMILY_FLEE_DELAY, "CreatureFamilyFleeDelay", 7000);
|
||||
@@ -402,9 +402,9 @@ void WorldConfig::BuildConfigCache()
|
||||
// always use declined names in the russian client
|
||||
SetConfigValue<bool>(CONFIG_DECLINED_NAMES_USED, "DeclinedNames", GetConfigValue<uint32>(CONFIG_REALM_ZONE) == REALM_ZONE_RUSSIAN);
|
||||
|
||||
SetConfigValue<float>(CONFIG_LISTEN_RANGE_SAY, "ListenRange.Say", 25);
|
||||
SetConfigValue<float>(CONFIG_LISTEN_RANGE_TEXTEMOTE, "ListenRange.TextEmote", 25);
|
||||
SetConfigValue<float>(CONFIG_LISTEN_RANGE_YELL, "ListenRange.Yell", 300);
|
||||
SetConfigValue<float>(CONFIG_LISTEN_RANGE_SAY, "ListenRange.Say", 40.0f);
|
||||
SetConfigValue<float>(CONFIG_LISTEN_RANGE_TEXTEMOTE, "ListenRange.TextEmote", 40.0f);
|
||||
SetConfigValue<float>(CONFIG_LISTEN_RANGE_YELL, "ListenRange.Yell", 300.0f);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, "Battleground.Override.LowLevels.MinPlayers", 0);
|
||||
SetConfigValue<bool>(CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG, "Battleground.DisableQuestShareInBG", false);
|
||||
@@ -419,9 +419,9 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER, "Battleground.QueueAnnouncer.Timer", 30000);
|
||||
SetConfigValue<bool>(CONFIG_BATTLEGROUND_STORE_STATISTICS_ENABLE, "Battleground.StoreStatistics.Enable", false);
|
||||
SetConfigValue<bool>(CONFIG_BATTLEGROUND_TRACK_DESERTERS, "Battleground.TrackDeserters.Enable", false);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, "Battleground.PrematureFinishTimer", 5);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, "Battleground.PrematureFinishTimer", 300000);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_INVITATION_TYPE, "Battleground.InvitationType", 0);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH, "Battleground.PremadeGroupWaitForMatch", 30);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH, "Battleground.PremadeGroupWaitForMatch", 1800000);
|
||||
SetConfigValue<bool>(CONFIG_BG_XP_FOR_KILL, "Battleground.GiveXPForKills", false);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_REPORT_AFK_TIMER, "Battleground.ReportAFK.Timer", 4);
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_REPORT_AFK, "Battleground.ReportAFK", 3, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= 9; }, "> 0 && value <= 9");
|
||||
@@ -437,25 +437,26 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<uint32>(CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS, "Battleground.EyeOfTheStorm.CapturePoints", 1600);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_MAX_RATING_DIFFERENCE, "Arena.MaxRatingDifference", 150);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_RATING_DISCARD_TIMER, "Arena.RatingDiscardTimer", 10);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, "Arena.PreviousOpponentsDiscardTimer", 2);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_RATING_DISCARD_TIMER, "Arena.RatingDiscardTimer", 600000);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, "Arena.PreviousOpponentsDiscardTimer", 120000);
|
||||
SetConfigValue<bool>(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS, "Arena.AutoDistributePoints", false);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS, "Arena.AutoDistributeInterval", 7);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_GAMES_REQUIRED, "Arena.GamesRequired", 10);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_START_RATING, "Arena.ArenaStartRating", 0);
|
||||
SetConfigValue<uint32>(CONFIG_LEGACY_ARENA_START_RATING, "Arena.LegacyArenaStartRating", 1500);
|
||||
SetConfigValue<uint32>(CONFIG_LEGACY_ARENA_POINTS_CALC, "Arena.LegacyArenaPoints", 0);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_START_PERSONAL_RATING, "Arena.ArenaStartPersonalRating", 0);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_START_MATCHMAKER_RATING, "Arena.ArenaStartMatchmakerRating", 1500);
|
||||
SetConfigValue<float>(CONFIG_ARENA_WIN_RATING_MODIFIER_1, "Arena.ArenaWinRatingModifier1", 48);
|
||||
SetConfigValue<float>(CONFIG_ARENA_WIN_RATING_MODIFIER_2, "Arena.ArenaWinRatingModifier2", 24);
|
||||
SetConfigValue<float>(CONFIG_ARENA_LOSE_RATING_MODIFIER, "Arena.ArenaLoseRatingModifier", 24);
|
||||
SetConfigValue<float>(CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER, "Arena.ArenaMatchmakerRatingModifier", 24);
|
||||
SetConfigValue<float>(CONFIG_ARENA_WIN_RATING_MODIFIER_1, "Arena.ArenaWinRatingModifier1", 48.0f);
|
||||
SetConfigValue<float>(CONFIG_ARENA_WIN_RATING_MODIFIER_2, "Arena.ArenaWinRatingModifier2", 24.0f);
|
||||
SetConfigValue<float>(CONFIG_ARENA_LOSE_RATING_MODIFIER, "Arena.ArenaLoseRatingModifier", 24.0f);
|
||||
SetConfigValue<float>(CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER, "Arena.ArenaMatchmakerRatingModifier", 24.0f);
|
||||
SetConfigValue<bool>(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE, "Arena.QueueAnnouncer.Enable", false);
|
||||
SetConfigValue<bool>(CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY, "Arena.QueueAnnouncer.PlayerOnly", false);
|
||||
SetConfigValue<uint32>(CONFIG_ARENA_QUEUE_ANNOUNCER_DETAIL, "Arena.QueueAnnouncer.Detail", 3);
|
||||
|
||||
SetConfigValue<bool>(CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN, "OffhandCheckAtSpellUnlearn", true);
|
||||
SetConfigValue<uint32>(CONFIG_CREATURE_STOP_FOR_PLAYER, "Creature.MovingStopTimeForPlayer", 3);
|
||||
SetConfigValue<uint32>(CONFIG_CREATURE_STOP_FOR_PLAYER, "Creature.MovingStopTimeForPlayer", 180000);
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_WATER_BREATH_TIMER, "WaterBreath.Timer", 180000, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0");
|
||||
|
||||
@@ -501,10 +502,10 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<uint32>(CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD, "DailyRBGArenaPoints.MinLevel", 71);
|
||||
|
||||
// Respawn
|
||||
SetConfigValue<float>(CONFIG_RESPAWN_DYNAMICRATE_CREATURE, "Respawn.DynamicRateCreature", 1);
|
||||
SetConfigValue<float>(CONFIG_RESPAWN_DYNAMICRATE_CREATURE, "Respawn.DynamicRateCreature", 1.0f);
|
||||
SetConfigValue<uint32>(CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE, "Respawn.DynamicMinimumCreature", 10);
|
||||
|
||||
SetConfigValue<float>(CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT, "Respawn.DynamicRateGameObject", 1);
|
||||
SetConfigValue<float>(CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT, "Respawn.DynamicRateGameObject", 1.0f);
|
||||
SetConfigValue<uint32>(CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT, "Respawn.DynamicMinimumGameObject", 10);
|
||||
|
||||
SetConfigValue<bool>(CONFIG_VMAP_INDOOR_CHECK, "vmap.enableIndoorCheck", true);
|
||||
@@ -602,8 +603,6 @@ void WorldConfig::BuildConfigCache()
|
||||
SetConfigValue<uint32>(CONFIG_ICC_BUFF_HORDE, "ICC.Buff.Horde", 73822);
|
||||
SetConfigValue<uint32>(CONFIG_ICC_BUFF_ALLIANCE, "ICC.Buff.Alliance", 73828);
|
||||
|
||||
SetConfigValue<bool>(CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE, "SetAllCreaturesWithWaypointMovementActive", false);
|
||||
|
||||
// packet spoof punishment
|
||||
SetConfigValue<uint32>(CONFIG_PACKET_SPOOF_BANMODE, "PacketSpoof.BanMode", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value == 0; }, "== 0");
|
||||
SetConfigValue<uint32>(CONFIG_PACKET_SPOOF_BANDURATION, "PacketSpoof.BanDuration", 86400);
|
||||
@@ -640,7 +639,7 @@ void WorldConfig::BuildConfigCache()
|
||||
// LFG group mechanics.
|
||||
SetConfigValue<uint32>(CONFIG_LFG_MAX_KICK_COUNT, "LFG.MaxKickCount", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 3; }, "<= 3");
|
||||
|
||||
SetConfigValue<uint32>(CONFIG_LFG_KICK_PREVENTION_TIMER, "LFG.KickPreventionTimer", 15, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 15 * MINUTE * IN_MILLISECONDS; }, "<= 15 * MINUTE * IN_MILLISECONDS");
|
||||
SetConfigValue<uint32>(CONFIG_LFG_KICK_PREVENTION_TIMER, "LFG.KickPreventionTimer", 900, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 15 * MINUTE * IN_MILLISECONDS; }, "<= 15 * MINUTE * IN_MILLISECONDS");
|
||||
|
||||
// Realm Availability
|
||||
SetConfigValue<bool>(CONFIG_REALM_LOGIN_ENABLED, "World.RealmAvailability", true);
|
||||
|
||||
@@ -117,7 +117,6 @@ enum ServerConfigs
|
||||
CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE,
|
||||
CONFIG_ITEMDELETE_METHOD,
|
||||
CONFIG_ITEMDELETE_VENDOR,
|
||||
CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE,
|
||||
CONFIG_DEBUG_BATTLEGROUND,
|
||||
CONFIG_DEBUG_ARENA,
|
||||
CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PORTAL_CHECK_ILVL,
|
||||
@@ -281,6 +280,7 @@ enum ServerConfigs
|
||||
CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS,
|
||||
CONFIG_ARENA_GAMES_REQUIRED,
|
||||
CONFIG_ARENA_START_RATING,
|
||||
CONFIG_LEGACY_ARENA_START_RATING,
|
||||
CONFIG_LEGACY_ARENA_POINTS_CALC,
|
||||
CONFIG_ARENA_START_PERSONAL_RATING,
|
||||
CONFIG_ARENA_START_MATCHMAKER_RATING,
|
||||
|
||||
@@ -15,13 +15,18 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "AreaDefines.h"
|
||||
#include "CreatureAIImpl.h"
|
||||
#include "GameEventMgr.h"
|
||||
#include "Log.h"
|
||||
#include "MapMgr.h"
|
||||
#include "Player.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "UnitAI.h"
|
||||
#include "Weather.h"
|
||||
#include "WorldState.h"
|
||||
#include "WorldStateDefines.h"
|
||||
#include <chrono>
|
||||
|
||||
WorldState* WorldState::instance()
|
||||
{
|
||||
@@ -87,12 +92,43 @@ void WorldState::Load()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SAVE_ID_SCOURGE_INVASION:
|
||||
if (data.size())
|
||||
{
|
||||
try
|
||||
{
|
||||
uint32 state;
|
||||
loadStream >> state;
|
||||
m_siData.m_state = SIState(state);
|
||||
for (TimePoint& m_timer : m_siData.m_timers)
|
||||
{
|
||||
uint64 time;
|
||||
loadStream >> time;
|
||||
m_timer = TimePoint(std::chrono::milliseconds(time));
|
||||
}
|
||||
loadStream >> m_siData.m_battlesWon >> m_siData.m_lastAttackZone;
|
||||
for (unsigned int& i : m_siData.m_remaining)
|
||||
loadStream >> i;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LOG_ERROR("scripts", "WorldState::Load: Exception reading ScourgeInvasion data {}", e.what());
|
||||
m_siData.Reset();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (result->NextRow());
|
||||
}
|
||||
StartSunsReachPhase(true);
|
||||
StartSunwellGatePhase();
|
||||
HandleSunsReachSubPhaseTransition(m_sunsReachData.m_subphaseMask, true);
|
||||
|
||||
if (m_siData.m_state == STATE_1_ENABLED)
|
||||
{
|
||||
StartScourgeInvasion(false);
|
||||
HandleDefendedZones();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldState::LoadWorldStates()
|
||||
@@ -156,6 +192,12 @@ void WorldState::Save(WorldStateSaveIds saveId)
|
||||
SaveHelper(expansionData, SAVE_ID_QUEL_DANAS);
|
||||
break;
|
||||
}
|
||||
case SAVE_ID_SCOURGE_INVASION:
|
||||
{
|
||||
std::string siData = m_siData.GetData();
|
||||
SaveHelper(siData, SAVE_ID_SCOURGE_INVASION);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -167,7 +209,7 @@ void WorldState::SaveHelper(std::string& stringToSave, WorldStateSaveIds saveId)
|
||||
CharacterDatabase.Execute("INSERT INTO world_state(Id,Data) VALUES('{}','{}')", saveId, stringToSave.data());
|
||||
}
|
||||
|
||||
bool WorldState::IsConditionFulfilled(WorldStateCondition conditionId, WorldStateConditionState state) const
|
||||
bool WorldState::IsConditionFulfilled(uint32 conditionId, uint32 state) const
|
||||
{
|
||||
switch (conditionId)
|
||||
{
|
||||
@@ -179,6 +221,18 @@ bool WorldState::IsConditionFulfilled(WorldStateCondition conditionId, WorldStat
|
||||
case WORLD_STATE_CONDITION_THE_PURPLE_PRINCESS:
|
||||
case WORLD_STATE_CONDITION_THE_THUNDERCALLER:
|
||||
return _transportStates.at(conditionId) == state;
|
||||
case WORLD_STATE_SCOURGE_INVASION_WINTERSPRING:
|
||||
return GetSIRemaining(SI_REMAINING_WINTERSPRING) > 0;
|
||||
case WORLD_STATE_SCOURGE_INVASION_AZSHARA:
|
||||
return GetSIRemaining(SI_REMAINING_AZSHARA) > 0;
|
||||
case WORLD_STATE_SCOURGE_INVASION_BLASTED_LANDS:
|
||||
return GetSIRemaining(SI_REMAINING_BLASTED_LANDS) > 0;
|
||||
case WORLD_STATE_SCOURGE_INVASION_BURNING_STEPPES:
|
||||
return GetSIRemaining(SI_REMAINING_BURNING_STEPPES) > 0;
|
||||
case WORLD_STATE_SCOURGE_INVASION_TANARIS:
|
||||
return GetSIRemaining(SI_REMAINING_TANARIS) > 0;
|
||||
case WORLD_STATE_SCOURGE_INVASION_EASTERN_PLAGUELANDS:
|
||||
return GetSIRemaining(SI_REMAINING_EASTERN_PLAGUELANDS) > 0;
|
||||
default:
|
||||
LOG_ERROR("scripts", "WorldState::IsConditionFulfilled: Unhandled WorldStateCondition {}", conditionId);
|
||||
return false;
|
||||
@@ -190,6 +244,18 @@ void WorldState::HandleConditionStateChange(WorldStateCondition conditionId, Wor
|
||||
_transportStates[conditionId] = state;
|
||||
}
|
||||
|
||||
Map* WorldState::GetMap(uint32 mapId, Position const& invZone)
|
||||
{
|
||||
Map* map = sMapMgr->FindBaseMap(mapId);
|
||||
if (!map)
|
||||
LOG_ERROR("scripts",
|
||||
"ScourgeInvasionEvent::GetMap found no map with mapId {} , x: {}, y: {}.",
|
||||
mapId,
|
||||
invZone.GetPositionX(),
|
||||
invZone.GetPositionY());
|
||||
return map;
|
||||
}
|
||||
|
||||
void WorldState::HandleExternalEvent(WorldStateEvent eventId, uint32 param)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_mutex);
|
||||
@@ -245,6 +311,32 @@ void WorldState::Update(uint32 diff)
|
||||
_adalSongOfBattleTimer -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_siData.m_state != STATE_0_DISABLED)
|
||||
{
|
||||
if (m_siData.m_broadcastTimer <= diff)
|
||||
{
|
||||
m_siData.m_broadcastTimer = 10000;
|
||||
BroadcastSIWorldstates();
|
||||
|
||||
if (m_siData.m_state == STATE_1_ENABLED)
|
||||
{
|
||||
for (auto& zone : m_siData.m_cityAttacks)
|
||||
{
|
||||
if (zone.second.zoneId == AREA_UNDERCITY)
|
||||
StartNewCityAttackIfTime(SI_TIMER_UNDERCITY, zone.second.zoneId);
|
||||
else if (zone.second.zoneId == AREA_STORMWIND_CITY)
|
||||
StartNewCityAttackIfTime(SI_TIMER_STORMWIND, zone.second.zoneId);
|
||||
}
|
||||
|
||||
TimePoint now = std::chrono::steady_clock::now();
|
||||
for (auto& zone : m_siData.m_activeInvasions)
|
||||
HandleActiveZone(GetTimerIdForZone(zone.second.zoneId), zone.second.zoneId, zone.second.remainingNecropoli, now);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_siData.m_broadcastTimer -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldState::HandlePlayerEnterZone(Player* player, AreaTableIDs zoneId)
|
||||
@@ -939,6 +1031,67 @@ std::string WorldState::GetSunsReachPrintout()
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string WorldState::GetScourgeInvasionPrintout()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
std::string output = "Scourge Invasion Status:\n";
|
||||
|
||||
auto formatState = [this]() -> std::string
|
||||
{
|
||||
switch (m_siData.m_state)
|
||||
{
|
||||
case STATE_0_DISABLED:
|
||||
return "Disabled";
|
||||
case STATE_1_ENABLED:
|
||||
return "Enabled";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
};
|
||||
auto formatRemaining = [this](SIRemaining val, char const* name) { return " " + std::string(name) + ": " + std::to_string(m_siData.m_remaining[val]) + "\n"; };
|
||||
|
||||
output += "State: " + formatState() + " (" + std::to_string(static_cast<uint32>(m_siData.m_state)) + ")\n";
|
||||
output += "Battles Won: " + std::to_string(m_siData.m_battlesWon) + "\n";
|
||||
output += "Last Attack Zone ID: " + std::to_string(m_siData.m_lastAttackZone) + "\n";
|
||||
|
||||
output += "Zone Necropolis Count Remaining:\n";
|
||||
output += formatRemaining(SI_REMAINING_WINTERSPRING, "Winterspring");
|
||||
output += formatRemaining(SI_REMAINING_AZSHARA, "Azshara");
|
||||
output += formatRemaining(SI_REMAINING_BLASTED_LANDS, "Blasted Lands");
|
||||
output += formatRemaining(SI_REMAINING_BURNING_STEPPES, "Burning Steppes");
|
||||
output += formatRemaining(SI_REMAINING_TANARIS, "Tanaris");
|
||||
output += formatRemaining(SI_REMAINING_EASTERN_PLAGUELANDS, "Eastern Plaguelands");
|
||||
|
||||
output += "Zone Timers (time until next event):\n";
|
||||
TimePoint now = std::chrono::steady_clock::now();
|
||||
auto formatTimer = [this](SITimers timerId, char const* name, TimePoint now)
|
||||
{
|
||||
TimePoint tp = m_siData.m_timers[timerId];
|
||||
std::string timerStr;
|
||||
if (tp.time_since_epoch().count() == 0)
|
||||
timerStr = "0 (not set)";
|
||||
else if (tp <= now)
|
||||
timerStr = "Elapsed";
|
||||
else
|
||||
{
|
||||
auto diff = std::chrono::duration_cast<std::chrono::seconds>(tp - now);
|
||||
timerStr = std::to_string(diff.count()) + "s remaining";
|
||||
}
|
||||
return " " + std::string(name) + ": " + timerStr + "\n";
|
||||
};
|
||||
|
||||
output += formatTimer(SI_TIMER_WINTERSPRING, "Winterspring Invasion", now);
|
||||
output += formatTimer(SI_TIMER_AZSHARA, "Azshara Invasion", now);
|
||||
output += formatTimer(SI_TIMER_BLASTED_LANDS, "Blasted Lands Invasion", now);
|
||||
output += formatTimer(SI_TIMER_BURNING_STEPPES, "Burning Steppes Invasion", now);
|
||||
output += formatTimer(SI_TIMER_TANARIS, "Tanaris Invasion", now);
|
||||
output += formatTimer(SI_TIMER_EASTERN_PLAGUELANDS, "Eastern Plaguelands Invasion", now);
|
||||
output += formatTimer(SI_TIMER_STORMWIND, "Stormwind City Attack", now);
|
||||
output += formatTimer(SI_TIMER_UNDERCITY, "Undercity Attack", now);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::string SunsReachReclamationData::GetData()
|
||||
{
|
||||
std::string output = std::to_string(m_phase) + " " + std::to_string(m_subphaseMask);
|
||||
@@ -1087,8 +1240,726 @@ uint32 SunsReachReclamationData::GetSunwellGatePercentage(uint32 gate)
|
||||
return percentage < 0 ? 0 : uint32(percentage);
|
||||
}
|
||||
|
||||
void WorldState::SetScourgeInvasionState(SIState state)
|
||||
{
|
||||
SIState oldState = m_siData.m_state;
|
||||
if (oldState == state)
|
||||
return;
|
||||
|
||||
m_siData.m_state = state;
|
||||
if (oldState == STATE_0_DISABLED)
|
||||
StartScourgeInvasion(true);
|
||||
else if (state == STATE_0_DISABLED)
|
||||
StopScourgeInvasion();
|
||||
Save(SAVE_ID_SCOURGE_INVASION);
|
||||
}
|
||||
|
||||
void WorldState::SendScourgeInvasionMail()
|
||||
{
|
||||
QueryResult result = CharacterDatabase.Query("SELECT guid FROM characters WHERE level >= 50");
|
||||
if (result)
|
||||
{
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
MailDraft draft(MAIL_TEMPLATE_ARGENT_DAWN_NEEDS_YOUR_HELP);
|
||||
uint32 count = 0;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
ObjectGuid playerGUID = ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>());
|
||||
|
||||
// Add item manually. SendMailTo does not add items for offline players
|
||||
if (Item* item = Item::CreateItem(ITEM_A_LETTER_FROM_THE_KEEPER_OF_THE_ROLLS, 1))
|
||||
{
|
||||
item->SaveToDB(trans);
|
||||
draft.AddItem(item);
|
||||
}
|
||||
|
||||
draft.SendMailTo(trans, MailReceiver(playerGUID.GetCounter()), NPC_ARGENT_EMISSARY, MAIL_CHECK_MASK_HAS_BODY);
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
LOG_INFO("WorldState", "SendScourgeInvasionMail sent to {} characters", count);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldState::StartScourgeInvasion(bool sendMail)
|
||||
{
|
||||
sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION, false);
|
||||
|
||||
if (sendMail)
|
||||
SendScourgeInvasionMail();
|
||||
|
||||
BroadcastSIWorldstates();
|
||||
if (m_siData.m_state == STATE_1_ENABLED)
|
||||
{
|
||||
for (auto& zone : m_siData.m_cityAttacks)
|
||||
{
|
||||
if (zone.second.zoneId == AREA_UNDERCITY)
|
||||
StartNewCityAttackIfTime(SI_TIMER_UNDERCITY, zone.second.zoneId);
|
||||
else if (zone.second.zoneId == AREA_STORMWIND_CITY)
|
||||
StartNewCityAttackIfTime(SI_TIMER_STORMWIND, zone.second.zoneId);
|
||||
}
|
||||
|
||||
// randomization of init so that not every invasion starts the same way
|
||||
std::vector<uint32> randomIds;
|
||||
randomIds.reserve(m_siData.m_activeInvasions.size());
|
||||
for (auto const& [zoneId, _] : m_siData.m_activeInvasions)
|
||||
randomIds.push_back(zoneId);
|
||||
Acore::Containers::RandomShuffle(randomIds);
|
||||
for (uint32 id : randomIds)
|
||||
OnEnable(m_siData.m_activeInvasions[id]);
|
||||
}
|
||||
}
|
||||
|
||||
ScourgeInvasionData::ScourgeInvasionData()
|
||||
: m_state(STATE_0_DISABLED), m_battlesWon(0), m_lastAttackZone(0), m_remaining{}, m_broadcastTimer(10000)
|
||||
{
|
||||
m_activeInvasions.emplace(
|
||||
AREA_WINTERSPRING,
|
||||
InvasionZone{
|
||||
.map = MAP_KALIMDOR,
|
||||
.zoneId = AREA_WINTERSPRING,
|
||||
.necropolisCount = 3,
|
||||
.remainingNecropoli = SI_REMAINING_WINTERSPRING,
|
||||
.mouth = { Position{7736.56f, -4033.75f, 696.327f, 5.51524f} }
|
||||
}
|
||||
);
|
||||
m_activeInvasions.emplace(
|
||||
AREA_TANARIS,
|
||||
InvasionZone{
|
||||
.map = MAP_KALIMDOR,
|
||||
.zoneId = AREA_TANARIS,
|
||||
.necropolisCount = 3,
|
||||
.remainingNecropoli = SI_REMAINING_TANARIS,
|
||||
.mouth = { Position{-8352.68f, -3972.68f, 10.0753f, 2.14675f} }
|
||||
}
|
||||
);
|
||||
m_activeInvasions.emplace(
|
||||
AREA_AZSHARA,
|
||||
InvasionZone{
|
||||
.map = MAP_KALIMDOR,
|
||||
.zoneId = AREA_AZSHARA,
|
||||
.necropolisCount = 2,
|
||||
.remainingNecropoli = SI_REMAINING_AZSHARA,
|
||||
.mouth = { Position{3273.75f, -4276.98f, 125.509f, 5.44543f} }
|
||||
}
|
||||
);
|
||||
m_activeInvasions.emplace(
|
||||
AREA_BLASTED_LANDS,
|
||||
InvasionZone{
|
||||
.map = MAP_EASTERN_KINGDOMS,
|
||||
.zoneId = AREA_BLASTED_LANDS,
|
||||
.necropolisCount = 2,
|
||||
.remainingNecropoli = SI_REMAINING_BLASTED_LANDS,
|
||||
.mouth = { Position{-11429.3f, -3327.82f, 7.73628f, 1.0821f} }
|
||||
}
|
||||
);
|
||||
m_activeInvasions.emplace(
|
||||
AREA_EASTERN_PLAGUELANDS,
|
||||
InvasionZone{
|
||||
.map = MAP_EASTERN_KINGDOMS,
|
||||
.zoneId = AREA_EASTERN_PLAGUELANDS,
|
||||
.necropolisCount = 2,
|
||||
.remainingNecropoli = SI_REMAINING_EASTERN_PLAGUELANDS,
|
||||
.mouth = { Position{2014.55f, -4934.52f, 73.9846f, 0.0698132f} }
|
||||
}
|
||||
);
|
||||
m_activeInvasions.emplace(
|
||||
AREA_BURNING_STEPPES,
|
||||
InvasionZone{
|
||||
.map = MAP_EASTERN_KINGDOMS,
|
||||
.zoneId = AREA_BURNING_STEPPES,
|
||||
.necropolisCount = 2,
|
||||
.remainingNecropoli = SI_REMAINING_BURNING_STEPPES,
|
||||
.mouth = { Position{-8229.53f, -1118.11f, 144.012f, 6.17846f} }
|
||||
}
|
||||
);
|
||||
|
||||
m_cityAttacks.emplace(
|
||||
AREA_UNDERCITY,
|
||||
CityAttack{
|
||||
.map = MAP_EASTERN_KINGDOMS,
|
||||
.zoneId = AREA_UNDERCITY,
|
||||
.pallid = {
|
||||
Position{1595.87f, 440.539f, -46.3349f, 2.28207f}, // Royal Quarter
|
||||
Position{1659.2f, 265.988f, -62.1788f, 3.64283f} // Trade Quarter
|
||||
}
|
||||
}
|
||||
);
|
||||
m_cityAttacks.emplace(
|
||||
AREA_STORMWIND_CITY,
|
||||
CityAttack{
|
||||
.map = MAP_EASTERN_KINGDOMS,
|
||||
.zoneId = AREA_STORMWIND_CITY,
|
||||
.pallid = {
|
||||
Position{-8578.15f, 886.382f, 87.3148f, 0.586275f}, // Stormwind Keep
|
||||
Position{-8578.15f, 886.382f, 87.3148f, 0.586275f} // Trade District
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void ScourgeInvasionData::Reset()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siMutex);
|
||||
for (auto& timepoint : m_timers)
|
||||
timepoint = TimePoint();
|
||||
|
||||
m_battlesWon = 0;
|
||||
m_lastAttackZone = 0;
|
||||
m_broadcastTimer = 10000;
|
||||
memset(m_remaining, 0, sizeof(m_remaining));
|
||||
}
|
||||
|
||||
std::string ScourgeInvasionData::GetData()
|
||||
{
|
||||
std::string output = std::to_string(m_state) + " ";
|
||||
for (auto& timer : m_timers)
|
||||
output += std::to_string(timer.time_since_epoch().count()) + " ";
|
||||
output += std::to_string(m_battlesWon) + " " + std::to_string(m_lastAttackZone) + " ";
|
||||
for (auto& remaining : m_remaining)
|
||||
output += std::to_string(remaining) + " ";
|
||||
return output;
|
||||
}
|
||||
|
||||
void WorldState::StopScourgeInvasion()
|
||||
{
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_WINTERSPRING);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_TANARIS);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_AZSHARA);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_BLASTED_LANDS);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_EASTERN_PLAGUELANDS);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_BURNING_STEPPES);
|
||||
BroadcastSIWorldstates();
|
||||
m_siData.Reset();
|
||||
|
||||
for (auto& [_, cityData] : m_siData.m_cityAttacks)
|
||||
OnDisable(cityData);
|
||||
|
||||
for (auto& [_, zoneData] : m_siData.m_activeInvasions)
|
||||
OnDisable(zoneData);
|
||||
}
|
||||
|
||||
uint32 WorldState::GetSIRemaining(SIRemaining remaining) const
|
||||
{
|
||||
return m_siData.m_remaining[remaining];
|
||||
}
|
||||
|
||||
uint32 WorldState::GetSIRemainingByZone(uint32 zoneId) const
|
||||
{
|
||||
SIRemaining remainingId;
|
||||
switch (zoneId)
|
||||
{
|
||||
case AREA_WINTERSPRING: remainingId = SI_REMAINING_WINTERSPRING; break;
|
||||
case AREA_AZSHARA: remainingId = SI_REMAINING_AZSHARA; break;
|
||||
case AREA_EASTERN_PLAGUELANDS: remainingId = SI_REMAINING_EASTERN_PLAGUELANDS; break;
|
||||
case AREA_BLASTED_LANDS: remainingId = SI_REMAINING_BLASTED_LANDS; break;
|
||||
case AREA_BURNING_STEPPES: remainingId = SI_REMAINING_BURNING_STEPPES; break;
|
||||
case AREA_TANARIS: remainingId = SI_REMAINING_TANARIS; break;
|
||||
default:
|
||||
LOG_ERROR("WorldState", "GetSIRemainingByZone called with invalid zone ID: {}", zoneId);
|
||||
return 0;
|
||||
}
|
||||
return GetSIRemaining(remainingId);
|
||||
}
|
||||
|
||||
void WorldState::SetSIRemaining(SIRemaining remaining, uint32 value)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
m_siData.m_remaining[remaining] = value;
|
||||
Save(SAVE_ID_SCOURGE_INVASION);
|
||||
}
|
||||
|
||||
TimePoint WorldState::GetSITimer(SITimers timer)
|
||||
{
|
||||
return m_siData.m_timers[timer];
|
||||
}
|
||||
|
||||
void WorldState::SetSITimer(SITimers timer, TimePoint timePoint)
|
||||
{
|
||||
m_siData.m_timers[timer] = timePoint;
|
||||
}
|
||||
|
||||
uint32 WorldState::GetBattlesWon()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
return m_siData.m_battlesWon;
|
||||
}
|
||||
|
||||
void WorldState::AddBattlesWon(int32 count)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
m_siData.m_battlesWon += count;
|
||||
HandleDefendedZones();
|
||||
Save(SAVE_ID_SCOURGE_INVASION);
|
||||
}
|
||||
|
||||
uint32 WorldState::GetLastAttackZone()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
return m_siData.m_lastAttackZone;
|
||||
}
|
||||
|
||||
void WorldState::SetLastAttackZone(uint32 zoneId)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
m_siData.m_lastAttackZone = zoneId;
|
||||
}
|
||||
|
||||
void WorldState::BroadcastSIWorldstates()
|
||||
{
|
||||
uint32 victories = GetBattlesWon();
|
||||
|
||||
uint32 remainingAzshara = GetSIRemaining(SI_REMAINING_AZSHARA);
|
||||
uint32 remainingBlastedLands = GetSIRemaining(SI_REMAINING_BLASTED_LANDS);
|
||||
uint32 remainingBurningSteppes = GetSIRemaining(SI_REMAINING_BURNING_STEPPES);
|
||||
uint32 remainingEasternPlaguelands = GetSIRemaining(SI_REMAINING_EASTERN_PLAGUELANDS);
|
||||
uint32 remainingTanaris = GetSIRemaining(SI_REMAINING_TANARIS);
|
||||
uint32 remainingWinterspring = GetSIRemaining(SI_REMAINING_WINTERSPRING);
|
||||
|
||||
sMapMgr->DoForAllMaps([&](Map* map) -> void
|
||||
{
|
||||
switch (map->GetId())
|
||||
{
|
||||
case MAP_EASTERN_KINGDOMS:
|
||||
case MAP_KALIMDOR:
|
||||
map->DoForAllPlayers([&](Player* pl)
|
||||
{
|
||||
// do not process players which are not in world
|
||||
if (!pl->IsInWorld())
|
||||
return;
|
||||
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_AZSHARA, remainingAzshara > 0 ? 1 : 0);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_BLASTED_LANDS, remainingBlastedLands > 0 ? 1 : 0);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_BURNING_STEPPES, remainingBurningSteppes > 0 ? 1 : 0);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_EASTERN_PLAGUELANDS, remainingEasternPlaguelands > 0 ? 1 : 0);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_TANARIS, remainingTanaris > 0 ? 1 : 0);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_WINTERSPRING, remainingWinterspring > 0 ? 1 : 0);
|
||||
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_VICTORIES, victories);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_AZSHARA, remainingAzshara);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_BLASTED_LANDS, remainingBlastedLands);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_BURNING_STEPPES, remainingBurningSteppes);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_EASTERN_PLAGUELANDS, remainingEasternPlaguelands);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_TANARIS, remainingTanaris);
|
||||
pl->SendUpdateWorldState(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_WINTERSPRING, remainingWinterspring);
|
||||
});
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void WorldState::HandleDefendedZones()
|
||||
{
|
||||
if (m_siData.m_battlesWon < 50)
|
||||
{
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_50_INVASIONS);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_100_INVASIONS);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_150_INVASIONS);
|
||||
}
|
||||
else if (m_siData.m_battlesWon >= 50 && m_siData.m_battlesWon < 100)
|
||||
sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_50_INVASIONS);
|
||||
else if (m_siData.m_battlesWon >= 100 && m_siData.m_battlesWon < 150)
|
||||
{
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_50_INVASIONS);
|
||||
sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_100_INVASIONS);
|
||||
}
|
||||
else if (m_siData.m_battlesWon >= 150)
|
||||
{
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_50_INVASIONS);
|
||||
sGameEventMgr->StopEvent(GAME_EVENT_SCOURGE_INVASION_100_INVASIONS);
|
||||
sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_INVASIONS_DONE);
|
||||
sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_150_INVASIONS);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldState::StartZoneEvent(SIZoneIds eventId)
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case SI_ZONE_AZSHARA: StartNewInvasion(AREA_AZSHARA); break;
|
||||
case SI_ZONE_BLASTED_LANDS: StartNewInvasion(AREA_BLASTED_LANDS); break;
|
||||
case SI_ZONE_BURNING_STEPPES: StartNewInvasion(AREA_BURNING_STEPPES); break;
|
||||
case SI_ZONE_EASTERN_PLAGUELANDS: StartNewInvasion(AREA_EASTERN_PLAGUELANDS); break;
|
||||
case SI_ZONE_TANARIS: StartNewInvasion(AREA_TANARIS); break;
|
||||
case SI_ZONE_WINTERSPRING: StartNewInvasion(AREA_WINTERSPRING); break;
|
||||
case SI_ZONE_STORMWIND: StartNewCityAttack(AREA_STORMWIND_CITY); break;
|
||||
case SI_ZONE_UNDERCITY: StartNewCityAttack(AREA_UNDERCITY); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldState::StartNewInvasionIfTime(uint32 attackTimeVar, uint32 zoneId)
|
||||
{
|
||||
TimePoint now = std::chrono::steady_clock::now();
|
||||
|
||||
// Not yet time
|
||||
if (now < sWorldState->GetSITimer(SITimers(attackTimeVar)))
|
||||
return;
|
||||
|
||||
StartNewInvasion(zoneId);
|
||||
}
|
||||
|
||||
void WorldState::StartNewCityAttackIfTime(uint32 attackTimeVar, uint32 zoneId)
|
||||
{
|
||||
TimePoint now = std::chrono::steady_clock::now();
|
||||
|
||||
// Not yet time
|
||||
if (now < sWorldState->GetSITimer(SITimers(attackTimeVar)))
|
||||
return;
|
||||
|
||||
StartNewCityAttack(zoneId);
|
||||
uint32 cityAttackTimer = urand(CITY_ATTACK_TIMER_MIN, CITY_ATTACK_TIMER_MAX);
|
||||
TimePoint next_attack = now + std::chrono::seconds(cityAttackTimer);
|
||||
sWorldState->SetSITimer(SITimers(attackTimeVar), next_attack);
|
||||
}
|
||||
|
||||
void WorldState::StartNewInvasion(uint32 zoneId)
|
||||
{
|
||||
if (IsActiveZone(zoneId))
|
||||
return;
|
||||
|
||||
// Don't attack same zone as before.
|
||||
if (zoneId == sWorldState->GetLastAttackZone())
|
||||
return;
|
||||
|
||||
// If we have at least one victory and more than 1 active zones stop here.
|
||||
if (GetActiveZones() > 1 && sWorldState->GetBattlesWon() > 0)
|
||||
return;
|
||||
|
||||
LOG_DEBUG("gameevent", "Scourge Invasion Event: Starting new invasion in {}.", zoneId);
|
||||
|
||||
ScourgeInvasionData::InvasionZone& zone = m_siData.m_activeInvasions[zoneId];
|
||||
|
||||
Map* map = GetMap(zone.map, zone.mouth[0]);
|
||||
|
||||
if (!map)
|
||||
{
|
||||
LOG_ERROR("gameevent", "ScourgeInvasionEvent::StartNewInvasion unable to access required map ({}). Retrying next update.", zone.map);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (zoneId)
|
||||
{
|
||||
case AREA_AZSHARA: sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_AZSHARA); break;
|
||||
case AREA_BLASTED_LANDS: sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_BLASTED_LANDS); break;
|
||||
case AREA_BURNING_STEPPES: sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_BURNING_STEPPES); break;
|
||||
case AREA_EASTERN_PLAGUELANDS: sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_EASTERN_PLAGUELANDS); break;
|
||||
case AREA_TANARIS: sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_TANARIS); break;
|
||||
case AREA_WINTERSPRING: sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_WINTERSPRING); break;
|
||||
default:
|
||||
LOG_ERROR("gameevent", "ScourgeInvasionEvent::StartNewInvasion unknown zoneId {}.", zoneId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (map)
|
||||
SummonMouth(map, zone, zone.mouth[0], true);
|
||||
}
|
||||
|
||||
void WorldState::StartNewCityAttack(uint32 zoneId)
|
||||
{
|
||||
LOG_DEBUG("gameevent", "Scourge Invasion Event: Starting new City attack in zone {}.", zoneId);
|
||||
|
||||
ScourgeInvasionData::CityAttack& zone = m_siData.m_cityAttacks[zoneId];
|
||||
|
||||
uint32 SpawnLocationID = urand(0, static_cast<uint32>(zone.pallid.size() - 1));
|
||||
|
||||
Map* map = GetMap(zone.map, zone.pallid[SpawnLocationID]);
|
||||
|
||||
// If any of the required maps are not available we return. Will cause the invasion to be started
|
||||
// on next update instead
|
||||
if (!map)
|
||||
{
|
||||
LOG_ERROR("gameevent", "ScourgeInvasionEvent::StartNewCityAttackIfTime unable to access required map (%{}). Retrying next update.", zone.map);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_siData.m_pendingPallids.find(zoneId) != m_siData.m_pendingPallids.end())
|
||||
return;
|
||||
|
||||
if (map && SummonPallid(map, zone, zone.pallid[SpawnLocationID], SpawnLocationID))
|
||||
LOG_DEBUG("gameevent", "ScourgeInvasionEvent::StartNewCityAttackIfTime pallid spawned in {}.", zone.map);
|
||||
else
|
||||
LOG_DEBUG("gameevent", "ScourgeInvasionEvent::StartNewCityAttackIfTime unable to spawn pallid in {}.", zone.map);
|
||||
}
|
||||
|
||||
bool WorldState::ResumeInvasion(ScourgeInvasionData::InvasionZone& zone)
|
||||
{
|
||||
// Dont have a save variable to know which necropolises had already been destroyed, so we
|
||||
// just summon the same amount, but not necessarily the same necropolises
|
||||
LOG_DEBUG("gameevent", "Scourge Invasion Event: Resuming Scourge invasion in zone {}", zone.zoneId);
|
||||
|
||||
uint32 num_necropolises_remaining = sWorldState->GetSIRemaining(SIRemaining(zone.remainingNecropoli));
|
||||
|
||||
// Just making sure we can access all maps before starting the invasion
|
||||
for (uint32 i = 0; i < num_necropolises_remaining; i++)
|
||||
{
|
||||
if (!GetMap(zone.map, zone.mouth[0]))
|
||||
{
|
||||
LOG_ERROR("gameevent", "ScourgeInvasionEvent::ResumeInvasion map {} not accessible. Retry next update.", zone.map);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Map* mapPtr = GetMap(zone.map, zone.mouth[0]);
|
||||
if (!mapPtr)
|
||||
{
|
||||
LOG_ERROR("gameevent", "ScourgeInvasionEvent::ResumeInvasion failed getting map, even after making sure they were loaded....");
|
||||
return false;
|
||||
}
|
||||
|
||||
SummonMouth(mapPtr, zone, zone.mouth[0], false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldState::SummonMouth(Map* map, ScourgeInvasionData::InvasionZone& zone, Position position, bool newInvasion)
|
||||
{
|
||||
AddPendingInvasion(zone.zoneId);
|
||||
// Remove old mouth if required.
|
||||
if (Creature* existingMouth = map->GetCreature(zone.mouthGuid))
|
||||
existingMouth->AddObjectToRemoveList();
|
||||
|
||||
if (Creature* mouth = map->SummonCreature(NPC_HERALD_OF_THE_LICH_KING, position))
|
||||
{
|
||||
mouth->GetAI()->DoAction(EVENT_HERALD_OF_THE_LICH_KING_ZONE_START);
|
||||
sWorldState->SetMouthGuid(zone.zoneId, mouth->GetGUID());
|
||||
if (newInvasion)
|
||||
sWorldState->SetSIRemaining(SIRemaining(zone.remainingNecropoli), zone.necropolisCount);
|
||||
}
|
||||
sWorldState->RemovePendingInvasion(zone.zoneId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum PallidHorrorPaths
|
||||
{
|
||||
PATH_STORMWIND_KEEP = 163941,
|
||||
PATH_STORMWIND_TRADE_DISTRICT = 163942,
|
||||
PATH_UNDERCITY_TRADE_QUARTER = 163943,
|
||||
PATH_UNDERCITY_ROYAL_QUARTER = 163944,
|
||||
};
|
||||
|
||||
bool WorldState::SummonPallid(Map* map, ScourgeInvasionData::CityAttack& zone, const Position& position, uint32 spawnLoc)
|
||||
{
|
||||
AddPendingPallid(zone.zoneId);
|
||||
// Remove old pallid if required.
|
||||
uint32 pathID = 0;
|
||||
if (Creature* existingPallid = map->GetCreature(zone.pallidGuid))
|
||||
existingPallid->AddObjectToRemoveList();
|
||||
|
||||
// if (Creature* pallid = map->SummonCreature(RAND(NPC_PALLID_HORROR, NPC_PATCHWORK_TERROR), position))
|
||||
if (Creature* pallid = map->SummonCreature(NPC_PALLID_HORROR, position))
|
||||
{
|
||||
pallid->GetMotionMaster()->Clear(false);
|
||||
if (pallid->GetZoneId() == AREA_UNDERCITY)
|
||||
pathID = spawnLoc == 0 ? PATH_UNDERCITY_ROYAL_QUARTER : PATH_UNDERCITY_TRADE_QUARTER;
|
||||
else
|
||||
pathID = spawnLoc == 0 ? PATH_STORMWIND_KEEP : PATH_STORMWIND_TRADE_DISTRICT;
|
||||
|
||||
pallid->GetMotionMaster()->MovePath(pathID, false);
|
||||
|
||||
sWorldState->SetPallidGuid(zone.zoneId, pallid->GetGUID());
|
||||
}
|
||||
sWorldState->RemovePendingPallid(zone.zoneId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorldState::HandleActiveZone(uint32 attackTimeVar, uint32 zoneId, uint32 remainingVar, TimePoint now)
|
||||
{
|
||||
TimePoint timePoint = sWorldState->GetSITimer(SITimers(attackTimeVar));
|
||||
|
||||
ScourgeInvasionData::InvasionZone& zone = m_siData.m_activeInvasions[zoneId];
|
||||
|
||||
Map* map = sMapMgr->FindMap(zone.map, 0);
|
||||
|
||||
if (zone.zoneId != zoneId)
|
||||
return;
|
||||
|
||||
uint32 remaining = GetSIRemaining(SIRemaining(remainingVar));
|
||||
|
||||
// Calculate the next possible attack between ZONE_ATTACK_TIMER_MIN and ZONE_ATTACK_TIMER_MAX.
|
||||
uint32 zoneAttackTimer = urand(ZONE_ATTACK_TIMER_MIN, ZONE_ATTACK_TIMER_MAX);
|
||||
TimePoint next_attack = now + std::chrono::seconds(zoneAttackTimer);
|
||||
uint64 timeToNextAttack = std::chrono::duration_cast<std::chrono::minutes>(next_attack-now).count();
|
||||
|
||||
if (zone.mouthGuid)
|
||||
{
|
||||
// Handles the inactive zone, without a Mouth of Kel'Thuzad summoned (which spawns the whole zone event).
|
||||
Creature* mouth = map->GetCreature(zone.mouthGuid);
|
||||
if (!mouth)
|
||||
sWorldState->SetMouthGuid(zone.zoneId, ObjectGuid()); // delays spawning until next tick
|
||||
// Handles the active zone that has no necropolis left.
|
||||
else if (timePoint < now && remaining == 0)
|
||||
{
|
||||
sWorldState->SetSITimer(SITimers(attackTimeVar), next_attack);
|
||||
sWorldState->AddBattlesWon(1);
|
||||
sWorldState->SetLastAttackZone(zoneId);
|
||||
|
||||
LOG_INFO("gameevent", "[Scourge Invasion Event] The Scourge has been defeated in {}, next attack starting in {} minutes.", zoneId, timeToNextAttack);
|
||||
LOG_DEBUG("gameevent", "[Scourge Invasion Event] {} victories", sWorldState->GetBattlesWon());
|
||||
|
||||
if (mouth)
|
||||
mouth->GetAI()->DoAction(EVENT_HERALD_OF_THE_LICH_KING_ZONE_STOP);
|
||||
else
|
||||
LOG_ERROR("gameevent", "ScourgeInvasionEvent::HandleActiveZone ObjectGuid {} not found.", zone.mouthGuid.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If more than one zones are alreay being attacked, set the timer again to ZONE_ATTACK_TIMER.
|
||||
if (GetActiveZones() > 1)
|
||||
sWorldState->SetSITimer(SITimers(attackTimeVar), next_attack);
|
||||
|
||||
// Try to start the zone if attackTimeVar is 0.
|
||||
StartNewInvasionIfTime(attackTimeVar, zoneId);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldState::SetPallidGuid(uint32 zoneId, ObjectGuid guid)
|
||||
{
|
||||
m_siData.m_cityAttacks[zoneId].pallidGuid = guid;
|
||||
}
|
||||
|
||||
void WorldState::SetMouthGuid(uint32 zoneId, ObjectGuid guid)
|
||||
{
|
||||
m_siData.m_activeInvasions[zoneId].mouthGuid = guid;
|
||||
}
|
||||
|
||||
void WorldState::AddPendingInvasion(uint32 zoneId)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
m_siData.m_pendingInvasions.insert(zoneId);
|
||||
}
|
||||
|
||||
void WorldState::RemovePendingInvasion(uint32 zoneId)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
m_siData.m_pendingInvasions.erase(zoneId);
|
||||
}
|
||||
|
||||
void WorldState::AddPendingPallid(uint32 zoneId)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
m_siData.m_pendingPallids.insert(zoneId);
|
||||
}
|
||||
|
||||
void WorldState::RemovePendingPallid(uint32 zoneId)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_siData.m_siMutex);
|
||||
m_siData.m_pendingPallids.erase(zoneId);
|
||||
}
|
||||
|
||||
void WorldState::OnEnable(ScourgeInvasionData::InvasionZone& zone)
|
||||
{
|
||||
// If there were remaining necropolises in the old zone before shutdown, we
|
||||
// restore that zone
|
||||
if (sWorldState->GetSIRemaining(SIRemaining(zone.remainingNecropoli)) > 0)
|
||||
ResumeInvasion(zone);
|
||||
// Otherwise we start a new Invasion
|
||||
else
|
||||
StartNewInvasionIfTime(GetTimerIdForZone(zone.zoneId), zone.zoneId);
|
||||
}
|
||||
|
||||
void WorldState::OnDisable(ScourgeInvasionData::InvasionZone& zone)
|
||||
{
|
||||
if (!zone.mouthGuid)
|
||||
return;
|
||||
|
||||
Map* map = GetMap(zone.map, zone.mouth[0]);
|
||||
|
||||
if (Creature* mouth = map->GetCreature(zone.mouthGuid))
|
||||
mouth->DespawnOrUnsummon();
|
||||
}
|
||||
|
||||
void WorldState::OnDisable(ScourgeInvasionData::CityAttack& zone)
|
||||
{
|
||||
if (!zone.pallidGuid)
|
||||
return;
|
||||
|
||||
Map* map = GetMap(zone.map, zone.pallid[0]);
|
||||
|
||||
if (Creature* pallid = map->GetCreature(zone.pallidGuid))
|
||||
pallid->DespawnOrUnsummon();
|
||||
}
|
||||
|
||||
bool WorldState::IsActiveZone(uint32 /*zoneId*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns the amount of pending zones and active zones with a mouth creature on the map
|
||||
uint32 WorldState::GetActiveZones()
|
||||
{
|
||||
size_t i = m_siData.m_pendingInvasions.size();
|
||||
for (auto const& [zoneId, invasionData] : m_siData.m_activeInvasions)
|
||||
{
|
||||
Map* map = GetMap(invasionData.map, invasionData.mouth[0]);
|
||||
if (!map)
|
||||
{
|
||||
LOG_ERROR("gameevent", "ScourgeInvasionEvent::GetActiveZones no map for zone {}.", invasionData.map);
|
||||
continue;
|
||||
}
|
||||
|
||||
Creature* mouth = map->GetCreature(invasionData.mouthGuid);
|
||||
if (mouth)
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
uint32 WorldState::GetTimerIdForZone(uint32 zoneId)
|
||||
{
|
||||
uint32 attackTime = 0;
|
||||
switch (zoneId)
|
||||
{
|
||||
case AREA_TANARIS: attackTime = SI_TIMER_TANARIS; break;
|
||||
case AREA_BLASTED_LANDS: attackTime = SI_TIMER_BLASTED_LANDS; break;
|
||||
case AREA_EASTERN_PLAGUELANDS: attackTime = SI_TIMER_EASTERN_PLAGUELANDS; break;
|
||||
case AREA_BURNING_STEPPES: attackTime = SI_TIMER_BURNING_STEPPES; break;
|
||||
case AREA_WINTERSPRING: attackTime = SI_TIMER_WINTERSPRING; break;
|
||||
case AREA_AZSHARA: attackTime = SI_TIMER_AZSHARA; break;
|
||||
default:
|
||||
LOG_ERROR("gameevent", "ScourgeInvasionEvent::GetTimerIdForZone unknown zoneId {}.", zoneId);
|
||||
return 0;
|
||||
}
|
||||
return attackTime;
|
||||
}
|
||||
|
||||
void WorldState::FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& packet, uint32 zoneId, uint32 /*areaId*/)
|
||||
{
|
||||
if (m_siData.m_state != STATE_0_DISABLED) // scourge invasion active - need to send all worldstates
|
||||
{
|
||||
uint32 victories = GetBattlesWon();
|
||||
|
||||
uint32 remainingAzshara = GetSIRemaining(SI_REMAINING_AZSHARA);
|
||||
uint32 remainingBlastedLands = GetSIRemaining(SI_REMAINING_BLASTED_LANDS);
|
||||
uint32 remainingBurningSteppes = GetSIRemaining(SI_REMAINING_BURNING_STEPPES);
|
||||
uint32 remainingEasternPlaguelands = GetSIRemaining(SI_REMAINING_EASTERN_PLAGUELANDS);
|
||||
uint32 remainingTanaris = GetSIRemaining(SI_REMAINING_TANARIS);
|
||||
uint32 remainingWinterspring = GetSIRemaining(SI_REMAINING_WINTERSPRING);
|
||||
|
||||
packet.Worldstates.reserve(13);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_AZSHARA, remainingAzshara > 0 ? 1 : 0);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_BLASTED_LANDS, remainingBlastedLands > 0 ? 1 : 0);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_BURNING_STEPPES, remainingBurningSteppes > 0 ? 1 : 0);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_EASTERN_PLAGUELANDS, remainingEasternPlaguelands > 0 ? 1 : 0);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_TANARIS, remainingTanaris > 0 ? 1 : 0);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_WINTERSPRING, remainingWinterspring > 0 ? 1 : 0);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_VICTORIES, victories);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_AZSHARA, remainingAzshara);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_BLASTED_LANDS, remainingBlastedLands);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_BURNING_STEPPES, remainingBurningSteppes);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_EASTERN_PLAGUELANDS, remainingEasternPlaguelands);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_TANARIS, remainingTanaris);
|
||||
packet.Worldstates.emplace_back(WORLD_STATE_SCOURGE_INVASION_NECROPOLIS_WINTERSPRING, remainingWinterspring);
|
||||
}
|
||||
|
||||
switch (zoneId)
|
||||
{
|
||||
case AREA_ISLE_OF_QUEL_DANAS:
|
||||
@@ -1136,5 +2007,7 @@ void WorldState::FillInitialWorldStates(WorldPackets::WorldState::InitWorldState
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "AreaDefines.h"
|
||||
#include "Player.h"
|
||||
#include "WorldStateDefines.h"
|
||||
#include <atomic>
|
||||
|
||||
enum WorldStateCondition
|
||||
@@ -56,11 +57,33 @@ enum WorldStateSpells
|
||||
|
||||
enum WorldStateSaveIds
|
||||
{
|
||||
SAVE_ID_SCOURGE_INVASION = 3,
|
||||
SAVE_ID_QUEL_DANAS = 20,
|
||||
};
|
||||
|
||||
enum WorldStateGameEvents
|
||||
{
|
||||
// Scourge Invasion
|
||||
GAME_EVENT_SCOURGE_INVASION = 17,
|
||||
GAME_EVENT_SCOURGE_INVASION_WINTERSPRING = 121,
|
||||
GAME_EVENT_SCOURGE_INVASION_TANARIS = 122,
|
||||
GAME_EVENT_SCOURGE_INVASION_AZSHARA = 123,
|
||||
GAME_EVENT_SCOURGE_INVASION_BLASTED_LANDS = 124,
|
||||
GAME_EVENT_SCOURGE_INVASION_EASTERN_PLAGUELANDS = 125,
|
||||
GAME_EVENT_SCOURGE_INVASION_BURNING_STEPPES = 126,
|
||||
GAME_EVENT_SCOURGE_INVASION_50_INVASIONS = 127,
|
||||
GAME_EVENT_SCOURGE_INVASION_100_INVASIONS = 128,
|
||||
GAME_EVENT_SCOURGE_INVASION_150_INVASIONS = 129,
|
||||
GAME_EVENT_SCOURGE_INVASION_INVASIONS_DONE = 130,
|
||||
|
||||
// Zombie infestation
|
||||
GAME_EVENT_ZOMBIE_INFESTATION_PHASE_1 = 110,
|
||||
GAME_EVENT_ZOMBIE_INFESTATION_PHASE_2 = 111,
|
||||
GAME_EVENT_ZOMBIE_INFESTATION_PHASE_3 = 112,
|
||||
GAME_EVENT_ZOMBIE_INFESTATION_PHASE_4 = 113,
|
||||
GAME_EVENT_ZOMBIE_INFESTATION_PHASE_5 = 114,
|
||||
GAME_EVENT_ZOMBIE_INFESTATION_PHASE_6 = 115,
|
||||
|
||||
// Isle phases
|
||||
GAME_EVENT_QUEL_DANAS_PHASE_1 = 101,
|
||||
GAME_EVENT_QUEL_DANAS_PHASE_2_ONLY = 102,
|
||||
@@ -84,6 +107,110 @@ enum WorldStateGameEvents
|
||||
GAME_EVENT_SWP_GATES_PHASE_3 = 119, // All Gates Open
|
||||
};
|
||||
|
||||
enum SIMisc
|
||||
{
|
||||
EVENT_HERALD_OF_THE_LICH_KING_ZONE_START = 7,
|
||||
EVENT_HERALD_OF_THE_LICH_KING_ZONE_STOP = 8,
|
||||
NPC_PALLID_HORROR = 16394,
|
||||
NPC_PATCHWORK_TERROR = 16382,
|
||||
NPC_HERALD_OF_THE_LICH_KING = 16995,
|
||||
ITEM_A_LETTER_FROM_THE_KEEPER_OF_THE_ROLLS = 22723,
|
||||
NPC_ARGENT_EMISSARY = 16285,
|
||||
MAIL_TEMPLATE_ARGENT_DAWN_NEEDS_YOUR_HELP = 171,
|
||||
};
|
||||
|
||||
enum SIState : uint32
|
||||
{
|
||||
STATE_0_DISABLED,
|
||||
STATE_1_ENABLED,
|
||||
SI_STATE_MAX,
|
||||
};
|
||||
|
||||
enum SIZoneIds
|
||||
{
|
||||
SI_ZONE_AZSHARA,
|
||||
SI_ZONE_BLASTED_LANDS,
|
||||
SI_ZONE_BURNING_STEPPES,
|
||||
SI_ZONE_EASTERN_PLAGUELANDS,
|
||||
SI_ZONE_TANARIS,
|
||||
SI_ZONE_WINTERSPRING,
|
||||
SI_ZONE_STORMWIND,
|
||||
SI_ZONE_UNDERCITY
|
||||
};
|
||||
|
||||
enum SITimers
|
||||
{
|
||||
SI_TIMER_AZSHARA,
|
||||
SI_TIMER_BLASTED_LANDS,
|
||||
SI_TIMER_BURNING_STEPPES,
|
||||
SI_TIMER_EASTERN_PLAGUELANDS,
|
||||
SI_TIMER_TANARIS,
|
||||
SI_TIMER_WINTERSPRING,
|
||||
SI_TIMER_STORMWIND,
|
||||
SI_TIMER_UNDERCITY,
|
||||
SI_TIMER_MAX,
|
||||
};
|
||||
|
||||
enum SIRemaining
|
||||
{
|
||||
SI_REMAINING_AZSHARA,
|
||||
SI_REMAINING_BLASTED_LANDS,
|
||||
SI_REMAINING_BURNING_STEPPES,
|
||||
SI_REMAINING_EASTERN_PLAGUELANDS,
|
||||
SI_REMAINING_TANARIS,
|
||||
SI_REMAINING_WINTERSPRING,
|
||||
SI_REMAINING_MAX,
|
||||
};
|
||||
|
||||
enum SICityTimers
|
||||
{
|
||||
// These timers may fail if you set it under 1 minute.
|
||||
ZONE_ATTACK_TIMER_MIN = 60 * 45, // 45 min.
|
||||
ZONE_ATTACK_TIMER_MAX = 60 * 60, // 60 min.
|
||||
CITY_ATTACK_TIMER_MIN = 60 * 45, // 45 min.
|
||||
CITY_ATTACK_TIMER_MAX = 60 * 60, // 60 min.
|
||||
};
|
||||
|
||||
struct ScourgeInvasionData
|
||||
{
|
||||
struct InvasionZone
|
||||
{
|
||||
uint32 map;
|
||||
uint32 zoneId;
|
||||
uint32 necropolisCount;
|
||||
uint32 remainingNecropoli;
|
||||
std::vector<Position> mouth;
|
||||
ObjectGuid mouthGuid {};
|
||||
};
|
||||
|
||||
struct CityAttack
|
||||
{
|
||||
uint32 map;
|
||||
uint32 zoneId;
|
||||
std::vector<Position> pallid;
|
||||
ObjectGuid pallidGuid {};
|
||||
};
|
||||
|
||||
SIState m_state;
|
||||
|
||||
TimePoint m_timers[SI_TIMER_MAX];
|
||||
uint32 m_battlesWon;
|
||||
uint32 m_lastAttackZone;
|
||||
uint32 m_remaining[SI_REMAINING_MAX];
|
||||
uint64 m_broadcastTimer;
|
||||
std::mutex m_siMutex;
|
||||
|
||||
std::set<uint32> m_pendingInvasions;
|
||||
std::set<uint32> m_pendingPallids;
|
||||
std::map<uint32, InvasionZone> m_activeInvasions;
|
||||
std::map<uint32, CityAttack> m_cityAttacks;
|
||||
|
||||
ScourgeInvasionData();
|
||||
|
||||
void Reset();
|
||||
std::string GetData();
|
||||
};
|
||||
|
||||
enum SunsReachPhases
|
||||
{
|
||||
SUNS_REACH_PHASE_1_STAGING_AREA,
|
||||
@@ -166,7 +293,7 @@ class WorldState
|
||||
void SaveHelper(std::string& stringToSave, WorldStateSaveIds saveId);
|
||||
void HandlePlayerEnterZone(Player* player, AreaTableIDs zoneId);
|
||||
void HandlePlayerLeaveZone(Player* player, AreaTableIDs zoneId);
|
||||
bool IsConditionFulfilled(WorldStateCondition conditionId, WorldStateConditionState state = WORLD_STATE_CONDITION_STATE_NONE) const;
|
||||
bool IsConditionFulfilled(uint32 conditionId, uint32 state = WORLD_STATE_CONDITION_STATE_NONE) const;
|
||||
void HandleConditionStateChange(WorldStateCondition conditionId, WorldStateConditionState state);
|
||||
void HandleExternalEvent(WorldStateEvent eventId, uint32 param);
|
||||
void Update(uint32 diff);
|
||||
@@ -195,8 +322,55 @@ class WorldState
|
||||
bool _isMagtheridonHeadSpawnedHorde;
|
||||
bool _isMagtheridonHeadSpawnedAlliance;
|
||||
SunsReachReclamationData m_sunsReachData;
|
||||
std::map<WorldStateCondition, std::atomic<WorldStateConditionState>> _transportStates; // atomic to avoid having to lock
|
||||
std::map<uint32, std::atomic<WorldStateConditionState>> _transportStates; // atomic to avoid having to lock
|
||||
std::mutex _mutex; // all World State operations are threat unsafe
|
||||
|
||||
// Scourge Invasion
|
||||
public:
|
||||
void SetScourgeInvasionState(SIState state);
|
||||
void StartScourgeInvasion(bool sendMail);
|
||||
void StopScourgeInvasion();
|
||||
[[nodiscard]] uint32 GetSIRemaining(SIRemaining remaining) const;
|
||||
[[nodiscard]] uint32 GetSIRemainingByZone(uint32 zoneId) const;
|
||||
void SetSIRemaining(SIRemaining remaining, uint32 value);
|
||||
TimePoint GetSITimer(SITimers timer);
|
||||
void SetSITimer(SITimers timer, TimePoint timePoint);
|
||||
uint32 GetBattlesWon();
|
||||
void AddBattlesWon(int32 count);
|
||||
uint32 GetLastAttackZone();
|
||||
void SetLastAttackZone(uint32 zoneId);
|
||||
void BroadcastSIWorldstates();
|
||||
void HandleDefendedZones();
|
||||
|
||||
std::string GetScourgeInvasionPrintout();
|
||||
void StartZoneEvent(SIZoneIds eventId);
|
||||
void StartNewInvasionIfTime(uint32 attackTimeVar, uint32 zoneId);
|
||||
void StartNewCityAttackIfTime(uint32 attackTimeVar, uint32 zoneId);
|
||||
void StartNewInvasion(uint32 zoneId);
|
||||
void StartNewCityAttack(uint32 zoneId);
|
||||
bool ResumeInvasion(ScourgeInvasionData::InvasionZone& zone);
|
||||
bool SummonMouth(Map* map, ScourgeInvasionData::InvasionZone& zone, Position position, bool newInvasion);
|
||||
bool SummonPallid(Map* map, ScourgeInvasionData::CityAttack& zone, const Position& position, uint32 spawnLoc);
|
||||
void HandleActiveZone(uint32 attackTimeVar, uint32 zoneId, uint32 remainingVar, TimePoint now);
|
||||
|
||||
Map* GetMap(uint32 mapId, Position const& invZone);
|
||||
bool IsActiveZone(uint32 zoneId);
|
||||
uint32 GetActiveZones();
|
||||
uint32 GetTimerIdForZone(uint32 zoneId);
|
||||
|
||||
void SetPallidGuid(uint32 zoneId, ObjectGuid guid);
|
||||
void SetMouthGuid(uint32 zoneId, ObjectGuid guid);
|
||||
void AddPendingInvasion(uint32 zoneId);
|
||||
void RemovePendingInvasion(uint32 zoneId);
|
||||
void AddPendingPallid(uint32 zoneId);
|
||||
void RemovePendingPallid(uint32 zoneId);
|
||||
|
||||
void OnEnable(ScourgeInvasionData::InvasionZone& zone);
|
||||
void OnDisable(ScourgeInvasionData::InvasionZone& zone);
|
||||
void OnDisable(ScourgeInvasionData::CityAttack& zone);
|
||||
private:
|
||||
void SendScourgeInvasionMail();
|
||||
ScourgeInvasionData m_siData;
|
||||
};
|
||||
|
||||
#define sWorldState WorldState::instance()
|
||||
|
||||
@@ -39,9 +39,18 @@ public:
|
||||
{ "gatecounter", HandleSunwellGateCounterCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
};
|
||||
|
||||
static ChatCommandTable scourgeInvasionCommandTable =
|
||||
{
|
||||
{ "show", HandleScourgeInvasionCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "state", HandleScourgeInvasionStateCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "battleswon", HandleScourgeInvasionBattlesWonCommand, SEC_ADMINISTRATOR, Console::Yes },
|
||||
{ "startzone", HandleScourgeInvasionStartZone, SEC_ADMINISTRATOR, Console::Yes },
|
||||
};
|
||||
|
||||
static ChatCommandTable worldStateCommandTable =
|
||||
{
|
||||
{ "sunsreach", sunsreachCommandTable }
|
||||
{ "sunsreach", sunsreachCommandTable },
|
||||
{ "scourgeinvasion", scourgeInvasionCommandTable }
|
||||
};
|
||||
|
||||
static ChatCommandTable commandTable =
|
||||
@@ -118,6 +127,46 @@ public:
|
||||
handler->PSendSysMessage(sWorldState->GetSunsReachPrintout());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleScourgeInvasionCommand(ChatHandler* handler)
|
||||
{
|
||||
handler->PSendSysMessage(sWorldState->GetScourgeInvasionPrintout());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleScourgeInvasionStateCommand(ChatHandler* handler, uint32 value)
|
||||
{
|
||||
if (value >= SI_STATE_MAX)
|
||||
{
|
||||
handler->PSendSysMessage("Syntax: .worldstate scourgeinvasion state <value>.");
|
||||
handler->PSendSysMessage("Valid values are: 0 (Disabled), 1 (Enabled).");
|
||||
return true;
|
||||
}
|
||||
sWorldState->SetScourgeInvasionState(SIState(value));
|
||||
handler->PSendSysMessage("Scourge Invasion state set to {}.", value);
|
||||
handler->PSendSysMessage(sWorldState->GetScourgeInvasionPrintout());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleScourgeInvasionBattlesWonCommand(ChatHandler* /* handler */, int32 value)
|
||||
{
|
||||
sWorldState->AddBattlesWon(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleScourgeInvasionStartZone(ChatHandler* handler, uint32 value)
|
||||
{
|
||||
|
||||
if (value >= SI_TIMER_MAX)
|
||||
{
|
||||
handler->PSendSysMessage("Syntax: .worldstate scourgeinvasion startzone <value>.\nvalid values: 0-7");
|
||||
return true;
|
||||
}
|
||||
sWorldState->StartZoneEvent(SIZoneIds(value));
|
||||
handler->PSendSysMessage("Scourge Invasion event started for zone {}.", value);
|
||||
handler->PSendSysMessage(sWorldState->GetScourgeInvasionPrintout());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_worldstate_commandscript()
|
||||
|
||||
@@ -28,161 +28,6 @@
|
||||
#include "ObjectAccessor.h"
|
||||
#include <limits>
|
||||
|
||||
//How to win friends and influence enemies
|
||||
// texts signed for creature 28939 but used for 28939, 28940, 28610
|
||||
enum win_friends
|
||||
{
|
||||
SAY_AGGRO = 0,
|
||||
SAY_CRUSADER = 1,
|
||||
SAY_PERSUADED1 = 2,
|
||||
SAY_PERSUADED2 = 3,
|
||||
SAY_PERSUADED3 = 4,
|
||||
SAY_PERSUADED4 = 5,
|
||||
SAY_PERSUADED5 = 6,
|
||||
SAY_PERSUADED6 = 7,
|
||||
SAY_PERSUADE_RAND = 8,
|
||||
SPELL_PERSUASIVE_STRIKE = 52781,
|
||||
SPELL_THREAT_PULSE = 58111,
|
||||
QUEST_HOW_TO_WIN_FRIENDS = 12720,
|
||||
};
|
||||
|
||||
class npc_crusade_persuaded : public CreatureScript
|
||||
{
|
||||
public:
|
||||
npc_crusade_persuaded() : CreatureScript("npc_crusade_persuaded") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return new npc_crusade_persuadedAI(creature);
|
||||
}
|
||||
|
||||
struct npc_crusade_persuadedAI : public CombatAI
|
||||
{
|
||||
npc_crusade_persuadedAI(Creature* creature) : CombatAI(creature) { }
|
||||
|
||||
const uint32 SAY_AGGRO_CHANCE = 33;
|
||||
const uint32 PERSUADE_SUCCESS_CHANCE = 3; // 30% chance
|
||||
const uint32 SPEECH_TIMER_DEFAULT = 1000;
|
||||
const uint32 SPEECH_TIMER_FOR_ROLEPLAY = 8000;
|
||||
const uint32 SPEECH_COUNTER_PREVENT_SUCCESS_ROLEPLAY = 0;
|
||||
const uint32 SPEECH_COUNTER_START_SUCCESS_ROLEPLAY = 1;
|
||||
|
||||
uint32 speechTimer;
|
||||
uint32 speechCounter;
|
||||
ObjectGuid playerGUID;
|
||||
bool persuaded;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
speechTimer = 0;
|
||||
speechCounter = SPEECH_COUNTER_PREVENT_SUCCESS_ROLEPLAY;
|
||||
playerGUID.Clear();
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
me->RestoreFaction();
|
||||
}
|
||||
|
||||
void JustEngagedWith(Unit*) override
|
||||
{
|
||||
if (roll_chance_i(SAY_AGGRO_CHANCE))
|
||||
Talk(SAY_AGGRO);
|
||||
}
|
||||
|
||||
void SpellHit(Unit* caster, SpellInfo const* spell) override
|
||||
{
|
||||
if (spell->Id == SPELL_PERSUASIVE_STRIKE && caster->IsPlayer() && me->IsAlive() && !speechCounter)
|
||||
{
|
||||
if (Player* player = caster->ToPlayer())
|
||||
{
|
||||
playerGUID = player->GetGUID();
|
||||
speechTimer = SPEECH_TIMER_DEFAULT;
|
||||
speechCounter = SPEECH_COUNTER_START_SUCCESS_ROLEPLAY;
|
||||
uint32 persuadeRoll = urand(1, 10);
|
||||
|
||||
sCreatureTextMgr->SendChat(me, SAY_PERSUADE_RAND, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_NEUTRAL, false, player);
|
||||
|
||||
if (persuadeRoll <= PERSUADE_SUCCESS_CHANCE && player->GetQuestStatus(QUEST_HOW_TO_WIN_FRIENDS) == QUEST_STATUS_INCOMPLETE)
|
||||
{
|
||||
persuaded = true;
|
||||
me->SetFaction(player->GetFaction());
|
||||
me->CombatStop(true);
|
||||
me->GetMotionMaster()->MoveIdle();
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
}
|
||||
else
|
||||
persuaded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (speechCounter)
|
||||
{
|
||||
if (speechTimer <= diff)
|
||||
{
|
||||
Player* player = ObjectAccessor::GetPlayer(*me, playerGUID);
|
||||
if (!player)
|
||||
{
|
||||
EnterEvadeMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (persuaded)
|
||||
{
|
||||
switch (speechCounter)
|
||||
{
|
||||
case 1:
|
||||
Talk(SAY_PERSUADED1);
|
||||
speechTimer = SPEECH_TIMER_FOR_ROLEPLAY;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
Talk(SAY_PERSUADED2);
|
||||
speechTimer = SPEECH_TIMER_FOR_ROLEPLAY;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
Talk(SAY_PERSUADED3);
|
||||
speechTimer = SPEECH_TIMER_FOR_ROLEPLAY;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
Talk(SAY_PERSUADED4);
|
||||
speechTimer = SPEECH_TIMER_FOR_ROLEPLAY;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
sCreatureTextMgr->SendChat(me, SAY_PERSUADED5, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_NEUTRAL, false, player);
|
||||
speechTimer = SPEECH_TIMER_FOR_ROLEPLAY;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
Talk(SAY_PERSUADED6);
|
||||
Unit::Kill(player, me);
|
||||
speechCounter = SPEECH_COUNTER_PREVENT_SUCCESS_ROLEPLAY;
|
||||
player->GroupEventHappens(QUEST_HOW_TO_WIN_FRIENDS, me);
|
||||
return;
|
||||
}
|
||||
|
||||
++speechCounter;
|
||||
}
|
||||
else
|
||||
{
|
||||
Talk(SAY_CRUSADER);
|
||||
speechCounter = SPEECH_COUNTER_PREVENT_SUCCESS_ROLEPLAY;
|
||||
}
|
||||
}
|
||||
else
|
||||
speechTimer = (speechTimer > diff) ? (speechTimer - diff) : 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CombatAI::UpdateAI(diff);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/*######
|
||||
## npc_koltira_deathweaver
|
||||
######*/
|
||||
@@ -1098,12 +943,135 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//How to win friends and influence enemies
|
||||
// texts signed for creature 28939 but used for 28939, 28940, 28610
|
||||
enum win_friends
|
||||
{
|
||||
SAY_CRUSADER = 1,
|
||||
SAY_PERSUADED1 = 2,
|
||||
SAY_PERSUADED2 = 3,
|
||||
SAY_PERSUADED3 = 4,
|
||||
SAY_PERSUADED4 = 5,
|
||||
SAY_PERSUADED5 = 6,
|
||||
SAY_PERSUADED6 = 7,
|
||||
SAY_PERSUADE_RAND = 8,
|
||||
QUEST_HOW_TO_WIN_FRIENDS = 12720,
|
||||
|
||||
NPC_SCARLET_PREACHER = 28939,
|
||||
NPC_SCARLET_COMMANDER = 28936,
|
||||
NPC_SCARLET_CRUSADER = 28940,
|
||||
NPC_SCARLET_MARKSMAN = 28610,
|
||||
NPC_SCARLET_LORD_MCCREE = 28964
|
||||
};
|
||||
|
||||
// 52781 - Persuasive Strike
|
||||
class spell_chapter2_persuasive_strike : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_chapter2_persuasive_strike);
|
||||
|
||||
bool Load() override
|
||||
{
|
||||
return GetCaster() && GetCaster()->IsPlayer()
|
||||
&& GetCaster()->ToPlayer()->GetQuestStatus(QUEST_HOW_TO_WIN_FRIENDS) == QUEST_STATUS_INCOMPLETE;
|
||||
}
|
||||
|
||||
void HandleHit(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
Creature* creature = GetHitCreature();
|
||||
Player* player = GetCaster()->ToPlayer();
|
||||
|
||||
if (!creature || !player)
|
||||
return;
|
||||
|
||||
if (!creature->EntryEquals(NPC_SCARLET_PREACHER, NPC_SCARLET_COMMANDER, NPC_SCARLET_CRUSADER, NPC_SCARLET_MARKSMAN, NPC_SCARLET_LORD_MCCREE))
|
||||
return;
|
||||
|
||||
sCreatureTextMgr->SendChat(creature, SAY_PERSUADE_RAND, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_NEUTRAL, false, player);
|
||||
|
||||
if (roll_chance_f(30.0f))
|
||||
{
|
||||
creature->CombatStop(true);
|
||||
creature->GetMotionMaster()->MoveIdle();
|
||||
creature->SetImmuneToPC(true);
|
||||
creature->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
|
||||
creature->SetReactState(REACT_PASSIVE);
|
||||
|
||||
creature->AI()->Talk(SAY_PERSUADED1, 8s);
|
||||
creature->AI()->Talk(SAY_PERSUADED2, 16s);
|
||||
creature->AI()->Talk(SAY_PERSUADED3, 24s);
|
||||
creature->AI()->Talk(SAY_PERSUADED4, 32s);
|
||||
|
||||
creature->m_Events.AddEventAtOffset([creature, player]
|
||||
{
|
||||
if (player)
|
||||
sCreatureTextMgr->SendChat(creature, SAY_PERSUADED5, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_NEUTRAL, false, player);
|
||||
}, 40s);
|
||||
|
||||
creature->m_Events.AddEventAtOffset([creature, player]
|
||||
{
|
||||
creature->AI()->Talk(SAY_PERSUADED6);
|
||||
if (player)
|
||||
{
|
||||
Unit::Kill(player, creature);
|
||||
player->GroupEventHappens(QUEST_HOW_TO_WIN_FRIENDS, creature);
|
||||
}
|
||||
}, 48s);
|
||||
}
|
||||
else
|
||||
creature->AI()->Talk(SAY_CRUSADER, 1s);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_chapter2_persuasive_strike::HandleHit, EFFECT_0, SPELL_EFFECT_DUMMY);
|
||||
}
|
||||
};
|
||||
|
||||
enum AcherusPortal
|
||||
{
|
||||
SPELL_PORTAL_EFFECT_ACHERUS = 53098,
|
||||
QUEST_SCARLET_ARMIES_APPROACH = 12757
|
||||
};
|
||||
|
||||
class spell_portal_effect_acherus : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_portal_effect_acherus);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_PORTAL_EFFECT_ACHERUS });
|
||||
}
|
||||
|
||||
SpellCastResult CheckCast()
|
||||
{
|
||||
Unit* target = GetExplTargetUnit();
|
||||
if (target && target->IsPlayer() && target->ToPlayer()->HasQuest(QUEST_SCARLET_ARMIES_APPROACH))
|
||||
return SPELL_CAST_OK;
|
||||
|
||||
return SPELL_FAILED_DONT_REPORT;
|
||||
}
|
||||
|
||||
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
if (Unit* caster = GetCaster())
|
||||
if (Player* player = GetHitPlayer())
|
||||
caster->CastSpell(player, GetEffectValue(), true);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnCheckCast += SpellCheckCastFn(spell_portal_effect_acherus::CheckCast);
|
||||
OnEffectHitTarget += SpellEffectFn(spell_portal_effect_acherus::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_the_scarlet_enclave_c2()
|
||||
{
|
||||
new npc_crusade_persuaded();
|
||||
new npc_scarlet_courier();
|
||||
new npc_koltira_deathweaver();
|
||||
new npc_a_special_surprise();
|
||||
new npc_acherus_necromancer();
|
||||
new npc_gothik_the_harvester();
|
||||
RegisterSpellScript(spell_chapter2_persuasive_strike);
|
||||
RegisterSpellScript(spell_portal_effect_acherus);
|
||||
}
|
||||
|
||||
@@ -548,6 +548,7 @@ public:
|
||||
summons.DespawnAll();
|
||||
|
||||
me->SetImmuneToAll(true);
|
||||
me->LoadEquipment(1, true);
|
||||
me->ReplaceAllNpcFlags(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER);
|
||||
me->SetStandState(UNIT_STAND_STATE_STAND);
|
||||
me->SetVisible(true);
|
||||
@@ -699,6 +700,7 @@ public:
|
||||
{
|
||||
summon->CombatStop(true);
|
||||
summon->GetThreatMgr().ClearAllThreat();
|
||||
summon->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
|
||||
summon->SetImmuneToAll(true);
|
||||
summon->SetReactState(REACT_PASSIVE);
|
||||
summon->GetMotionMaster()->Clear(false);
|
||||
@@ -949,6 +951,7 @@ public:
|
||||
if (summon->GetEntry() <= NPC_RIMBLAT_EARTHSHATTER && summon->GetEntry() != NPC_HIGHLORD_TIRION_FORDRING)
|
||||
{
|
||||
float o = lk->GetAngle(summon);
|
||||
summon->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
|
||||
summon->GetMotionMaster()->MovePoint(3, lk->GetPositionX() + 2.0f * cos(o), lk->GetPositionY() + 2.0f * std::sin(o), lk->GetPositionZ());
|
||||
summon->ToTempSummon()->SetTempSummonType(TEMPSUMMON_MANUAL_DESPAWN);
|
||||
}
|
||||
|
||||
@@ -25,17 +25,29 @@
|
||||
## npcs_riverbreeze_and_silversky
|
||||
######*/
|
||||
|
||||
#define GOSSIP_ITEM_BEACON "Please make me a Cenarion Beacon"
|
||||
|
||||
enum RiverbreezeAndSilversky
|
||||
{
|
||||
SPELL_CENARION_BEACON = 15120,
|
||||
ITEM_CENARION_BEACON = 11511,
|
||||
ACTION_CREATE_CENARION_BEACON = GOSSIP_ACTION_INFO_DEF + 1,
|
||||
|
||||
NPC_ARATHANDRIS_SILVERSKY = 9528,
|
||||
NPC_MAYBESS_RIVERBREEZE = 9529,
|
||||
|
||||
QUEST_CLEASING_FELWOOD_A = 4101,
|
||||
QUEST_CLEASING_FELWOOD_H = 4102
|
||||
QUEST_CLEASING_FELWOOD_H = 4102,
|
||||
|
||||
// Texts
|
||||
GOSSIP_MENU_SILVERSKY = 2208,
|
||||
GOSSIP_MENU_RIVERBREEZE = 21400,
|
||||
GOSSIP_OPTION_BEACON = 0,
|
||||
|
||||
TEXT_SILVERSKY_1 = 2848,
|
||||
TEXT_SILVERSKY_2 = 2845,
|
||||
TEXT_SILVERSKY_3 = 2844,
|
||||
TEXT_RIVERBREEZE_1 = 2849,
|
||||
TEXT_RIVERBREEZE_2 = 2843,
|
||||
TEXT_RIVERBREEZE_3 = 2842,
|
||||
};
|
||||
|
||||
class npcs_riverbreeze_and_silversky : public CreatureScript
|
||||
@@ -59,32 +71,36 @@ public:
|
||||
if (creature->IsQuestGiver())
|
||||
player->PrepareQuestMenu(creature->GetGUID());
|
||||
|
||||
uint32 creatureId = creature->GetEntry();
|
||||
uint32 const creatureId = creature->GetEntry();
|
||||
|
||||
if (creatureId == NPC_ARATHANDRIS_SILVERSKY)
|
||||
{
|
||||
if (player->GetQuestRewardStatus(QUEST_CLEASING_FELWOOD_A))
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_BEACON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
|
||||
SendGossipMenuFor(player, 2848, creature->GetGUID());
|
||||
if (!player->HasItemCount(ITEM_CENARION_BEACON, 1, true))
|
||||
AddGossipItemFor(player, GOSSIP_MENU_SILVERSKY, GOSSIP_OPTION_BEACON, GOSSIP_SENDER_MAIN, ACTION_CREATE_CENARION_BEACON);
|
||||
|
||||
SendGossipMenuFor(player, TEXT_SILVERSKY_1, creature->GetGUID());
|
||||
}
|
||||
else if (player->GetTeamId() == TEAM_HORDE)
|
||||
SendGossipMenuFor(player, 2845, creature->GetGUID());
|
||||
SendGossipMenuFor(player, TEXT_SILVERSKY_2, creature->GetGUID());
|
||||
else
|
||||
SendGossipMenuFor(player, 2844, creature->GetGUID());
|
||||
SendGossipMenuFor(player, TEXT_SILVERSKY_3, creature->GetGUID());
|
||||
}
|
||||
|
||||
if (creatureId == NPC_MAYBESS_RIVERBREEZE)
|
||||
{
|
||||
if (player->GetQuestRewardStatus(QUEST_CLEASING_FELWOOD_H))
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_BEACON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
|
||||
SendGossipMenuFor(player, 2849, creature->GetGUID());
|
||||
if (!player->HasItemCount(ITEM_CENARION_BEACON, 1, true))
|
||||
AddGossipItemFor(player, GOSSIP_MENU_RIVERBREEZE, GOSSIP_OPTION_BEACON, GOSSIP_SENDER_MAIN, ACTION_CREATE_CENARION_BEACON);
|
||||
|
||||
SendGossipMenuFor(player, TEXT_RIVERBREEZE_1, creature->GetGUID());
|
||||
}
|
||||
else if (player->GetTeamId() == TEAM_ALLIANCE)
|
||||
SendGossipMenuFor(player, 2843, creature->GetGUID());
|
||||
SendGossipMenuFor(player, TEXT_RIVERBREEZE_2, creature->GetGUID());
|
||||
else
|
||||
SendGossipMenuFor(player, 2842, creature->GetGUID());
|
||||
SendGossipMenuFor(player, TEXT_RIVERBREEZE_3, creature->GetGUID());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1347,6 +1347,27 @@ class spell_hun_target_self_and_pet : public SpellScript
|
||||
}
|
||||
};
|
||||
|
||||
// -53301 - Explosive Shot
|
||||
class spell_hun_explosive_shot : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_hun_explosive_shot);
|
||||
|
||||
void HandleFinish()
|
||||
{
|
||||
// Handling of explosive shot initial cast without LnL proc
|
||||
if (!GetCaster() || !GetCaster()->IsPlayer())
|
||||
return;
|
||||
|
||||
if (!GetCaster()->HasAura(SPELL_LOCK_AND_LOAD_TRIGGER))
|
||||
GetSpell()->TakeAmmo();
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterCast += SpellCastFn(spell_hun_explosive_shot::HandleFinish);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_hunter_spell_scripts()
|
||||
{
|
||||
RegisterSpellScript(spell_hun_check_pet_los);
|
||||
@@ -1378,4 +1399,5 @@ void AddSC_hunter_spell_scripts()
|
||||
RegisterSpellScript(spell_hun_intimidation);
|
||||
RegisterSpellScript(spell_hun_bestial_wrath);
|
||||
RegisterSpellScript(spell_hun_target_self_and_pet);
|
||||
RegisterSpellScript(spell_hun_explosive_shot);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore 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 Affero General Public License as published by the
|
||||
* Free Software Foundation; either version 3 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 Affero 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/>.
|
||||
*/
|
||||
|
||||
#ifndef SCOURGE_INVASION_H
|
||||
#define SCOURGE_INVASION_H
|
||||
|
||||
enum ScourgeInvasionSpells
|
||||
{
|
||||
SPELL_SPIRIT_PARTICLES_PURPLE = 28126, // Purple Minions Aura.
|
||||
|
||||
// GameObject Necropolis
|
||||
SPELL_SUMMON_NECROPOLIS_CRITTERS = 27866, // Spawns NPCs Necropolis Health and Necropolis.
|
||||
|
||||
// Necropolis Health -> Necropolis
|
||||
SPELL_DESPAWNER_OTHER = 28349, // Casted by the NPC "Necropolis health" after getting hit by,
|
||||
// on the NPC "Necropolis" which destroys itself and the Necropolis Object.
|
||||
|
||||
// Necropolis Health
|
||||
SPELL_ZAP_NECROPOLIS = 28386, // There are always 3 Necrotic Shards spawns per Necropolis. This Spell is castet on the NPC "Necropolis Health" if a Shard dies and does 40 Physical damage.
|
||||
// NPC "Necropolis Health" has 42 health. 42 health / 3 Shards = 14 damage.
|
||||
// We have set the armor value from the NPC "Necropolis Health" to 950 to reduce the damage from 40 to 14.
|
||||
|
||||
// Necropolis -> Proxy
|
||||
SPELL_COMMUNIQUE_TIMER_NECROPOLIS = 28395, // Periodically triggers 28373 Communique, Necropolis-to-Proxies every 15 seconds.
|
||||
SPELL_COMMUNIQUE_NECROPOLIS_TO_PROXIES = 28373, // purple bolt Visual (BIG).
|
||||
|
||||
// Proxy -> Necropolis
|
||||
SPELL_COMMUNIQUE_PROXY_TO_NECROPOLIS = 28367, // Purple bolt Visual (SMALL).
|
||||
|
||||
// Proxy -> Relay
|
||||
SPELL_COMMUNIQUE_PROXY_TO_RELAY = 28366, // purple bolt Visual (BIG).
|
||||
|
||||
// Relay -> Proxy
|
||||
SPELL_COMMUNIQUE_RELAY_TO_PROXY = 28365, // Purple bolt Visual (SMALL).
|
||||
|
||||
// Relay -> Shard
|
||||
SPELL_COMMUNIQUE_RELAY_TO_CAMP = 28326, // Purple bolt Visual (BIG).
|
||||
|
||||
// Shard
|
||||
SPELL_CREATE_CRYSTAL = 28344, // Spawn a Necrotic Shard.
|
||||
SPELL_CREATE_CRYSTAL_CORPSE = 27895, // Summon (Damaged Necrotic Shard).
|
||||
SPELL_CAMP_RECEIVES_COMMUNIQUE = 28449, // Impact Visual.
|
||||
SPELL_COMMUNIQUE_TIMER_CAMP = 28346, // Cast on npc_necrotic_shard on spawn? Periodically triggers 28345 Communique Trigger every 35 seconds.
|
||||
SPELL_COMMUNIQUE_TRIGGER = 28345, // Triggers 28281 SPELL_COMMUNIQUE_CAMP_TO_RELAY via void Spell::EffectDummy.
|
||||
SPELL_DAMAGE_CRYSTAL = 28041, // 100 Damage (Physical). Casted on itself, if 16143 (Shadow of Doom) spawns.
|
||||
SPELL_SOUL_REVIVAL = 28681, // Increases all damage caused by 10%.
|
||||
SPELL_CAMP_TYPE_GHOST_SKELETON = 28197, // Camp Type, tells the NPC "Scourge Invasion Minion, finder" which Camp type the Shard has.
|
||||
SPELL_CAMP_TYPE_GHOST_GHOUL = 28198, // ""
|
||||
SPELL_CAMP_TYPE_GHOUL_SKELETON = 28199, // ""
|
||||
SPELL_MINION_SPAWNER_SMALL = 27887, // Triggers 27885 (Disturb Minion Trap, small) every 5 seconds. Activates up to 3 unknown Objects wich spawns the Minions.
|
||||
SPELL_MINION_SPAWNER_BUTTRESS = 27888, // Triggers 27886 (Disturb Minion Trap, Buttress) every 1 hour. Activates unknown Objects, They may also spawn the Cultists.
|
||||
SPELL_CHOOSE_CAMP_TYPE = 28201, // casted by Necrotic Shard.
|
||||
|
||||
// Shard -> Relay
|
||||
SPELL_COMMUNIQUE_CAMP_TO_RELAY = 28281, // Purple bolt Visual (SMALL)
|
||||
SPELL_COMMUNIQUE_CAMP_TO_RELAY_DEATH = 28351, // Visual when Damaged Necrotic Shard dies.
|
||||
|
||||
// Camp - Minion spawning system
|
||||
SPELL_FIND_CAMP_TYPE = 28203, // casted by Scourge Invasion Minion, finder.
|
||||
|
||||
// Scourge Invasion Minion, spawner, Ghost/Ghoul
|
||||
SPELL_PH_SUMMON_MINION_TRAP_GHOST_GHOUL = 27883,
|
||||
|
||||
// Scourge Invasion Minion, spawner, Ghost/Skeleton
|
||||
SPELL_PH_SUMMON_MINION_TRAP_GHOST_SKELETON = 28186,
|
||||
|
||||
// Scourge Invasion Minion, spawner, Ghoul/Skeleton
|
||||
SPELL_PH_SUMMON_MINION_TRAP_GHOUL_SKELETON = 28187,
|
||||
|
||||
// Minions Spells
|
||||
SPELL_ZAP_CRYSTAL = 28032, // 15 damage to a Necrotic Shard on death.
|
||||
SPELL_MINION_SPAWN_IN = 28234, // Pink Lightning.
|
||||
SPELL_SPIRIT_SPAWN_OUT = 17680, // Makes invisible.
|
||||
SPELL_MINION_DESPAWN_TIMER = 28090, // Triggers 28091 (Despawner, self) every 150 seconds. Triggers 17680 SPELL_SPIRIT_SPAWN_OUT via void Spell::EffectDummy.
|
||||
SPELL_CONTROLLER_TIMER = 28095, // Triggers 28091 (Despawner, self) every 60 seconds for 1 hour. (Unknown who is casting this).
|
||||
SPELL_DESPAWNER_SELF = 28091, // Trigger from Spell above.
|
||||
SPELL_SUMMON_SCOURGE_CONTROLLER = 28092,
|
||||
|
||||
// Minion Abilities
|
||||
SPELL_SCOURGE_STRIKE = 28265, // Pink Lightning (Instakill).
|
||||
SPELL_ENRAGE = 8599, // Used by 16141 (Ghoul Berserker).
|
||||
SPELL_BONE_SHARDS = 17014, // [shortest sniff CD: 16,583 seconds] Used by 16299 (Skeletal Shocktrooper).
|
||||
SPELL_INFECTED_BITE = 7367, // [shortest sniff CD: 13,307 seconds] Used by 16141 (Ghoul Berserker).
|
||||
SPELL_DEMORALIZING_SHOUT = 16244, // [shortest sniff CD: 19,438 seconds] Used by 16298 (Spectral Soldier).
|
||||
SPELL_SUNDER_ARMOR = 21081, // [shortest sniff CD: 6,489 seconds] Used by 16298 (Spectral Soldier).
|
||||
SPELL_SHADOW_WORD_PAIN = 589, // Used by 16438 (Skeletal Trooper).
|
||||
SPELL_DUAL_WIELD = 674, // Used by Skeletal Soldier and Skeletal Shocktrooper.
|
||||
|
||||
// Marks of the Dawn
|
||||
SPELL_CREATE_LESSER_MARK_OF_THE_DAWN = 28319, // Create Lesser Mark of the Dawn.
|
||||
SPELL_CREATE_MARK_OF_THE_DAWN = 28320, // Create Mark of the Dawn.
|
||||
SPELL_CREATE_GREATER_MARK_OF_THE_DAWN = 28321, // Create Greater Mark of the Dawn.
|
||||
|
||||
// Rare Minions
|
||||
SPELL_KNOCKDOWN = 16790, // Used by 14697 (Lumbering Horror).
|
||||
SPELL_TRAMPLE = 5568, // Used by 14697 (Lumbering Horror).
|
||||
SPELL_AURA_OF_FEAR = 28313, // Used by 14697 (Lumbering Horror).
|
||||
SPELL_RIBBON_OF_SOULS = 16243, // [shortest sniff CD: 1,638 seconds] Used by 16379 (Spirit of the Damned).
|
||||
SPELL_PSYCHIC_SCREAM = 22884, // or 26042, used by 16379 (Spirit of the Damned).
|
||||
SPELL_MINION_DESPAWN_TIMER_UNCOMMON = 28292, // Triggers 28091 (Despawner, self) every 10 minutes. Triggers 17680 SPELL_SPIRIT_SPAWN_OUT via void Spell::EffectDummy.
|
||||
SPELL_ARCANE_BOLT = 13748, /* 20720 Used by 16380 (Bone Witch).
|
||||
https://classicdb.ch/?npc=16380#abilities says 13748 but 20720 is the only "Arcane Bolt" whichs requires no mana.
|
||||
Danage is very high, so i guess it has a very long cd.
|
||||
Spell description in the Bestiary is: Hurls a magical bolt at an enemy, inflicting Arcane damage.
|
||||
*/
|
||||
|
||||
// Cultist Engineer
|
||||
SPELL_CREATE_SUMMONER_SHIELD = 28132, // Summon Object - Temporary (181142),
|
||||
// Casted exactly the same time with 28234 (Minion Spawn-in) on spawn.
|
||||
SPELL_BUTTRESS_CHANNEL = 28078, // Channeled by Cultist Engineer on Damaged Necrotic Shard shortly after spawning.
|
||||
SPELL_BUTTRESS_TRAP = 28054, // Unknown.
|
||||
SPELL_KILL_SUMMONER_SUMMON_BOSS = 28250, // Reagents, 1 Necrotic Rune
|
||||
|
||||
// Probably used to spawn Shadow of Doom. Casting sequence (All these [x] spells are being casted the following order within 1-2 seconds):
|
||||
SPELL_PH_KILL_SUMMONER_BUFF = 27852, // [1] Casted by Cultist on Player.
|
||||
SPELL_KILL_SUMMONER_WHO_WILL_SUMMON_BOSS = 27894, // [2] Casted by Player on Cultist.
|
||||
SPELL_QUIET_SUICIDE = 3617, // [3] Instakill, casted exactly same time as 31316 (Summon Boss Buff).
|
||||
SPELL_SUMMON_BOSS_BUFF = 31316, // [4] Summon Boss Buff, casted on Player
|
||||
SPELL_SUMMON_BOSS = 31315, /* [5] Reagents, 8 Necrotic Rune, Summon (Shadow of Doom) for 1 hour.
|
||||
The question is: What happens after this hour if the Shadow of Doom despawns?
|
||||
Do the cultists respawn and channeling again on the damaged shard or
|
||||
Does the Necrotic crystal respawn without Cultists or Shadows of Doom?
|
||||
*/
|
||||
|
||||
// Shadow of Doom
|
||||
SPELL_SPAWN_SMOKE = 10389, // Spawning Visual.
|
||||
SPELL_ZAP_CRYSTAL_CORPSE = 28056, // Casted on Shard if Shadow of Doom dies.
|
||||
SPELL_MINDFLAY = 16568,
|
||||
SPELL_FEAR = 12542,
|
||||
|
||||
// Pallid Horror - Patchwerk Terror (also uses: 28315)
|
||||
SPELL_SUMMON_CRACKED_NECROTIC_CRYSTAL = 28424, // Alliance.
|
||||
SPELL_SUMMON_FAINT_NECROTIC_CRYSTAL = 28699, // Horde.
|
||||
SPELL_DAMAGE_VS_GUARDS = 28364, // [shortest sniff CD: 11 seconds, longest 81 sec] hits 13839 (Royal Dreadguard).
|
||||
|
||||
// Flameshocker (also uses: 28234, 17680)
|
||||
SPELL_FLAMESHOCKERS_TOUCH = 28314, // [shortest sniff CD: 30 seconds]
|
||||
SPELL_FLAMESHOCKERS_REVENGE = 28323, // On death.
|
||||
SPELL_FLAMESHOCKERS_TOUCH2 = 28329, // [shortest sniff CD: 30 seconds]
|
||||
SPELL_FLAMESHOCKER_IMMOLATE_VISUAL = 28330
|
||||
|
||||
/*
|
||||
These spells are not used by any NPCs or GameObjects.
|
||||
The [PH] in the name means it's a placeholder. B often adds that to the names of things they add to the game but haven't finalized.
|
||||
The fact that the [PH] is still there means the quest was never finished. (Google)
|
||||
SPELL_PH_SUMMON_MINION_PARENT_GHOST_GHOUL = 28183,
|
||||
SPELL_PH_SUMMON_MINION_PARENT_GHOST_SKELETON = 28184,
|
||||
SPELL_PH_SUMMON_MINION_PARENT_GHOUL_SKELETON = 28185,
|
||||
SPELL_PH_GET_TOKEN = 27922, // Create Item "Necrotic Rune".
|
||||
SPELL_PH_BUTTRESS_ACTIVATOR = 28086,
|
||||
SPELL_PH_CRYSTAL_CORPSE_DESPAWN = 28020,
|
||||
SPELL_PH_CRYSTAL_CORPSE_TIMER = 28018, // Triggers 28020 ([PH] Crystal Corpse Despawn) after 2 hours.
|
||||
SPELL_PH_CYSTAL_BAZOOKA = 27849,
|
||||
SPELL_PH_SUMMON_BUTTRESS = 28024, // Summon (Cultist Engineer) for 1 hour.
|
||||
SPELL_DND_SUMMON_CRYSTAL_MINION_FINDER = 28227,
|
||||
*/
|
||||
};
|
||||
|
||||
enum ScourgeInvasionNPC
|
||||
{
|
||||
// Visible NPCs
|
||||
NPC_NECROTIC_SHARD = 16136,
|
||||
NPC_DAMAGED_NECROTIC_SHARD = 16172,
|
||||
NPC_CULTIST_ENGINEER = 16230,
|
||||
NPC_SHADOW_OF_DOOM = 16143,
|
||||
|
||||
// Camp Helpers (invisible)
|
||||
NPC_SCOURGE_INVASION_MINION_FINDER = 16356, // Casting 28203 (Find Camp Type).
|
||||
NPC_SCOURGE_INVASION_MINION_SPAWNER_GHOST_GHOUL = 16306,
|
||||
NPC_SCOURGE_INVASION_MINION_SPAWNER_GHOST_SKELETON = 16336,
|
||||
NPC_SCOURGE_INVASION_MINION_SPAWNER_GHOUL_SKELETON = 16338,
|
||||
|
||||
// Necropolis Helpers (invisible)
|
||||
NPC_NECROPOLIS = 16401,
|
||||
NPC_NECROPOLIS_HEALTH = 16421,
|
||||
NPC_NECROPOLIS_PROXY = 16398,
|
||||
NPC_NECROPOLIS_RELAY = 16386,
|
||||
|
||||
// Minions
|
||||
NPC_SKELETAL_SHOCKTROOPER = 16299,
|
||||
NPC_GHOUL_BERSERKER = 16141,
|
||||
NPC_SPECTRAL_SOLDIER = 16298,
|
||||
|
||||
// Rare Minions
|
||||
NPC_LUMBERING_HORROR = 14697,
|
||||
NPC_BONE_WITCH = 16380,
|
||||
NPC_SPIRIT_OF_THE_DAMNED = 16379,
|
||||
|
||||
// 50 Zones cleared
|
||||
NPC_ARGENT_DAWN_INITIATE = 16384,
|
||||
NPC_ARGENT_DAWN_CLERIC = 16435,
|
||||
// 100 Zones cleared
|
||||
NPC_ARGENT_DAWN_PRIEST = 16436,
|
||||
NPC_ARGENT_DAWN_PALADIN = 16395,
|
||||
// 150 Zones cleared
|
||||
NPC_ARGENT_DAWN_CRUSADER = 16433,
|
||||
NPC_ARGENT_DAWN_CHAMPION = 16434,
|
||||
|
||||
// Low level Minions
|
||||
NPC_SKELETAL_TROOPER = 16438,
|
||||
NPC_SPECTRAL_SPIRIT = 16437,
|
||||
NPC_SKELETAL_SOLDIER = 16422,
|
||||
NPC_SPECTRAL_APPARITATION = 16423,
|
||||
|
||||
// Stormwind - Undercity Attacks https://www.youtube.com/watch?v=c0QjLqHVPRU&t=17s
|
||||
// NPC_PALLID_HORROR = 16394,
|
||||
// NPC_PATCHWORK_TERROR = 16382,
|
||||
NPC_CRACKED_NECROTIC_CRYSTAL = 16431,
|
||||
NPC_FAINT_NECROTIC_CRYSTAL = 16531,
|
||||
NPC_FLAMESHOCKER = 16383,
|
||||
NPC_HIGHLORD_BOLVAR_FORDRAGON = 1748,
|
||||
NPC_VARIAN = 29611,
|
||||
NPC_LADY_SYLVANAS_WINDRUNNER = 10181,
|
||||
NPC_VARIMATHRAS = 2425,
|
||||
NPC_ROYAL_DREADGUARD = 13839,
|
||||
NPC_STORMWIND_ROYAL_GUARD = 1756,
|
||||
NPC_UNDERCITY_ELITE_GUARDIAN = 16432,
|
||||
NPC_UNDERCITY_GUARDIAN = 5624,
|
||||
NPC_DEATHGUARD_ELITE = 7980,
|
||||
NPC_STORMWIND_CITY_GUARD = 68,
|
||||
NPC_STORMWIND_ELITE_GUARD = 16396,
|
||||
|
||||
// Citizens
|
||||
NPC_RENATO_GALLINA = 1432,
|
||||
NPC_MICHAEL_GARRETT = 4551,
|
||||
NPC_HANNAH_AKELEY = 4575,
|
||||
NPC_INNKEEPER_NORMAN = 6741,
|
||||
NPC_OFFICER_MALOOF = 15766,
|
||||
NPC_STEPHANIE_TURNER = 6174,
|
||||
NPC_THOMAS_MILLER = 3518,
|
||||
NPC_WILLIAM_MONTAGUE = 4549
|
||||
};
|
||||
|
||||
enum ScourgeInvasionObjects
|
||||
{
|
||||
// Invisible Objects
|
||||
GO_BUTTRESS_TRAP = 181112, // [Guessed] Those objects can't be sniffed and are not available in any database.
|
||||
|
||||
GO_SUMMON_MINION_TRAP_GHOST_GHOUL = 181111, // Object is not in sniffed files or any database such as WoWHead, but spell 28196 (Create Minion Trap: Ghost/Skeleton) should probably summon them.
|
||||
GO_SUMMON_MINION_TRAP_GHOST_SKELETON = 181155, // ""
|
||||
GO_SUMMON_MINION_TRAP_GHOUL_SKELETON = 181156, // ""
|
||||
|
||||
// Visible Objects
|
||||
GO_SUMMON_CIRCLE = 181136,
|
||||
GO_SUMMONER_SHIELD = 181142,
|
||||
|
||||
GO_UNDEAD_FIRE = 181173,
|
||||
GO_UNDEAD_FIRE_AURA = 181174,
|
||||
GO_SKULLPILE_01 = 181191,
|
||||
GO_SKULLPILE_02 = 181192,
|
||||
GO_SKULLPILE_03 = 181193,
|
||||
GO_SKULLPILE_04 = 181194,
|
||||
|
||||
GO_NECROPOLIS_TINY = 181154, // Necropolis (scale 1.0).
|
||||
GO_NECROPOLIS_SMALL = 181373, // Necropolis (scale 1.5).
|
||||
GO_NECROPOLIS_MEDIUM = 181374, // Necropolis (scale 2.0).
|
||||
GO_NECROPOLIS_BIG = 181215, // Necropolis (scale 2.5).
|
||||
GO_NECROPOLIS_HUGE = 181223, // Necropolis (scale 3.5).
|
||||
GO_NECROPOLIS_CITY = 181172, // Necropolis at the Citys (scale 2.5).
|
||||
};
|
||||
|
||||
enum ScourgeInvasionMisc
|
||||
{
|
||||
ITEM_NECROTIC_RUNE = 22484,
|
||||
ACTION_FLAMESHOCKER_SCHEDULE_DESPAWN = 1, // Used by Flameshocker to schedule despawn.
|
||||
};
|
||||
|
||||
enum ScourgeInvasionNPCEvents
|
||||
{
|
||||
EVENT_SHARD_MINION_SPAWNER_SMALL = 1,
|
||||
EVENT_SHARD_MINION_SPAWNER_BUTTRESS = 2,
|
||||
EVENT_SPAWNER_SUMMON_MINION = 3,
|
||||
EVENT_SHARD_FIND_DAMAGED_SHARD = 4,
|
||||
EVENT_CULTIST_CHANNELING = 5,
|
||||
EVENT_HERALD_OF_THE_LICH_KING_YELL = 6,
|
||||
// EVENT_HERALD_OF_THE_LICH_KING_ZONE_START = 7,
|
||||
// EVENT_HERALD_OF_THE_LICH_KING_ZONE_STOP = 8,
|
||||
EVENT_HERALD_OF_THE_LICH_KING_UPDATE = 9,
|
||||
|
||||
// Shadow of Doom Events
|
||||
EVENT_DOOM_MINDFLAY = 20,
|
||||
EVENT_DOOM_FEAR = 21,
|
||||
EVENT_DOOM_START_ATTACK = 22,
|
||||
|
||||
// Rare Events
|
||||
EVENT_RARE_KNOCKDOWN = 31,
|
||||
EVENT_RARE_TRAMPLE = 32,
|
||||
EVENT_RARE_RIBBON_OF_SOULS = 33,
|
||||
|
||||
// Minion Events
|
||||
EVENT_MINION_ENRAGE = 40,
|
||||
EVENT_MINION_BONE_SHARDS = 41,
|
||||
EVENT_MINION_INFECTED_BITE = 42,
|
||||
EVENT_MINION_DAZED = 43,
|
||||
EVENT_MINION_DEMORALIZING_SHOUT = 44,
|
||||
EVENT_MINION_SUNDER_ARMOR = 45,
|
||||
EVENT_MINION_ARCANE_BOLT = 46,
|
||||
EVENT_MINION_PSYCHIC_SCREAM = 47,
|
||||
EVENT_MINION_SCOURGE_STRIKE = 48,
|
||||
EVENT_MINION_SHADOW_WORD_PAIN = 49,
|
||||
EVENT_MINION_FLAMESHOCKERS_TOUCH = 50,
|
||||
EVENT_MINION_FLAMESHOCKERS_DESPAWN = 51,
|
||||
|
||||
// Pallid Horror Events
|
||||
EVENT_PALLID_RANDOM_YELL = 52,
|
||||
EVENT_PALLID_SPELL_DAMAGE_VS_GUARDS = 53,
|
||||
EVENT_SYLVANAS_ANSWER_YELL = 54,
|
||||
EVENT_PALLID_RANDOM_SAY = 55,
|
||||
EVENT_PALLID_SUMMON_FLAMESHOCKER = 56
|
||||
};
|
||||
|
||||
enum ScourgeInvasionQuests
|
||||
{
|
||||
QUEST_UNDER_THE_SHADOW = 9153,
|
||||
QUEST_CRACKED_NECROTIC_CRYSTAL = 9292,
|
||||
QUEST_FAINT_NECROTIC_CRYSTAL = 9310
|
||||
};
|
||||
|
||||
enum ScourgeInvasionTalk
|
||||
{
|
||||
HERALD_OF_THE_LICH_KING_SAY_ATTACK_START = 0,
|
||||
HERALD_OF_THE_LICH_KING_SAY_ATTACK_END = 1,
|
||||
HERALD_OF_THE_LICH_KING_SAY_ATTACK_RANDOM = 2,
|
||||
PALLID_HORROR_SAY_RANDOM_YELL = 0,
|
||||
SHADOW_OF_DOOM_SAY_AGGRO = 0,
|
||||
SYLVANAS_SAY_ATTACK_END = 3,
|
||||
VARIAN_SAY_ATTACK_END = 3
|
||||
};
|
||||
|
||||
enum ScourgeInvasionLang
|
||||
{
|
||||
// Pallid Horror random yelling every 65-300 seconds
|
||||
BCT_PALLID_HORROR_YELL1 = 12329, // What? This not Naxxramas! We not like this place... destroy!
|
||||
BCT_PALLID_HORROR_YELL2 = 12327, // Raaarrrrggghhh! We come for you!
|
||||
BCT_PALLID_HORROR_YELL3 = 12326, // Kel'Thuzad say to tell you... DIE!
|
||||
BCT_PALLID_HORROR_YELL4 = 12342, // Why you run away? We make your corpse into Scourge.
|
||||
BCT_PALLID_HORROR_YELL5 = 12343, // No worry, we find you.
|
||||
BCT_PALLID_HORROR_YELL6 = 12330, // You spare parts! We make more Scourge in necropolis.
|
||||
BCT_PALLID_HORROR_YELL7 = 12328, // Hahaha, your guards no match for Scourge!
|
||||
BCT_PALLID_HORROR_YELL8 = 12325, // We come destroy puny ones!
|
||||
|
||||
// Undercity Guardian
|
||||
BCT_UNDERCITY_GUARDIAN_ROGUES_QUARTER = 12336, // Rogues' Quarter attacked by Scourge! Help!
|
||||
BCT_UNDERCITY_GUARDIAN_MAGIC_QUARTER = 12335, // Scourge attack Magic Quarter!
|
||||
BCT_UNDERCITY_GUARDIAN_TRADE_QUARTER = 12353, // There Scourge outside Trade Quarter!
|
||||
BCT_UNDERCITY_GUARDIAN_SEWERS = 12334, // Scourge in sewers! We need help!
|
||||
|
||||
// Undercity Elite Guardian
|
||||
BCT_UNDERCITY_ELITE_GUARDIAN_1 = 12354, // Scourge inside Trade Quarter! Destroy!
|
||||
|
||||
// Royal Dreadguard
|
||||
BCT_UNDERCITY_ROYAL_DREADGUARD_1 = 12337, // The Scourge are at the entrance to the Royal Quarter! Kill them!!
|
||||
|
||||
// Varimathras
|
||||
BCT_UNDERCITY_VARIMATHRAS_1 = 12333, // Dreadguard, hold your line. Halt the advance of those Scourge!
|
||||
|
||||
// Lady Sylvanas Windrunner
|
||||
BCT_UNDERCITY_SYLVANAS_1 = 12331, // The Scourge attack against my court has been eliminated. You may go about your business.
|
||||
BCT_UNDERCITY_SYLVANAS_2 = 12332, // My Royal Dreadguard, you will deal with this matter as befits your station. That, or you will wish that you had.
|
||||
|
||||
// Citizens
|
||||
BCT_UNDERCITY_RANDOM_1 = 12355, // Scourge spotted nearby!
|
||||
BCT_STORMWIND_RANDOM_1 = 12366, // Scourge spotted nearby! Renato Gallina
|
||||
BCT_UNDERCITY_RANDOM_2 = 12356, // I just saw a Scourge! Kill it!
|
||||
BCT_STORMWIND_RANDOM_2 = 12367, // I just saw a Scourge! Kill it! Thomas Miller
|
||||
BCT_UNDERCITY_RANDOM_3 = 12357, // Did you see that? There's a Scourge over there! Michael Garrett, Hannah Akeley
|
||||
BCT_STORMWIND_RANDOM_3 = 12368, // Did you see that? There's a Scourge over there! Thomas Miller
|
||||
BCT_UNDERCITY_RANDOM_4 = 12359, // There's one of the Scourge, right over there! Innkeeper Norman, Michael Garrett
|
||||
BCT_STORMWIND_RANDOM_4 = 12370, // There's one of the Scourge, right over there!
|
||||
BCT_UNDERCITY_RANDOM_5 = 12357, // Did you see that? There's a Scourge over there! Michael Garrett, Hannah Akeley
|
||||
BCT_STORMWIND_RANDOM_5 = 12368, // Did you see that? There's a Scourge over there! Thomas Miller
|
||||
BCT_UNDERCITY_RANDOM_6 = 12361, // Will these unrelenting Scourge attacks never end? Innkeeper Norman, William Montague
|
||||
BCT_STORMWIND_RANDOM_6 = 12372, // Will these unrelenting Scourge attacks never end?
|
||||
BCT_UNDERCITY_RANDOM_7 = 12360, // This has gone too far. How dare the Scourge attack Undercity! Destroy it before more come! Innkeeper Norman
|
||||
BCT_STORMWIND_RANDOM_7 = 12371, // This has gone too far. How dare the Scourge attack Stormwind! Destroy it before more come! Stephanie Turner
|
||||
BCT_UNDERCITY_RANDOM_8 = 12362, // Destroy the Scourge invader now, before it's too late! Michael Garrett
|
||||
BCT_STORMWIND_RANDOM_8 = 12373, // Destroy the Scourge invader now, before it's too late! Officer Maloof
|
||||
BCT_UNDERCITY_RANDOM_9 = 12358, // How can I get anything done with the Scourge running amok in here?! Innkeeper Norman
|
||||
BCT_STORMWIND_RANDOM_9 = 12369, // How can I get anything done with the Scourge running amok around here?! Stephanie Turner
|
||||
|
||||
// Stormwind City Guard
|
||||
BCT_STORMWIND_CITY_GUARD_1 = 12310, // To arms! Scourge spotted in the Cathedral of Light!
|
||||
BCT_STORMWIND_CITY_GUARD_2 = 12311, // Scourge in the Trade District! Have at them!
|
||||
BCT_STORMWIND_CITY_GUARD_3 = 12315, // Light help us... the Scourge are in the Park!
|
||||
|
||||
// Stormwind Royal Guard
|
||||
BCT_STORMWIND_CITY_GUARD_4 = 12316, // The Scourge are at the castle entrance! For Stormwind! For King Anduin!
|
||||
|
||||
// Highlord Bolvar Fordragon?
|
||||
BCT_STORMWIND_BOLVAR_1 = 12317, // Hold the line! Protect the King at all costs!
|
||||
BCT_STORMWIND_BOLVAR_2 = 12318, // Good work, one and all! The Scourge at the castle have been defeated.
|
||||
|
||||
// Misc
|
||||
BCT_CULTIST_ENGINEER_OPTION = 12112, // Use 8 necrotic runes and disrupt his ritual.
|
||||
BCT_GIVE_MAGIC_ITEM_OPTION = 12302, // Give me one of your magic items.
|
||||
BCT_SHADOW_OF_DOOM_TEXT_0 = 12420, // Our dark master has noticed your trifling, and sends me to bring a message... of doom!
|
||||
BCT_SHADOW_OF_DOOM_TEXT_1 = 12421, // These heroics mean nothing, $c. Your future is sealed and your soul is doomed to servitude!
|
||||
BCT_SHADOW_OF_DOOM_TEXT_2 = 12422, // Your battle here is but the smallest mote of a world wide invasion, whelp! It is time you learned of the powers you face!
|
||||
BCT_SHADOW_OF_DOOM_TEXT_3 = 12243, // You will not stop our deepening shadow, $c. Now... join us! Join the ranks of the Chosen!
|
||||
BCT_HERALD_OF_THE_LICH_KING_ZONE_ATTACK_START_1 = 13121, // Spawn.
|
||||
BCT_HERALD_OF_THE_LICH_KING_ZONE_ATTACK_START_2 = 13125, // Spawn. 53 min between 2-3 in sniffs.
|
||||
BCT_HERALD_OF_THE_LICH_KING_ZONE_ATTACK_ENDS_1 = 13165, // Despawn.
|
||||
BCT_HERALD_OF_THE_LICH_KING_ZONE_ATTACK_ENDS_2 = 13164, // Despawn.
|
||||
BCT_HERALD_OF_THE_LICH_KING_ZONE_ATTACK_ENDS_3 = 13163, // Despawn.
|
||||
BCT_HERALD_OF_THE_LICH_KING_RANDOM_1 = 13126, // Random.
|
||||
BCT_HERALD_OF_THE_LICH_KING_RANDOM_2 = 13124, // Random.
|
||||
BCT_HERALD_OF_THE_LICH_KING_RANDOM_3 = 13122, // 180 seconds between 5-6 in sniffs.
|
||||
BCT_HERALD_OF_THE_LICH_KING_RANDOM_4 = 13123, // Random. 30 min between 8-2 in sniffs.
|
||||
|
||||
BCT_CULTIST_ENGINEER_GOSSIP = 8436, // 12111 - This cultist is in a deep trance...
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -34,6 +34,7 @@ void AddSC_npc_stave_of_ancients();
|
||||
void AddSC_server_mail();
|
||||
void AddSC_transport_zeppelins();
|
||||
void AddSC_suns_reach_reclamation();
|
||||
void AddSC_scourge_invasion();
|
||||
|
||||
// The name of this function should match:
|
||||
// void Add${NameOfDirectory}Scripts()
|
||||
@@ -57,4 +58,5 @@ void AddWorldScripts()
|
||||
AddSC_server_mail();
|
||||
AddSC_transport_zeppelins();
|
||||
AddSC_suns_reach_reclamation();
|
||||
AddSC_scourge_invasion();
|
||||
}
|
||||
|
||||
@@ -2726,6 +2726,245 @@ enum CreatureTypeFlags
|
||||
CREATURE_TYPE_FLAG_QUEST_BOSS = 0x80000000 // Not verified
|
||||
};
|
||||
|
||||
enum class CreatureStaticFlags : uint32
|
||||
{
|
||||
MOUNTABLE = 0x00000001,
|
||||
NO_XP = 0x00000002,
|
||||
NO_LOOT = 0x00000004,
|
||||
UNKILLABLE = 0x00000008,
|
||||
TAMEABLE = 0x00000010, // CREATURE_TYPE_FLAG_TAMEABLE
|
||||
IMMUNE_TO_PC = 0x00000020, // UNIT_FLAG_IMMUNE_TO_PC
|
||||
IMMUNE_TO_NPC = 0x00000040, // UNIT_FLAG_IMMUNE_TO_NPC
|
||||
CAN_WIELD_LOOT = 0x00000080,
|
||||
SESSILE = 0x00000100, // creature_template_movement.Rooted = 1
|
||||
UNINTERACTIBLE = 0x00000200, // UNIT_FLAG_UNINTERACTIBLE
|
||||
NO_AUTOMATIC_REGEN = 0x00000400, // Creatures with that flag uses no UNIT_FLAG2_REGENERATE_POWER
|
||||
DESPAWN_INSTANTLY = 0x00000800, // Creature instantly disappear when killed
|
||||
CORPSE_RAID = 0x00001000,
|
||||
CREATOR_LOOT = 0x00002000, // Lootable only by creator(engineering dummies)
|
||||
NO_DEFENSE = 0x00004000,
|
||||
NO_SPELL_DEFENSE = 0x00008000,
|
||||
BOSS_MOB = 0x00010000, // CREATURE_TYPE_FLAG_BOSS_MOB, original description: Raid Boss Mob
|
||||
COMBAT_PING = 0x00020000,
|
||||
AQUATIC = 0x00040000, // aka Water Only, creature_template_movement.Ground = 0
|
||||
AMPHIBIOUS = 0x00080000, // creature_template_movement.Swim = 1
|
||||
NO_MELEE_FLEE = 0x00100000, // Prevents melee and makes npc flee if it enters combat - also misused to just prevent melee on npcs that cant move
|
||||
VISIBLE_TO_GHOSTS = 0x00200000, // CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS
|
||||
PVP_ENABLING = 0x00400000, // Old UNIT_FLAG_PVP_ENABLING, now UNIT_BYTES_2_OFFSET_PVP_FLAG from UNIT_FIELD_BYTES_2
|
||||
DO_NOT_PLAY_WOUND_ANIM = 0x00800000, // CREATURE_TYPE_FLAG_DO_NOT_PLAY_WOUND_ANIM
|
||||
NO_FACTION_TOOLTIP = 0x01000000, // CREATURE_TYPE_FLAG_NO_FACTION_TOOLTIP
|
||||
IGNORE_COMBAT = 0x02000000, // Actually only changes react state to passive
|
||||
ONLY_ATTACK_PVP_ENABLING = 0x04000000, // "Only attack targets that are PvP enabling"
|
||||
CALLS_GUARDS = 0x08000000, // Creature will summon a guard if player is within its aggro range (even if creature doesn't attack per se)
|
||||
CAN_SWIM = 0x10000000, // UnitFlags 0x8000 UNIT_FLAG_CAN_SWIM
|
||||
FLOATING = 0x20000000, // creature_template_movement.Flight = 1
|
||||
MORE_AUDIBLE = 0x40000000, // CREATURE_TYPE_FLAG_MORE_AUDIBLE
|
||||
LARGE_AOI = 0x80000000 // UnitFlags2 0x200000
|
||||
};
|
||||
|
||||
enum class CreatureStaticFlags2 : uint32
|
||||
{
|
||||
NO_PET_SCALING = 0x00000001,
|
||||
FORCE_PARTY_MEMBERS_INTO_COMBAT = 0x00000002, // Original description: Force Raid Combat
|
||||
LOCK_TAPPERS_TO_RAID_ON_DEATH = 0x00000004, // "Lock Tappers To Raid On Death", toggleable by 'Set "RAID_LOCK_ON_DEATH" flag for unit(s)' action, CREATURE_FLAG_EXTRA_INSTANCE_BIND
|
||||
SPELL_ATTACKABLE = 0x00000008, // CREATURE_TYPE_FLAG_SPELL_ATTACKABLE, original description(not valid anymore?): No Harmful Vertex Coloring
|
||||
NO_CRUSHING_BLOWS = 0x00000010,
|
||||
NO_OWNER_THREAT = 0x00000020,
|
||||
NO_WOUNDED_SLOWDOWN = 0x00000040,
|
||||
USE_CREATOR_BONUSES = 0x00000080,
|
||||
IGNORE_FEIGN_DEATH = 0x00000100,
|
||||
IGNORE_SANCTUARY = 0x00000200,
|
||||
ACTION_TRIGGERS_WHILE_CHARMED = 0x00000400,
|
||||
INTERACT_WHILE_DEAD = 0x00000800, // CREATURE_TYPE_FLAG_INTERACT_WHILE_DEAD
|
||||
NO_INTERRUPT_SCHOOL_COOLDOWN = 0x00001000,
|
||||
RETURN_SOUL_SHARD_TO_MASTER_OF_PET = 0x00002000,
|
||||
SKIN_WITH_HERBALISM = 0x00004000, // CREATURE_TYPE_FLAG_SKIN_WITH_HERBALISM
|
||||
SKIN_WITH_MINING = 0x00008000, // CREATURE_TYPE_FLAG_SKIN_WITH_MINING
|
||||
ALERT_CONTENT_TEAM_ON_DEATH = 0x00010000,
|
||||
ALERT_CONTENT_TEAM_AT_90_PCT_HP = 0x00020000,
|
||||
ALLOW_MOUNTED_COMBAT = 0x00040000, // CREATURE_TYPE_FLAG_ALLOW_MOUNTED_COMBAT
|
||||
PVP_ENABLING_OOC = 0x00080000,
|
||||
NO_DEATH_MESSAGE = 0x00100000, // CREATURE_TYPE_FLAG_NO_DEATH_MESSAGE
|
||||
IGNORE_PATHING_FAILURE = 0x00200000,
|
||||
FULL_SPELL_LIST = 0x00400000,
|
||||
DOES_NOT_REDUCE_REPUTATION_FOR_RAIDS = 0x00800000,
|
||||
IGNORE_MISDIRECTION = 0x01000000,
|
||||
HIDE_BODY = 0x02000000, // UNIT_FLAG2_HIDE_BODY
|
||||
SPAWN_DEFENSIVE = 0x04000000,
|
||||
SERVER_ONLY = 0x08000000,
|
||||
CAN_SAFE_FALL = 0x10000000, // Original description: No Collision
|
||||
CAN_ASSIST = 0x20000000, // CREATURE_TYPE_FLAG_CAN_ASSIST, original description: Player Can Heal/Buff
|
||||
NO_SKILL_GAINS = 0x40000000,
|
||||
NO_PET_BAR = 0x80000000 // CREATURE_TYPE_FLAG_NO_PET_BAR
|
||||
};
|
||||
|
||||
enum class CreatureStaticFlags3 : uint32
|
||||
{
|
||||
NO_DAMAGE_HISTORY = 0x00000001,
|
||||
DONT_PVP_ENABLE_OWNER = 0x00000002,
|
||||
DO_NOT_FADE_IN = 0x00000004, // UNIT_FLAG2_DO_NOT_FADE_IN
|
||||
MASK_UID = 0x00000008, // CREATURE_TYPE_FLAG_MASK_UID, original description: Non-Unique In Combat Log
|
||||
SKIN_WITH_ENGINEERING = 0x00000010, // CREATURE_TYPE_FLAG_SKIN_WITH_ENGINEERING
|
||||
NO_AGGRO_ON_LEASH = 0x00000020,
|
||||
NO_FRIENDLY_AREA_AURAS = 0x00000040,
|
||||
EXTENDED_CORPSE_DURATION = 0x00000080,
|
||||
CANNOT_SWIM = 0x00000100, // UNIT_FLAG_CANNOT_SWIM
|
||||
TAMEABLE_EXOTIC = 0x00000200, // CREATURE_TYPE_FLAG_TAMEABLE_EXOTIC
|
||||
GIGANTIC_AOI = 0x00000400, // Since MoP, creatures with that flag have UnitFlags2 0x400000
|
||||
INFINITE_AOI = 0x00000800, // Since MoP, creatures with that flag have UnitFlags2 0x40000000
|
||||
CANNOT_PENETRATE_WATER = 0x00001000, // Waterwalking
|
||||
NO_NAME_PLATE = 0x00002000, // CREATURE_TYPE_FLAG_NO_NAME_PLATE
|
||||
CHECKS_LIQUIDS = 0x00004000,
|
||||
NO_THREAT_FEEDBACK = 0x00008000,
|
||||
USE_MODEL_COLLISION_SIZE = 0x00010000, // CREATURE_TYPE_FLAG_USE_MODEL_COLLISION_SIZE
|
||||
ATTACKER_IGNORES_FACING = 0x00020000, // In 3.3.5 used only by Rocket Propelled Warhead
|
||||
ALLOW_INTERACTION_WHILE_IN_COMBAT = 0x00040000, // CREATURE_TYPE_FLAG_ALLOW_INTERACTION_WHILE_IN_COMBAT
|
||||
SPELL_CLICK_FOR_PARTY_ONLY = 0x00080000,
|
||||
FACTION_LEADER = 0x00100000,
|
||||
IMMUNE_TO_PLAYER_BUFFS = 0x00200000,
|
||||
COLLIDE_WITH_MISSILES = 0x00400000, // CREATURE_TYPE_FLAG_COLLIDE_WITH_MISSILES
|
||||
CAN_BE_MULTITAPPED = 0x00800000, // Original description: Do Not Tap (Credit to threat list)
|
||||
DO_NOT_PLAY_MOUNTED_ANIMATIONS = 0x01000000, // CREATURE_TYPE_FLAG_DO_NOT_PLAY_MOUNTED_ANIMATIONS, original description: Disable Dodge, Parry and Block Animations
|
||||
CANNOT_TURN = 0x02000000, // UNIT_FLAG2_CANNOT_TURN
|
||||
ENEMY_CHECK_IGNORES_LOS = 0x04000000,
|
||||
FOREVER_CORPSE_DURATION = 0x08000000, // 7 days
|
||||
PETS_ATTACK_WITH_3D_PATHING = 0x10000000, // "Pets attack with 3d pathing (Kologarn)"
|
||||
LINK_ALL = 0x20000000, // CREATURE_TYPE_FLAG_LINK_ALL
|
||||
AI_CAN_AUTO_TAKEOFF_IN_COMBAT = 0x40000000,
|
||||
AI_CAN_AUTO_LAND_IN_COMBAT = 0x80000000
|
||||
};
|
||||
|
||||
enum class CreatureStaticFlags4 : uint32
|
||||
{
|
||||
NO_BIRTH_ANIM = 0x00000001, // SMSG_UPDATE_OBJECT's "NoBirthAnim"
|
||||
TREAT_AS_PLAYER_FOR_DIMINISHING_RETURNS = 0x00000002, // Primarily used by ToC champions
|
||||
TREAT_AS_PLAYER_FOR_PVP_DEBUFF_DURATION = 0x00000004, // Primarily used by ToC champions
|
||||
INTERACT_ONLY_WITH_CREATOR = 0x00000008, // CREATURE_TYPE_FLAG_INTERACT_ONLY_WITH_CREATOR, original description: Only Display Gossip for Summoner
|
||||
DO_NOT_PLAY_UNIT_EVENT_SOUNDS = 0x00000010, // CREATURE_TYPE_FLAG_DO_NOT_PLAY_UNIT_EVENT_SOUNDS, original description: No Death Scream
|
||||
HAS_NO_SHADOW_BLOB = 0x00000020, // CREATURE_TYPE_FLAG_HAS_NO_SHADOW_BLOB, original description(wrongly linked type flag or behavior was changed?): Can be Healed by Enemies
|
||||
DEALS_TRIPLE_DAMAGE_TO_PC_CONTROLLED_PETS = 0x00000040,
|
||||
NO_NPC_DAMAGE_BELOW_85PTC = 0x00000080,
|
||||
OBEYS_TAUNT_DIMINISHING_RETURNS = 0x00000100, // CREATURE_FLAG_EXTRA_OBEYS_TAUNT_DIMINISHING_RETURNS
|
||||
NO_MELEE_APPROACH = 0x00000200,
|
||||
UPDATE_CREATURE_RECORD_WHEN_INSTANCE_CHANGES_DIFFICULTY = 0x00000400, // Used only by Snobold Vassal
|
||||
CANNOT_DAZE = 0x00000800, // "Cannot Daze (Combat Stun)"
|
||||
FLAT_HONOR_AWARD = 0x00001000,
|
||||
IGNORE_LOS_WHEN_CASTING_ON_ME = 0x00002000, // "Other objects can ignore line of sight requirements when casting spells on me", used only by Ice Tomb in 3.3.5
|
||||
GIVE_QUEST_KILL_CREDIT_WHILE_OFFLINE = 0x00004000,
|
||||
TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS = 0x00008000, // CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT, "Treat as Raid Unit For Helpful Spells (Instances ONLY)", used by Valithria Dreamwalker
|
||||
DONT_REPOSITION_IF_MELEE_TARGET_IS_TOO_CLOSE = 0x00010000, // "Don't reposition because melee target is too close"
|
||||
PET_OR_GUARDIAN_AI_DONT_GO_BEHIND_TARGET = 0x00020000,
|
||||
MINUTE_5_LOOT_ROLL_TIMER = 0x00040000, // Used by Lich King
|
||||
FORCE_GOSSIP = 0x00080000, // CREATURE_TYPE_FLAG_FORCE_GOSSIP
|
||||
DONT_REPOSITION_WITH_FRIENDS_IN_COMBAT = 0x00100000,
|
||||
DO_NOT_SHEATHE = 0x00200000, // CREATURE_TYPE_FLAG_DO_NOT_SHEATHE, original description: Manual Sheathing control
|
||||
IGNORE_SPELL_MIN_RANGE_RESTRICTIONS = 0x00400000, // UnitFlags2 0x8000000, original description: Attacker Ignores Minimum Ranges
|
||||
SUPPRESS_INSTANCE_WIDE_RELEASE_IN_COMBAT = 0x00800000,
|
||||
PREVENT_SWIM = 0x01000000, // UnitFlags2 0x1000000, original description: AI will only swim if target swims
|
||||
HIDE_IN_COMBAT_LOG = 0x02000000, // UnitFlags2 0x2000000, original description: Don't generate combat log when engaged with NPC's
|
||||
ALLOW_NPC_COMBAT_WHILE_UNINTERACTIBLE = 0x04000000,
|
||||
PREFER_NPCS_WHEN_SEARCHING_FOR_ENEMIES = 0x08000000,
|
||||
ONLY_GENERATE_INITIAL_THREAT = 0x10000000,
|
||||
DO_NOT_TARGET_ON_INTERACTION = 0x20000000, // CREATURE_TYPE_FLAG_DO_NOT_TARGET_ON_INTERACTION, original description: Doesn't change target on right click
|
||||
DO_NOT_RENDER_OBJECT_NAME = 0x40000000, // CREATURE_TYPE_FLAG_DO_NOT_RENDER_OBJECT_NAME, original description: Hide name in world frame
|
||||
QUEST_BOSS = 0x80000000 // CREATURE_TYPE_FLAG_QUEST_BOSS
|
||||
};
|
||||
|
||||
enum class CreatureStaticFlags5 : uint32
|
||||
{
|
||||
UNTARGETABLE_BY_CLIENT = 0x00000001, // UnitFlags2 0x4000000 UNIT_FLAG2_UNTARGETABLE_BY_CLIENT
|
||||
FORCE_SELF_MOUNTING = 0x00000002,
|
||||
UNINTERACTIBLE_IF_HOSTILE = 0x00000004, // UnitFlags2 0x10000000
|
||||
DISABLES_XP_AWARD = 0x00000008,
|
||||
DISABLE_AI_PREDICTION = 0x00000010,
|
||||
NO_LEAVECOMBAT_STATE_RESTORE = 0x00000020,
|
||||
BYPASS_INTERACT_INTERRUPTS = 0x00000040,
|
||||
DEGREE_BACK_ARC_240 = 0x00000080,
|
||||
INTERACT_WHILE_HOSTILE = 0x00000100, // UnitFlags2 0x4000 UNIT_FLAG2_INTERACT_WHILE_HOSTILE
|
||||
DONT_DISMISS_ON_FLYING_MOUNT = 0x00000200,
|
||||
PREDICTIVE_POWER_REGEN = 0x00000400, // CREATURE_TYPEFLAGS_2_UNK1
|
||||
HIDE_LEVEL_INFO_IN_TOOLTIP = 0x00000800, // CREATURE_TYPEFLAGS_2_UNK2
|
||||
HIDE_HEALTH_BAR_UNDER_TOOLTIP = 0x00001000, // CREATURE_TYPEFLAGS_2_UNK3
|
||||
SUPPRESS_HIGHLIGHT_WHEN_TARGETED_OR_MOUSED_OVER = 0x00002000, // UnitFlags2 0x80000
|
||||
AI_PREFER_PATHABLE_TARGETS = 0x00004000,
|
||||
FREQUENT_AREA_TRIGGER_CHECKS = 0x00008000,
|
||||
ASSIGN_KILL_CREDIT_TO_ENCOUNTER_LIST = 0x00010000,
|
||||
NEVER_EVADE = 0x00020000,
|
||||
AI_CANT_PATH_ON_STEEP_SLOPES = 0x00040000,
|
||||
AI_IGNORE_LOS_TO_MELEE_TARGET = 0x00080000,
|
||||
NO_TEXT_IN_CHAT_BUBBLE = 0x00100000, // "Never display emote or chat text in a chat bubble", CREATURE_TYPEFLAGS_2_UNK4
|
||||
CLOSE_IN_ON_UNPATHABLE_TARGET = 0x00200000, // "AI Pets close in on unpathable target"
|
||||
DONT_GO_BEHIND_ME = 0x00400000, // "Pet/Guardian AI Don't Go Behind Me (use on target)"
|
||||
NO_DEATH_THUD = 0x00800000, // CREATURE_TYPEFLAGS_2_UNK5
|
||||
CLIENT_LOCAL_CREATURE = 0x01000000,
|
||||
CAN_DROP_LOOT_WHILE_IN_A_CHALLENGE_MODE_INSTANCE = 0x02000000,
|
||||
HAS_SAFE_LOCATION = 0x04000000,
|
||||
NO_HEALTH_REGEN = 0x08000000,
|
||||
NO_POWER_REGEN = 0x10000000,
|
||||
NO_PET_UNIT_FRAME = 0x20000000,
|
||||
NO_INTERACT_ON_LEFT_CLICK = 0x40000000, // CREATURE_TYPEFLAGS_2_UNK6
|
||||
GIVE_CRITERIA_KILL_CREDIT_WHEN_CHARMED = 0x80000000
|
||||
};
|
||||
|
||||
enum class CreatureStaticFlags6 : uint32
|
||||
{
|
||||
DO_NOT_AUTO_RESUMMON = 0x00000001, // "Do not auto-resummon this companion creature"
|
||||
REPLACE_VISIBLE_UNIT_IF_AVAILABLE = 0x00000002, // "Smooth Phasing: Replace visible unit if available"
|
||||
IGNORE_REALM_COALESCING_HIDING_CODE = 0x00000004, // "Ignore the realm coalescing hiding code (always show)"
|
||||
TAPS_TO_FACTION = 0x00000008,
|
||||
ONLY_QUESTGIVER_FOR_SUMMONER = 0x00000010,
|
||||
AI_COMBAT_RETURN_PRECISE = 0x00000020,
|
||||
HOME_REALM_ONLY_LOOT = 0x00000040,
|
||||
NO_INTERACT_RESPONSE = 0x00000080, // TFLAG2_UNK7
|
||||
NO_INITIAL_POWER = 0x00000100,
|
||||
DONT_CANCEL_CHANNEL_ON_MASTER_MOUNTING = 0x00000200,
|
||||
CAN_TOGGLE_BETWEEN_DEATH_AND_PERSONAL_LOOT = 0x00000400,
|
||||
ALWAYS_STAND_ON_TOP_OF_TARGET = 0x00000800, // "Always, ALWAYS tries to stand right on top of his move to target. ALWAYS!!", toggleable by 'Set "Always Stand on Target" flag for unit(s)' or not same?
|
||||
UNCONSCIOUS_ON_DEATH = 0x00001000,
|
||||
DONT_REPORT_TO_LOCAL_DEFENSE_CHANNEL_ON_DEATH = 0x00002000,
|
||||
PREFER_UNENGAGED_MONSTERS = 0x00004000, // "Prefer unengaged monsters when picking a target"
|
||||
USE_PVP_POWER_AND_RESILIENCE = 0x00008000, // "Use PVP power and resilience when players attack this creature"
|
||||
DONT_CLEAR_DEBUFFS_ON_LEAVE_COMBAT = 0x00010000,
|
||||
PERSONAL_LOOT_HAS_FULL_SECURITY = 0x00020000, // "Personal loot has full security (guaranteed push/mail delivery)"
|
||||
TRIPLE_SPELL_VISUALS = 0x00040000,
|
||||
USE_GARRISON_OWNER_LEVEL = 0x00080000,
|
||||
IMMEDIATE_AOI_UPDATE_ON_SPAWN = 0x00100000,
|
||||
UI_CAN_GET_POSITION = 0x00200000,
|
||||
SEAMLESS_TRANSFER_PROHIBITED = 0x00400000,
|
||||
ALWAYS_USE_GROUP_LOOT_METHOD = 0x00800000,
|
||||
NO_BOSS_KILL_BANNER = 0x01000000,
|
||||
FORCE_TRIGGERING_PLAYER_LOOT_ONLY = 0x02000000,
|
||||
SHOW_BOSS_FRAME_WHILE_UNINTERACTABLE = 0x04000000,
|
||||
SCALES_TO_PLAYER_LEVEL = 0x08000000,
|
||||
AI_DONT_LEAVE_MELEE_FOR_RANGED_WHEN_TARGET_GETS_ROOTED = 0x10000000,
|
||||
DONT_USE_COMBAT_REACH_FOR_CHAINING = 0x20000000,
|
||||
DO_NOT_PLAY_PROCEDURAL_WOUND_ANIM = 0x40000000,
|
||||
APPLY_PROCEDURAL_WOUND_ANIM_TO_BASE = 0x80000000 // TFLAG2_UNK14
|
||||
};
|
||||
|
||||
enum class CreatureStaticFlags7 : uint32
|
||||
{
|
||||
IMPORTANT_NPC = 0x00000001,
|
||||
IMPORTANT_QUEST_NPC = 0x00000002,
|
||||
LARGE_NAMEPLATE = 0x00000004,
|
||||
TRIVIAL_PET = 0x00000008,
|
||||
AI_ENEMIES_DONT_BACKUP_WHEN_I_GET_ROOTED = 0x00000010,
|
||||
NO_AUTOMATIC_COMBAT_ANCHOR = 0x00000020,
|
||||
ONLY_TARGETABLE_BY_CREATOR = 0x00000040,
|
||||
TREAT_AS_PLAYER_FOR_ISPLAYERCONTROLLED = 0x00000080,
|
||||
GENERATE_NO_THREAT_OR_DAMAGE = 0x00000100,
|
||||
INTERACT_ONLY_ON_QUEST = 0x00000200,
|
||||
DISABLE_KILL_CREDIT_FOR_OFFLINE_PLAYERS = 0x00000400,
|
||||
AI_ADDITIONAL_PATHING = 0x00080000,
|
||||
};
|
||||
|
||||
enum class CreatureStaticFlags8 : uint32
|
||||
{
|
||||
FORCE_CLOSE_IN_ON_PATH_FAIL_BEHAVIOR = 0x00000002,
|
||||
USE_2D_CHASING_CALCULATION = 0x00000020,
|
||||
USE_FAST_CLASSIC_HEARTBEAT = 0x00000040,
|
||||
};
|
||||
|
||||
enum CreatureEliteType
|
||||
{
|
||||
CREATURE_ELITE_NORMAL = 0,
|
||||
|
||||
Reference in New Issue
Block a user