Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2025-02-04 23:40:06 +08:00
81 changed files with 3144 additions and 611 deletions
+39 -5
View File
@@ -15,8 +15,9 @@ results = {
"Multiple blank lines check": "Passed",
"Trailing whitespace check": "Passed",
"SQL codestyle check": "Passed",
"INSERT safety usage check": "Passed",
"Missing semicolon check": "Passed"
"INSERT & DELETE safety usage check": "Passed",
"Missing semicolon check": "Passed",
"Backtick check": "Passed"
}
# Collect all files in all directories
@@ -44,8 +45,9 @@ def parsing_file(files: list) -> None:
multiple_blank_lines_check(file, file_path)
trailing_whitespace_check(file, file_path)
sql_check(file, file_path)
insert_safety_check(file, file_path)
insert_delete_safety_check(file, file_path)
semicolon_check(file, file_path)
backtick_check(file, file_path)
except UnicodeDecodeError:
print(f"\nCould not decode file {file_path}")
sys.exit(1)
@@ -134,9 +136,10 @@ def sql_check(file: io, file_path: str) -> None:
error_handler = True
results["SQL codestyle check"] = "Failed"
def insert_safety_check(file: io, file_path: str) -> None:
def insert_delete_safety_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0) # Reset file pointer to the beginning
not_delete = ["creature_template", "gameobject_template", "item_template", "quest_template"]
check_failed = False
previous_line = ""
@@ -148,11 +151,18 @@ def insert_safety_check(file: io, file_path: str) -> None:
print(f"No DELETE keyword found after the INSERT in {file_path} at line {line_number}\nIf this error is intended, please advert a maintainer")
check_failed = True
previous_line = line
match = re.match(r"DELETE FROM\s+`([^`]+)`", line, re.IGNORECASE)
if match:
table_name = match.group(1)
if table_name in not_delete:
print(
f"Entries from {table} should not be deleted! {file_path} at line {line_number}")
check_failed = True
# Handle the script error and update the result output
if check_failed:
error_handler = True
results["INSERT safety usage check"] = "Failed"
results["INSERT & DELETE safety usage check"] = "Failed"
def semicolon_check(file: io, file_path: str) -> None:
global error_handler, results
@@ -192,6 +202,30 @@ def semicolon_check(file: io, file_path: str) -> None:
error_handler = True
results["Missing semicolon check"] = "Failed"
def backtick_check(file: io, file_path: str) -> None:
global error_handler, results
file.seek(0)
check_failed = False
pattern = re.compile(
r'\b(SELECT|FROM|JOIN|WHERE|GROUP BY|ORDER BY|DELETE FROM|UPDATE|INSERT INTO|SET)\s+([^;]+)',
re.IGNORECASE)
for line_number, line in enumerate(file, start=1):
matches = pattern.findall(line)
for clause, content in matches:
words = re.findall(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', content)
for word in words:
if word.upper() in {"SELECT", "FROM", "JOIN", "WHERE", "GROUP", "BY", "ORDER",
"DELETE", "UPDATE", "INSERT", "INTO", "SET", "VALUES"}:
continue
if not re.search(rf'`{re.escape(word)}`', content):
print(f"Missing backticks around ({word}). {file_path} at line {line_number}")
check_failed = True
if check_failed:
error_handler = True
results["Backtick check"] = "Failed"
# Collect all files from matching directories
all_files = collect_files_from_directories(src_directory)
@@ -1,6 +1,6 @@
# install chocolatey before
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
# powershell.exe -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
# install automatically following packages:
# cmake
@@ -24,7 +24,7 @@ fi
choco install -y --skip-checksums $INSTALL_ARGS cmake.install -y --installargs 'ADD_CMAKE_TO_PATH=System'
choco install -y --skip-checksums $INSTALL_ARGS visualstudio2022-workload-nativedesktop
choco install -y --skip-checksums $INSTALL_ARGS openssl --version=3.1.1
choco install -y --skip-checksums $INSTALL_ARGS boost-msvc-14.3 --version=1.82.0
choco install -y --skip-checksums $INSTALL_ARGS mysql --version=8.0.31
choco install -y --skip-checksums $INSTALL_ARGS openssl --force --version=3.3.2
choco install -y --skip-checksums $INSTALL_ARGS boost-msvc-14.3 --force --version=1.87.0
choco install -y --skip-checksums $INSTALL_ARGS mysql --force --version=8.4.4
@@ -0,0 +1,13 @@
-- DB update 2024_11_15_00 -> 2025_01_31_00
--
DROP TABLE IF EXISTS `world_state`;
CREATE TABLE IF NOT EXISTS `world_state` (
`Id` INT UNSIGNED NOT NULL COMMENT 'Internal save ID',
`Data` longtext,
PRIMARY KEY(`Id`)
) ENGINE=MYISAM DEFAULT CHARSET=utf8mb4 COMMENT='WorldState save system';
-- Isle of Quel'danas is in its final stage with all subphases completed
-- open all Sunwell Plateau gates
DELETE FROM `world_state` WHERE `Id` = 20;
INSERT INTO `world_state` (`Id`, `Data`) VALUES(20, '3 15 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 3 80 80 80');
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,11 @@
-- DB update 2025_01_31_00 -> 2025_01_31_01
-- Add pure energy immune script for Vex
UPDATE `creature_template` SET `ScriptName` = 'npc_pure_energy' WHERE (`entry` = 24745);
-- AOE_IMMUNE Flag for pure energy
UPDATE `creature_template` SET `flags_extra` = (`flags_extra` | 4194304) WHERE (`entry` = 24745);
-- Vexallus (Entry: 24744) Overload Emote with broadcasttextid 77866
DELETE FROM `creature_text` WHERE `CreatureID` = 24744 AND `GroupID` = 5 AND `ID` = 0;
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(24744, 5, 0, 'Vexallus overloads!', 41, 0, 100, 0, 0, 0, 23781, 0, 'Vexallus - EMOTE_OVERLOAD');
@@ -0,0 +1,8 @@
-- DB update 2025_01_31_01 -> 2025_02_01_00
--
DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24761);
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(24761, 0, 0, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 44406, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Brightscale Wyrm - On Just Died - Cast \'Energy Infusion\'');
DELETE FROM `spell_custom_attr` WHERE `spell_id`= 44406;
INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES (44406, 4194304);
@@ -0,0 +1,3 @@
-- DB update 2025_02_01_00 -> 2025_02_01_01
--
UPDATE `creature_template` SET `ScriptName` = '' WHERE (`entry` = 24745);
@@ -0,0 +1,3 @@
-- DB update 2025_02_01_01 -> 2025_02_01_02
--
DELETE FROM `acore_string` WHERE `entry` = 6617;
@@ -0,0 +1,16 @@
-- DB update 2025_02_01_02 -> 2025_02_03_00
-- Add Russian locale for QuestID: 4641
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 4641 AND `locale` = 'ruRU';
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES (4641,'ruRU','Очередной рекрут Калтунка?$B$BВидно, плохи наши дела, если у нас нет никого получше. Ну, ничего. К тому времени, как мы решим, что тебе пора покинуть долину, из тебя $c получится бравый воитель Орды!',0);
-- Add Russian locale for QuestID: 788
DELETE FROM `quest_offer_reward_locale` WHERE `ID` = 788 AND `locale` = 'ruRU';
INSERT INTO `quest_offer_reward_locale` (`ID`, `locale`, `RewardText`, `VerifiedBuild`) VALUES (788,'ruRU','Ну что ж, $N, неплохо. Но не задирай носа: грядут бои с куда более серьезными врагами.$b$bОднако ты показал себя достойным бойцом. В следующем испытании тебе придется встретиться с более опасным противником, так что дополнительная защита не помешает.',0);
-- Fix Russian locale for QuestID: 783
DELETE FROM `quest_template_locale` WHERE `ID` = 783 AND `locale` = 'ruRU';
INSERT INTO `quest_template_locale` (`ID`, `locale`, `Title`, `Details`, `Objectives`, `VerifiedBuild`) VALUES (783, 'ruRU', 'Внутренняя угроза', 'Надеюсь, ты $gготов:готова; к подвигам, |3-6($c), поскольку для тебя есть дело здесь, в Североземье.$B$BИ я имею в виду не мирные сельские заботы.$B$BСтража Штормграда с трудом поддерживает здесь мир: слишком многие из нас находятся в дальних краях, а тревожных вестей меньше не становится. И потому мы надеемся на помощь всех, кто готов защитить свой дом и своих близких.$B$BЕсли ты $gготов:готова; откликнуться на призыв, то поговори с моим командиром, маршалом Макбрайдом. Он здесь, в аббатстве у меня за спиной.', 'Поговорите с маршалом Макбрайдом.', 0);
-- Fix Russian locale for QuestID: 792
DELETE FROM `quest_template_locale` WHERE `ID` = 792 AND `locale` = 'ruRU';
INSERT INTO `quest_template_locale` (`ID`, `locale`, `Title`, `Details`, `Objectives`, `VerifiedBuild`) VALUES (792, 'ruRU', 'Злобные фамильяры', 'Что ж, |3-6($c), надеюсь, у нас ты многому научишься.$b$bМеня послали сюда, чтобы я стала твоей наставницей, но оказывается, здесь не все ладно!$b$bВ долине Испытаний обосновался клан Пылающего Клинка. Эти твари скрываются в пещере на северо-востоке, и их злобные фамильяры выбираются оттуда и сеют хаос.$b$bИтак, вот твое первое задание в борьбе с Пылающим Клинком: перебей их столько, сколько сумеешь, и, если выживешь, возвращайся ко мне.', 'Убейте 8 злобных фамильяров и вернитесь к Зурите Востроглазой, которая ждет вас у Логова.', 0);
@@ -0,0 +1,3 @@
-- DB update 2025_02_03_00 -> 2025_02_03_01
--
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 25485) AND (`source_type` = 0) AND (`id` IN (0));
@@ -0,0 +1,7 @@
-- DB update 2025_02_03_01 -> 2025_02_04_00
--
-- Set questId on WP start
UPDATE `smart_scripts` SET `action_param4` = 5203 WHERE (`entryorguid` = 1101600) AND (`source_type` = 9) AND (`id` = 9);
-- Delete area explored action
DELETE FROM `smart_scripts` WHERE (`entryorguid` = 11016) AND (`source_type` = 0) AND (`id` = 38);
UPDATE `smart_scripts` SET `link`=39 WHERE (`entryorguid` = 11016) AND (`source_type` = 0) AND (`id` = 37);
@@ -4304,6 +4304,19 @@ Event.Announce = 0
#
###################################################################################################
###################################################################################################
# WORLD STATE
#
# Sunsreach.CounterMax
# Description: Counter value to be reached to transition phases
# during the Sun's Reach Reclamation event.
# Default: 10000
Sunsreach.CounterMax = 10000
#
###################################################################################################
###################################################################################################
# AUCTION HOUSE
#
@@ -2696,7 +2696,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_PLAYER_TALK:
{
char const* text = sObjectMgr->GetAcoreString(e.action.playerTalk.textId, DEFAULT_LOCALE);
std::string text = sObjectMgr->GetAcoreString(e.action.playerTalk.textId, DEFAULT_LOCALE);
if (!targets.empty())
for (WorldObject* target : targets)
@@ -3282,6 +3282,16 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
sWorldState->HandleExternalEvent(static_cast<WorldStateEvent>(e.action.worldStateScript.eventId), e.action.worldStateScript.param);
break;
}
case SMART_ACTION_DISABLE_REWARD:
{
for (WorldObject* target : targets)
if (IsCreature(target))
{
target->ToCreature()->SetReputationRewardDisabled(static_cast<bool>(e.action.reward.reputation));
target->ToCreature()->SetLootRewardDisabled(static_cast<bool>(e.action.reward.loot));
}
break;
}
default:
LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Unhandled Action type {}", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
break;
@@ -860,6 +860,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_MOVEMENT_PAUSE: return sizeof(SmartAction::move);
case SMART_ACTION_MOVEMENT_RESUME: return sizeof(SmartAction::move);
case SMART_ACTION_WORLD_SCRIPT: return sizeof(SmartAction::worldStateScript);
case SMART_ACTION_DISABLE_REWARD: return sizeof(SmartAction::reward);
default:
LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an action with no unused params specified in SmartAIMgr::CheckUnusedActionParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
@@ -1894,6 +1895,9 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
return IsSAIBoolValid(e, e.action.setHealthRegen.regenHealth);
case SMART_ACTION_CALL_TIMED_ACTIONLIST:
return IsSAIBoolValid(e, e.action.timedActionList.allowOverride);
case SMART_ACTION_DISABLE_REWARD:
return IsSAIBoolValid(e, e.action.reward.reputation) &&
IsSAIBoolValid(e, e.action.reward.loot);
case SMART_ACTION_FLEE_FOR_ASSIST:
case SMART_ACTION_MOVE_TO_POS:
case SMART_ACTION_EVADE:
@@ -738,8 +738,9 @@ enum SMART_ACTION
SMART_ACTION_MOVEMENT_PAUSE = 235, // timer
SMART_ACTION_MOVEMENT_RESUME = 236, // timerOverride
SMART_ACTION_WORLD_SCRIPT = 237, // eventId, param
SMART_ACTION_DISABLE_REWARD = 238, // reputation 0/1, loot 0/1
SMART_ACTION_AC_END = 238, // placeholder
SMART_ACTION_AC_END = 239, // placeholder
};
enum class SmartActionSummonCreatureFlags
@@ -1501,6 +1502,12 @@ struct SmartAction
uint32 param5;
uint32 param6;
} raw;
struct
{
SAIBool reputation;
SAIBool loot;
} reward;
};
};
@@ -2438,7 +2438,7 @@ bool AchievementMgr::HasAchieved(uint32 achievementId) const
bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement)
{
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, criteria->ID, nullptr))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, criteria->ID, nullptr))
return false;
if (achievement->mapID != -1 && GetPlayer()->GetMapId() != uint32(achievement->mapID))
@@ -2917,7 +2917,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
continue;
}
if (!GetCriteriaDataSet(criteria) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, entryId, nullptr))
if (!GetCriteriaDataSet(criteria) && !sDisableMgr->IsDisabledFor(DISABLE_TYPE_ACHIEVEMENT_CRITERIA, entryId, nullptr))
LOG_ERROR("sql.sql", "Table `achievement_criteria_data` does not have expected data for criteria (Entry: {} Type: {}) for achievement {}.", criteria->ID, criteria->requiredType, criteria->referredAchievement);
}
+8 -11
View File
@@ -58,7 +58,8 @@ namespace Acore
void operator()(WorldPacket& data, LocaleConstant loc_idx)
{
char const* text = sObjectMgr->GetAcoreString(_textId, loc_idx);
std::string strtext = sObjectMgr->GetAcoreString(_textId, loc_idx);
char const* text = strtext.c_str();
if (_args)
{
// we need copy va_list before use or original va_list will corrupted
@@ -95,9 +96,12 @@ namespace Acore
void operator()(WorldPacket& data, LocaleConstant loc_idx)
{
char const* text = sObjectMgr->GetAcoreString(_textId, loc_idx);
char const* arg1str = _arg1 ? sObjectMgr->GetAcoreString(_arg1, loc_idx) : "";
char const* arg2str = _arg2 ? sObjectMgr->GetAcoreString(_arg2, loc_idx) : "";
std::string strtext = sObjectMgr->GetAcoreString(_textId, loc_idx);
char const* text = strtext.c_str();
std::string stragr1str = sObjectMgr->GetAcoreString(_arg1, loc_idx);
char const* arg1str = _arg1 ? stragr1str.c_str() : "";
std::string strarg2str = sObjectMgr->GetAcoreString(_arg2, loc_idx);
char const* arg2str = _arg2 ? strarg2str.c_str() : "";
char str[2048];
snprintf(str, 2048, text, arg1str, arg2str);
@@ -1659,13 +1663,6 @@ void Battleground::EndNow()
SetEndTime(0);
}
// To be removed
char const* Battleground::GetAcoreString(int32 entry)
{
// FIXME: now we have different DBC locales and need localized message for each target client
return sObjectMgr->GetAcoreStringForDBCLocale(entry);
}
void Battleground::HandleTriggerBuff(GameObject* gameObject)
{
// Xinef: crash fix?
@@ -559,8 +559,6 @@ public:
void DoorOpen(uint32 type);
void DoorClose(uint32 type);
//to be removed
const char* GetAcoreString(int32 entry);
virtual bool HandlePlayerUnderMap(Player* /*player*/) { return false; }
@@ -467,7 +467,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates()
BattlegroundTypeId bgTypeId = static_cast<BattlegroundTypeId>(fields[0].Get<uint32>());
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId, nullptr))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId, nullptr))
continue;
// can be overwrite by values from DB
+2 -2
View File
@@ -37,7 +37,7 @@ Player* ChatHandler::GetPlayer() const
return m_session ? m_session->GetPlayer() : nullptr;
}
char const* ChatHandler::GetAcoreString(uint32 entry) const
std::string ChatHandler::GetAcoreString(uint32 entry) const
{
return m_session->GetAcoreString(entry);
}
@@ -953,7 +953,7 @@ std::string ChatHandler::GetNameLink(Player* chr) const
return playerLink(chr->GetName());
}
char const* CliHandler::GetAcoreString(uint32 entry) const
std::string CliHandler::GetAcoreString(uint32 entry) const
{
return sObjectMgr->GetAcoreStringForDBCLocale(entry);
}
+2 -2
View File
@@ -148,7 +148,7 @@ public:
}
// function with different implementation for chat/console
virtual char const* GetAcoreString(uint32 entry) const;
virtual std::string GetAcoreString(uint32 entry) const;
virtual void SendSysMessage(std::string_view str, bool escapeCharacters = false);
void SendSysMessage(uint32 entry);
@@ -276,7 +276,7 @@ public:
explicit CliHandler(void* callbackArg, Print* zprint) : m_callbackArg(callbackArg), m_print(zprint) { }
// overwrite functions
char const* GetAcoreString(uint32 entry) const override;
std::string GetAcoreString(uint32 entry) const override;
void SendSysMessage(std::string_view, bool escapeCharacters) override;
bool ParseCommands(std::string_view str) override;
std::string GetNameLink() const override;
@@ -24,7 +24,7 @@ void Acore::Impl::ChatCommands::SendErrorMessageToHandler(ChatHandler* handler,
handler->SetSentErrorMessage(true);
}
char const* Acore::Impl::ChatCommands::GetAcoreString(ChatHandler const* handler, AcoreStrings which)
std::string Acore::Impl::ChatCommands::GetAcoreString(ChatHandler const* handler, AcoreStrings which)
{
return handler->GetAcoreString(which);
}
@@ -120,7 +120,7 @@ namespace Acore::Impl::ChatCommands
};
AC_GAME_API void SendErrorMessageToHandler(ChatHandler* handler, std::string_view str);
AC_GAME_API char const* GetAcoreString(ChatHandler const* handler, AcoreStrings which);
AC_GAME_API std::string GetAcoreString(ChatHandler const* handler, AcoreStrings which);
template <typename... Ts>
std::string FormatAcoreString(ChatHandler const* handler, AcoreStrings which, Ts&&... args)
{
+385 -360
View File
@@ -28,393 +28,418 @@
#include "VMapMgr2.h"
#include "World.h"
namespace DisableMgr
std::map<DisableType, std::map<uint32, DisableData>> DisableMgr::m_DisableMap;
DisableMgr::DisableMgr() {}
DisableMgr::~DisableMgr() {}
DisableMgr* DisableMgr::instance()
{
namespace
static DisableMgr instance;
return &instance;
}
void DisableMgr::LoadDisables()
{
uint32 oldMSTime = getMSTime();
// reload case
for (DisableMap::iterator itr = m_DisableMap.begin(); itr != m_DisableMap.end(); ++itr)
itr->second.clear();
m_DisableMap.clear();
QueryResult result = WorldDatabase.Query("SELECT sourceType, entry, flags, params_0, params_1 FROM disables");
uint32 total_count = 0;
if (!result)
{
struct DisableData
{
uint8 flags;
std::set<uint32> params[2]; // params0, params1
};
// single disables here with optional data
typedef std::map<uint32, DisableData> DisableTypeMap;
// global disable map by source
typedef std::map<DisableType, DisableTypeMap> DisableMap;
DisableMap m_DisableMap;
uint8 MAX_DISABLE_TYPES = 11;
}
void LoadDisables()
{
uint32 oldMSTime = getMSTime();
// reload case
for (DisableMap::iterator itr = m_DisableMap.begin(); itr != m_DisableMap.end(); ++itr)
itr->second.clear();
m_DisableMap.clear();
QueryResult result = WorldDatabase.Query("SELECT sourceType, entry, flags, params_0, params_1 FROM disables");
uint32 total_count = 0;
if (!result)
{
LOG_WARN("server.loading", ">> Loaded 0 disables. DB table `disables` is empty!");
LOG_INFO("server.loading", " ");
return;
}
Field* fields;
do
{
fields = result->Fetch();
DisableType type = DisableType(fields[0].Get<uint32>());
if (type >= MAX_DISABLE_TYPES)
{
LOG_ERROR("sql.sql", "Invalid type {} specified in `disables` table, skipped.", type);
continue;
}
uint32 entry = fields[1].Get<uint32>();
uint8 flags = fields[2].Get<uint8>();
std::string params_0 = fields[3].Get<std::string>();
std::string params_1 = fields[4].Get<std::string>();
DisableData data;
data.flags = flags;
switch (type)
{
case DISABLE_TYPE_GO_LOS:
if (!sObjectMgr->GetGameObjectTemplate(entry))
{
LOG_ERROR("sql.sql", "Gameobject entry {} from `disables` doesn't exist in dbc, skipped.", entry);
continue;
}
if (flags)
LOG_ERROR("sql.sql", "Disable flags specified for gameobject {}, useless data.", entry);
break;
case DISABLE_TYPE_SPELL:
if (!(sSpellMgr->GetSpellInfo(entry) || flags & SPELL_DISABLE_DEPRECATED_SPELL))
{
LOG_ERROR("sql.sql", "Spell entry {} from `disables` doesn't exist in dbc, skipped.", entry);
continue;
}
if (!flags || flags > MAX_SPELL_DISABLE_TYPE)
{
LOG_ERROR("sql.sql", "Disable flags for spell {} are invalid, skipped.", entry);
continue;
}
if (flags & SPELL_DISABLE_MAP)
{
for (std::string_view mapStr : Acore::Tokenize(params_0, ',', true))
{
if (Optional<uint32> mapId = Acore::StringTo<uint32>(mapStr))
data.params[0].insert(*mapId);
else
LOG_ERROR("sql.sql", "Disable map '{}' for spell {} is invalid, skipped.", mapStr, entry);
}
}
if (flags & SPELL_DISABLE_AREA)
{
for (std::string_view areaStr : Acore::Tokenize(params_1, ',', true))
{
if (Optional<uint32> areaId = Acore::StringTo<uint32>(areaStr))
data.params[1].insert(*areaId);
else
LOG_ERROR("sql.sql", "Disable area '{}' for spell {} is invalid, skipped.", areaStr, entry);
}
}
// xinef: if spell has disabled los, add flag
if (flags & SPELL_DISABLE_LOS)
{
SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->GetSpellInfo(entry));
spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT;
}
break;
// checked later
case DISABLE_TYPE_QUEST:
break;
case DISABLE_TYPE_MAP:
case DISABLE_TYPE_LFG_MAP:
{
MapEntry const* mapEntry = sMapStore.LookupEntry(entry);
if (!mapEntry)
{
LOG_ERROR("sql.sql", "Map entry {} from `disables` doesn't exist in dbc, skipped.", entry);
continue;
}
bool isFlagInvalid = false;
switch (mapEntry->map_type)
{
case MAP_COMMON:
if (flags)
isFlagInvalid = true;
break;
case MAP_INSTANCE:
case MAP_RAID:
if (flags & DUNGEON_STATUSFLAG_HEROIC && !GetMapDifficultyData(entry, DUNGEON_DIFFICULTY_HEROIC))
isFlagInvalid = true;
else if (flags & RAID_STATUSFLAG_10MAN_HEROIC && !GetMapDifficultyData(entry, RAID_DIFFICULTY_10MAN_HEROIC))
isFlagInvalid = true;
else if (flags & RAID_STATUSFLAG_25MAN_HEROIC && !GetMapDifficultyData(entry, RAID_DIFFICULTY_25MAN_HEROIC))
isFlagInvalid = true;
break;
case MAP_BATTLEGROUND:
case MAP_ARENA:
LOG_ERROR("sql.sql", "Battleground map {} specified to be disabled in map case, skipped.", entry);
continue;
}
if (isFlagInvalid)
{
LOG_ERROR("sql.sql", "Disable flags for map {} are invalid, skipped.", entry);
continue;
}
break;
}
case DISABLE_TYPE_BATTLEGROUND:
if (!sBattlemasterListStore.LookupEntry(entry))
{
LOG_ERROR("sql.sql", "Battleground entry {} from `disables` doesn't exist in dbc, skipped.", entry);
continue;
}
if (flags)
LOG_ERROR("sql.sql", "Disable flags specified for battleground {}, useless data.", entry);
break;
case DISABLE_TYPE_OUTDOORPVP:
if (entry > MAX_OUTDOORPVP_TYPES)
{
LOG_ERROR("sql.sql", "OutdoorPvPTypes value {} from `disables` is invalid, skipped.", entry);
continue;
}
if (flags)
LOG_ERROR("sql.sql", "Disable flags specified for outdoor PvP {}, useless data.", entry);
break;
case DISABLE_TYPE_ACHIEVEMENT_CRITERIA:
if (!sAchievementCriteriaStore.LookupEntry(entry))
{
LOG_ERROR("sql.sql", "Achievement Criteria entry {} from `disables` doesn't exist in dbc, skipped.", entry);
continue;
}
if (flags)
LOG_ERROR("sql.sql", "Disable flags specified for Achievement Criteria {}, useless data.", entry);
break;
case DISABLE_TYPE_VMAP:
{
MapEntry const* mapEntry = sMapStore.LookupEntry(entry);
if (!mapEntry)
{
LOG_ERROR("sql.sql", "Map entry {} from `disables` doesn't exist in dbc, skipped.", entry);
continue;
}
switch (mapEntry->map_type)
{
case MAP_COMMON:
if (flags & VMAP::VMAP_DISABLE_AREAFLAG)
LOG_INFO("disable", "Areaflag disabled for world map {}.", entry);
if (flags & VMAP::VMAP_DISABLE_LIQUIDSTATUS)
LOG_INFO("disable", "Liquid status disabled for world map {}.", entry);
break;
case MAP_INSTANCE:
case MAP_RAID:
if (flags & VMAP::VMAP_DISABLE_HEIGHT)
LOG_INFO("disable", "Height disabled for instance map {}.", entry);
if (flags & VMAP::VMAP_DISABLE_LOS)
LOG_INFO("disable", "LoS disabled for instance map {}.", entry);
break;
case MAP_BATTLEGROUND:
if (flags & VMAP::VMAP_DISABLE_HEIGHT)
LOG_INFO("disable", "Height disabled for battleground map {}.", entry);
if (flags & VMAP::VMAP_DISABLE_LOS)
LOG_INFO("disable", "LoS disabled for battleground map {}.", entry);
break;
case MAP_ARENA:
if (flags & VMAP::VMAP_DISABLE_HEIGHT)
LOG_INFO("disable", "Height disabled for arena map {}.", entry);
if (flags & VMAP::VMAP_DISABLE_LOS)
LOG_INFO("disable", "LoS disabled for arena map {}.", entry);
break;
default:
break;
}
break;
}
case DISABLE_TYPE_GAME_EVENT:
{
GameEventMgr::ActiveEvents const& activeEvents = sGameEventMgr->GetActiveEventList();
if (activeEvents.find(entry) != activeEvents.end())
{
sGameEventMgr->StopEvent(entry);
LOG_INFO("disable", "Event entry {} was stopped because it has been disabled.", entry);
}
break;
}
case DISABLE_TYPE_LOOT:
break;
default:
break;
}
m_DisableMap[type].insert(DisableTypeMap::value_type(entry, data));
++total_count;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} Disables in {} ms", total_count, GetMSTimeDiffToNow(oldMSTime));
LOG_WARN("server.loading", ">> Loaded 0 disables. DB table `disables` is empty!");
LOG_INFO("server.loading", " ");
return;
}
void CheckQuestDisables()
Field* fields;
do
{
uint32 oldMSTime = getMSTime();
uint32 count = m_DisableMap[DISABLE_TYPE_QUEST].size();
if (!count)
fields = result->Fetch();
DisableType type = DisableType(fields[0].Get<uint32>());
if (type >= MAX_DISABLE_TYPES)
{
LOG_INFO("server.loading", ">> Checked 0 quest disables.");
LOG_INFO("server.loading", " ");
return;
LOG_ERROR("sql.sql", "Invalid type {} specified in `disables` table, skipped.", type);
continue;
}
// check only quests, rest already done at startup
for (DisableTypeMap::iterator itr = m_DisableMap[DISABLE_TYPE_QUEST].begin(); itr != m_DisableMap[DISABLE_TYPE_QUEST].end();)
uint32 entry = fields[1].Get<uint32>();
uint8 flags = fields[2].Get<uint8>();
std::string params_0 = fields[3].Get<std::string>();
std::string params_1 = fields[4].Get<std::string>();
DisableData data;
data.flags = flags;
if (!HandleDisableType(type, entry, flags, params_0, params_1, data))
continue;
m_DisableMap[type].insert(DisableTypeMap::value_type(entry, data));
++total_count;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} Disables in {} ms", total_count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
/**
* @brief Allow to add disables without adding it to the database. Useful for modules.
*
* @param type Disable type
* @param entry Entry of Spell/Quest/Map/BG/Achievement/Map/GameEvent/Item
* @param flags Flag depending on Type
* @param param0 MapId if DISABLE_TYPE_SPELL used, 0 for all maps.
* @param param1 AreaId if DISABLE_TYPE_SPELL used, 0 for all areas.
*/
void DisableMgr::AddDisable(DisableType type, uint32 entry, uint8 flags, std::string param0, std::string param1)
{
if (type >= MAX_DISABLE_TYPES)
{
LOG_ERROR("disables", "AddDisable: Invalid type {} specified for entry {}, skipped.", type);
return;
}
DisableData data;
data.flags = flags;
if (!HandleDisableType(type, entry, flags, param0, param1, data))
return;
m_DisableMap[type].insert(DisableTypeMap::value_type(entry, data));
}
bool DisableMgr::HandleDisableType(DisableType type, uint32 entry, uint8 flags, std::string const& params_0, std::string const& params_1, DisableData& data)
{
switch (type)
{
case DISABLE_TYPE_GO_LOS:
if (!sObjectMgr->GetGameObjectTemplate(entry))
{
const uint32 entry = itr->first;
if (!sObjectMgr->GetQuestTemplate(entry))
LOG_ERROR("sql.sql", "Gameobject entry {} from `disables` doesn't exist in dbc, skipped.", entry);
return false;
}
if (flags)
LOG_ERROR("sql.sql", "Disable flags specified for gameobject {}, useless data.", entry);
break;
case DISABLE_TYPE_SPELL:
if (!(sSpellMgr->GetSpellInfo(entry) || flags & SPELL_DISABLE_DEPRECATED_SPELL))
{
LOG_ERROR("sql.sql", "Spell entry {} from `disables` doesn't exist in dbc, skipped.", entry);
return false;
}
if (!flags || flags > MAX_SPELL_DISABLE_TYPE)
{
LOG_ERROR("sql.sql", "Disable flags for spell {} are invalid, skipped.", entry);
return false;
}
if (flags & SPELL_DISABLE_MAP)
{
for (std::string_view mapStr : Acore::Tokenize(params_0, ',', true))
{
LOG_ERROR("sql.sql", "Quest entry {} from `disables` doesn't exist, skipped.", entry);
m_DisableMap[DISABLE_TYPE_QUEST].erase(itr++);
continue;
if (Optional<uint32> mapId = Acore::StringTo<uint32>(mapStr))
data.params[0].insert(*mapId);
else
LOG_ERROR("sql.sql", "Disable map '{}' for spell {} is invalid, skipped.", mapStr, entry);
}
if (itr->second.flags)
LOG_ERROR("sql.sql", "Disable flags specified for quest {}, useless data.", entry);
++itr;
}
LOG_INFO("server.loading", ">> Checked {} Quest Disables in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
if (flags & SPELL_DISABLE_AREA)
{
for (std::string_view areaStr : Acore::Tokenize(params_1, ',', true))
{
if (Optional<uint32> areaId = Acore::StringTo<uint32>(areaStr))
data.params[1].insert(*areaId);
else
LOG_ERROR("sql.sql", "Disable area '{}' for spell {} is invalid, skipped.", areaStr, entry);
}
}
// xinef: if spell has disabled los, add flag
if (flags & SPELL_DISABLE_LOS)
{
SpellInfo* spellInfo = const_cast<SpellInfo*>(sSpellMgr->GetSpellInfo(entry));
spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT;
}
break;
// checked later
case DISABLE_TYPE_QUEST:
break;
case DISABLE_TYPE_MAP:
case DISABLE_TYPE_LFG_MAP:
{
MapEntry const* mapEntry = sMapStore.LookupEntry(entry);
if (!mapEntry)
{
LOG_ERROR("sql.sql", "Map entry {} from `disables` doesn't exist in dbc, skipped.", entry);
return false;
}
bool isFlagInvalid = false;
switch (mapEntry->map_type)
{
case MAP_COMMON:
if (flags)
isFlagInvalid = true;
break;
case MAP_INSTANCE:
case MAP_RAID:
if (flags & DUNGEON_STATUSFLAG_HEROIC && !GetMapDifficultyData(entry, DUNGEON_DIFFICULTY_HEROIC))
isFlagInvalid = true;
else if (flags & RAID_STATUSFLAG_10MAN_HEROIC && !GetMapDifficultyData(entry, RAID_DIFFICULTY_10MAN_HEROIC))
isFlagInvalid = true;
else if (flags & RAID_STATUSFLAG_25MAN_HEROIC && !GetMapDifficultyData(entry, RAID_DIFFICULTY_25MAN_HEROIC))
isFlagInvalid = true;
break;
case MAP_BATTLEGROUND:
case MAP_ARENA:
LOG_ERROR("sql.sql", "Battleground map {} specified to be disabled in map case, skipped.", entry);
return false;
}
if (isFlagInvalid)
{
LOG_ERROR("sql.sql", "Disable flags for map {} are invalid, skipped.", entry);
return false;
}
break;
}
case DISABLE_TYPE_BATTLEGROUND:
if (!sBattlemasterListStore.LookupEntry(entry))
{
LOG_ERROR("sql.sql", "Battleground entry {} from `disables` doesn't exist in dbc, skipped.", entry);
return false;
}
if (flags)
LOG_ERROR("sql.sql", "Disable flags specified for battleground {}, useless data.", entry);
break;
case DISABLE_TYPE_OUTDOORPVP:
if (entry > MAX_OUTDOORPVP_TYPES)
{
LOG_ERROR("sql.sql", "OutdoorPvPTypes value {} from `disables` is invalid, skipped.", entry);
return false;
}
if (flags)
LOG_ERROR("sql.sql", "Disable flags specified for outdoor PvP {}, useless data.", entry);
break;
case DISABLE_TYPE_ACHIEVEMENT_CRITERIA:
if (!sAchievementCriteriaStore.LookupEntry(entry))
{
LOG_ERROR("sql.sql", "Achievement Criteria entry {} from `disables` doesn't exist in dbc, skipped.", entry);
return false;
}
if (flags)
LOG_ERROR("sql.sql", "Disable flags specified for Achievement Criteria {}, useless data.", entry);
break;
case DISABLE_TYPE_VMAP:
{
MapEntry const* mapEntry = sMapStore.LookupEntry(entry);
if (!mapEntry)
{
LOG_ERROR("sql.sql", "Map entry {} from `disables` doesn't exist in dbc, skipped.", entry);
return false;
}
switch (mapEntry->map_type)
{
case MAP_COMMON:
if (flags & VMAP::VMAP_DISABLE_AREAFLAG)
LOG_INFO("disable", "Areaflag disabled for world map {}.", entry);
if (flags & VMAP::VMAP_DISABLE_LIQUIDSTATUS)
LOG_INFO("disable", "Liquid status disabled for world map {}.", entry);
break;
case MAP_INSTANCE:
case MAP_RAID:
if (flags & VMAP::VMAP_DISABLE_HEIGHT)
LOG_INFO("disable", "Height disabled for instance map {}.", entry);
if (flags & VMAP::VMAP_DISABLE_LOS)
LOG_INFO("disable", "LoS disabled for instance map {}.", entry);
break;
case MAP_BATTLEGROUND:
if (flags & VMAP::VMAP_DISABLE_HEIGHT)
LOG_INFO("disable", "Height disabled for battleground map {}.", entry);
if (flags & VMAP::VMAP_DISABLE_LOS)
LOG_INFO("disable", "LoS disabled for battleground map {}.", entry);
break;
case MAP_ARENA:
if (flags & VMAP::VMAP_DISABLE_HEIGHT)
LOG_INFO("disable", "Height disabled for arena map {}.", entry);
if (flags & VMAP::VMAP_DISABLE_LOS)
LOG_INFO("disable", "LoS disabled for arena map {}.", entry);
break;
default:
break;
}
break;
}
case DISABLE_TYPE_GAME_EVENT:
{
GameEventMgr::ActiveEvents const& activeEvents = sGameEventMgr->GetActiveEventList();
if (activeEvents.find(entry) != activeEvents.end())
{
sGameEventMgr->StopEvent(entry);
LOG_INFO("disable", "Event entry {} was stopped because it has been disabled.", entry);
}
break;
}
case DISABLE_TYPE_LOOT:
break;
case MAX_DISABLE_TYPES:
{
LOG_ERROR("disable", "SourceType out of range for entry {}, skipped", entry);
return false;
}
default:
return false;
}
return true;
}
void DisableMgr::CheckQuestDisables()
{
uint32 oldMSTime = getMSTime();
uint32 count = m_DisableMap[DISABLE_TYPE_QUEST].size();
if (!count)
{
LOG_INFO("server.loading", ">> Checked 0 quest disables.");
LOG_INFO("server.loading", " ");
return;
}
bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags)
// check only quests, rest already done at startup
for (DisableTypeMap::iterator itr = m_DisableMap[DISABLE_TYPE_QUEST].begin(); itr != m_DisableMap[DISABLE_TYPE_QUEST].end();)
{
if (type > MAX_DISABLE_TYPES)
const uint32 entry = itr->first;
if (!sObjectMgr->GetQuestTemplate(entry))
{
LOG_ERROR("server", "Disables::IsDisabledFor() called with unknown disable type {}! (entry {}, flags {}).", type, entry, flags);
return false;
LOG_ERROR("sql.sql", "Quest entry {} from `disables` doesn't exist, skipped.", entry);
m_DisableMap[DISABLE_TYPE_QUEST].erase(itr++);
continue;
}
if (itr->second.flags)
LOG_ERROR("sql.sql", "Disable flags specified for quest {}, useless data.", entry);
++itr;
}
if (m_DisableMap[type].empty())
return false;
DisableTypeMap::iterator itr = m_DisableMap[type].find(entry);
if (itr == m_DisableMap[type].end()) // not disabled
return false;
switch (type)
{
case DISABLE_TYPE_SPELL:
{
uint8 spellFlags = itr->second.flags;
if (unit)
{
if ((spellFlags & SPELL_DISABLE_PLAYER && unit->IsPlayer()) ||
(unit->IsCreature() && ((unit->IsPet() && spellFlags & SPELL_DISABLE_PET) || spellFlags & SPELL_DISABLE_CREATURE)))
{
if (spellFlags & SPELL_DISABLE_MAP)
{
std::set<uint32> const& mapIds = itr->second.params[0];
if (mapIds.find(unit->GetMapId()) != mapIds.end())
return true; // Spell is disabled on current map
if (!(spellFlags & SPELL_DISABLE_AREA))
return false; // Spell is disabled on another map, but not this one, return false
// Spell is disabled in an area, but not explicitly our current mapId. Continue processing.
}
if (spellFlags & SPELL_DISABLE_AREA)
{
std::set<uint32> const& areaIds = itr->second.params[1];
if (areaIds.find(unit->GetAreaId()) != areaIds.end())
return true; // Spell is disabled in this area
return false; // Spell is disabled in another area, but not this one, return false
}
else
return true; // Spell disabled for all maps
}
return false;
}
else if (spellFlags & SPELL_DISABLE_DEPRECATED_SPELL) // call not from spellcast
return true;
break;
}
case DISABLE_TYPE_MAP:
case DISABLE_TYPE_LFG_MAP:
if (Player const* player = unit->ToPlayer())
{
MapEntry const* mapEntry = sMapStore.LookupEntry(entry);
if (mapEntry->IsDungeon())
{
uint8 disabledModes = itr->second.flags;
Difficulty targetDifficulty = player->GetDifficulty(mapEntry->IsRaid());
GetDownscaledMapDifficultyData(entry, targetDifficulty);
switch (targetDifficulty)
{
case DUNGEON_DIFFICULTY_NORMAL:
return disabledModes & DUNGEON_STATUSFLAG_NORMAL;
case DUNGEON_DIFFICULTY_HEROIC:
return disabledModes & DUNGEON_STATUSFLAG_HEROIC;
case RAID_DIFFICULTY_10MAN_HEROIC:
return disabledModes & RAID_STATUSFLAG_10MAN_HEROIC;
case RAID_DIFFICULTY_25MAN_HEROIC:
return disabledModes & RAID_STATUSFLAG_25MAN_HEROIC;
}
}
else if (mapEntry->map_type == MAP_COMMON)
return true;
}
return false;
case DISABLE_TYPE_QUEST:
return true;
case DISABLE_TYPE_BATTLEGROUND:
case DISABLE_TYPE_OUTDOORPVP:
case DISABLE_TYPE_ACHIEVEMENT_CRITERIA:
return true;
case DISABLE_TYPE_VMAP:
return flags & itr->second.flags;
case DISABLE_TYPE_GO_LOS:
return true;
case DISABLE_TYPE_GAME_EVENT:
return true;
case DISABLE_TYPE_LOOT:
return true;
}
LOG_INFO("server.loading", ">> Checked {} Quest Disables in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
bool DisableMgr::IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags)
{
if (type >= MAX_DISABLE_TYPES)
{
LOG_ERROR("server", "Disables::IsDisabledFor() called with unknown disable type {}! (entry {}, flags {}).", type, entry, flags);
return false;
}
bool IsVMAPDisabledFor(uint32 entry, uint8 flags)
{
return IsDisabledFor(DISABLE_TYPE_VMAP, entry, nullptr, flags);
}
if (m_DisableMap[type].empty())
return false;
bool IsPathfindingEnabled(const Map* map)
DisableTypeMap::iterator itr = m_DisableMap[type].find(entry);
if (itr == m_DisableMap[type].end()) // not disabled
return false;
switch (type)
{
if (!map)
case DISABLE_TYPE_SPELL:
{
uint8 spellFlags = itr->second.flags;
if (unit)
{
if ((spellFlags & SPELL_DISABLE_PLAYER && unit->IsPlayer()) ||
(unit->IsCreature() && ((unit->IsPet() && spellFlags & SPELL_DISABLE_PET) || spellFlags & SPELL_DISABLE_CREATURE)))
{
if (spellFlags & SPELL_DISABLE_MAP)
{
std::set<uint32> const& mapIds = itr->second.params[0];
if (mapIds.find(unit->GetMapId()) != mapIds.end())
return true; // Spell is disabled on current map
if (!(spellFlags & SPELL_DISABLE_AREA))
return false; // Spell is disabled on another map, but not this one, return false
// Spell is disabled in an area, but not explicitly our current mapId. Continue processing.
}
if (spellFlags & SPELL_DISABLE_AREA)
{
std::set<uint32> const& areaIds = itr->second.params[1];
if (areaIds.find(unit->GetAreaId()) != areaIds.end())
return true; // Spell is disabled in this area
return false; // Spell is disabled in another area, but not this one, return false
}
else
return true; // Spell disabled for all maps
}
return false;
}
else if (spellFlags & SPELL_DISABLE_DEPRECATED_SPELL) // call not from spellcast
return true;
break;
}
case DISABLE_TYPE_MAP:
case DISABLE_TYPE_LFG_MAP:
if (Player const* player = unit->ToPlayer())
{
MapEntry const* mapEntry = sMapStore.LookupEntry(entry);
if (mapEntry->IsDungeon())
{
uint8 disabledModes = itr->second.flags;
Difficulty targetDifficulty = player->GetDifficulty(mapEntry->IsRaid());
GetDownscaledMapDifficultyData(entry, targetDifficulty);
switch (targetDifficulty)
{
case DUNGEON_DIFFICULTY_NORMAL:
return disabledModes & DUNGEON_STATUSFLAG_NORMAL;
case DUNGEON_DIFFICULTY_HEROIC:
return disabledModes & DUNGEON_STATUSFLAG_HEROIC;
case RAID_DIFFICULTY_10MAN_HEROIC:
return disabledModes & RAID_STATUSFLAG_10MAN_HEROIC;
case RAID_DIFFICULTY_25MAN_HEROIC:
return disabledModes & RAID_STATUSFLAG_25MAN_HEROIC;
}
}
else if (mapEntry->map_type == MAP_COMMON)
return true;
}
return false;
return !MMAP::MMapFactory::forbiddenMaps[map->GetId()] && (sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) ? true : map->IsBattlegroundOrArena());
case DISABLE_TYPE_VMAP:
return flags & itr->second.flags;
case DISABLE_TYPE_QUEST:
case DISABLE_TYPE_BATTLEGROUND:
case DISABLE_TYPE_OUTDOORPVP:
case DISABLE_TYPE_ACHIEVEMENT_CRITERIA:
case DISABLE_TYPE_GO_LOS:
case DISABLE_TYPE_GAME_EVENT:
case DISABLE_TYPE_LOOT:
return true;
default:
break;
}
} // Namespace
return false;
}
bool DisableMgr::IsVMAPDisabledFor(uint32 entry, uint8 flags)
{
return IsDisabledFor(DISABLE_TYPE_VMAP, entry, nullptr, flags);
}
bool DisableMgr::IsPathfindingEnabled(Map const* map)
{
if (!map)
return false;
return !MMAP::MMapFactory::forbiddenMaps[map->GetId()] && (sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) ? true : map->IsBattlegroundOrArena());
}
+40 -14
View File
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACORE_DISABLEMGR_H
#define ACORE_DISABLEMGR_H
#ifndef _DISABLEMGR_H
#define _DISABLEMGR_H
#include "Define.h"
#include "Map.h"
@@ -34,7 +34,8 @@ enum DisableType
DISABLE_TYPE_GO_LOS = 7,
DISABLE_TYPE_LFG_MAP = 8,
DISABLE_TYPE_GAME_EVENT = 9,
DISABLE_TYPE_LOOT = 10
DISABLE_TYPE_LOOT = 10,
MAX_DISABLE_TYPES
};
enum SpellDisableTypes
@@ -46,18 +47,43 @@ enum SpellDisableTypes
SPELL_DISABLE_MAP = 0x10,
SPELL_DISABLE_AREA = 0x20,
SPELL_DISABLE_LOS = 0x40,
MAX_SPELL_DISABLE_TYPE = ( SPELL_DISABLE_PLAYER | SPELL_DISABLE_CREATURE | SPELL_DISABLE_PET |
SPELL_DISABLE_DEPRECATED_SPELL | SPELL_DISABLE_MAP | SPELL_DISABLE_AREA |
SPELL_DISABLE_LOS)
MAX_SPELL_DISABLE_TYPE = (SPELL_DISABLE_PLAYER | SPELL_DISABLE_CREATURE | SPELL_DISABLE_PET |
SPELL_DISABLE_DEPRECATED_SPELL | SPELL_DISABLE_MAP | SPELL_DISABLE_AREA |
SPELL_DISABLE_LOS)
};
namespace DisableMgr
struct DisableData
{
void LoadDisables();
bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags = 0);
void CheckQuestDisables();
bool IsVMAPDisabledFor(uint32 entry, uint8 flags);
bool IsPathfindingEnabled(const Map* map);
}
uint8 flags;
std::set<uint32> params[2]; // params0, params1
};
#endif //ACORE_DISABLEMGR_H
class DisableMgr
{
private:
DisableMgr();
~DisableMgr();
public:
static DisableMgr* instance();
void LoadDisables();
void AddDisable(DisableType type, uint32 entry, uint8 flags, std::string param0, std::string param1);
bool HandleDisableType(DisableType type, uint32 entry, uint8 flags, std::string const& params_0, std::string const& params_1, DisableData& data);
static bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags = 0);
void CheckQuestDisables();
static bool IsVMAPDisabledFor(uint32 entry, uint8 flags);
static bool IsPathfindingEnabled(Map const* map);
private:
// single disables here with optional data
typedef std::map<uint32, DisableData> DisableTypeMap;
// global disable map by source
typedef std::map<DisableType, DisableTypeMap> DisableMap;
static DisableMap m_DisableMap;
};
#define sDisableMgr DisableMgr::instance()
#endif //_DISABLEMGR_H
+2 -2
View File
@@ -410,9 +410,9 @@ namespace lfg
uint32 lockData = 0;
if (dungeon->expansion > expansion || (onlySeasonalBosses && !dungeon->seasonal))
lockData = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION;
else if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, dungeon->map, player))
else if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_MAP, dungeon->map, player))
lockData = LFG_LOCKSTATUS_RAID_LOCKED;
else if (DisableMgr::IsDisabledFor(DISABLE_TYPE_LFG_MAP, dungeon->map, player))
else if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_LFG_MAP, dungeon->map, player))
lockData = LFG_LOCKSTATUS_RAID_LOCKED;
else if (dungeon->difficulty > DUNGEON_DIFFICULTY_NORMAL && (!mapEntry || !mapEntry->IsRaid()) && sInstanceSaveMgr->PlayerIsPermBoundToInstance(player->GetGUID(), dungeon->map, Difficulty(dungeon->difficulty)))
lockData = LFG_LOCKSTATUS_RAID_LOCKED;
@@ -286,7 +286,9 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(),
m_ProhibitSchoolTime[i] = 0;
m_CreatureSpellCooldowns.clear();
DisableReputationGain = false;
DisableReputationReward = false;
DisableLootReward = false;
m_SightDistance = sWorld->getFloatConfig(CONFIG_SIGHT_MONSTER);
m_CombatDistance = 0.0f;
+6 -3
View File
@@ -359,8 +359,10 @@ public:
Unit* SelectVictim();
void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; }
[[nodiscard]] bool IsReputationGainDisabled() const { return DisableReputationGain; }
void SetReputationRewardDisabled(bool disable) { DisableReputationReward = disable; }
[[nodiscard]] bool IsReputationRewardDisabled() const { return DisableReputationReward; }
void SetLootRewardDisabled(bool disable) { DisableLootReward = disable; }
[[nodiscard]] bool IsLootRewardDisabled() const { return DisableLootReward; }
[[nodiscard]] bool IsDamageEnoughForLootingAndReward() const;
void LowerPlayerDamageReq(uint32 unDamage, bool damagedByPlayer = true);
void ResetPlayerDamageReq();
@@ -478,7 +480,8 @@ protected:
Position m_homePosition;
Position m_transportHomePosition;
bool DisableReputationGain;
bool DisableReputationReward;
bool DisableLootReward;
CreatureTemplate const* m_creatureInfo; // in difficulty mode > 0 can different from sObjectMgr->GetCreatureTemplate(GetEntry())
CreatureData const* m_creatureData;
@@ -2594,7 +2594,7 @@ void GameObject::EnableCollision(bool enable)
GetMap()->InsertGameObjectModel(*m_model);*/
uint32 phaseMask = 0;
if (enable && !DisableMgr::IsDisabledFor(DISABLE_TYPE_GO_LOS, GetEntry(), nullptr))
if (enable && !sDisableMgr->IsDisabledFor(DISABLE_TYPE_GO_LOS, GetEntry(), nullptr))
phaseMask = GetPhaseMask();
m_model->enable(phaseMask);
+7 -2
View File
@@ -1340,7 +1340,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
return false;
}
if (AccountMgr::IsPlayerAccount(GetSession()->GetSecurity()) && DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, mapid, this))
if (AccountMgr::IsPlayerAccount(GetSession()->GetSecurity()) && sDisableMgr->IsDisabledFor(DISABLE_TYPE_MAP, mapid, this))
{
LOG_ERROR("entities.player", "Player ({}, name: {}) tried to enter a forbidden map {}", GetGUID().ToString(), GetName(), mapid);
SendTransferAborted(mapid, TRANSFER_ABORT_MAP_NOT_ALLOWED);
@@ -4493,6 +4493,9 @@ void Player::BuildPlayerRepop()
void Player::ResurrectPlayer(float restore_percent, bool applySickness)
{
if (!sScriptMgr->CanPlayerResurrect(this))
return;
WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4 * 4); // remove spirit healer position
data << uint32(-1);
data << float(0);
@@ -5993,7 +5996,7 @@ void Player::RewardReputation(Unit* victim)
if (!victim || victim->IsPlayer())
return;
if (victim->ToCreature()->IsReputationGainDisabled())
if (victim->ToCreature()->IsReputationRewardDisabled())
return;
ReputationOnKillEntry const* Rep = sObjectMgr->GetReputationOnKilEntry(victim->ToCreature()->GetCreatureTemplate()->Entry);
@@ -8858,6 +8861,8 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
}
}
sWorldState->FillInitialWorldStates(data, zoneid, areaid);
uint16 length = (data.wpos() - countPos) / 8;
data.put<uint16>(countPos, length);
+4
View File
@@ -1728,6 +1728,10 @@ public:
[[nodiscard]] bool HasTalent(uint32 spell_id, uint8 spec) const;
[[nodiscard]] uint32 CalculateTalentsPoints() const;
void SetBonusTalentCount(uint32 count) { m_extraBonusTalentCount = count; };
uint32 GetBonusTalentCount() { return m_extraBonusTalentCount; };
void AddBonusTalent(uint32 count) { m_extraBonusTalentCount += count; };
void RemoveBonusTalent(uint32 count) { m_extraBonusTalentCount -= count; };
// Dual Spec
void UpdateSpecCount(uint8 count);
@@ -236,7 +236,7 @@ Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* quest)
bool Player::CanSeeStartQuest(Quest const* quest)
{
if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this) && SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) &&
if (!sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this) && SatisfyQuestClass(quest, false) && SatisfyQuestRace(quest, false) &&
SatisfyQuestSkill(quest, false) && SatisfyQuestExclusiveGroup(quest, false) && SatisfyQuestReputation(quest, false) &&
SatisfyQuestPreviousQuest(quest, false) && SatisfyQuestNextChain(quest, false) &&
SatisfyQuestPrevChain(quest, false) && SatisfyQuestDay(quest, false) && SatisfyQuestWeek(quest, false) &&
@@ -250,7 +250,7 @@ bool Player::CanSeeStartQuest(Quest const* quest)
bool Player::CanTakeQuest(Quest const* quest, bool msg)
{
return !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this)
return !sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST, quest->GetQuestId(), this)
&& SatisfyQuestStatus(quest, msg) && SatisfyQuestExclusiveGroup(quest, msg)
&& SatisfyQuestClass(quest, msg) && SatisfyQuestRace(quest, msg) && SatisfyQuestLevel(quest, msg)
&& SatisfyQuestSkill(quest, msg) && SatisfyQuestReputation(quest, msg)
@@ -5619,7 +5619,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
bool Player::isAllowedToLoot(Creature const* creature)
{
if (!creature->isDead() || !creature->IsDamageEnoughForLootingAndReward())
if (!creature->isDead() || !creature->IsDamageEnoughForLootingAndReward() || creature->IsLootRewardDisabled())
return false;
if (HasPendingBind())
@@ -6722,7 +6722,7 @@ bool Player::Satisfy(DungeonProgressionRequirements const* ar, uint32 target_map
LevelMax = ar->levelMax;
}
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, target_map, this))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_MAP, target_map, this))
{
GetSession()->SendAreaTriggerMessage("{}", GetSession()->GetAcoreString(LANG_INSTANCE_CLOSED));
return false;
@@ -555,7 +555,7 @@ void Player::UpdateLocalChannels(uint32 newZone)
if (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY)
currentNameExt = sObjectMgr->GetAcoreStringForDBCLocale(
LANG_CHANNEL_CITY);
LANG_CHANNEL_CITY).c_str();
else
currentNameExt = current_zone_name.c_str();
+8 -2
View File
@@ -17798,7 +17798,7 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp
bool isRewardAllowed = true;
if (creature)
{
isRewardAllowed = creature->IsDamageEnoughForLootingAndReward();
isRewardAllowed = (creature->IsDamageEnoughForLootingAndReward() && !creature->IsLootRewardDisabled());
if (!isRewardAllowed)
creature->SetLootRecipient(nullptr);
}
@@ -20444,7 +20444,7 @@ void Unit::PetSpellFail(SpellInfo const* spellInfo, Unit* target, uint32 result)
if (!charmInfo || !IsCreature())
return;
if ((DisableMgr::IsPathfindingEnabled(GetMap()) || result != SPELL_FAILED_LINE_OF_SIGHT) && target)
if ((sDisableMgr->IsPathfindingEnabled(GetMap()) || result != SPELL_FAILED_LINE_OF_SIGHT) && target)
{
if ((result == SPELL_FAILED_LINE_OF_SIGHT || result == SPELL_FAILED_OUT_OF_RANGE) || !ToCreature()->HasReactState(REACT_PASSIVE))
if (Unit* owner = GetOwner())
@@ -21355,6 +21355,12 @@ bool Unit::CanRestoreMana(SpellInfo const* spellInfo) const
return false;
}
void Unit::SetShapeshiftForm(ShapeshiftForm form)
{
SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
sScriptMgr->OnUnitSetShapeshiftForm((Unit*)this, form);
}
bool Unit::IsInDisallowedMountForm() const
{
if (SpellInfo const* transformSpellInfo = sSpellMgr->GetSpellInfo(getTransForm()))
+1 -4
View File
@@ -1869,10 +1869,7 @@ public:
// ShapeShitForm (use by druid)
[[nodiscard]] ShapeshiftForm GetShapeshiftForm() const { return ShapeshiftForm(GetByteValue(UNIT_FIELD_BYTES_2, 3)); }
void SetShapeshiftForm(ShapeshiftForm form)
{
SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
}
void SetShapeshiftForm(ShapeshiftForm form);
bool IsAttackSpeedOverridenShapeShift() const;
[[nodiscard]] bool IsInFeralForm() const
{
+1 -1
View File
@@ -133,7 +133,7 @@ void GameEventMgr::StartInternalEvent(uint16 event_id)
bool GameEventMgr::StartEvent(uint16 event_id, bool overwrite)
{
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_GAME_EVENT, event_id, nullptr) && !overwrite)
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_GAME_EVENT, event_id, nullptr) && !overwrite)
{
return false;
}
+12 -11
View File
@@ -3072,7 +3072,7 @@ void ObjectMgr::LoadItemTemplates()
else if (itemTemplate.Spells[1].SpellId != -1)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itemTemplate.Spells[1].SpellId);
if (!spellInfo && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Spells[1].SpellId, nullptr))
if (!spellInfo && !sDisableMgr->IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Spells[1].SpellId, nullptr))
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong (not existing) spell in spellid_{} ({})", entry, 1 + 1, itemTemplate.Spells[1].SpellId);
itemTemplate.Spells[0].SpellId = 0;
@@ -3120,7 +3120,7 @@ void ObjectMgr::LoadItemTemplates()
if (itemTemplate.Spells[j].SpellId && itemTemplate.Spells[j].SpellId != -1)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itemTemplate.Spells[j].SpellId);
if (!spellInfo && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Spells[j].SpellId, nullptr))
if (!spellInfo && !sDisableMgr->IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Spells[j].SpellId, nullptr))
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong (not existing) spell in spellid_{} ({})", entry, j + 1, itemTemplate.Spells[j].SpellId);
itemTemplate.Spells[j].SpellId = 0;
@@ -4616,7 +4616,7 @@ void ObjectMgr::LoadQuests()
for (QuestMap::iterator iter = _questTemplates.begin(); iter != _questTemplates.end(); ++iter)
{
// skip post-loading checks for disabled quests
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, iter->first, nullptr))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST, iter->first, nullptr))
continue;
Quest* qinfo = iter->second;
@@ -8770,19 +8770,20 @@ bool ObjectMgr::LoadAcoreStrings()
return true;
}
char const* ObjectMgr::GetAcoreString(uint32 entry, LocaleConstant locale) const
std::string ObjectMgr::GetAcoreString(uint32 entry, LocaleConstant locale) const
{
if (AcoreString const* ts = GetAcoreString(entry))
AcoreString const* as = GetAcoreString(entry);
if (as && !as->Content.empty())
{
if (ts->Content.size() > std::size_t(locale) && !ts->Content[locale].empty())
return ts->Content[locale].c_str();
if (as->Content.size() > std::size_t(locale) && !as->Content[locale].empty())
return as->Content[locale];
return ts->Content[DEFAULT_LOCALE].c_str();
return as->Content[DEFAULT_LOCALE];
}
LOG_ERROR("sql.sql", "Acore string entry {} not found in DB.", entry);
return "<error>";
std::string msg = Acore::StringFormat("No entry for acore_string ({}) in DB.", entry);
LOG_ERROR("sql.sql", msg);
return msg;
}
void ObjectMgr::LoadFishingBaseSkillLevel()
+2 -2
View File
@@ -1315,8 +1315,8 @@ public:
return &itr->second;
}
[[nodiscard]] char const* GetAcoreString(uint32 entry, LocaleConstant locale) const;
[[nodiscard]] char const* GetAcoreStringForDBCLocale(uint32 entry) const { return GetAcoreString(entry, DBCLocaleIndex); }
[[nodiscard]] std::string GetAcoreString(uint32 entry, LocaleConstant locale) const;
[[nodiscard]] std::string GetAcoreStringForDBCLocale(uint32 entry) const { return GetAcoreString(entry, DBCLocaleIndex); }
[[nodiscard]] LocaleConstant GetDBCLocaleIndex() const { return DBCLocaleIndex; }
void SetDBCLocaleIndex(LocaleConstant locale) { DBCLocaleIndex = locale; }
@@ -90,7 +90,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData)
}
// chosen battleground type is disabled
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId_, nullptr))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId_, nullptr))
{
ChatHandler(this).PSendSysMessage(LANG_BG_DISABLED);
return;
@@ -721,7 +721,7 @@ void WorldSession::HandleBattlemasterJoinArena(WorldPacket& recvData)
}
// arenas disabled
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, BATTLEGROUND_AA, nullptr))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, BATTLEGROUND_AA, nullptr))
{
ChatHandler(this).PSendSysMessage(LANG_ARENA_DISABLED);
return;
+1 -1
View File
@@ -158,7 +158,7 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recvData*/)
{
HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(entry);
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_GAME_EVENT, sGameEventMgr->GetHolidayEventId(holiday->Id), nullptr))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_GAME_EVENT, sGameEventMgr->GetHolidayEventId(holiday->Id), nullptr))
{
continue;
}
+1 -1
View File
@@ -232,7 +232,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
return;
// Not let attack through obstructions
bool checkLos = !DisableMgr::IsPathfindingEnabled(pet->GetMap()) ||
bool checkLos = !sDisableMgr->IsPathfindingEnabled(pet->GetMap()) ||
(TargetUnit->IsCreature() && (TargetUnit->ToCreature()->isWorldBoss() || TargetUnit->ToCreature()->IsDungeonBoss()));
if (checkLos && !pet->IsWithinLOSInMap(TargetUnit))
+1 -1
View File
@@ -421,7 +421,7 @@ bool LootItem::AllowedForPlayer(Player const* player, ObjectGuid source) const
return false;
}
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_LOOT, itemid, nullptr))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_LOOT, itemid, nullptr))
{
return false;
}
+1 -1
View File
@@ -145,7 +145,7 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy)
void Map::LoadMMap(int gx, int gy)
{
if (!DisableMgr::IsPathfindingEnabled(this)) // pussywizard
if (!sDisableMgr->IsPathfindingEnabled(this)) // pussywizard
return;
int mmapLoadResult = MMAP::MMapFactory::createOrGetMMapMgr()->loadMap(GetId(), gx, gy);
+1 -2
View File
@@ -1169,8 +1169,7 @@ enum AcoreStrings
LANG_GM_SILENCE = 6616, // "Silence is ON for %s" - Spell 1852
// Used for .string command
LANG_NO_ACORE_STRING_FOUND = 6617,
// Free strings 6617-7522
LANG_WORLD_CLOSED = 7523,
LANG_WORLD_OPENED = 7524,
@@ -63,7 +63,7 @@ void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner)
}
owner->UpdateAllowedPositionZ(x, y, z);
init.MoveTo(x, y, z, DisableMgr::IsPathfindingEnabled(owner->FindMap()), true);
init.MoveTo(x, y, z, sDisableMgr->IsPathfindingEnabled(owner->FindMap()), true);
init.SetWalk(_walk);
init.Launch();
@@ -35,7 +35,7 @@ PathGenerator::PathGenerator(WorldObject const* owner) :
memset(_pathPolyRefs, 0, sizeof(_pathPolyRefs));
uint32 mapId = _source->GetMapId();
//if (DisableMgr::IsPathfindingEnabled(_sourceUnit->FindMap()))
//if (sDisableMgr->IsPathfindingEnabled(_sourceUnit->FindMap()))
{
MMAP::MMapMgr* mmap = MMAP::MMapFactory::createOrGetMMapMgr();
_navMesh = mmap->GetNavMesh(mapId);
+1 -1
View File
@@ -60,7 +60,7 @@ void OutdoorPvPMgr::InitOutdoorPvP()
{
typeId = fields[0].Get<uint8>();
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_OUTDOORPVP, typeId, nullptr))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_OUTDOORPVP, typeId, nullptr))
continue;
if (typeId >= MAX_OUTDOORPVP_TYPES)
@@ -903,6 +903,11 @@ bool ScriptMgr::AnticheatCheckMovementInfo(Player* player, MovementInfo const& m
CALL_ENABLED_BOOLEAN_HOOKS(PlayerScript, PLAYERHOOK_ANTICHEAT_CHECK_MOVEMENT_INFO, !script->AnticheatCheckMovementInfo(player, movementInfo, mover, jump));
}
bool ScriptMgr::CanPlayerResurrect(Player* player)
{
CALL_ENABLED_BOOLEAN_HOOKS(PlayerScript, PLAYERHOOK_CAN_RESURRECT, !script->CanPlayerResurrect(player));
}
PlayerScript::PlayerScript(const char* name, std::vector<uint16> enabledHooks)
: ScriptObject(name, PLAYERHOOK_END)
{
@@ -204,6 +204,7 @@ enum PlayerHook
PLAYERHOOK_CAN_SEND_ERROR_ALREADY_LOOTED,
PLAYERHOOK_ON_AFTER_CREATURE_LOOT,
PLAYERHOOK_ON_AFTER_CREATURE_LOOT_MONEY,
PLAYERHOOK_CAN_RESURRECT,
PLAYERHOOK_END
};
@@ -766,6 +767,15 @@ public:
* @param player Contains information about the Player
*/
virtual void OnAfterCreatureLootMoney(Player* /*player*/) { }
/**
* @brief This hook is called, to avoid player resurrect
*
* @param player Contains information about the Player
*
* @return true if player is authorized to resurect
*/
virtual bool CanPlayerResurrect(Player* /*player*/) { return true; }
};
#endif
@@ -148,6 +148,11 @@ void ScriptMgr::OnUnitDeath(Unit* unit, Unit* killer)
CALL_ENABLED_HOOKS(UnitScript, UNITHOOK_ON_UNIT_DEATH, script->OnUnitDeath(unit, killer));
}
void ScriptMgr::OnUnitSetShapeshiftForm(Unit* unit, uint8 form)
{
CALL_ENABLED_HOOKS(UnitScript, UNITHOOK_ON_UNIT_SET_SHAPESHIFT_FORM, script->OnUnitSetShapeshiftForm(unit, form));
}
UnitScript::UnitScript(const char* name, bool addToScripts, std::vector<uint16> enabledHooks)
: ScriptObject(name, UNITHOOK_END)
{
@@ -45,6 +45,7 @@ enum UnitHook
UNITHOOK_ON_UNIT_ENTER_EVADE_MODE,
UNITHOOK_ON_UNIT_ENTER_COMBAT,
UNITHOOK_ON_UNIT_DEATH,
UNITHOOK_ON_UNIT_SET_SHAPESHIFT_FORM,
UNITHOOK_END
};
@@ -115,6 +116,7 @@ public:
virtual void OnUnitEnterEvadeMode(Unit* /*unit*/, uint8 /*evadeReason*/) { }
virtual void OnUnitEnterCombat(Unit* /*unit*/, Unit* /*victim*/) { }
virtual void OnUnitDeath(Unit* /*unit*/, Unit* /*killer*/) { }
virtual void OnUnitSetShapeshiftForm(Unit* /*unit*/, uint8 /*form*/) { }
};
#endif
+2
View File
@@ -492,6 +492,7 @@ public: /* PlayerScript */
void OnAfterCreatureLoot(Player* player);
void OnAfterCreatureLootMoney(Player* player);
bool OnCanPlayerFlyInZone(Player* player, uint32 mapId, uint32 zoneId, SpellInfo const* bySpell);
bool CanPlayerResurrect(Player* player);
// Anti cheat
void AnticheatSetCanFlybyServer(Player* player, bool apply);
@@ -589,6 +590,7 @@ public: /* UnitScript */
void OnUnitEnterEvadeMode(Unit* unit, uint8 why);
void OnUnitEnterCombat(Unit* unit, Unit* victim);
void OnUnitDeath(Unit* unit, Unit* killer);
void OnUnitSetShapeshiftForm(Unit* unit, uint8 form);
public: /* MovementHandlerScript */
void OnPlayerMove(Player* player, MovementInfo movementInfo, uint32 opcode);
+1 -1
View File
@@ -830,7 +830,7 @@ bool WorldSession::DisallowHyperlinksAndMaybeKick(std::string_view str)
return false;
}
char const* WorldSession::GetAcoreString(uint32 entry) const
std::string WorldSession::GetAcoreString(uint32 entry) const
{
return sObjectMgr->GetAcoreString(entry, GetSessionDbLocaleIndex());
}
+4 -4
View File
@@ -372,7 +372,7 @@ public:
void SendAreaTriggerMessage(std::string_view str);
template<typename... Args>
void SendAreaTriggerMessage(char const* fmt, Args&&... args)
void SendAreaTriggerMessage(std::string fmt, Args&&... args)
{
if (!m_playerLoading)
SendAreaTriggerMessage(Acore::StringFormat(fmt, std::forward<Args>(args)...));
@@ -528,9 +528,9 @@ public:
time_t m_muteTime;
// Locales
LocaleConstant GetSessionDbcLocale() const { return /*_isBot? LOCALE_enUS : */m_sessionDbcLocale; }
LocaleConstant GetSessionDbLocaleIndex() const { return /*_isBot? LOCALE_enUS : */m_sessionDbLocaleIndex; }
char const* GetAcoreString(uint32 entry) const;
LocaleConstant GetSessionDbcLocale() const { return m_sessionDbcLocale; }
LocaleConstant GetSessionDbLocaleIndex() const { return m_sessionDbLocaleIndex; }
std::string GetAcoreString(uint32 entry) const;
std::string const* GetModuleString(std::string module, uint32 id) const;
uint32 GetLatency() const { return m_latency; }
+3 -3
View File
@@ -3530,7 +3530,7 @@ SpellCastResult Spell::prepare(SpellCastTargets const* targets, AuraEffect const
_spellEvent = new SpellEvent(this);
m_caster->m_Events.AddEvent(_spellEvent, m_caster->m_Events.CalculateTime(1));
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, m_caster))
if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, m_caster))
{
SendCastResult(SPELL_FAILED_SPELL_UNAVAILABLE);
finish(false);
@@ -6247,7 +6247,7 @@ SpellCastResult Spell::CheckCast(bool strict)
return SPELL_FAILED_BAD_TARGETS;
// Xinef: Pass only explicit unit target spells
// pussywizard:
if (DisableMgr::IsPathfindingEnabled(m_caster->FindMap()) && m_spellInfo->NeedsExplicitUnitTarget())
if (sDisableMgr->IsPathfindingEnabled(m_caster->FindMap()) && m_spellInfo->NeedsExplicitUnitTarget())
{
Unit* target = m_targets.GetUnitTarget();
if (!target)
@@ -7960,7 +7960,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff) const
// if spell is triggered, need to check for LOS disable on the aura triggering it and inherit that behaviour
if (IsTriggered() && m_triggeredByAuraSpell && (m_triggeredByAuraSpell.spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) ||
DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_triggeredByAuraSpell.spellInfo->Id, nullptr, SPELL_DISABLE_LOS)))
sDisableMgr->IsDisabledFor(DISABLE_TYPE_SPELL, m_triggeredByAuraSpell.spellInfo->Id, nullptr, SPELL_DISABLE_LOS)))
{
return true;
}
@@ -1602,6 +1602,7 @@ void SpellMgr::LoadSpellInfoCorrections()
ApplySpellFix({ 44335 }, [](SpellInfo* spellInfo)
{
spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CHANGE_MAP;
spellInfo->AttributesCu |= SPELL_ATTR0_CU_SINGLE_AURA_STACK;
});
ApplySpellFix({
+2 -1
View File
@@ -39,7 +39,8 @@ void Acore::CustomChatTextBuilder::operator()(WorldPacket& data, LocaleConstant
void Acore::AcoreStringChatBuilder::operator()(WorldPacket& data, LocaleConstant locale) const
{
char const* text = sObjectMgr->GetAcoreString(_textId, locale);
std::string strtext = sObjectMgr->GetAcoreString(_textId, locale);
char const* text = strtext.c_str();
if (_args)
{
+2 -1
View File
@@ -422,6 +422,7 @@ enum WorldIntConfigs
CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD,
CONFIG_AUCTIONHOUSE_WORKERTHREADS,
CONFIG_SPELL_QUEUE_WINDOW,
CONFIG_SUNSREACH_COUNTER_MAX,
INT_CONFIG_VALUE_COUNT
};
@@ -572,7 +573,7 @@ public:
virtual void SendGlobalMessage(WorldPacket const* packet, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) = 0;
virtual void SendGlobalGMMessage(WorldPacket const* packet, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) = 0;
virtual bool SendZoneMessage(uint32 zone, WorldPacket const* packet, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) = 0;
virtual void SendZoneText(uint32 zone, const char* text, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) = 0;
virtual void SendZoneText(uint32 zone, std::string text, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) = 0;
virtual void SendServerMessage(ServerMessageType messageID, std::string stringParam = "", Player* player = nullptr) = 0;
[[nodiscard]] virtual bool IsShuttingDown() const = 0;
[[nodiscard]] virtual uint32 GetShutDownTimeLeft() const = 0;
+11 -5
View File
@@ -1499,6 +1499,8 @@ void World::LoadConfigSettings(bool reload)
_bool_configs[CONFIG_SPELL_QUEUE_ENABLED] = sConfigMgr->GetOption<bool>("SpellQueue.Enabled", true);
_int_configs[CONFIG_SPELL_QUEUE_WINDOW] = sConfigMgr->GetOption<uint32>("SpellQueue.Window", 400);
_int_configs[CONFIG_SUNSREACH_COUNTER_MAX] = sConfigMgr->GetOption<uint32>("Sunsreach.CounterMax", 10000);
// call ScriptMgr if we're reloading the configuration
sScriptMgr->OnAfterConfigLoad(reload);
}
@@ -1720,7 +1722,7 @@ void World::SetInitialWorldSettings()
LoadRandomEnchantmentsTable();
LOG_INFO("server.loading", "Loading Disables");
DisableMgr::LoadDisables(); // must be before loading quests and items
sDisableMgr->LoadDisables(); // must be before loading quests and items
LOG_INFO("server.loading", "Loading Items..."); // must be after LoadRandomEnchantmentsTable and LoadPageTexts
sObjectMgr->LoadItemTemplates();
@@ -1801,7 +1803,7 @@ void World::SetInitialWorldSettings()
sObjectMgr->LoadQuests(); // must be loaded after DBCs, creature_template, item_template, gameobject tables
LOG_INFO("server.loading", "Checking Quest Disables");
DisableMgr::CheckQuestDisables(); // must be after loading quests
sDisableMgr->CheckQuestDisables(); // must be after loading quests
LOG_INFO("server.loading", "Loading Quest POI");
sObjectMgr->LoadQuestPOI();
@@ -2126,6 +2128,9 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", " ");
sGameEventMgr->StartArenaSeason();
LOG_INFO("server.loading", "Loading WorldState...");
sWorldState->Load();
sTicketMgr->Initialize();
///- Initialize Battlegrounds
@@ -2571,7 +2576,8 @@ namespace Acore
explicit WorldWorldTextBuilder(uint32 textId, va_list* args = nullptr) : i_textId(textId), i_args(args) {}
void operator()(WorldPacketList& data_list, LocaleConstant loc_idx)
{
char const* text = sObjectMgr->GetAcoreString(i_textId, loc_idx);
std::string strtext = sObjectMgr->GetAcoreString(i_textId, loc_idx);
char const* text = strtext.c_str();
if (i_args)
{
@@ -2630,10 +2636,10 @@ bool World::SendZoneMessage(uint32 zone, WorldPacket const* packet, WorldSession
}
/// Send a System Message to all players in the zone (except self if mentioned)
void World::SendZoneText(uint32 zone, const char* text, WorldSession* self, TeamId teamId)
void World::SendZoneText(uint32 zone, std::string text, WorldSession* self, TeamId teamId)
{
WorldPacket data;
ChatHandler::BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, text);
ChatHandler::BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, text.c_str());
SendZoneMessage(zone, &data, self, teamId);
}
+1 -1
View File
@@ -239,7 +239,7 @@ public:
void SendGlobalMessage(WorldPacket const* packet, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) override;
void SendGlobalGMMessage(WorldPacket const* packet, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) override;
bool SendZoneMessage(uint32 zone, WorldPacket const* packet, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) override;
void SendZoneText(uint32 zone, const char* text, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) override;
void SendZoneText(uint32 zone, std::string text, WorldSession* self = nullptr, TeamId teamId = TEAM_NEUTRAL) override;
void SendServerMessage(ServerMessageType messageID, std::string stringParam = "", Player* player = nullptr) override;
/// Are we in the middle of a shutdown?
+795
View File
@@ -15,9 +15,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GameEventMgr.h"
#include "MapMgr.h"
#include "Player.h"
#include "SharedDefines.h"
#include "Weather.h"
#include "WorldState.h"
WorldState* WorldState::instance()
@@ -37,6 +39,82 @@ WorldState::~WorldState()
{
}
void WorldState::Load()
{
QueryResult result = CharacterDatabase.Query("SELECT Id, Data FROM world_state");
if (result)
{
do
{
Field* fields = result->Fetch();
uint32 id = fields[0].Get<uint32>();
std::string data = fields[1].Get<std::string>();
std::istringstream loadStream(data);
switch (id)
{
case SAVE_ID_QUEL_DANAS:
{
if (data.size())
{
try
{
loadStream >> m_sunsReachData.m_phase >> m_sunsReachData.m_subphaseMask;
for (uint32 i = 0; i < COUNTERS_MAX; ++i)
loadStream >> m_sunsReachData.m_sunsReachReclamationCounters[i];
loadStream >> m_sunsReachData.m_gate;
for (uint32 i = 0; i < COUNTERS_MAX_GATES; ++i)
loadStream >> m_sunsReachData.m_gateCounters[i];
}
catch (std::exception& e)
{
LOG_ERROR("scripts", "WorldState::Load: Exception reading SunsReach data {}", e.what());
m_sunsReachData.m_phase = 0;
m_sunsReachData.m_subphaseMask = 0;
memset(m_sunsReachData.m_sunsReachReclamationCounters, 0, sizeof(m_sunsReachData.m_sunsReachReclamationCounters));
m_sunsReachData.m_gate = SUNWELL_ALL_GATES_CLOSED;
memset(m_sunsReachData.m_gateCounters, 0, sizeof(m_sunsReachData.m_gateCounters));
}
}
else
{
m_sunsReachData.m_phase = 0;
m_sunsReachData.m_subphaseMask = 0;
memset(m_sunsReachData.m_sunsReachReclamationCounters, 0, sizeof(m_sunsReachData.m_sunsReachReclamationCounters));
m_sunsReachData.m_gate = SUNWELL_ALL_GATES_CLOSED;
memset(m_sunsReachData.m_gateCounters, 0, sizeof(m_sunsReachData.m_gateCounters));
}
break;
}
}
} while (result->NextRow());
}
StartSunsReachPhase(true);
StartSunwellGatePhase();
HandleSunsReachSubPhaseTransition(m_sunsReachData.m_subphaseMask, true);
}
void WorldState::Save(WorldStateSaveIds saveId)
{
switch (saveId)
{
case SAVE_ID_QUEL_DANAS:
{
std::string expansionData = m_sunsReachData.GetData();
SaveHelper(expansionData, SAVE_ID_QUEL_DANAS);
break;
}
default:
break;
}
}
void WorldState::SaveHelper(std::string& stringToSave, WorldStateSaveIds saveId)
{
CharacterDatabase.Execute("DELETE FROM world_state WHERE Id='{}'", saveId);
CharacterDatabase.Execute("INSERT INTO world_state(Id,Data) VALUES('{}','{}')", saveId, stringToSave.data());
}
bool WorldState::IsConditionFulfilled(WorldStateCondition conditionId, WorldStateConditionState state) const
{
switch (conditionId)
@@ -140,6 +218,16 @@ void WorldState::HandlePlayerEnterZone(Player* player, WorldStateZoneId zoneId)
else if (_isMagtheridonHeadSpawnedHorde && player->GetTeamId() == TEAM_HORDE)
player->CastSpell(player, SPELL_NAZGRELS_FAVOR, true);
break;
case ZONEID_ISLE_OF_QUEL_DANAS:
case ZONEID_MAGISTERS_TERRACE:
case ZONEID_SUNWELL_PLATEAU:
{
std::lock_guard<std::mutex> guard(m_sunsReachData.m_sunsReachReclamationMutex);
m_sunsReachData.m_sunsReachReclamationPlayers.push_back(player->GetGUID());
if (m_sunsReachData.m_subphaseMask == SUBPHASE_ALL)
player->CastSpell(player, SPELL_KIRU_SONG_OF_VICTORY, true);
break;
}
default:
break;
}
@@ -167,6 +255,17 @@ void WorldState::HandlePlayerLeaveZone(Player* player, WorldStateZoneId zoneId)
else if (player->GetTeamId() == TEAM_HORDE)
player->RemoveAurasDueToSpell(SPELL_NAZGRELS_FAVOR);
break;
case ZONEID_ISLE_OF_QUEL_DANAS:
case ZONEID_MAGISTERS_TERRACE:
case ZONEID_SUNWELL_PLATEAU:
{
std::lock_guard<std::mutex> guard(m_sunsReachData.m_sunsReachReclamationMutex);
player->RemoveAurasDueToSpell(SPELL_KIRU_SONG_OF_VICTORY);
auto position = std::find(m_sunsReachData.m_sunsReachReclamationPlayers.begin(), m_sunsReachData.m_sunsReachReclamationPlayers.end(), player->GetGUID());
if (position != m_sunsReachData.m_sunsReachReclamationPlayers.end())
m_sunsReachData.m_sunsReachReclamationPlayers.erase(position);
break;
}
default:
break;
}
@@ -291,3 +390,699 @@ void WorldState::DispelAdalsSongOfBattle()
}
});
}
void WorldState::SendWorldstateUpdate(std::mutex& mutex, GuidVector const& guids, uint32 value, uint32 worldStateId)
{
std::lock_guard<std::mutex> guard(mutex);
for (ObjectGuid const& guid : guids)
if (Player* player = ObjectAccessor::FindPlayer(guid))
player->SendUpdateWorldState(worldStateId, value);
}
enum WorldStateSunsReachQuests
{
QUEST_ERRATIC_BEHAVIOR = 11524,
QUEST_SANCTUM_WARDS = 11496,
QUEST_BATTLE_FOR_THE_SUNS_REACH_ARMORY = 11538,
QUEST_DISTRACTION_AT_THE_DEAD_SCAR = 11532,
QUEST_INTERCEPTING_THE_MANA_CELLS = 11513,
QUEST_INTERCEPT_THE_REINFORCEMENTS = 11542,
QUEST_TAKING_THE_HARBOR = 11539,
QUEST_MAKING_READY = 11535,
QUEST_DISCOVERING_YOUR_ROOTS = 11520,
QUEST_A_CHARITABLE_DONATION = 11545,
QUEST_A_MAGNANIMOUS_BENEFACTOR = 11549,
COUNTER_MAX_VAL_REQ = 10000,
};
void WorldState::AddSunsReachProgress(uint32 questId)
{
uint32 counter = 0;
int32 otherCounter = -1;
int32 worldState = 0;
uint32 subPhaseMask = 0;
uint32 addedValue = 1;
switch (questId)
{
case QUEST_ERRATIC_BEHAVIOR:
counter = COUNTER_ERRATIC_BEHAVIOR;
otherCounter = COUNTER_SANCTUM_WARDS;
worldState = WORLD_STATE_QUEL_DANAS_SANCTUM;
break;
case QUEST_SANCTUM_WARDS:
counter = COUNTER_SANCTUM_WARDS;
otherCounter = COUNTER_SANCTUM_WARDS;
worldState = WORLD_STATE_QUEL_DANAS_SANCTUM;
break;
case QUEST_BATTLE_FOR_THE_SUNS_REACH_ARMORY:
counter = COUNTER_BATTLE_FOR_THE_SUNS_REACH_ARMORY;
otherCounter = COUNTER_DISTRACTION_AT_THE_DEAD_SCAR;
worldState = WORLD_STATE_QUEL_DANAS_ARMORY;
break;
case QUEST_DISTRACTION_AT_THE_DEAD_SCAR:
counter = COUNTER_DISTRACTION_AT_THE_DEAD_SCAR;
otherCounter = COUNTER_BATTLE_FOR_THE_SUNS_REACH_ARMORY;
worldState = WORLD_STATE_QUEL_DANAS_ARMORY;
break;
case QUEST_INTERCEPTING_THE_MANA_CELLS:
counter = COUNTER_INTERCEPTING_THE_MANA_CELLS;
subPhaseMask = SUBPHASE_PORTAL;
worldState = WORLD_STATE_QUEL_DANAS_PORTAL;
break;
case QUEST_INTERCEPT_THE_REINFORCEMENTS:
counter = COUNTER_INTERCEPT_THE_REINFORCEMENTS;
otherCounter = COUNTER_TAKING_THE_HARBOR;
worldState = WORLD_STATE_QUEL_DANAS_HARBOR;
break;
case QUEST_TAKING_THE_HARBOR:
counter = COUNTER_TAKING_THE_HARBOR;
otherCounter = COUNTER_INTERCEPT_THE_REINFORCEMENTS;
worldState = WORLD_STATE_QUEL_DANAS_HARBOR;
break;
case QUEST_MAKING_READY:
counter = COUNTER_MAKING_READY;
subPhaseMask = SUBPHASE_ANVIL;
worldState = WORLD_STATE_QUEL_DANAS_ANVIL;
break;
case QUEST_DISCOVERING_YOUR_ROOTS:
counter = COUNTER_DISCOVERING_YOUR_ROOTS;
subPhaseMask = SUBPHASE_ALCHEMY_LAB;
worldState = WORLD_STATE_QUEL_DANAS_ALCHEMY_LAB;
break;
case QUEST_A_CHARITABLE_DONATION:
counter = COUNTER_A_CHARITABLE_DONATION;
subPhaseMask = SUBPHASE_MONUMENT;
worldState = WORLD_STATE_QUEL_DANAS_MONUMENT;
break;
case QUEST_A_MAGNANIMOUS_BENEFACTOR:
counter = COUNTER_A_CHARITABLE_DONATION;
subPhaseMask = SUBPHASE_MONUMENT;
worldState = WORLD_STATE_QUEL_DANAS_MONUMENT;
addedValue = 150;
break;
default:
return;
}
uint32 previousValue = 0;
uint32 newValue = 0;
if (!subPhaseMask)
previousValue = m_sunsReachData.GetPhasePercentage(m_sunsReachData.m_phase);
else
previousValue = m_sunsReachData.GetSubPhasePercentage(subPhaseMask);
m_sunsReachData.m_sunsReachReclamationCounters[counter] += addedValue;
if (!subPhaseMask)
newValue = m_sunsReachData.GetPhasePercentage(m_sunsReachData.m_phase);
else
newValue = m_sunsReachData.GetSubPhasePercentage(subPhaseMask);
if (previousValue != newValue)
SendWorldstateUpdate(m_sunsReachData.m_sunsReachReclamationMutex, m_sunsReachData.m_sunsReachReclamationPlayers, newValue, worldState);
bool save = true;
uint32 counterValue = m_sunsReachData.m_sunsReachReclamationCounters[counter];
uint32 modifier = 1;
if (otherCounter != -1)
{
modifier = 2;
counterValue += m_sunsReachData.m_sunsReachReclamationCounters[otherCounter];
}
if (counterValue >= sWorld->getIntConfig(CONFIG_SUNSREACH_COUNTER_MAX) * modifier)
{
save = false;
switch (questId)
{
case QUEST_ERRATIC_BEHAVIOR:
case QUEST_SANCTUM_WARDS:
{
if (m_sunsReachData.m_phase == SUNS_REACH_PHASE_1_STAGING_AREA)
HandleSunsReachPhaseTransition(SUNS_REACH_PHASE_2_SANCTUM);
break;
}
case QUEST_BATTLE_FOR_THE_SUNS_REACH_ARMORY:
case QUEST_DISTRACTION_AT_THE_DEAD_SCAR:
{
if (m_sunsReachData.m_phase == SUNS_REACH_PHASE_2_SANCTUM)
HandleSunsReachPhaseTransition(SUNS_REACH_PHASE_3_ARMORY);
break;
}
case QUEST_INTERCEPTING_THE_MANA_CELLS:
{
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_PORTAL) == 0)
HandleSunsReachSubPhaseTransition(SUBPHASE_PORTAL);
break;
}
case QUEST_INTERCEPT_THE_REINFORCEMENTS:
case QUEST_TAKING_THE_HARBOR:
{
if (m_sunsReachData.m_phase == SUNS_REACH_PHASE_3_ARMORY)
HandleSunsReachPhaseTransition(SUNS_REACH_PHASE_4_HARBOR);
break;
}
case QUEST_MAKING_READY:
{
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_ANVIL) == 0)
HandleSunsReachSubPhaseTransition(SUBPHASE_ANVIL);
break;
}
case QUEST_DISCOVERING_YOUR_ROOTS:
{
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_ALCHEMY_LAB) == 0)
HandleSunsReachSubPhaseTransition(SUBPHASE_ALCHEMY_LAB);
break;
}
case QUEST_A_CHARITABLE_DONATION:
case QUEST_A_MAGNANIMOUS_BENEFACTOR:
{
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_MONUMENT) == 0)
HandleSunsReachSubPhaseTransition(SUBPHASE_MONUMENT);
break;
}
}
}
if (save)
Save(SAVE_ID_QUEL_DANAS);
}
void WorldState::HandleSunsReachPhaseTransition(uint32 newPhase)
{
if (newPhase < m_sunsReachData.m_phase)
{
while (newPhase != m_sunsReachData.m_phase)
{
StopSunsReachPhase(newPhase > m_sunsReachData.m_phase);
--m_sunsReachData.m_phase;
}
StartSunsReachPhase();
}
else
{
StopSunsReachPhase(newPhase > m_sunsReachData.m_phase);
bool moreThanOne = newPhase > m_sunsReachData.m_phase + 1; // custom command case
m_sunsReachData.m_phase = newPhase;
StartSunsReachPhase(moreThanOne);
}
switch (m_sunsReachData.m_phase)
{
case SUNS_REACH_PHASE_2_SANCTUM: if ((m_sunsReachData.m_subphaseMask & SUBPHASE_PORTAL) == 0) sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_2_NO_PORTAL); break;
case SUNS_REACH_PHASE_3_ARMORY: if ((m_sunsReachData.m_subphaseMask & SUBPHASE_ANVIL) == 0) sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_3_NO_ANVIL); break;
case SUNS_REACH_PHASE_4_HARBOR:
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_ALCHEMY_LAB) == 0) sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_4_NO_MONUMENT);
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_MONUMENT) == 0) sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_4_NO_ALCHEMY_LAB);
break;
default: break;
}
SendWorldstateUpdate(m_sunsReachData.m_sunsReachReclamationMutex, m_sunsReachData.m_sunsReachReclamationPlayers, m_sunsReachData.m_phase, WORLD_STATE_QUEL_DANAS_MUSIC);
Save(SAVE_ID_QUEL_DANAS);
}
void WorldState::HandleSunsReachSubPhaseTransition(int32 subPhaseMask, bool initial)
{
bool start = true;
if (subPhaseMask < 0)
{
start = false;
subPhaseMask = -subPhaseMask;
}
bool all = false;
if (start)
{
m_sunsReachData.m_subphaseMask |= subPhaseMask;
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_ALL) == SUBPHASE_ALL)
all = true;
}
else
{
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_ALL) == SUBPHASE_ALL)
all = true;
m_sunsReachData.m_subphaseMask &= ~subPhaseMask;
}
if (initial)
{
if (m_sunsReachData.m_phase >= SUNS_REACH_PHASE_2_SANCTUM)
if ((subPhaseMask & SUBPHASE_PORTAL) == 0)
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_2_NO_PORTAL);
if (m_sunsReachData.m_phase >= SUNS_REACH_PHASE_3_ARMORY)
if ((subPhaseMask & SUBPHASE_ANVIL) == 0)
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_3_NO_ANVIL);
if (m_sunsReachData.m_phase >= SUNS_REACH_PHASE_4_HARBOR)
{
if ((subPhaseMask & SUBPHASE_ALCHEMY_LAB) == 0)
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_4_NO_ALCHEMY_LAB);
if ((subPhaseMask & SUBPHASE_MONUMENT) == 0)
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_4_NO_MONUMENT);
}
}
if ((subPhaseMask & SUBPHASE_PORTAL))
{
uint32 first = GAME_EVENT_QUEL_DANAS_PHASE_2_NO_PORTAL;
uint32 second = GAME_EVENT_QUEL_DANAS_PHASE_2_PORTAL;
if (start)
{
sGameEventMgr->StopEvent(first);
sGameEventMgr->StartEvent(second);
}
else
{
sGameEventMgr->StopEvent(second);
sGameEventMgr->StartEvent(first);
}
}
if ((subPhaseMask & SUBPHASE_ANVIL))
{
uint32 first = GAME_EVENT_QUEL_DANAS_PHASE_3_NO_ANVIL;
uint32 second = GAME_EVENT_QUEL_DANAS_PHASE_3_ANVIL;
if (start)
{
sGameEventMgr->StopEvent(first);
sGameEventMgr->StartEvent(second);
}
else
{
sGameEventMgr->StopEvent(second);
sGameEventMgr->StartEvent(first);
}
}
if ((subPhaseMask & SUBPHASE_ALCHEMY_LAB))
{
uint32 first = GAME_EVENT_QUEL_DANAS_PHASE_4_NO_ALCHEMY_LAB;
uint32 second = GAME_EVENT_QUEL_DANAS_PHASE_4_ALCHEMY_LAB;
if (start)
{
sGameEventMgr->StopEvent(first);
sGameEventMgr->StartEvent(second);
}
else
{
sGameEventMgr->StopEvent(second);
sGameEventMgr->StartEvent(first);
}
}
if ((subPhaseMask & SUBPHASE_MONUMENT))
{
uint32 first = GAME_EVENT_QUEL_DANAS_PHASE_4_NO_MONUMENT;
uint32 second = GAME_EVENT_QUEL_DANAS_PHASE_4_MONUMENT;
if (start)
{
sGameEventMgr->StopEvent(first);
sGameEventMgr->StartEvent(second);
}
else
{
sGameEventMgr->StopEvent(second);
sGameEventMgr->StartEvent(first);
}
}
if (all)
{
if (start)
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_4_KIRU);
else
sGameEventMgr->StopEvent(GAME_EVENT_QUEL_DANAS_PHASE_4_KIRU);
if (!initial)
{
std::lock_guard<std::mutex> guard(m_sunsReachData.m_sunsReachReclamationMutex);
for (ObjectGuid& guid : m_sunsReachData.m_sunsReachReclamationPlayers)
if (Player* player = ObjectAccessor::FindPlayer(guid))
{
if (start)
player->CastSpell(player, SPELL_KIRU_SONG_OF_VICTORY, true);
else
player->RemoveAurasDueToSpell(SPELL_KIRU_SONG_OF_VICTORY);
}
}
}
if (!initial)
Save(SAVE_ID_QUEL_DANAS);
}
void WorldState::HandleSunwellGateTransition(uint32 newGate)
{
if (newGate < m_sunsReachData.m_gate)
{
while (newGate != m_sunsReachData.m_gate)
{
StopSunwellGatePhase();
--m_sunsReachData.m_gate;
}
StartSunwellGatePhase();
}
else
{
StopSunwellGatePhase();
m_sunsReachData.m_gate = newGate;
StartSunwellGatePhase();
}
int32 worldState = 0;
switch (newGate)
{
case SUNWELL_AGAMATH_GATE1_OPEN: worldState = WORLD_STATE_AGAMATH_THE_FIRST_GATE_HEALTH; break;
case SUNWELL_ROHENDOR_GATE2_OPEN: worldState = WORLD_STATE_ROHENDOR_THE_SECOND_GATE_HEALTH; break;
case SUNWELL_ARCHONISUS_GATE3_OPEN: worldState = WORLD_STATE_ARCHONISUS_THE_FINAL_GATE_HEALTH; break;
}
if (worldState)
SendWorldstateUpdate(m_sunsReachData.m_sunsReachReclamationMutex, m_sunsReachData.m_sunsReachReclamationPlayers, m_sunsReachData.m_gate, worldState);
Save(SAVE_ID_QUEL_DANAS);
}
void WorldState::SetSunsReachCounter(SunsReachCounters index, uint32 value)
{
m_sunsReachData.m_sunsReachReclamationCounters[index] = value;
}
void WorldState::StopSunsReachPhase(bool forward)
{
switch (m_sunsReachData.m_phase)
{
case SUNS_REACH_PHASE_1_STAGING_AREA: sGameEventMgr->StopEvent(GAME_EVENT_QUEL_DANAS_PHASE_1); break;
case SUNS_REACH_PHASE_2_SANCTUM: sGameEventMgr->StopEvent(GAME_EVENT_QUEL_DANAS_PHASE_2_ONLY); if (!forward) sGameEventMgr->StopEvent(GAME_EVENT_QUEL_DANAS_PHASE_2_PERMANENT); break;
case SUNS_REACH_PHASE_3_ARMORY: sGameEventMgr->StopEvent(GAME_EVENT_QUEL_DANAS_PHASE_3_ONLY); if (!forward) sGameEventMgr->StopEvent(GAME_EVENT_QUEL_DANAS_PHASE_3_PERMANENT); break;
case SUNS_REACH_PHASE_4_HARBOR: sGameEventMgr->StopEvent(GAME_EVENT_QUEL_DANAS_PHASE_4); break;
default: break;
}
}
void WorldState::StartSunsReachPhase(bool initial)
{
switch (m_sunsReachData.m_phase)
{
case SUNS_REACH_PHASE_1_STAGING_AREA:
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_1);
if (Map* map = sMapMgr->FindBaseNonInstanceMap(530))
map->SetZoneWeather(ZONEID_ISLE_OF_QUEL_DANAS, WEATHER_STATE_MEDIUM_RAIN, 0.75f);
break;
case SUNS_REACH_PHASE_2_SANCTUM:
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_2_ONLY);
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_2_PERMANENT);
if (Map* map = sMapMgr->FindBaseNonInstanceMap(530))
map->SetZoneWeather(ZONEID_ISLE_OF_QUEL_DANAS, WEATHER_STATE_LIGHT_RAIN, 0.5f);
break;
case SUNS_REACH_PHASE_3_ARMORY:
if (initial)
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_2_PERMANENT);
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_3_ONLY); sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_3_PERMANENT);
// TODO: Should be id 2 0.25f?
if (Map* map = sMapMgr->FindBaseNonInstanceMap(530))
map->SetZoneWeather(ZONEID_ISLE_OF_QUEL_DANAS, WEATHER_STATE_LIGHT_RAIN, 0.25f);
break;
case SUNS_REACH_PHASE_4_HARBOR:
if (initial)
{
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_2_PERMANENT);
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_3_PERMANENT);
}
sGameEventMgr->StartEvent(GAME_EVENT_QUEL_DANAS_PHASE_4);
if (Map* map = sMapMgr->FindBaseNonInstanceMap(530))
map->SetZoneWeather(ZONEID_ISLE_OF_QUEL_DANAS, WEATHER_STATE_FINE, 0.0f);
break;
default: break;
}
}
std::string WorldState::GetSunsReachPrintout()
{
auto formatPhase = [this]() -> std::string {
std::string name;
switch (m_sunsReachData.m_phase)
{
case SUNS_REACH_PHASE_1_STAGING_AREA: name = "Phase 1: Staging Area"; break;
case SUNS_REACH_PHASE_2_SANCTUM: name = "Phase 2: Sanctum"; break;
case SUNS_REACH_PHASE_3_ARMORY: name = "Phase 3: Armory"; break;
case SUNS_REACH_PHASE_4_HARBOR: name = "Phase 4: Harbor"; break;
default: name = "Unknown"; break;
}
return "Phase: " + std::to_string(m_sunsReachData.m_phase) + " (" + name + ") " + std::to_string(m_sunsReachData.GetPhasePercentage(m_sunsReachData.m_phase)) + "%\n";
};
auto formatSubPhase = [this](uint32 subPhase) -> std::string {
std::string name;
switch (subPhase)
{
case SUBPHASE_PORTAL: name = "Portal"; break;
case SUBPHASE_ANVIL: name = "Anvil"; break;
case SUBPHASE_ALCHEMY_LAB: name = "Alchemy Lab"; break;
case SUBPHASE_MONUMENT: name = "Monument"; break;
default: name = "Unknown"; break;
}
return name + ": " + (m_sunsReachData.m_subphaseMask & subPhase ? "100%" : std::to_string(m_sunsReachData.GetSubPhasePercentage(subPhase)) + "%");
};
auto formatCounter = [](uint32 counter, uint32 value) -> std::string {
switch (counter)
{
case COUNTER_ERRATIC_BEHAVIOR:
return "Erratic Behavior: " + std::to_string(value) + " (counts towards Phase 2: Sanctum)";
case COUNTER_SANCTUM_WARDS:
return "Sanctum Wards: " + std::to_string(value) + " (counts towards Phase 2: Sanctum)";
case COUNTER_BATTLE_FOR_THE_SUNS_REACH_ARMORY:
return "Battle for the Sun's Reach Armory: " + std::to_string(value) + " (counts towards Phase 3: Armory)";
case COUNTER_DISTRACTION_AT_THE_DEAD_SCAR:
return "Distraction at the Dead Scar: " + std::to_string(value) + " (counts towards Phase 3: Armory)";
case COUNTER_INTERCEPTING_THE_MANA_CELLS:
return "Intercepting the Mana Cells: " + std::to_string(value) + " (counts towards Subphase: Portal)";
case COUNTER_INTERCEPT_THE_REINFORCEMENTS:
return "Intercept the Reinforcements: " + std::to_string(value) + " (counts towards Phase 4: Harbor)";
case COUNTER_TAKING_THE_HARBOR:
return "Taking the Harbor: " + std::to_string(value) + " (counts towards Phase 4: Harbor)";
case COUNTER_MAKING_READY:
return "Making Ready: " + std::to_string(value) + " (counts towards Subphase: Anvil)";
case COUNTER_DISCOVERING_YOUR_ROOTS:
return "Discovering Your Roots: " + std::to_string(value) + " (counts towards Subphase: Alchemy Lab)";
case COUNTER_A_CHARITABLE_DONATION:
return "A Charitable Donation: " + std::to_string(value) + " (counts towards Subphase: Monument)";
default:
return "Unknown: " + std::to_string(value) + " (Unknown goal)";
}
};
std::string output = formatPhase();
output += "Subphase mask: " + std::to_string(m_sunsReachData.m_subphaseMask) + "\n";
for (uint32 i = 0; i < 4; ++i)
{
uint32 subPhaseMask = 1 << i;
output += " " + formatSubPhase(subPhaseMask) + "\n";
}
output += "Counters:\n";
output += " Sunsreach.CounterMax = " + std::to_string(sWorld->getIntConfig(CONFIG_SUNSREACH_COUNTER_MAX)) + "\n";
for (uint32 i = 0; i < COUNTERS_MAX; ++i)
output += " " + std::to_string(i) + ". " + formatCounter(i, m_sunsReachData.m_sunsReachReclamationCounters[i]) + "\n";
// Sunwell Gates
auto formatGatePhase = [](uint32 gate) -> std::string {
switch (gate)
{
case SUNWELL_ALL_GATES_CLOSED: return "All Gates Closed"; break;
case SUNWELL_AGAMATH_GATE1_OPEN: return "Gate 1 Agamath Open"; break;
case SUNWELL_ROHENDOR_GATE2_OPEN: return "Gate 2 Rohendar Open"; break;
case SUNWELL_ARCHONISUS_GATE3_OPEN: return "Gate 3 Archonisus Open"; break;
default: return "Unknown"; break;
}
};
output += "Sunwell Plateau Gate Phase " + std::to_string(m_sunsReachData.m_gate) + " (" + formatGatePhase(m_sunsReachData.m_gate) + ")" + ":\n";
output += " 0. Gate 1 (Agamath): " + std::string(m_sunsReachData.m_gate >= SUNWELL_AGAMATH_GATE1_OPEN ? "Open " : "Closed ") + std::to_string(m_sunsReachData.m_sunsReachReclamationCounters[COUNTER_AGAMATH_THE_FIRST_GATE]) + " (" + std::to_string(m_sunsReachData.GetSunwellGatePercentage(SUNWELL_ALL_GATES_CLOSED)) + "%)\n";
output += " 1. Gate 2 (Rohendor): " + std::string(m_sunsReachData.m_gate >= SUNWELL_ROHENDOR_GATE2_OPEN ? "Open " : "Closed ") + std::to_string(m_sunsReachData.m_sunsReachReclamationCounters[COUNTER_ROHENDOR_THE_SECOND_GATE]) + " (" + std::to_string(m_sunsReachData.GetSunwellGatePercentage(SUNWELL_AGAMATH_GATE1_OPEN)) + "%)\n";
output += " 2. Gate 3 (Archonisus): " + std::string(m_sunsReachData.m_gate >= SUNWELL_ARCHONISUS_GATE3_OPEN ? "Open " : "Closed ") + std::to_string(m_sunsReachData.m_sunsReachReclamationCounters[COUNTER_ARCHONISUS_THE_FINAL_GATE]) + " (" + std::to_string(m_sunsReachData.GetSunwellGatePercentage(SUNWELL_ROHENDOR_GATE2_OPEN)) + "%)\n";
return output;
}
std::string SunsReachReclamationData::GetData()
{
std::string output = std::to_string(m_phase) + " " + std::to_string(m_subphaseMask);
for (uint32 value : m_sunsReachReclamationCounters)
output += " " + std::to_string(value);
output += " " + std::to_string(m_gate);
for (uint32 value : m_gateCounters)
output += " " + std::to_string(value);
return output;
}
uint32 SunsReachReclamationData::GetPhasePercentage(uint32 phase)
{
switch (phase)
{
case SUNS_REACH_PHASE_1_STAGING_AREA: return uint32((m_sunsReachReclamationCounters[COUNTER_ERRATIC_BEHAVIOR] + m_sunsReachReclamationCounters[COUNTER_SANCTUM_WARDS]) * 100 / (2 * sWorld->getIntConfig(CONFIG_SUNSREACH_COUNTER_MAX)));
case SUNS_REACH_PHASE_2_SANCTUM: return uint32((m_sunsReachReclamationCounters[COUNTER_BATTLE_FOR_THE_SUNS_REACH_ARMORY] + m_sunsReachReclamationCounters[COUNTER_DISTRACTION_AT_THE_DEAD_SCAR]) * 100 / (2 * sWorld->getIntConfig(CONFIG_SUNSREACH_COUNTER_MAX)));
case SUNS_REACH_PHASE_3_ARMORY: return uint32((m_sunsReachReclamationCounters[COUNTER_INTERCEPT_THE_REINFORCEMENTS] + m_sunsReachReclamationCounters[COUNTER_TAKING_THE_HARBOR]) * 100 / (2 * sWorld->getIntConfig(CONFIG_SUNSREACH_COUNTER_MAX)));
default: return 0;
}
}
uint32 SunsReachReclamationData::GetSubPhasePercentage(uint32 subPhase)
{
switch (subPhase)
{
case SUBPHASE_PORTAL: return uint32(m_sunsReachReclamationCounters[COUNTER_INTERCEPTING_THE_MANA_CELLS] * 100 / sWorld->getIntConfig(CONFIG_SUNSREACH_COUNTER_MAX));
case SUBPHASE_ANVIL: return uint32(m_sunsReachReclamationCounters[COUNTER_MAKING_READY] * 100 / sWorld->getIntConfig(CONFIG_SUNSREACH_COUNTER_MAX));
case SUBPHASE_ALCHEMY_LAB: return uint32(m_sunsReachReclamationCounters[COUNTER_DISCOVERING_YOUR_ROOTS] * 100 / sWorld->getIntConfig(CONFIG_SUNSREACH_COUNTER_MAX));
case SUBPHASE_MONUMENT: return uint32(m_sunsReachReclamationCounters[COUNTER_A_CHARITABLE_DONATION] * 100 / sWorld->getIntConfig(CONFIG_SUNSREACH_COUNTER_MAX));
default: return 0;
}
}
enum WorldStateSunwellGateQuests
{
// Sunwell Plateau PTR progressive release gates
QUEST_AGAMATH_THE_FIRST_GATE = 11551,
QUEST_ROHENDOR_THE_SECOND_GATE = 11552,
QUEST_ARCHONISUS_THE_FINAL_GATE = 11553,
COUNTER_MAX_VAL_REQ_SWP_GATES = 80,
};
void WorldState::AddSunwellGateProgress(uint32 questId)
{
uint32 counter = 0;
int32 worldState = 0;
uint32 addedValue = 1;
switch (questId)
{
case QUEST_AGAMATH_THE_FIRST_GATE:
case QUEST_ROHENDOR_THE_SECOND_GATE:
case QUEST_ARCHONISUS_THE_FINAL_GATE:
break;
default: return;
}
switch (m_sunsReachData.m_gate)
{
case SUNWELL_ALL_GATES_CLOSED: counter = COUNTER_AGAMATH_THE_FIRST_GATE; worldState = WORLD_STATE_AGAMATH_THE_FIRST_GATE_HEALTH; break;
case SUNWELL_AGAMATH_GATE1_OPEN: counter = COUNTER_ROHENDOR_THE_SECOND_GATE; worldState = WORLD_STATE_ROHENDOR_THE_SECOND_GATE_HEALTH; break;
case SUNWELL_ROHENDOR_GATE2_OPEN: counter = COUNTER_ARCHONISUS_THE_FINAL_GATE; worldState = WORLD_STATE_ARCHONISUS_THE_FINAL_GATE_HEALTH; break;
default: return;
}
uint32 previousValue = m_sunsReachData.GetSunwellGatePercentage(m_sunsReachData.m_gate);
uint32 newValue = 0;
m_sunsReachData.m_gateCounters[counter] += addedValue;
newValue = m_sunsReachData.GetSunwellGatePercentage(m_sunsReachData.m_gate);
if (previousValue != newValue)
SendWorldstateUpdate(m_sunsReachData.m_sunsReachReclamationMutex, m_sunsReachData.m_sunsReachReclamationPlayers, newValue, worldState);
bool save = true;
if (m_sunsReachData.m_gateCounters[counter] >= COUNTER_MAX_VAL_REQ_SWP_GATES)
{
save = false;
switch (questId)
{
case QUEST_AGAMATH_THE_FIRST_GATE:
{
if (m_sunsReachData.m_gate == SUNWELL_ALL_GATES_CLOSED)
HandleSunwellGateTransition(SUNWELL_AGAMATH_GATE1_OPEN);
break;
}
case QUEST_ROHENDOR_THE_SECOND_GATE:
{
if (m_sunsReachData.m_gate == SUNWELL_AGAMATH_GATE1_OPEN)
HandleSunwellGateTransition(SUNWELL_ROHENDOR_GATE2_OPEN);
break;
}
case QUEST_ARCHONISUS_THE_FINAL_GATE:
{
if (m_sunsReachData.m_gate == SUNWELL_ROHENDOR_GATE2_OPEN)
HandleSunwellGateTransition(SUNWELL_ARCHONISUS_GATE3_OPEN);
break;
}
}
}
if (save)
Save(SAVE_ID_QUEL_DANAS);
}
void WorldState::SetSunwellGateCounter(SunwellGateCounters index, uint32 value)
{
m_sunsReachData.m_gateCounters[index] = value;
}
void WorldState::StartSunwellGatePhase()
{
switch (m_sunsReachData.m_gate)
{
case SUNWELL_ALL_GATES_CLOSED: sGameEventMgr->StartEvent(GAME_EVENT_SWP_GATES_PHASE_0); break;
case SUNWELL_AGAMATH_GATE1_OPEN: sGameEventMgr->StartEvent(GAME_EVENT_SWP_GATES_PHASE_1); break;
case SUNWELL_ROHENDOR_GATE2_OPEN: sGameEventMgr->StartEvent(GAME_EVENT_SWP_GATES_PHASE_2); break;
case SUNWELL_ARCHONISUS_GATE3_OPEN: sGameEventMgr->StartEvent(GAME_EVENT_SWP_GATES_PHASE_3); break;
default: break;
}
}
void WorldState::StopSunwellGatePhase()
{
switch (m_sunsReachData.m_gate)
{
case SUNWELL_ALL_GATES_CLOSED: sGameEventMgr->StopEvent(GAME_EVENT_SWP_GATES_PHASE_0); break;
case SUNWELL_AGAMATH_GATE1_OPEN: sGameEventMgr->StopEvent(GAME_EVENT_SWP_GATES_PHASE_1); break;
case SUNWELL_ROHENDOR_GATE2_OPEN: sGameEventMgr->StopEvent(GAME_EVENT_SWP_GATES_PHASE_2); break;
case SUNWELL_ARCHONISUS_GATE3_OPEN: sGameEventMgr->StopEvent(GAME_EVENT_SWP_GATES_PHASE_3); break;
default: break;
}
}
uint32 SunsReachReclamationData::GetSunwellGatePercentage(uint32 gate)
{
int32 percentage = 0;
switch (gate)
{
case SUNWELL_ALL_GATES_CLOSED:
percentage = 100 - int32(m_gateCounters[COUNTER_AGAMATH_THE_FIRST_GATE] * 100 / COUNTER_MAX_VAL_REQ_SWP_GATES);
break;
case SUNWELL_AGAMATH_GATE1_OPEN:
percentage = 100 - int32(m_gateCounters[COUNTER_ROHENDOR_THE_SECOND_GATE] * 100 / COUNTER_MAX_VAL_REQ_SWP_GATES);
break;
case SUNWELL_ROHENDOR_GATE2_OPEN:
percentage = 100 - int32(m_gateCounters[COUNTER_ARCHONISUS_THE_FINAL_GATE] * 100 / COUNTER_MAX_VAL_REQ_SWP_GATES);
break;
default:
return 0;
}
return percentage < 0 ? 0 : uint32(percentage);
}
void WorldState::FillInitialWorldStates(ByteBuffer& data, uint32 zoneId, uint32 /*areaId*/)
{
switch (zoneId)
{
case ZONEID_ISLE_OF_QUEL_DANAS:
case ZONEID_MAGISTERS_TERRACE:
case ZONEID_SUNWELL_PLATEAU:
case ZONEID_SHATTRATH:
{
// Sunwell Reclamation
switch (m_sunsReachData.m_phase)
{
case SUNS_REACH_PHASE_1_STAGING_AREA:
data << WORLD_STATE_QUEL_DANAS_SANCTUM << m_sunsReachData.GetPhasePercentage(m_sunsReachData.m_phase);
break;
case SUNS_REACH_PHASE_2_SANCTUM:
data << WORLD_STATE_QUEL_DANAS_ARMORY << m_sunsReachData.GetPhasePercentage(m_sunsReachData.m_phase);
break;
case SUNS_REACH_PHASE_3_ARMORY:
data << WORLD_STATE_QUEL_DANAS_HARBOR << m_sunsReachData.GetPhasePercentage(m_sunsReachData.m_phase);
break;
case SUNS_REACH_PHASE_4_HARBOR:
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_ALCHEMY_LAB) == 0)
data << WORLD_STATE_QUEL_DANAS_ALCHEMY_LAB << m_sunsReachData.GetSubPhasePercentage(SUBPHASE_ALCHEMY_LAB);
if ((m_sunsReachData.m_subphaseMask & SUBPHASE_MONUMENT) == 0)
data << WORLD_STATE_QUEL_DANAS_MONUMENT << m_sunsReachData.GetSubPhasePercentage(SUBPHASE_MONUMENT);
break;
}
if (m_sunsReachData.m_phase >= SUNS_REACH_PHASE_2_SANCTUM && (m_sunsReachData.m_subphaseMask & SUBPHASE_PORTAL) == 0)
data << WORLD_STATE_QUEL_DANAS_PORTAL << m_sunsReachData.GetSubPhasePercentage(SUBPHASE_PORTAL);
if (m_sunsReachData.m_phase >= SUNS_REACH_PHASE_3_ARMORY && (m_sunsReachData.m_subphaseMask & SUBPHASE_ANVIL) == 0)
data << WORLD_STATE_QUEL_DANAS_ANVIL << m_sunsReachData.GetSubPhasePercentage(SUBPHASE_ANVIL);
data << WORLD_STATE_QUEL_DANAS_MUSIC << m_sunsReachData.m_phase;
// Sunwell Gates
switch (m_sunsReachData.m_gate)
{
case SUNWELL_ALL_GATES_CLOSED:
data << WORLD_STATE_AGAMATH_THE_FIRST_GATE_HEALTH << m_sunsReachData.GetSunwellGatePercentage(m_sunsReachData.m_gate);
break;
case SUNWELL_AGAMATH_GATE1_OPEN:
data << WORLD_STATE_ROHENDOR_THE_SECOND_GATE_HEALTH << m_sunsReachData.GetSunwellGatePercentage(m_sunsReachData.m_gate);
break;
case SUNWELL_ROHENDOR_GATE2_OPEN:
data << WORLD_STATE_ARCHONISUS_THE_FINAL_GATE_HEALTH << m_sunsReachData.GetSunwellGatePercentage(m_sunsReachData.m_gate);
break;
}
break;
}
}
}
+139
View File
@@ -21,6 +21,24 @@
#include "Player.h"
#include <atomic>
// TODO: Move these to WorldStateDefines.h
enum WorldStateWorldStates
{
// Sun's Reach Reclamation
WORLD_STATE_QUEL_DANAS_MUSIC = 3426,
WORLD_STATE_QUEL_DANAS_HARBOR = 3238,
WORLD_STATE_QUEL_DANAS_ALCHEMY_LAB = 3223,
WORLD_STATE_QUEL_DANAS_ARMORY = 3233,
WORLD_STATE_QUEL_DANAS_SANCTUM = 3244,
WORLD_STATE_QUEL_DANAS_PORTAL = 3269,
WORLD_STATE_QUEL_DANAS_ANVIL = 3228,
WORLD_STATE_QUEL_DANAS_MONUMENT = 3275,
// Sunwell Gate
WORLD_STATE_AGAMATH_THE_FIRST_GATE_HEALTH = 3253, // guessed, potentially wrong
WORLD_STATE_ROHENDOR_THE_SECOND_GATE_HEALTH = 3255,
WORLD_STATE_ARCHONISUS_THE_FINAL_GATE_HEALTH = 3257,
};
enum WorldStateCondition
{
WORLD_STATE_CONDITION_TROLLBANES_COMMAND = 39911,
@@ -56,6 +74,10 @@ enum WorldStateZoneId
ZONEID_BLOOD_FURNACE = 3713,
ZONEID_SHATTERED_HALLS = 3714,
ZONEID_MAGTHERIDON_LAIR = 3836,
ZONEID_ISLE_OF_QUEL_DANAS = 4080,
ZONEID_MAGISTERS_TERRACE = 4131,
ZONEID_SUNWELL_PLATEAU = 4075,
};
enum WorldStateSpells
@@ -64,6 +86,105 @@ enum WorldStateSpells
SPELL_TROLLBANES_COMMAND = 39911,
SPELL_NAZGRELS_FAVOR = 39913,
SPELL_KIRU_SONG_OF_VICTORY = 46302,
};
enum WorldStateSaveIds
{
SAVE_ID_QUEL_DANAS = 20,
};
enum WorldStateGameEvents
{
// Isle phases
GAME_EVENT_QUEL_DANAS_PHASE_1 = 101,
GAME_EVENT_QUEL_DANAS_PHASE_2_ONLY = 102,
GAME_EVENT_QUEL_DANAS_PHASE_2_PERMANENT = 103,
GAME_EVENT_QUEL_DANAS_PHASE_2_NO_PORTAL = 104,
GAME_EVENT_QUEL_DANAS_PHASE_2_PORTAL = 105,
GAME_EVENT_QUEL_DANAS_PHASE_3_ONLY = 106,
GAME_EVENT_QUEL_DANAS_PHASE_3_PERMANENT = 107,
GAME_EVENT_QUEL_DANAS_PHASE_3_NO_ANVIL = 108,
GAME_EVENT_QUEL_DANAS_PHASE_3_ANVIL = 109,
GAME_EVENT_QUEL_DANAS_PHASE_4 = 110,
GAME_EVENT_QUEL_DANAS_PHASE_4_NO_MONUMENT = 111,
GAME_EVENT_QUEL_DANAS_PHASE_4_MONUMENT = 112,
GAME_EVENT_QUEL_DANAS_PHASE_4_NO_ALCHEMY_LAB= 113,
GAME_EVENT_QUEL_DANAS_PHASE_4_ALCHEMY_LAB = 114,
GAME_EVENT_QUEL_DANAS_PHASE_4_KIRU = 115,
// SWP Phases
GAME_EVENT_SWP_GATES_PHASE_0 = 116, // All Gates Closed
GAME_EVENT_SWP_GATES_PHASE_1 = 117, // First Gate Open
GAME_EVENT_SWP_GATES_PHASE_2 = 118, // Second Gate Open
GAME_EVENT_SWP_GATES_PHASE_3 = 119, // All Gates Open
};
enum SunsReachPhases
{
SUNS_REACH_PHASE_1_STAGING_AREA,
SUNS_REACH_PHASE_2_SANCTUM,
SUNS_REACH_PHASE_3_ARMORY,
SUNS_REACH_PHASE_4_HARBOR,
};
enum SunsReachSubPhases
{
SUBPHASE_PORTAL = 0x01,
SUBPHASE_ANVIL = 0x02,
SUBPHASE_ALCHEMY_LAB = 0x04,
SUBPHASE_MONUMENT = 0x08,
SUBPHASE_ALL = SUBPHASE_PORTAL | SUBPHASE_ANVIL | SUBPHASE_ALCHEMY_LAB | SUBPHASE_MONUMENT,
};
enum SunsReachCounters
{
COUNTER_ERRATIC_BEHAVIOR,
COUNTER_SANCTUM_WARDS,
COUNTER_BATTLE_FOR_THE_SUNS_REACH_ARMORY,
COUNTER_DISTRACTION_AT_THE_DEAD_SCAR,
COUNTER_INTERCEPTING_THE_MANA_CELLS,
COUNTER_INTERCEPT_THE_REINFORCEMENTS,
COUNTER_TAKING_THE_HARBOR,
COUNTER_MAKING_READY,
COUNTER_DISCOVERING_YOUR_ROOTS,
COUNTER_A_CHARITABLE_DONATION,
COUNTERS_MAX,
};
enum SunwellGates
{
SUNWELL_ALL_GATES_CLOSED,
SUNWELL_AGAMATH_GATE1_OPEN,
SUNWELL_ROHENDOR_GATE2_OPEN,
SUNWELL_ARCHONISUS_GATE3_OPEN,
};
enum SunwellGateCounters
{
COUNTER_AGAMATH_THE_FIRST_GATE,
COUNTER_ROHENDOR_THE_SECOND_GATE,
COUNTER_ARCHONISUS_THE_FINAL_GATE,
COUNTERS_MAX_GATES,
};
struct SunsReachReclamationData
{
uint32 m_phase;
uint32 m_subphaseMask;
uint32 m_sunsReachReclamationCounters[COUNTERS_MAX];
GuidVector m_sunsReachReclamationPlayers;
std::mutex m_sunsReachReclamationMutex;
uint32 m_gate;
uint32 m_gateCounters[COUNTERS_MAX_GATES];
SunsReachReclamationData() : m_phase(SUNS_REACH_PHASE_1_STAGING_AREA), m_subphaseMask(0), m_gate(SUNWELL_ALL_GATES_CLOSED)
{
memset(m_sunsReachReclamationCounters, 0, sizeof(m_sunsReachReclamationCounters));
memset(m_gateCounters, 0, sizeof(m_gateCounters));
}
std::string GetData();
uint32 GetPhasePercentage(uint32 phase);
uint32 GetSubPhasePercentage(uint32 subPhase);
uint32 GetSunwellGatePercentage(uint32 gate);
};
// Intended for implementing server wide scripts, note: all behaviour must be safeguarded towards multithreading
@@ -73,13 +194,30 @@ class WorldState
WorldState();
virtual ~WorldState();
static WorldState* instance();
void Load();
void Save(WorldStateSaveIds saveId);
void SaveHelper(std::string& stringToSave, WorldStateSaveIds saveId);
void HandlePlayerEnterZone(Player* player, WorldStateZoneId zoneId);
void HandlePlayerLeaveZone(Player* player, WorldStateZoneId zoneId);
bool IsConditionFulfilled(WorldStateCondition conditionId, WorldStateConditionState state = WORLD_STATE_CONDITION_STATE_NONE) const;
void HandleConditionStateChange(WorldStateCondition conditionId, WorldStateConditionState state);
void HandleExternalEvent(WorldStateEvent eventId, uint32 param);
void Update(uint32 diff);
void AddSunwellGateProgress(uint32 questId);
void AddSunsReachProgress(uint32 questId);
std::string GetSunsReachPrintout();
void FillInitialWorldStates(ByteBuffer& data, uint32 zoneId, uint32 areaId);
void HandleSunsReachPhaseTransition(uint32 newPhase);
void HandleSunsReachSubPhaseTransition(int32 subPhaseMask, bool initial = false);
void SetSunsReachCounter(SunsReachCounters index, uint32 value);
void HandleSunwellGateTransition(uint32 newGate);
void SetSunwellGateCounter(SunwellGateCounters index, uint32 value);
private:
void SendWorldstateUpdate(std::mutex& mutex, GuidVector const& guids, uint32 value, uint32 worldStateId);
void StopSunsReachPhase(bool forward);
void StartSunsReachPhase(bool initial = false);
void StartSunwellGatePhase();
void StopSunwellGatePhase();
void BuffAdalsSongOfBattle();
void DispelAdalsSongOfBattle();
uint32 _adalSongOfBattleTimer;
@@ -87,6 +225,7 @@ class WorldState
void DispelMagtheridonTeam(TeamId team);
bool _isMagtheridonHeadSpawnedHorde;
bool _isMagtheridonHeadSpawnedAlliance;
SunsReachReclamationData m_sunsReachData;
std::map<WorldStateCondition, std::atomic<WorldStateConditionState>> _transportStates; // atomic to avoid having to lock
std::mutex _mutex; // all World State operations are threat unsafe
};
+2 -2
View File
@@ -292,7 +292,7 @@ public:
Player const* target = player->GetConnectedPlayer();
LocaleConstant loc = handler->GetSessionDbcLocale();
char const* knownStr = handler->GetAcoreString(LANG_KNOWN);
std::string knownStr = handler->GetAcoreString(LANG_KNOWN);
// Search in CharTitles.dbc
for (uint32 id = 0; id < sCharTitlesStore.GetNumRows(); id++)
@@ -308,7 +308,7 @@ public:
if (!*name)
continue;
char const* activeStr = "";
std::string activeStr = "";
if (target->GetUInt32Value(PLAYER_CHOSEN_TITLE) == titleInfo->bit_index)
activeStr = handler->GetAcoreString(LANG_ACTIVE);
+2 -8
View File
@@ -61,28 +61,22 @@ public:
GameEventMgr::GameEventDataMap const& events = sGameEventMgr->GetEventMap();
GameEventMgr::ActiveEvents const& activeEvents = sGameEventMgr->GetActiveEventList();
char const* active = handler->GetAcoreString(LANG_ACTIVE);
std::string active = handler->GetAcoreString(LANG_ACTIVE);
for (uint16 eventId : activeEvents)
{
GameEventData const& eventData = events[eventId];
if (handler->GetSession())
{
handler->PSendSysMessage(LANG_EVENT_ENTRY_LIST_CHAT, eventId, eventId, eventData.description, active);
}
else
{
handler->PSendSysMessage(LANG_EVENT_ENTRY_LIST_CONSOLE, eventId, eventData.description, active);
}
++counter;
}
if (counter == 0)
{
handler->SendSysMessage(LANG_NOEVENTFOUND);
}
handler->SetSentErrorMessage(true);
@@ -108,7 +102,7 @@ public:
GameEventMgr::ActiveEvents const& activeEvents = sGameEventMgr->GetActiveEventList();
bool active = activeEvents.find(eventId) != activeEvents.end();
char const* activeStr = active ? handler->GetAcoreString(LANG_ACTIVE) : "";
std::string activeStr = active ? handler->GetAcoreString(LANG_ACTIVE) : "";
std::string startTimeStr = Acore::Time::TimeToTimestampStr(Seconds(eventData.start));
std::string endTimeStr = Acore::Time::TimeToTimestampStr(Seconds(eventData.end));
+2 -2
View File
@@ -460,8 +460,8 @@ public:
wstrToLower(namePart);
char const* talentStr = handler->GetAcoreString(LANG_TALENT);
char const* passiveStr = handler->GetAcoreString(LANG_PASSIVE);
std::string talentStr = handler->GetAcoreString(LANG_TALENT);
std::string passiveStr = handler->GetAcoreString(LANG_PASSIVE);
Unit::AuraApplicationMap const& auras = unit->GetAppliedAuras();
handler->PSendSysMessage(LANG_COMMAND_TARGET_LISTAURAS, auras.size());
+5 -9
View File
@@ -308,7 +308,7 @@ public:
return true;
}
char const* active = activeEvents.find(id) != activeEvents.end() ? handler->GetAcoreString(LANG_ACTIVE) : "";
std::string active = activeEvents.find(id) != activeEvents.end() ? handler->GetAcoreString(LANG_ACTIVE) : "";
if (handler->GetSession())
{
@@ -806,7 +806,7 @@ public:
return true;
}
char const* statusStr = "";
std::string statusStr = "";
if (target)
{
@@ -862,7 +862,7 @@ public:
return true;
}
char const* statusStr = "";
std::string statusStr = "";
if (target)
{
@@ -1498,20 +1498,16 @@ public:
return true;
}
char const* knownStr = target && target->HasTitle(titleInfo) ? handler->GetAcoreString(LANG_KNOWN) : "";
char const* activeStr = target && target->GetUInt32Value(PLAYER_CHOSEN_TITLE) == titleInfo->bit_index ? handler->GetAcoreString(LANG_ACTIVE) : "";
std::string knownStr = target && target->HasTitle(titleInfo) ? handler->GetAcoreString(LANG_KNOWN) : "";
std::string activeStr = target && target->GetUInt32Value(PLAYER_CHOSEN_TITLE) == titleInfo->bit_index ? handler->GetAcoreString(LANG_ACTIVE) : "";
std::string titleNameStr = Acore::StringFormat(name, targetName);
// send title in "id (idx:idx) - [namedlink locale]" format
if (handler->GetSession())
{
handler->PSendSysMessage(LANG_TITLE_LIST_CHAT, titleInfo->ID, titleInfo->bit_index, titleInfo->ID, titleNameStr, localeNames[locale], knownStr, activeStr);
}
else
{
handler->PSendSysMessage(LANG_TITLE_LIST_CONSOLE, titleInfo->ID, titleInfo->bit_index, titleNameStr, localeNames[locale], knownStr, activeStr);
}
++counter;
}
+7 -22
View File
@@ -1992,8 +1992,8 @@ public:
uint32 mapId;
uint32 areaId;
uint32 phase = 0;
char const* areaName = nullptr;
char const* zoneName = nullptr;
std::string areaName = "";
std::string zoneName = "";
// Guild data print variables defined so that they exist, but are not necessarily used
uint32 guildId = 0;
@@ -2325,19 +2325,13 @@ public:
}
}
if (!zoneName)
{
if (zoneName.empty())
zoneName = handler->GetAcoreString(LANG_UNKNOWN);
}
if (areaName)
{
if (!areaName.empty())
handler->PSendSysMessage(LANG_PINFO_CHR_MAP_WITH_AREA, map->name[locale], zoneName, areaName);
}
else
{
handler->PSendSysMessage(LANG_PINFO_CHR_MAP, map->name[locale], zoneName);
}
// Output XVII. - XVIX. if they are not empty
if (!guildName.empty())
@@ -3022,18 +3016,9 @@ public:
return false;
}
const char* str = sObjectMgr->GetAcoreString(id, locale ? static_cast<LocaleConstant>(*locale) : DEFAULT_LOCALE);
if (!strcmp(str, "<error>"))
{
handler->PSendSysMessage(LANG_NO_ACORE_STRING_FOUND, id);
return true;
}
else
{
handler->SendSysMessage(str);
return true;
}
std::string str = sObjectMgr->GetAcoreString(id, locale ? static_cast<LocaleConstant>(*locale) : DEFAULT_LOCALE);
handler->SendSysMessage(str);
return true;
}
static bool HandleOpenDoorCommand(ChatHandler* handler, Optional<float> range)
+1 -2
View File
@@ -217,8 +217,7 @@ public:
static bool HandleMmapStatsCommand(ChatHandler* handler)
{
handler->PSendSysMessage("mmap stats:");
//handler->PSendSysMessage(" global mmap pathfinding is {}abled", DisableMgr::IsPathfindingEnabled(mapId) ? "en" : "dis");
//handler->PSendSysMessage(" global mmap pathfinding is {}abled", sDisableMgr->IsPathfindingEnabled(mapId) ? "en" : "dis");
MMAP::MMapMgr* manager = MMAP::MMapFactory::createOrGetMMapMgr();
handler->PSendSysMessage(" {} maps loaded with {} tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount());
+2 -2
View File
@@ -1063,9 +1063,9 @@ public:
static bool HandleReloadDisablesCommand(ChatHandler* handler)
{
LOG_INFO("server.loading", "Reloading disables table...");
DisableMgr::LoadDisables();
sDisableMgr->LoadDisables();
LOG_INFO("server.loading", "Checking quest disables...");
DisableMgr::CheckQuestDisables();
sDisableMgr->CheckQuestDisables();
handler->SendGlobalGMSysMessage("DB table `disables` reloaded.");
return true;
}
@@ -62,6 +62,7 @@ void AddSC_wp_commandscript();
void AddSC_cache_commandscript();
void AddSC_item_commandscript();
void AddSC_player_settings_commandscript();
void AddSC_worldstate_commandscript();
// The name of this function should match:
// void Add${NameOfDirectory}Scripts()
@@ -113,4 +114,5 @@ void AddCommandsScripts()
AddSC_cache_commandscript();
AddSC_item_commandscript();
AddSC_player_settings_commandscript();
AddSC_worldstate_commandscript();
}
@@ -0,0 +1,133 @@
/*
* 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/>.
*/
/* ScriptData
Name: worldstate_commandscript
%Complete: 100
Comment: All worldstate related commands
Category: commandscripts
EndScriptData */
#include "Chat.h"
#include "CommandScript.h"
#include "Common.h"
#include "WorldState.h"
using namespace Acore::ChatCommands;
class worldstate_commandscript : public CommandScript
{
public:
worldstate_commandscript() : CommandScript("worldstate_commandscript") { }
ChatCommandTable GetCommands() const override
{
static ChatCommandTable sunsreachCommandTable =
{
{ "status", HandleSunsReachReclamationStatusCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "phase", HandleSunsReachReclamationPhaseCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "subphase", HandleSunsReachReclamationSubPhaseCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "counter", HandleSunsReachReclamationCounterCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "gate", HandleSunwellGateCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "gatecounter", HandleSunwellGateCounterCommand, SEC_ADMINISTRATOR, Console::Yes },
};
static ChatCommandTable worldStateCommandTable =
{
{ "sunsreach", sunsreachCommandTable }
};
static ChatCommandTable commandTable =
{
{ "worldstate", worldStateCommandTable }
};
return commandTable;
}
static bool HandleSunsReachReclamationStatusCommand(ChatHandler* handler )
{
handler->PSendSysMessage(sWorldState->GetSunsReachPrintout());
return true;
}
static bool HandleSunsReachReclamationPhaseCommand(ChatHandler* handler, uint32 phase)
{
if (phase > SUNS_REACH_PHASE_4_HARBOR)
{
handler->PSendSysMessage("Invalid phase, see \".worldstate sunsreach phase\" for usage");
return false;
}
sWorldState->HandleSunsReachPhaseTransition(phase);
handler->PSendSysMessage(sWorldState->GetSunsReachPrintout());
return true;
}
static bool HandleSunsReachReclamationSubPhaseCommand(ChatHandler* handler, uint32 subphase)
{
if (subphase > SUBPHASE_ALL)
{
handler->PSendSysMessage("Invalid subphase, see \".worldstate sunsreach subphase\" for usage");
return false;
}
sWorldState->HandleSunsReachSubPhaseTransition(subphase);;
handler->PSendSysMessage(sWorldState->GetSunsReachPrintout());
return true;
}
static bool HandleSunsReachReclamationCounterCommand(ChatHandler* handler, Optional<uint32> index, Optional<uint32> value)
{
if (!index || !value || index.value() >= COUNTERS_MAX)
{
handler->PSendSysMessage("Syntax: .worldstate sunsreach counter <index> <value>.");
handler->PSendSysMessage(sWorldState->GetSunsReachPrintout());
return true;
}
sWorldState->SetSunsReachCounter(SunsReachCounters(index.value()), value.value());
handler->PSendSysMessage(sWorldState->GetSunsReachPrintout());
return true;
}
static bool HandleSunwellGateCommand(ChatHandler* handler, uint32 newGate)
{
if (newGate > SUNWELL_ARCHONISUS_GATE3_OPEN)
{
handler->PSendSysMessage("Invalid phase, see \".worldstate sunsreach gate\" for usage");
return false;
}
sWorldState->HandleSunwellGateTransition(newGate);
handler->PSendSysMessage(sWorldState->GetSunsReachPrintout());
return true;
}
static bool HandleSunwellGateCounterCommand(ChatHandler* handler, Optional<uint32> index, Optional<uint32> value)
{
if (!index || !value || index.value() >= COUNTERS_MAX_GATES)
{
handler->PSendSysMessage("Syntax: .worldstate sunsreach gatecounter <index> <value>.");
handler->PSendSysMessage(sWorldState->GetSunsReachPrintout());
return true;
}
sWorldState->SetSunwellGateCounter(SunwellGateCounters(index.value()), value.value());
handler->PSendSysMessage(sWorldState->GetSunsReachPrintout());
return true;
}
};
void AddSC_worldstate_commandscript()
{
new worldstate_commandscript();
}
@@ -21,102 +21,120 @@
enum Yells
{
SAY_AGGRO = 0,
SAY_ENERGY = 1,
SAY_OVERLOAD = 2,
SAY_KILL = 3,
EMOTE_DISCHARGE_ENERGY = 4
SAY_AGGRO = 0, // Combat start
SAY_ENERGY = 1, // Pure energy spawn
SAY_OVERLOAD = 2, // Final phase
SAY_KILL = 3, // Player kill
EMOTE_DISCHARGE_ENERGY = 4, // Energy discharge warning
EMOTE_OVERLOAD = 5 // Overload emote
};
enum Spells
{
// Pure energy spell info
SPELL_ENERGY_FEEDBACK_CHANNEL = 44328,
SPELL_ENERGY_FEEDBACK = 44335,
// Vexallus spell info
SPELL_CHAIN_LIGHTNING = 44318,
SPELL_OVERLOAD = 44352,
SPELL_ARCANE_SHOCK = 44319,
SPELL_SUMMON_PURE_ENERGY_N = 44322,
SPELL_SUMMON_PURE_ENERGY_H1 = 46154,
SPELL_SUMMON_PURE_ENERGY_H2 = 46159
};
enum Misc
{
NPC_PURE_ENERGY = 24745
SPELL_ENERGY_FEEDBACK_CHANNEL = 44328, // Pure energy channel
SPELL_ENERGY_FEEDBACK = 44335, // Pure energy death effect
SPELL_CHAIN_LIGHTNING = 44318, // Basic attack
SPELL_OVERLOAD = 44352, // Enrage ability
SPELL_ARCANE_SHOCK = 44319, // Basic attack
SPELL_SUMMON_PURE_ENERGY_N = 44322, // Normal mode summon
SPELL_SUMMON_PURE_ENERGY_H1 = 46154, // Heroic mode summon 1
SPELL_SUMMON_PURE_ENERGY_H2 = 46159 // Heroic mode summon 2
};
struct boss_vexallus : public BossAI
{
boss_vexallus(Creature* creature) : BossAI(creature, DATA_VEXALLUS), _energyCooldown(false) { }
boss_vexallus(Creature* creature) : BossAI(creature, DATA_VEXALLUS), _energyCooldown(false), _energyCount(0) { }
void Reset() override
{
_Reset();
_energyCooldown = false;
_energyCount = 0;
ScheduleHealthCheckEvent({ 85, 70, 55, 40, 25 }, [&]
{
if (!_energyCooldown)
scheduler.Schedule(1s, [this](TaskContext context)
{
Talk(SAY_ENERGY);
Talk(EMOTE_DISCHARGE_ENERGY);
if (IsHeroic())
if (!_energyCooldown)
{
DoCastSelf(SPELL_SUMMON_PURE_ENERGY_H1);
DoCastSelf(SPELL_SUMMON_PURE_ENERGY_H2);
Talk(SAY_ENERGY);
Talk(EMOTE_DISCHARGE_ENERGY);
if (IsHeroic())
{
DoCastSelf(SPELL_SUMMON_PURE_ENERGY_H1);
DoCastSelf(SPELL_SUMMON_PURE_ENERGY_H2);
}
else
DoCastSelf(SPELL_SUMMON_PURE_ENERGY_N);
_energyCooldown = true;
++_energyCount;
me->m_Events.AddEventAtOffset([&]
{
_energyCooldown = false;
}, 5s);
}
else
DoCastSelf(SPELL_SUMMON_PURE_ENERGY_N);
context.Repeat(5s);
_energyCooldown = true;
scheduler.Schedule(5s, [this](TaskContext)
{
_energyCooldown = false;
});
}
});
});
ScheduleHealthCheckEvent(20, [&]
{
scheduler.CancelAll();
DoCastSelf(SPELL_OVERLOAD, true);
scheduler.Schedule(1s, [this](TaskContext context)
{
if (_energyCount == 5 && !_energyCooldown)
{
scheduler.CancelAll();
Talk(SAY_OVERLOAD);
Talk(EMOTE_OVERLOAD);
DoCastSelf(SPELL_OVERLOAD, true);
}
else
context.Repeat(5s);
});
});
}
void JustDied(Unit*) override
{
_JustDied();
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_ENERGY_FEEDBACK);
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer())
Talk(SAY_KILL);
}
void JustEngagedWith(Unit* /*who*/) override
void JustEngagedWith(Unit*) override
{
_JustEngagedWith();
Talk(SAY_AGGRO);
ScheduleTimedEvent(8s, [&]
ScheduleTimedEvent(5s, [&]
{
DoCastRandomTarget(SPELL_CHAIN_LIGHTNING);
}, 8s, 8s);
}, 8s);
ScheduleTimedEvent(5s, [&]
{
DoCastRandomTarget(SPELL_ARCANE_SHOCK);
}, 8s, 8s);
}, 5s);
}
void JustSummoned(Creature* summon) override
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
summon->SetReactState(REACT_PASSIVE);
summon->GetMotionMaster()->MoveFollow(target, 0.0f, 0.0f);
summon->CastSpell(target, SPELL_ENERGY_FEEDBACK_CHANNEL, false);
}
summons.Summon(summon);
}
@@ -130,6 +148,7 @@ struct boss_vexallus : public BossAI
private:
bool _energyCooldown;
uint8 _energyCount;
};
void AddSC_boss_vexallus()
@@ -96,14 +96,6 @@ public:
if (GameObject* gobj = GetGameObject(DATA_ICEBARRIER))
gobj->SendUpdateToPlayer(player);
}
void OnCreatureCreate(Creature* creature) override
{
if (creature->GetSpawnId() > 0 || !creature->GetOwnerGUID().IsPlayer())
creature->CastSpell(creature, SPELL_SUNWELL_RADIANCE, true);
InstanceScript::OnCreatureCreate(creature);
}
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override
@@ -106,11 +106,6 @@ enum GameObjectIds
GO_ORB_OF_THE_BLUE_DRAGONFLIGHT4 = 188116
};
enum SpellIds
{
SPELL_SUNWELL_RADIANCE = 45769,
};
template <class AI, class T>
inline AI* GetSunwellPlateauAI(T* obj)
{
@@ -115,11 +115,13 @@ public:
for (uint8 i = 0; i < RAND_VENDOR; ++i)
RandVendor[i] = NOT_STARTED;
StorePersistentData(DATA_TIMED_RUN, 0);
}
void OnPlayerEnter(Player* /*player*/) override
{
if (!scheduler.IsGroupScheduled(GROUP_TIMED_RUN))
if (!scheduler.IsGroupScheduled(GROUP_TIMED_RUN) && GetPersistentData(DATA_TIMED_RUN))
DoAction(ACTION_START_TIMED_RUN);
}
@@ -293,53 +295,36 @@ public:
if (!InstanceScript::SetBossState(type, state))
return false;
switch (type)
if (state == DONE)
{
case DATA_NALORAKK:
if (state == DONE)
{
switch (type)
{
case DATA_NALORAKK:
if (uint32 timer = GetPersistentData(DATA_TIMED_RUN))
{
StorePersistentData(DATA_TIMED_RUN, timer += 15);
DoUpdateWorldState(WORLDSTATE_TIME_TO_SACRIFICE, timer);
}
SummonHostage(0);
}
break;
case DATA_AKILZON:
if (state == DONE)
{
SummonHostage(type);
break;
case DATA_AKILZON:
if (uint32 timer = GetPersistentData(DATA_TIMED_RUN))
{
StorePersistentData(DATA_TIMED_RUN, timer += 10);
DoUpdateWorldState(WORLDSTATE_TIME_TO_SACRIFICE, timer);
}
SummonHostage(1);
}
break;
case DATA_JANALAI:
if (state == DONE)
SummonHostage(2);
break;
case DATA_HALAZZI:
if (state == DONE)
SummonHostage(3);
break;
case DATA_HEXLORD:
if (state == IN_PROGRESS)
HandleGameObject(ObjectGuid::Empty, false, GetGameObject(DATA_HEXLORD_GATE));
else if (state == NOT_STARTED)
CheckInstanceStatus();
else if (state == DONE)
{
SummonHostage(type);
break;
case DATA_JANALAI:
case DATA_HALAZZI:
SummonHostage(type);
break;
case DATA_HEXLORD:
if (GameObject* zuljinGate = GetGameObject(DATA_ZULJIN_GATE))
zuljinGate->RemoveGameObjectFlag(GO_FLAG_LOCKED);
}
break;
}
break;
}
if (state == DONE)
{
if (GetPersistentData(DATA_TIMED_RUN) && AllBossesDone({ DATA_NALORAKK, DATA_AKILZON, DATA_JANALAI, DATA_HALAZZI }))
{
StorePersistentData(DATA_TIMED_RUN, 0);
@@ -348,6 +333,16 @@ public:
CheckInstanceStatus();
}
else
{
if (type == DATA_HEXLORD)
{
if (state == IN_PROGRESS)
HandleGameObject(ObjectGuid::Empty, false, GetGameObject(DATA_HEXLORD_GATE));
else if (state == NOT_STARTED)
CheckInstanceStatus();
}
}
return true;
}
@@ -191,7 +191,7 @@ public:
if (creature->IsSummon() && _bossWave != TO_BE_DECIDED)
{
if (_currentWave == 0)
creature->SetDisableReputationGain(true);
creature->SetReputationRewardDisabled(true);
DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, ++trash); // Update the instance wave count on new trash spawn
_encounterNPCs.insert(creature->GetGUID()); // Used for despawning on wipe
}
@@ -107,7 +107,7 @@ public:
InstanceScript* pInstance;
EventMap events;
bool lock;
bool lock, inCenter;
float x, y, z;
int32 releaseLockTimer;
@@ -134,6 +134,7 @@ public:
me->CastSpell(me, SPELL_EVOCATION, true);
events.Reset();
lock = false;
inCenter = false;
x = 0.0f;
y = 0.0f;
z = 0.0f;
@@ -222,9 +223,14 @@ public:
{
pInstance->SetData(DATA_UROM, DONE);
}
me->SetCanFly(false);
me->SetDisableGravity(false);
me->NearTeleportTo(x, y, z, 0.0f);
// Body teleportation required only when boss is flying in the center
if (inCenter)
{
me->SetCanFly(false);
me->SetDisableGravity(false);
me->NearTeleportTo(x, y, z, 0.0f);
}
}
void KilledUnit(Unit* /*victim*/) override
@@ -281,6 +287,7 @@ public:
me->SetCanFly(true);
me->SetDisableGravity(true);
me->NearTeleportTo(1103.69f, 1048.76f, 512.279f, 1.16f);
inCenter = true;
Talk(SAY_ARCANE_EXPLOSION);
Talk(EMOTE_ARCANE_EXPLOSION);
@@ -356,6 +363,7 @@ public:
me->SetControlled(false, UNIT_STATE_ROOT);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->GetMotionMaster()->MoveChase(me->GetVictim());
inCenter = false;
break;
}
}
@@ -365,6 +373,7 @@ public:
me->SetCanFly(false);
me->SetDisableGravity(false);
me->SetControlled(false, UNIT_STATE_ROOT);
inCenter = false;
ScriptedAI::EnterEvadeMode(why);
}
};
@@ -0,0 +1,49 @@
/*
* 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/>.
*/
#include "CreatureScript.h"
#include "WorldState.h"
class npc_suns_reach_reclamation : public CreatureScript
{
public:
npc_suns_reach_reclamation() : CreatureScript("npc_suns_reach_reclamation") { }
bool OnQuestReward(Player* /*player*/, Creature* /*creature*/, const Quest* quest, uint32 /*slot*/) override
{
sWorldState->AddSunsReachProgress(quest->GetQuestId());
return true;
}
};
class npc_sunwell_gate : public CreatureScript
{
public:
npc_sunwell_gate() : CreatureScript("npc_sunwell_gate") { }
bool OnQuestReward(Player* /*player*/, Creature* /*creature*/, const Quest* quest, uint32 /*slot*/) override
{
sWorldState->AddSunwellGateProgress(quest->GetQuestId());
return true;
}
};
void AddSC_suns_reach_reclamation()
{
new npc_suns_reach_reclamation();
new npc_sunwell_gate();
}
@@ -33,6 +33,7 @@ void AddSC_player_scripts();
void AddSC_npc_stave_of_ancients();
void AddSC_server_mail();
void AddSC_transport_zeppelins();
void AddSC_suns_reach_reclamation();
// The name of this function should match:
// void Add${NameOfDirectory}Scripts()
@@ -55,4 +56,5 @@ void AddWorldScripts()
AddSC_npc_stave_of_ancients();
AddSC_server_mail();
AddSC_transport_zeppelins();
AddSC_suns_reach_reclamation();
}
+1 -1
View File
@@ -75,7 +75,7 @@ public:
MOCK_METHOD(void, SendGlobalMessage, (WorldPacket const* packet, WorldSession* self, TeamId teamId), ());
MOCK_METHOD(void, SendGlobalGMMessage, (WorldPacket const* packet, WorldSession* self, TeamId teamId), ());
MOCK_METHOD(bool, SendZoneMessage, (uint32 zone, WorldPacket const* packet, WorldSession* self, TeamId teamId), ());
MOCK_METHOD(void, SendZoneText, (uint32 zone, const char* text, WorldSession* self, TeamId teamId), ());
MOCK_METHOD(void, SendZoneText, (uint32 zone, std::string text, WorldSession* self, TeamId teamId), ());
MOCK_METHOD(void, SendServerMessage, (ServerMessageType messageID, std::string stringParam, Player* player));
MOCK_METHOD(bool, IsShuttingDown, (), (const));
MOCK_METHOD(uint32, GetShutDownTimeLeft, (), (const));