From 27da054661b9994ff6c6dbd2cce6d0b14deca835 Mon Sep 17 00:00:00 2001 From: NoxMax <50133316+NoxMax@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:09:20 -0600 Subject: [PATCH 01/45] feat(Core): make BGs and Arena prep time configurable (#22136) Co-authored-by: Tereneckla --- .../apps/worldserver/worldserver.conf.dist | 15 +++++ .../game/Battlegrounds/Battleground.cpp | 56 ++++++++++++++++--- src/server/game/Battlegrounds/Battleground.h | 3 + src/server/game/World/WorldConfig.cpp | 2 + src/server/game/World/WorldConfig.h | 2 + 5 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index f2662fda9..79b496ff0 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -3501,6 +3501,14 @@ Wintergrasp.CrashRestartTimer = 10 ################################################################################################### # BATTLEGROUND +# +# Battleground.PrepTime +# Description: Time (in seconds) for battleground preparation phase. Strand of the Ancients will be +# the exception and will always use the default 120 seconds timer, due to its boat timing mechanic. +# Default: 120 + +Battleground.PrepTime = 120 + # # Battleground.CastDeserter # Description: Cast Deserter spell at players who leave battlegrounds in progress. @@ -3766,6 +3774,13 @@ Battleground.EyeOfTheStorm.CapturePoints = 1600 ################################################################################################### # ARENA +# +# Arena.PrepTime +# Description: Time (in seconds) for arena preparation phase. +# Default: 60 + +Arena.PrepTime = 60 + # # Arena.MaxRatingDifference # Description: Maximum rating difference between two teams in rated matches. diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 1d87e34e8..7ddd4e87e 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -195,6 +195,8 @@ Battleground::Battleground() m_HonorMode = BG_NORMAL; + m_SetupCompleted = false; + StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M; StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M; StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S; @@ -475,10 +477,8 @@ inline void Battleground::_ProcessJoin(uint32 diff) itr->second->ResetAllPowers(); } - if (!(m_Events & BG_STARTING_EVENT_1)) + if (!m_SetupCompleted) { - m_Events |= BG_STARTING_EVENT_1; - if (!FindBgMap()) { LOG_ERROR("bg.battleground", "Battleground::_ProcessJoin: map (map id: {}, instance id: {}) is not created!", m_MapId, m_InstanceID); @@ -494,13 +494,52 @@ inline void Battleground::_ProcessJoin(uint32 diff) } StartingEventCloseDoors(); - SetStartDelayTime(StartDelayTimes[BG_STARTING_EVENT_FIRST]); - // First start warning - 2 or 1 minute + // Get the configured prep time + uint32 configuredPrepTime; + + // Special case for Strand of the Ancients - always use 120 seconds due to boat timing mechanics + if (GetBgTypeID() == BATTLEGROUND_SA) + configuredPrepTime = 120 * IN_MILLISECONDS; + else + configuredPrepTime = isArena() ? + sWorld->getIntConfig(CONFIG_ARENA_PREP_TIME) * IN_MILLISECONDS : + sWorld->getIntConfig(CONFIG_BATTLEGROUND_PREP_TIME) * IN_MILLISECONDS; + + SetStartDelayTime(configuredPrepTime); + + // Pre-mark events for announcements that should be skipped based on configured prep time + if (configuredPrepTime < StartDelayTimes[BG_STARTING_EVENT_FIRST]) + { + // Skip first announcement (120s for BG, 60s for Arena) + m_Events |= BG_STARTING_EVENT_1; + + if (configuredPrepTime < StartDelayTimes[BG_STARTING_EVENT_SECOND]) + { + // Skip second announcement (60s for BG, 30s for Arena) + m_Events |= BG_STARTING_EVENT_2; + + if (configuredPrepTime < StartDelayTimes[BG_STARTING_EVENT_THIRD]) + { + // Skip third announcement (30s for BG, 15s for Arena) + m_Events |= BG_STARTING_EVENT_3; + } + } + } + + // Mark setup as completed + m_SetupCompleted = true; + } + + // First announcement at 120s or 60s (Depending on BG or Arena and configured time) + if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_FIRST] && !(m_Events & BG_STARTING_EVENT_1)) + { + m_Events |= BG_STARTING_EVENT_1; + if (StartMessageIds[BG_STARTING_EVENT_FIRST]) SendBroadcastText(StartMessageIds[BG_STARTING_EVENT_FIRST], CHAT_MSG_BG_SYSTEM_NEUTRAL); } - // After 1 minute or 30 seconds, warning is signaled + // Second announcement at 60s or 30s else if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_SECOND] && !(m_Events & BG_STARTING_EVENT_2)) { m_Events |= BG_STARTING_EVENT_2; @@ -508,7 +547,7 @@ inline void Battleground::_ProcessJoin(uint32 diff) if (StartMessageIds[BG_STARTING_EVENT_SECOND]) SendBroadcastText(StartMessageIds[BG_STARTING_EVENT_SECOND], CHAT_MSG_BG_SYSTEM_NEUTRAL); } - // After 30 or 15 seconds, warning is signaled + // Third announcement at 30s or 15s else if (GetStartDelayTime() <= StartDelayTimes[BG_STARTING_EVENT_THIRD] && !(m_Events & BG_STARTING_EVENT_3)) { m_Events |= BG_STARTING_EVENT_3; @@ -543,11 +582,12 @@ inline void Battleground::_ProcessJoin(uint32 diff) break; } } - // Delay expired (after 2 or 1 minute) + // Delay expired (after configured prep time) else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4)) { m_Events |= BG_STARTING_EVENT_4; + // Start the battle StartingEventOpenDoors(); if (StartMessageIds[BG_STARTING_EVENT_FOURTH]) diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h index 35ffebc4d..90b828e69 100644 --- a/src/server/game/Battlegrounds/Battleground.h +++ b/src/server/game/Battlegrounds/Battleground.h @@ -614,6 +614,9 @@ protected: void _ProcessJoin(uint32 diff); void _CheckSafePositions(uint32 diff); + // Setup completion marker + bool m_SetupCompleted; + // Scorekeeping BattlegroundScoreMap PlayerScores; // Player scores // must be implemented in BG subclass diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 5db780298..19a55465e 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -406,6 +406,7 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_LISTEN_RANGE_TEXTEMOTE, "ListenRange.TextEmote", 40.0f); SetConfigValue(CONFIG_LISTEN_RANGE_YELL, "ListenRange.Yell", 300.0f); + SetConfigValue(CONFIG_BATTLEGROUND_PREP_TIME, "Battleground.PrepTime", 120); SetConfigValue(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, "Battleground.Override.LowLevels.MinPlayers", 0); SetConfigValue(CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG, "Battleground.DisableQuestShareInBG", false); SetConfigValue(CONFIG_BATTLEGROUND_DISABLE_READY_CHECK_IN_BG, "Battleground.DisableReadyCheckInBG", false); @@ -436,6 +437,7 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH, "Battleground.Alterac.ReputationOnBossDeath", 350); SetConfigValue(CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS, "Battleground.EyeOfTheStorm.CapturePoints", 1600); + SetConfigValue(CONFIG_ARENA_PREP_TIME, "Arena.PrepTime", 60); SetConfigValue(CONFIG_ARENA_MAX_RATING_DIFFERENCE, "Arena.MaxRatingDifference", 150); SetConfigValue(CONFIG_ARENA_RATING_DISCARD_TIMER, "Arena.RatingDiscardTimer", 600000); SetConfigValue(CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, "Arena.PreviousOpponentsDiscardTimer", 120000); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 989746b81..b874ebc75 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -254,6 +254,7 @@ enum ServerConfigs CONFIG_DEATH_SICKNESS_LEVEL, CONFIG_INSTANT_LOGOUT, CONFIG_DISABLE_BREATHING, + CONFIG_BATTLEGROUND_PREP_TIME, CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY, CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER, @@ -274,6 +275,7 @@ enum ServerConfigs CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH, CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS, CONFIG_WINTERGRASP_ENABLE, + CONFIG_ARENA_PREP_TIME, CONFIG_ARENA_MAX_RATING_DIFFERENCE, CONFIG_ARENA_RATING_DISCARD_TIMER, CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, From 71c2dbfa20420542e45066e4c1ee7b071a8de407 Mon Sep 17 00:00:00 2001 From: chaosua <544218+chaosua@users.noreply.github.com> Date: Sat, 26 Jul 2025 13:06:09 +0300 Subject: [PATCH 02/45] fix(DB/page_text_locale): fix gender tags and html for 3539, 3540, 3541 (#22562) --- data/sql/updates/pending_db_world/page_text.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/page_text.sql diff --git a/data/sql/updates/pending_db_world/page_text.sql b/data/sql/updates/pending_db_world/page_text.sql new file mode 100644 index 000000000..6e0e6ec1f --- /dev/null +++ b/data/sql/updates/pending_db_world/page_text.sql @@ -0,0 +1,3 @@ +UPDATE `page_text_locale` SET `Text`='\r\n\r\n
\r\n

О, великий вождь!

да озарит Свет каждый ваш шаг, и да пребудет он вечно в вашем сердце. Я молю вас о пощаде для этого павшего рыцаря, и смиренно прошу выслушать меня до конца.

Податель сего письма некогда $Gбыл:была; героем Орды. Хотя отныне чело $Gего:ее; отмечено печатью смерти и обликом Плети, душой $Gон остался верен:она осталась верна; нашему делу.\r\n

\r\n\r\n' WHERE `ID` = 3539 AND `locale`='ruRU'; +UPDATE `page_text_locale` SET `Text`='\r\n\r\n
\r\n

$N $Gбыл избран:была избрана; мной в качестве представителя нового союза, сражающегося против Короля-лича. Этот орден носит имя Рыцарей Черного Клинка. Да, ваше величество, вы не ошибаетесь: рыцари смерти объединились против своего бывшего повелителя. Их возглавляет сын лорда Александроса Могрейна – сам Испепелитель, и жаждут они того же, что и все мы: положить конец Плети.

Я не смею просить вас принять $N и всех Рыцарей Черного Клинка в Орду, но надеюсь, что вы проявите к ним снисхождение.\r\n

\r\n\r\n' WHERE `ID` = 3540 AND `locale`='ruRU'; +UPDATE `page_text_locale` SET `Text`='\r\n\r\n
\r\n

Помните, вождь, что все мы служим кровью и честью во имя великой цели.

С глубочайшим уважением,

Верховный лорд Тирион Фордринг
P.S. Передайте Эйтриггу мои пожелания долгого здравия и скажите ему, что мне не помешает такой союзник в Нордсколе – разумеется, если вы не будете возражать.\r\n

\r\n\r\n' WHERE `ID`= 3541 AND `locale`='ruRU'; From 5a96fbd23e240294f8705a400adfa3960a3c3617 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 26 Jul 2025 10:07:12 +0000 Subject: [PATCH 03/45] chore(DB): import pending files Referenced commit(s): 71c2dbfa20420542e45066e4c1ee7b071a8de407 --- .../page_text.sql => db_world/2025_07_26_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/page_text.sql => db_world/2025_07_26_00.sql} (98%) diff --git a/data/sql/updates/pending_db_world/page_text.sql b/data/sql/updates/db_world/2025_07_26_00.sql similarity index 98% rename from data/sql/updates/pending_db_world/page_text.sql rename to data/sql/updates/db_world/2025_07_26_00.sql index 6e0e6ec1f..803f5abb2 100644 --- a/data/sql/updates/pending_db_world/page_text.sql +++ b/data/sql/updates/db_world/2025_07_26_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_25_00 -> 2025_07_26_00 UPDATE `page_text_locale` SET `Text`='\r\n\r\n
\r\n

О, великий вождь!

да озарит Свет каждый ваш шаг, и да пребудет он вечно в вашем сердце. Я молю вас о пощаде для этого павшего рыцаря, и смиренно прошу выслушать меня до конца.

Податель сего письма некогда $Gбыл:была; героем Орды. Хотя отныне чело $Gего:ее; отмечено печатью смерти и обликом Плети, душой $Gон остался верен:она осталась верна; нашему делу.\r\n

\r\n\r\n' WHERE `ID` = 3539 AND `locale`='ruRU'; UPDATE `page_text_locale` SET `Text`='\r\n\r\n
\r\n

$N $Gбыл избран:была избрана; мной в качестве представителя нового союза, сражающегося против Короля-лича. Этот орден носит имя Рыцарей Черного Клинка. Да, ваше величество, вы не ошибаетесь: рыцари смерти объединились против своего бывшего повелителя. Их возглавляет сын лорда Александроса Могрейна – сам Испепелитель, и жаждут они того же, что и все мы: положить конец Плети.

Я не смею просить вас принять $N и всех Рыцарей Черного Клинка в Орду, но надеюсь, что вы проявите к ним снисхождение.\r\n

\r\n\r\n' WHERE `ID` = 3540 AND `locale`='ruRU'; UPDATE `page_text_locale` SET `Text`='\r\n\r\n
\r\n

Помните, вождь, что все мы служим кровью и честью во имя великой цели.

С глубочайшим уважением,

Верховный лорд Тирион Фордринг
P.S. Передайте Эйтриггу мои пожелания долгого здравия и скажите ему, что мне не помешает такой союзник в Нордсколе – разумеется, если вы не будете возражать.\r\n

\r\n\r\n' WHERE `ID`= 3541 AND `locale`='ruRU'; From dad67f9bb0919c7a6e639084f57e145796a0ea53 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 26 Jul 2025 16:42:49 -0300 Subject: [PATCH 04/45] fix(Scripts/CullingOfStratholme): Prevent Arthas from despawning (#22570) --- .../CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp index b9b8cef94..32ec43e80 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp @@ -692,6 +692,7 @@ public: // Start Event Start(true, false); SetDespawnAtEnd(false); + SetDespawnAtFar(false); ScheduleNextEvent(currentEvent, 9000); break; From 9f8cd74205dd887d57877c509fe9b3d3e85d9c1c Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 26 Jul 2025 16:43:17 -0300 Subject: [PATCH 05/45] fix(DB/Spell): Fix serverside spell Grow (#22569) Co-authored-by: Gustavo Cherry-picked from commit: https://github.com/TrinityCore/TrinityCore/commit/0b766db6a41a4db0d631e36fcc753c658b746328 --- data/sql/updates/pending_db_world/rev_1753553537414069400.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1753553537414069400.sql diff --git a/data/sql/updates/pending_db_world/rev_1753553537414069400.sql b/data/sql/updates/pending_db_world/rev_1753553537414069400.sql new file mode 100644 index 000000000..a8d39f42e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1753553537414069400.sql @@ -0,0 +1,2 @@ +-- +UPDATE `spell_dbc` SET `Effect_1` = 6, `EffectBasePoints_1`= 100, `ImplicitTargetA_1`= 1, `EffectAura_1` = 61, `CumulativeAura`= 3 WHERE `ID`= 57059; From 00f411c0b13a5b6af35ad2fe3a5aa5cc456a33f8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 26 Jul 2025 19:43:50 +0000 Subject: [PATCH 06/45] chore(DB): import pending files Referenced commit(s): dad67f9bb0919c7a6e639084f57e145796a0ea53 --- .../rev_1753553537414069400.sql => db_world/2025_07_26_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1753553537414069400.sql => db_world/2025_07_26_01.sql} (77%) diff --git a/data/sql/updates/pending_db_world/rev_1753553537414069400.sql b/data/sql/updates/db_world/2025_07_26_01.sql similarity index 77% rename from data/sql/updates/pending_db_world/rev_1753553537414069400.sql rename to data/sql/updates/db_world/2025_07_26_01.sql index a8d39f42e..0e5487ea0 100644 --- a/data/sql/updates/pending_db_world/rev_1753553537414069400.sql +++ b/data/sql/updates/db_world/2025_07_26_01.sql @@ -1,2 +1,3 @@ +-- DB update 2025_07_26_00 -> 2025_07_26_01 -- UPDATE `spell_dbc` SET `Effect_1` = 6, `EffectBasePoints_1`= 100, `ImplicitTargetA_1`= 1, `EffectAura_1` = 61, `CumulativeAura`= 3 WHERE `ID`= 57059; From d2fb893f876cde690fcbdd3b7ef217b4f24eb873 Mon Sep 17 00:00:00 2001 From: Christian M Date: Sat, 26 Jul 2025 18:26:00 -0400 Subject: [PATCH 07/45] feat(Core/Player): Allow spell validation to be skipped at server owner's risk (#22521) Co-authored-by: Tereneckla --- src/server/apps/worldserver/worldserver.conf.dist | 10 ++++++++++ src/server/game/Entities/Player/Player.cpp | 3 +++ src/server/game/World/WorldConfig.cpp | 1 + src/server/game/World/WorldConfig.h | 1 + 4 files changed, 15 insertions(+) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 79b496ff0..9034da67a 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -1765,6 +1765,16 @@ CleanCharacterDB = 0 PersistentCharacterCleanFlags = 0 +# +# ValidateSkillLearnedBySpells +# Description: If enabled, players will lose spells that are invalid for their race/class. +# Default: 1 - (Enabled, enforce valid spells) +# 0 - (Disabled, allow invalid spells) +# Disabling this and then having your character learn spells which require DBC edits can result in the character not being saved in the database +# Disable AT YOUR OWN RISK + +ValidateSkillLearnedBySpells = 1 + # ################################################################################################### diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 7173ad844..5fb5f85be 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -3103,6 +3103,9 @@ bool Player::addSpell(uint32 spellId, uint8 addSpecMask, bool updateActive, bool bool Player::CheckSkillLearnedBySpell(uint32 spellId) { + if (!sWorld->getBoolConfig(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS)) + return true; + SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId); uint32 errorSkill = 0; for (SkillLineAbilityMap::const_iterator sla = skill_bounds.first; sla != skill_bounds.second; ++sla) diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 19a55465e..7a0b1db40 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -160,6 +160,7 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_INTERVAL_SAVE, "PlayerSaveInterval", 900000); SetConfigValue(CONFIG_INTERVAL_DISCONNECT_TOLERANCE, "DisconnectToleranceInterval", 0); SetConfigValue(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, "PlayerSave.Stats.SaveOnlyOnLogout", true); + SetConfigValue(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS, "ValidateSkillLearnedBySpells", true); SetConfigValue(CONFIG_MIN_LEVEL_STAT_SAVE, "PlayerSave.Stats.MinLevel", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value < MAX_LEVEL; }, "< MAX_LEVEL"); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index b874ebc75..3776fcac9 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -474,6 +474,7 @@ enum ServerConfigs RATE_MISS_CHANCE_MULTIPLIER_TARGET_CREATURE, RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER, CONFIG_NEW_CHAR_STRING, + CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS, MAX_NUM_SERVER_CONFIGS }; From 24d1a4808fc63d2218600b3469e7853fefebb8f6 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Sat, 26 Jul 2025 20:12:49 -0700 Subject: [PATCH 08/45] fix(Scripts/Spells): Death and Decay not breaking Hungering Cold (#22573) --- src/server/scripts/Spells/spell_dk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 8841b63dd..a880e5dc8 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -342,7 +342,7 @@ class spell_dk_death_and_decay_aura : public AuraScript if (GetCaster() && GetTarget()) { int32 basePoints0 = aurEff->GetAmount(); - GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, true, 0, aurEff); + GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, false, 0, aurEff); } } From 3f46e05d3691895b6b8a5b3832d17ecb1e210791 Mon Sep 17 00:00:00 2001 From: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Date: Sun, 27 Jul 2025 08:54:16 +0200 Subject: [PATCH 09/45] refactor(Core/Account): Move account flag logic from AccountMgr to WorldSession (#22558) --- .../Database/Implementation/LoginDatabase.cpp | 3 +- .../Database/Implementation/LoginDatabase.h | 3 +- src/server/game/Accounts/AccountMgr.cpp | 33 ------------------- src/server/game/Accounts/AccountMgr.h | 4 --- src/server/game/Server/WorldSession.cpp | 27 ++++++++++++++- src/server/game/Server/WorldSession.h | 8 ++++- src/server/game/Server/WorldSocket.cpp | 6 ++-- 7 files changed, 38 insertions(+), 46 deletions(-) diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 9c782ff3a..8821fe9d3 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -102,8 +102,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_CHECK_PASSWORD, "SELECT salt, verifier FROM account WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT salt, verifier FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_FLAG, "SELECT Flags FROM account WHERE id = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_UPD_ADD_ACCOUNT_FLAG, "UPDATE account SET Flags = Flags | ? WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_UPD_REMOVE_ACCOUNT_FLAG, "UPDATE account SET Flags = Flags &~ ? WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_SET_ACCOUNT_FLAG, "UPDATE account SET Flags = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_PINFO, "SELECT a.username, aa.gmlevel, a.email, a.reg_mail, a.last_ip, DATE_FORMAT(a.last_login, '%Y-%m-%d %T'), a.mutetime, a.mutereason, a.muteby, a.failed_logins, a.locked, a.OS FROM account a LEFT JOIN account_access aa ON (a.id = aa.id AND (aa.RealmID = ? OR aa.RealmID = -1)) WHERE a.id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM account_banned WHERE id = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_GM_ACCOUNTS, "SELECT a.username, aa.gmlevel FROM account a, account_access aa WHERE a.id=aa.id AND aa.gmlevel >= ? AND (aa.realmid = -1 OR aa.realmid = ?)", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index 230e86452..e11c285b4 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -86,8 +86,7 @@ enum LoginDatabaseStatements : uint32 LOGIN_SEL_CHECK_PASSWORD, LOGIN_SEL_CHECK_PASSWORD_BY_NAME, LOGIN_SEL_ACCOUNT_FLAG, - LOGIN_UPD_ADD_ACCOUNT_FLAG, - LOGIN_UPD_REMOVE_ACCOUNT_FLAG, + LOGIN_UPD_SET_ACCOUNT_FLAG, LOGIN_SEL_PINFO, LOGIN_SEL_PINFO_BANS, LOGIN_SEL_GM_ACCOUNTS, diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index c4f0649c1..4129609db 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -299,39 +299,6 @@ namespace AccountMgr return false; } - bool HasAccountFlag(uint32 accountId, uint32 flag) - { - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_FLAG); - stmt->SetData(0, accountId); - if (PreparedQueryResult result = LoginDatabase.Query(stmt)) - { - uint32 flags = (*result)[0].Get(); - return (flags & flag) != 0; - } - - return false; - } - - void UpdateAccountFlag(uint32 accountId, uint32 flag, bool remove /*= false*/) - { - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement( - remove ? LOGIN_UPD_REMOVE_ACCOUNT_FLAG : LOGIN_UPD_ADD_ACCOUNT_FLAG - ); - stmt->SetData(0, flag); - stmt->SetData(1, accountId); - LoginDatabase.Execute(stmt); - } - - void ValidateAccountFlags(uint32 accountId, uint32 flags, uint32 security) - { - bool hasGMFlag = (flags & ACCOUNT_FLAG_GM) != 0; - - if (IsGMAccount(security) && !hasGMFlag) - UpdateAccountFlag(accountId, ACCOUNT_FLAG_GM); - else if (hasGMFlag && !IsGMAccount(security)) - UpdateAccountFlag(accountId, ACCOUNT_FLAG_GM, true); - } - uint32 GetCharactersCount(uint32 accountId) { // check character count diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h index d2fa715b5..3f2eb983e 100644 --- a/src/server/game/Accounts/AccountMgr.h +++ b/src/server/game/Accounts/AccountMgr.h @@ -56,10 +56,6 @@ namespace AccountMgr bool IsGMAccount(uint32 gmlevel); bool IsAdminAccount(uint32 gmlevel); bool IsConsoleAccount(uint32 gmlevel); - - bool HasAccountFlag(uint32 accountId, uint32 flag); - void UpdateAccountFlag(uint32 accountId, uint32 flag, bool remove = false); - void ValidateAccountFlags(uint32 accountId, uint32 flags, uint32 security); }; #endif diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 07167f5b0..c8d336fe5 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -104,7 +104,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet) } /// WorldSession constructor -WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr sock, AccountTypes sec, uint8 expansion, +WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) : m_muteTime(mute_time), m_timeOutTime(0), @@ -116,6 +116,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptrSetData(0, _accountFlags); + stmt->SetData(1, GetAccountId()); + LoginDatabase.Execute(stmt); +} + +void WorldSession::ValidateAccountFlags() +{ + bool hasGMFlag = HasAccountFlag(ACCOUNT_FLAG_GM); + + if (IsGMAccount() && !hasGMFlag) + UpdateAccountFlag(ACCOUNT_FLAG_GM); + else if (hasGMFlag && !IsGMAccount()) + UpdateAccountFlag(ACCOUNT_FLAG_GM, true); +} + bool WorldSession::IsGMAccount() const { return GetSecurity() >= SEC_GAMEMASTER; diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 73ec2b542..02948d249 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -329,9 +329,14 @@ struct PacketCounter class WorldSession { public: - WorldSession(uint32 id, std::string&& name, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime); + WorldSession(uint32 id, std::string&& name, uint32 accountFlags, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime); ~WorldSession(); + uint32 GetAccountFlags() const { return _accountFlags; } + bool HasAccountFlag(uint32 flag) const { return (_accountFlags & flag) != 0; } + void UpdateAccountFlag(uint32 flag, bool remove = false); + void ValidateAccountFlags(); + bool IsGMAccount() const; bool PlayerLoading() const { return m_playerLoading; } @@ -1158,6 +1163,7 @@ private: bool _skipQueue; uint32 _accountId; std::string _accountName; + uint32 _accountFlags; uint8 m_expansion; uint32 m_total_time; diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 9c4d9de8e..51705d18e 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -698,8 +698,6 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr authSes LoginDatabase.Execute(stmt); - AccountMgr::ValidateAccountFlags(account.Id, account.Flags, account.Security); - // At this point, we can safely hook a successful login sScriptMgr->OnAccountLogin(account.Id); @@ -707,7 +705,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr authSes sScriptMgr->OnLastIpUpdate(account.Id, address); - _worldSession = new WorldSession(account.Id, std::move(authSession->Account), shared_from_this(), account.Security, + _worldSession = new WorldSession(account.Id, std::move(authSession->Account), account.Flags, shared_from_this(), account.Security, account.Expansion, account.MuteTime, account.Locale, account.Recruiter, account.IsRectuiter, account.Security ? true : false, account.TotalTime); _worldSession->ReadAddonsInfo(authSession->AddonInfo); @@ -718,6 +716,8 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr authSes _worldSession->InitWarden(account.SessionKey, account.OS); } + _worldSession->ValidateAccountFlags(); + sWorldSessionMgr->AddSession(_worldSession); AsyncRead(); From 4da58845323915295ae1bd30dbdb4447b60c453a Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sun, 27 Jul 2025 04:11:27 -0300 Subject: [PATCH 10/45] =?UTF-8?q?fix(DB/SAI):=20Grant=20quest=20credit=20t?= =?UTF-8?q?o=20all=20group=20members=20for=20quest=20The=20Mi=E2=80=A6=20(?= =?UTF-8?q?#22572)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/sql/updates/pending_db_world/rev_1753569937939796800.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1753569937939796800.sql diff --git a/data/sql/updates/pending_db_world/rev_1753569937939796800.sql b/data/sql/updates/pending_db_world/rev_1753569937939796800.sql new file mode 100644 index 000000000..44e93d9b0 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1753569937939796800.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 496101) AND (`source_type` = 9) AND (`id` IN (0)); +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 +(496101, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 26, 1447, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Dashel Stonefist - On Script - Quest Credit \'The Missing Diplomat\''); From e66f763a9ca71c93b6dea885187d2d742ad792b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 27 Jul 2025 07:28:39 +0000 Subject: [PATCH 11/45] chore(DB): import pending files Referenced commit(s): 4da58845323915295ae1bd30dbdb4447b60c453a --- .../rev_1753569937939796800.sql => db_world/2025_07_27_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1753569937939796800.sql => db_world/2025_07_27_00.sql} (94%) diff --git a/data/sql/updates/pending_db_world/rev_1753569937939796800.sql b/data/sql/updates/db_world/2025_07_27_00.sql similarity index 94% rename from data/sql/updates/pending_db_world/rev_1753569937939796800.sql rename to data/sql/updates/db_world/2025_07_27_00.sql index 44e93d9b0..159d80deb 100644 --- a/data/sql/updates/pending_db_world/rev_1753569937939796800.sql +++ b/data/sql/updates/db_world/2025_07_27_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_26_01 -> 2025_07_27_00 -- DELETE FROM `smart_scripts` WHERE (`entryorguid` = 496101) AND (`source_type` = 9) AND (`id` IN (0)); 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 From 60643ce5e735dee636b01acfa4e6eb141119a41a Mon Sep 17 00:00:00 2001 From: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Date: Sun, 27 Jul 2025 09:59:13 +0200 Subject: [PATCH 12/45] feat(Script/Command): Add AccountFlag pinfo output (#22548) --- .../rev_1753369405792232900.sql | 4 ++ src/common/Common.h | 3 +- src/server/game/Miscellaneous/Language.h | 6 ++- src/server/scripts/Commands/cs_misc.cpp | 53 +++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1753369405792232900.sql diff --git a/data/sql/updates/pending_db_world/rev_1753369405792232900.sql b/data/sql/updates/pending_db_world/rev_1753369405792232900.sql new file mode 100644 index 000000000..942685594 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1753369405792232900.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `acore_string` WHERE `entry` = 179; +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES +(179, '| AccountFlags:'); diff --git a/src/common/Common.h b/src/common/Common.h index 98f343a45..8a28414c0 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -61,6 +61,7 @@ enum AccountTypes SEC_CONSOLE = 4 // must be always last in list, accounts must have less security level always also }; +#define MAX_ACCOUNT_FLAG 32 enum AccountFlag { ACCOUNT_FLAG_GM = 0x1, // Account is GM @@ -96,7 +97,7 @@ enum AccountFlag // Below might be StarCraft II related ACCOUNT_FLAG_S2_REQUIRE_IGR = 0x40000000, // NYI UNK ACCOUNT_FLAG_S2_TRIAL = 0x80000000, // NYI UNK - ACCOUNT_FLAG_S2_RESTRICTED = 0xFFFFFFFF // NYI UNK + // ACCOUNT_FLAG_S2_RESTRICTED = 0xFFFFFFFF, // NYI UNK }; enum LocaleConstant diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 3565a6cce..5658e4147 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -217,7 +217,11 @@ enum AcoreStrings LANG_INVALID_GAMEOBJECT_TYPE = 176, LANG_GAMEOBJECT_DAMAGED = 177, LANG_GRID_POSITION = 178, - // 179-185 used in other client versions + + LANG_ACCOUNT_FLAGS_PINFO = 179, + + // Free 180-185 + LANG_TRANSPORT_POSITION = 186, LANG_PROFANITY_NAME = 187, LANG_2FA_SECRET_TOO_LONG = 188, diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 5e83822c3..5ea7ce706 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -22,6 +22,7 @@ #include "CharacterCache.h" #include "Chat.h" #include "CommandScript.h" +#include "Common.h" #include "GameGraveyard.h" #include "GameTime.h" #include "GridNotifiers.h" @@ -57,6 +58,49 @@ constexpr auto SPELL_STUCK = 7355; constexpr auto SPELL_FREEZE = 9454; +struct AccountFlagText +{ + AccountFlag flag; + std::string text; +}; + +AccountFlagText const accountFlagText[MAX_ACCOUNT_FLAG] = +{ + { ACCOUNT_FLAG_GM, "ACCOUNT_FLAG_GM" }, + { ACCOUNT_FLAG_NOKICK, "ACCOUNT_FLAG_NOKICK" }, + { ACCOUNT_FLAG_COLLECTOR, "ACCOUNT_FLAG_COLLECTOR" }, + { ACCOUNT_FLAG_TRIAL, "ACCOUNT_FLAG_TRIAL" }, + { ACCOUNT_FLAG_CANCELLED, "ACCOUNT_FLAG_CANCELLED" }, + { ACCOUNT_FLAG_IGR, "ACCOUNT_FLAG_IGR" }, + { ACCOUNT_FLAG_WHOLESALER, "ACCOUNT_FLAG_WHOLESALER" }, + { ACCOUNT_FLAG_PRIVILEGED, "ACCOUNT_FLAG_PRIVILEGED" }, + { ACCOUNT_FLAG_EU_FORBID_ELV, "ACCOUNT_FLAG_EU_FORBID_ELV" }, + { ACCOUNT_FLAG_EU_FORBID_BILLING, "ACCOUNT_FLAG_EU_FORBID_BILLING" }, + { ACCOUNT_FLAG_RESTRICTED, "ACCOUNT_FLAG_RESTRICTED" }, + { ACCOUNT_FLAG_REFERRAL, "ACCOUNT_FLAG_REFERRAL" }, + { ACCOUNT_FLAG_BLIZZARD, "ACCOUNT_FLAG_BLIZZARD" }, + { ACCOUNT_FLAG_RECURRING_BILLING, "ACCOUNT_FLAG_RECURRING_BILLING" }, + { ACCOUNT_FLAG_NOELECTUP, "ACCOUNT_FLAG_NOELECTUP" }, + { ACCOUNT_FLAG_KR_CERTIFICATE, "ACCOUNT_FLAG_KR_CERTIFICATE" }, + { ACCOUNT_FLAG_EXPANSION_COLLECTOR, "ACCOUNT_FLAG_EXPANSION_COLLECTOR" }, + { ACCOUNT_FLAG_DISABLE_VOICE, "ACCOUNT_FLAG_DISABLE_VOICE" }, + { ACCOUNT_FLAG_DISABLE_VOICE_SPEAK, "ACCOUNT_FLAG_DISABLE_VOICE_SPEAK" }, + { ACCOUNT_FLAG_REFERRAL_RESURRECT, "ACCOUNT_FLAG_REFERRAL_RESURRECT" }, + { ACCOUNT_FLAG_EU_FORBID_CC, "ACCOUNT_FLAG_EU_FORBID_CC" }, + { ACCOUNT_FLAG_OPENBETA_DELL, "ACCOUNT_FLAG_OPENBETA_DELL" }, + { ACCOUNT_FLAG_PROPASS, "ACCOUNT_FLAG_PROPASS" }, + { ACCOUNT_FLAG_PROPASS_LOCK, "ACCOUNT_FLAG_PROPASS_LOCK" }, + { ACCOUNT_FLAG_PENDING_UPGRADE, "ACCOUNT_FLAG_PENDING_UPGRADE" }, + { ACCOUNT_FLAG_RETAIL_FROM_TRIAL, "ACCOUNT_FLAG_RETAIL_FROM_TRIAL" }, + { ACCOUNT_FLAG_EXPANSION2_COLLECTOR, "ACCOUNT_FLAG_EXPANSION2_COLLECTOR" }, + { ACCOUNT_FLAG_OVERMIND_LINKED, "ACCOUNT_FLAG_OVERMIND_LINKED" }, + { ACCOUNT_FLAG_DEMOS, "ACCOUNT_FLAG_DEMOS" }, + { ACCOUNT_FLAG_DEATH_KNIGHT_OK, "ACCOUNT_FLAG_DEATH_KNIGHT_OK" }, + { ACCOUNT_FLAG_S2_REQUIRE_IGR, "ACCOUNT_FLAG_S2_REQUIRE_IGR" }, + { ACCOUNT_FLAG_S2_TRIAL, "ACCOUNT_FLAG_S2_TRIAL" }, + // { ACCOUNT_FLAG_S2_RESTRICTED, "ACCOUNT_FLAG_S2_RESTRICTED" } +}; + std::string const GetLocalizeCreatureName(Creature* creature, LocaleConstant locale) { auto creatureTemplate = sObjectMgr->GetCreatureTemplate(creature->GetEntry()); @@ -2193,6 +2237,15 @@ public: // Output V. LANG_PINFO_ACC_ACCOUNT handler->PSendSysMessage(LANG_PINFO_ACC_ACCOUNT, userName, accId, security); + if (playerTarget) + { + uint32 accountFlags = playerTarget->GetSession()->GetAccountFlags(); + handler->PSendSysMessage(LANG_ACCOUNT_FLAGS_PINFO); + for (uint8 i = 0; i < MAX_ACCOUNT_FLAG; i++) + if (accountFlags & static_cast(accountFlagText[i].flag)) + handler->PSendSysMessage(LANG_SUBCMDS_LIST_ENTRY, accountFlagText[i].text); + } + // Output VI. LANG_PINFO_ACC_LASTLOGIN handler->PSendSysMessage(LANG_PINFO_ACC_LASTLOGIN, lastLogin, failedLogins); From 90ac29815554013d32fe497c3ca975622df1135c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 27 Jul 2025 08:02:39 +0000 Subject: [PATCH 13/45] chore(DB): import pending files Referenced commit(s): 60643ce5e735dee636b01acfa4e6eb141119a41a --- .../rev_1753369405792232900.sql => db_world/2025_07_27_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1753369405792232900.sql => db_world/2025_07_27_01.sql} (76%) diff --git a/data/sql/updates/pending_db_world/rev_1753369405792232900.sql b/data/sql/updates/db_world/2025_07_27_01.sql similarity index 76% rename from data/sql/updates/pending_db_world/rev_1753369405792232900.sql rename to data/sql/updates/db_world/2025_07_27_01.sql index 942685594..8c911b1b5 100644 --- a/data/sql/updates/pending_db_world/rev_1753369405792232900.sql +++ b/data/sql/updates/db_world/2025_07_27_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_27_00 -> 2025_07_27_01 -- DELETE FROM `acore_string` WHERE `entry` = 179; INSERT INTO `acore_string` (`entry`, `content_default`) VALUES From dbee211506014f8aa415dfccab0941fcd3cfec6b Mon Sep 17 00:00:00 2001 From: Yehonal Date: Sun, 27 Jul 2025 13:50:20 +0200 Subject: [PATCH 14/45] fix(bash/starter): enhance interactive mode handling (#22516) This pull request updates the startup script in `apps/startup-scripts/src/starter` to improve handling of interactive and non-interactive modes, particularly when running under different session managers. ### Improvements to interactive and non-interactive mode handling: * Updated the condition to check if the application should run interactively by combining `AC_LAUNCHED_BY_PM2` and `AC_DISABLE_INTERACTIVE` environment variables. This ensures more accurate handling of interactive mode. Fixes: https://github.com/azerothcore/azerothcore-wotlk/issues/22507 --- apps/startup-scripts/src/starter | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/startup-scripts/src/starter b/apps/startup-scripts/src/starter index a802f6455..4e58e8e7b 100755 --- a/apps/startup-scripts/src/starter +++ b/apps/startup-scripts/src/starter @@ -122,12 +122,26 @@ EOF fi else echo "Starting $BINFILE without GDB" - if [[ "$AC_LAUNCHED_BY_PM2" == "1" ]]; then - echo "Running under PM2" + # Determine if PM2 is active + is_pm2_active="0" + [ "$AC_LAUNCHED_BY_PM2" == "1" ] && is_pm2_active="1" + + # Determine if interactive mode is enabled + is_interactive_enabled="1" + [ "$AC_DISABLE_INTERACTIVE" == "1" ] && is_interactive_enabled="0" + + # use normal execution if we are running the binary under PM2 + # or when interactive mode is enabled + if [[ "$is_pm2_active" == "1" || "$is_interactive_enabled" == "1" ]]; then + echo "Running AC" "$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"} else + # When AC_DISABLE_INTERACTIVE is set to 1 and we are not in PM2 + # This means we are using systemd without interactive mode and no session managers + # in this case we need to run AC with unbuffer for line-buffered output + # NOTE unbuffer doesn't fully support interactive mode if command -v unbuffer >/dev/null 2>&1; then - export AC_DISABLE_INTERACTIVE=0 + echo "Running AC with unbuffer for line-buffered output" unbuffer "$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"} else echo "⚠️ unbuffer not found, the output may not be line-buffered. Try installing expect." From b6bef0f6af84acec30cde6809f65ac73de213b69 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Sun, 27 Jul 2025 15:25:15 +0200 Subject: [PATCH 15/45] fix(DB/SAI): Implement Dialogue in the tavern (#22577) --- .../Stratholme_conversation.sql | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 data/sql/updates/pending_db_world/Stratholme_conversation.sql diff --git a/data/sql/updates/pending_db_world/Stratholme_conversation.sql b/data/sql/updates/pending_db_world/Stratholme_conversation.sql new file mode 100644 index 000000000..4b1f71f6d --- /dev/null +++ b/data/sql/updates/pending_db_world/Stratholme_conversation.sql @@ -0,0 +1,85 @@ + +-- Haerthsinger Forresten +DELETE FROM `creature_text` WHERE (`CreatureID` = 30551); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(30551, 0, 0, 'This whole situation seems a bit paranoid, don\'t you think?', 12, 0, 100, 1, 0, 0, 31324, 0, ''), +(30551, 1, 0, 'Thank the Light for that.', 12, 0, 100, 1, 0, 0, 32573, 0, ''); + +-- Fras Siabi +DELETE FROM `creature_text` WHERE (`CreatureID` = 30552); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(30552, 0, 0, 'It\'s a strange order, you can\'t deny. Suspicious food? Under that definition, you should arrest Belfast!', 12, 0, 100, 1, 0, 0, 31326, 0, ''), +(30552, 1, 0, '%s nods.', 16, 0, 100, 0, 0, 0, 32046, 0, ''); + +-- Footman James +DELETE FROM `creature_text` WHERE (`CreatureID` = 30553); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(30553, 0, 0, 'Orders are orders. If the Prince says jump...', 12, 0, 100, 1, 0, 0, 31325, 0, ''), +(30553, 1, 0, 'Don\'t worry too much. By the time I went off duty, we hadn\'t found a scrap of befouled grain here.', 12, 0, 100, 1, 0, 0, 32572, 0, ''); + +-- Michael Belfast +DELETE FROM `creature_text` WHERE (`CreatureID` = 30571); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(30571, 0, 0, 'Hey! Stop rooting around in my cellar! Clear out!', 12, 0, 100, 5, 0, 0, 31322, 0, ''), +(30571, 0, 1, 'What were you doing in my cellar? There\'s a food scare going on, and the last thing I need is strangers rummaging around in my goods! Shoo!', 12, 0, 100, 5, 0, 0, 31323, 0, ''), +(30571, 1, 0, 'I HEARD THAT! No more ale for you! Not a drop!', 12, 0, 100, 25, 0, 0, 31327, 0, ''); + +-- Mal Corricks +DELETE FROM `creature_text` WHERE (`CreatureID` = 31017); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(31017, 0, 0, 'Enough, Michael. Business is hurting enough with this scare as it is. We can use every copper.', 12, 0, 100, 274, 0, 0, 32560, 0, ''), +(31017, 1, 0, '%s grudgingly nods.', 16, 0, 100, 0, 0, 0, 32569, 0, ''), +(31017, 2, 0, 'I can\'t argue with that.', 12, 0, 100, 1, 0, 0, 32570, 0, ''); + +-- Gryan Stoutmantle +DELETE FROM `creature_text` WHERE (`CreatureID` = 30561); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(30561, 0, 0, 'The soldiers are doing important work. The safety of the people is more important, Mal, if you\'re interested in your customers living to spend another day.', 12, 0, 0, 1, 0, 0, 32571, 0, ''); + +-- Move old Waypoint into waypoint_data. +DELETE FROM `waypoints` WHERE `entry` IN (3057100); +DELETE FROM `waypoint_data` WHERE `id` IN (3057100); +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +("3057100", 1, 1549.5714, 575.55707, 100.05218, NULL, 10000, 0, 0, 100, 0), +("3057100", 2, 1550.5083, 579.5038, 99.76226, NULL, 10000, 0, 0, 100, 0), +("3057100", 3, 1553.4889, 578.15356, 99.76225, NULL, 10000, 0, 0, 100, 0); + +-- Set New Waypoint (Michael Belfast) +DELETE FROM `waypoint_data` WHERE `id` IN (3057101); +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +("3057101", 1, 1554.98, 588.784, 99.7754, NULL, 0, 0, 0, 100, 0); + +-- Set New SmartAI (Michael Belfast) +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 30571; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 30571); +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 +(30571, 0, 0, 0, 38, 0, 100, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Data Set 0 1 - Say Line 0 (No Repeat)'), +(30571, 0, 1, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 232, 3057100, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Respawn - Start Path 3057100'), +(30571, 0, 2, 3, 108, 0, 100, 0, 1, 3057100, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Point 1 of Path 3057100 Reached - Set Emote State 0'), +(30571, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 66, 1, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4.5, 'Michael Belfast - On Point 1 of Path 3057100 Reached - Set Orientation 4.5'), +(30571, 0, 4, 5, 108, 0, 100, 0, 2, 3057100, 0, 0, 0, 0, 66, 1, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 3, 'Michael Belfast - On Point 2 of Path 3057100 Reached - Set Orientation 3'), +(30571, 0, 5, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 90, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Point 2 of Path 3057100 Reached - Set Flag Standstate Kneel'), +(30571, 0, 6, 7, 108, 0, 100, 0, 3, 3057100, 0, 0, 0, 0, 91, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Point 3 of Path 3057100 Reached - Remove FlagStandstate Kneel'), +(30571, 0, 7, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 69, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Point 3 of Path 3057100 Reached - Set Emote State 69'), +(30571, 0, 8, 0, 52, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 3057100, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Text 0 Over - Run Script'), +(30571, 0, 9, 10, 38, 0, 100, 0, 0, 2, 0, 0, 0, 0, 91, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Data Set 0 2 - Remove FlagStandstate Kneel'), +(30571, 0, 10, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 232, 3057101, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Data Set 0 2 - Start Path 3057101'), +(30571, 0, 12, 0, 38, 0, 100, 0, 0, 3, 0, 0, 0, 0, 232, 3057100, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - On Data Set 0 3 - Start Path 3057100'); + +-- Set New Timed Action List and Remove the old ones (Michael Belfast - Conversation). +DELETE FROM `smart_scripts` WHERE (`source_type` = 9) AND (`entryorguid` IN (3057100, 3057101, 3057102)); +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 +(3057100, 9, 0, 0, 0, 0, 100, 0, 14000, 14000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970878, 30551, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'), +(3057100, 9, 1, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970880, 30553, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'), +(3057100, 9, 2, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970879, 30552, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'), +(3057100, 9, 3, 0, 0, 0, 100, 0, 7000, 7000, 0, 0, 0, 0, 45, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Set Data 0 2'), +(3057100, 9, 4, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1'), +(3057100, 9, 5, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970865, 31017, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'), +(3057100, 9, 6, 0, 0, 0, 100, 0, 7000, 7000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 10, 1970898, 30561, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 0'), +(3057100, 9, 7, 0, 0, 0, 100, 0, 12000, 12000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 1970865, 31017, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1'), +(3057100, 9, 8, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 10, 1970865, 31017, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 2'), +(3057100, 9, 9, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Set Data 0 3'), +(3057100, 9, 10, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 1970880, 30553, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1'), +(3057100, 9, 11, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 1970878, 30551, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1'), +(3057100, 9, 12, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 10, 1970879, 30552, 0, 0, 0, 0, 0, 0, 'Michael Belfast - Actionlist - Say Line 1'); From f5d39a2f8b6d71587b539e8c05d7fc2f36d12763 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 27 Jul 2025 13:31:48 +0000 Subject: [PATCH 16/45] chore(DB): import pending files Referenced commit(s): b6bef0f6af84acec30cde6809f65ac73de213b69 --- .../Stratholme_conversation.sql => db_world/2025_07_27_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Stratholme_conversation.sql => db_world/2025_07_27_02.sql} (99%) diff --git a/data/sql/updates/pending_db_world/Stratholme_conversation.sql b/data/sql/updates/db_world/2025_07_27_02.sql similarity index 99% rename from data/sql/updates/pending_db_world/Stratholme_conversation.sql rename to data/sql/updates/db_world/2025_07_27_02.sql index 4b1f71f6d..10a9ac895 100644 --- a/data/sql/updates/pending_db_world/Stratholme_conversation.sql +++ b/data/sql/updates/db_world/2025_07_27_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_27_01 -> 2025_07_27_02 -- Haerthsinger Forresten DELETE FROM `creature_text` WHERE (`CreatureID` = 30551); From 970b6cf7b61cc57fa65772b6f5922da4ef74d014 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Mon, 28 Jul 2025 02:03:00 -0300 Subject: [PATCH 17/45] fix(Scripts/AhnKahet): Refactor Prince Taldaram vanish and embrace logic (#22524) --- .../ahnkahet/boss_prince_taldaram.cpp | 230 ++++++------------ 1 file changed, 73 insertions(+), 157 deletions(-) diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp index 6a24d9e8e..de9d3e176 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp @@ -56,6 +56,8 @@ enum Misc MAX_EMBRACE_DMG_H = 40000, SUMMON_GROUP_TRIGGERS = 0, + + GROUP_COMBAT_ABILITIES = 1, }; enum Actions @@ -64,15 +66,6 @@ enum Actions ACTION_SPHERE, }; -enum Event -{ - EVENT_PRINCE_FLAME_SPHERES = 1, - EVENT_PRINCE_VANISH, - EVENT_PRINCE_BLOODTHIRST, - EVENT_PRINCE_VANISH_RUN, - EVENT_PRINCE_RESCHEDULE, -}; - enum Yells { //SAY_SPHERE_ACTIVATED = 0, @@ -178,9 +171,12 @@ private: struct boss_taldaram : public BossAI { - boss_taldaram(Creature* pCreature) : BossAI(pCreature, DATA_PRINCE_TALDARAM), - vanishDamage(0) + boss_taldaram(Creature* pCreature) : BossAI(pCreature, DATA_PRINCE_TALDARAM), vanishDamage(0) { + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); } void InitializeAI() override @@ -193,19 +189,16 @@ struct boss_taldaram : public BossAI me->SetImmuneToAll(true); me->SetDisableGravity(true); me->SetHover(true); + if (!me->HasAura(SPELL_BEAM_VISUAL)) - { DoCastSelf(SPELL_BEAM_VISUAL, true); - } me->SummonCreatureGroup(SUMMON_GROUP_TRIGGERS); return; } if (instance->GetPersistentData(DATA_TELDRAM_SPHERE1) == DONE && instance->GetPersistentData(DATA_TELDRAM_SPHERE2) == DONE) - { DoAction(ACTION_REMOVE_PRISON_AT_RESET); - } } void Reset() override @@ -213,7 +206,45 @@ struct boss_taldaram : public BossAI _Reset(); vanishDamage = 0; - vanishTarget_GUID.Clear(); + + me->SetReactState(REACT_AGGRESSIVE); + + ScheduleHealthCheckEvent({ 75, 50, 25 }, [&] { + scheduler.Schedule(1s, [this](TaskContext) + { + Talk(SAY_VANISH); + DoCastSelf(SPELL_VANISH); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->Clear(); + DoStopAttack(); + scheduler.CancelGroup(GROUP_COMBAT_ABILITIES); + }); + }); + } + + void OnAuraRemove(AuraApplication* auraApp, AuraRemoveMode mode) override + { + if (auraApp->GetBase()->GetId() == SPELL_VANISH && mode == AURA_REMOVE_BY_EXPIRE) + { + scheduler.Schedule(2500ms, [this](TaskContext) + { + if (Unit* vanishTarget = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) + { + vanishDamage = 0; + DoCast(vanishTarget, SPELL_SHADOWSTEP); + DoCast(vanishTarget, SPELL_EMBRACE_OF_THE_VAMPYR); + } + + me->m_Events.AddEventAtOffset([&] { + if (!scheduler.IsGroupScheduled(GROUP_COMBAT_ABILITIES)) + { + ScheduleCombatEvents(); + me->SetReactState(REACT_AGGRESSIVE); + me->ResumeChasingVictim(); + } + }, 20s); + }); + } } void DoAction(int32 action) override @@ -256,20 +287,20 @@ struct boss_taldaram : public BossAI } } - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*school*/) override + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageType, SpellSchoolMask school) override { - if (vanishTarget_GUID) + BossAI::DamageTaken(attacker, damage, damageType, school); + + if (me->FindCurrentSpellBySpellId(SPELL_EMBRACE_OF_THE_VAMPYR)) { - if (me->FindCurrentSpellBySpellId(SPELL_EMBRACE_OF_THE_VAMPYR)) + vanishDamage += damage; + if (vanishDamage >= DUNGEON_MODE(MAX_EMBRACE_DMG, MAX_EMBRACE_DMG_H)) { - vanishDamage += damage; - if (vanishDamage >= DUNGEON_MODE(MAX_EMBRACE_DMG, MAX_EMBRACE_DMG_H)) - { - ScheduleCombatEvents(); - me->CastStop(); - vanishTarget_GUID.Clear(); - vanishDamage = 0; - } + ScheduleCombatEvents(); + me->CastStop(); + me->ResumeChasingVictim(); + me->SetReactState(REACT_AGGRESSIVE); + vanishDamage = 0; } } } @@ -283,17 +314,9 @@ struct boss_taldaram : public BossAI void KilledUnit(Unit* victim) override { if (!victim->IsPlayer()) - { return; - } Talk(SAY_SLAY); - - if (vanishTarget_GUID && victim->GetGUID() == vanishTarget_GUID) - { - vanishTarget_GUID.Clear(); - vanishDamage = 0; - } } void JustEngagedWith(Unit* /*who*/) override @@ -309,9 +332,7 @@ struct boss_taldaram : public BossAI void SpellHitTarget(Unit* /*target*/, SpellInfo const* spellInfo) override { if (spellInfo->Id == SPELL_CONJURE_FLAME_SPHERE) - { summons.DoAction(ACTION_SPHERE); - } } void JustSummoned(Creature* summon) override @@ -324,9 +345,7 @@ struct boss_taldaram : public BossAI case NPC_FLAME_SPHERE_3: { if (npc_taldaram_flamesphere* summonAI = dynamic_cast(summon->AI())) - { summonAI->SetVictimPos(victimSperePos); - } break; } @@ -338,130 +357,27 @@ struct boss_taldaram : public BossAI } } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - { - return; - } - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - - while (uint32 const eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_PRINCE_BLOODTHIRST: - { - DoCastVictim(SPELL_BLOODTHIRST); - events.Repeat(10s); - break; - } - case EVENT_PRINCE_FLAME_SPHERES: - { - if (Unit* victim = me->GetVictim()) - { - DoCast(victim, SPELL_CONJURE_FLAME_SPHERE); - victimSperePos = victim->GetPosition(); - } - - if (!events.GetNextEventTime(EVENT_PRINCE_VANISH)) - { - events.RescheduleEvent(EVENT_PRINCE_VANISH, 14s); - } - else - { - // Make sure that Vanish won't get triggered at same time as sphere summon - events.DelayEvents(4s); - } - - events.Repeat(15s); - break; - } - case EVENT_PRINCE_VANISH: - { - //Count alive players - uint8 count = 0; - std::list const t_list = me->GetThreatMgr().GetThreatList(); - if (!t_list.empty()) - { - for (HostileReference const* reference : t_list) - { - if (reference) - { - Unit const* pTarget = ObjectAccessor::GetUnit(*me, reference->getUnitGuid()); - if (pTarget && pTarget->IsPlayer() && pTarget->IsAlive()) - { - ++count; - } - } - } - } - - // He only vanishes if there are 3 or more alive players - if (count > 2) - { - Talk(SAY_VANISH); - DoCastSelf(SPELL_VANISH, false); - if (Unit* pEmbraceTarget = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - { - vanishTarget_GUID = pEmbraceTarget->GetGUID(); - } - - events.CancelEvent(EVENT_PRINCE_FLAME_SPHERES); - events.CancelEvent(EVENT_PRINCE_BLOODTHIRST); - events.ScheduleEvent(EVENT_PRINCE_VANISH_RUN, 2499ms); - } - break; - } - case EVENT_PRINCE_VANISH_RUN: - { - if (Unit* _vanishTarget = ObjectAccessor::GetUnit(*me, vanishTarget_GUID)) - { - vanishDamage = 0; - DoCast(_vanishTarget, SPELL_SHADOWSTEP); - me->CastSpell(_vanishTarget, SPELL_EMBRACE_OF_THE_VAMPYR, false); - me->RemoveAura(SPELL_VANISH); - } - - events.ScheduleEvent(EVENT_PRINCE_RESCHEDULE, 20s); - break; - } - case EVENT_PRINCE_RESCHEDULE: - { - ScheduleCombatEvents(); - break; - } - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - { - return; - } - } - - if (me->IsVisible()) - { - DoMeleeAttackIfReady(); - } - } - private: Position victimSperePos; - ObjectGuid vanishTarget_GUID; uint32 vanishDamage; void ScheduleCombatEvents() { - events.Reset(); - events.RescheduleEvent(EVENT_PRINCE_FLAME_SPHERES, 10s); - events.RescheduleEvent(EVENT_PRINCE_BLOODTHIRST, 10s); - vanishTarget_GUID.Clear(); + scheduler.Schedule(10s, GROUP_COMBAT_ABILITIES, [this](TaskContext context) + { + DoCastVictim(SPELL_BLOODTHIRST); + context.Repeat(15s); + }).Schedule(10s, GROUP_COMBAT_ABILITIES, [this](TaskContext context) + { + if (Unit* victim = me->GetVictim()) + { + DoCast(victim, SPELL_CONJURE_FLAME_SPHERE); + victimSperePos = victim->GetPosition(); + } + + context.Repeat(15s); + }); + vanishDamage = 0; } }; From fc4b2939ee3c336da39f38a60b466770501c9daf Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Sun, 27 Jul 2025 22:04:03 -0700 Subject: [PATCH 18/45] refactor(World/WorldState): use prepared statements (#22582) --- .../Database/Implementation/CharacterDatabase.cpp | 4 ++++ .../Database/Implementation/CharacterDatabase.h | 3 +++ src/server/game/World/WorldState.cpp | 10 +++++++--- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index f8b840ea2..b397b0142 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -616,6 +616,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() "ON DUPLICATE KEY UPDATE state = VALUES(state)", CONNECTION_ASYNC); PrepareStatement(CHAR_DELETE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SANITIZE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id NOT IN (SELECT instance.id FROM instance)", CONNECTION_ASYNC); + + // world_state + PrepareStatement(CHAR_SEL_WORLD_STATE, "SELECT Id, Data FROM world_state", CONNECTION_SYNCH); + PrepareStatement(CHAR_REP_WORLD_STATE, "REPLACE INTO world_state (Id, Data) VALUES(?, ?)", CONNECTION_ASYNC); } CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index d0f5d11a1..e7e05e473 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -528,6 +528,9 @@ enum CharacterDatabaseStatements : uint32 CHAR_DELETE_INSTANCE_SAVED_DATA, CHAR_SANITIZE_INSTANCE_SAVED_DATA, + CHAR_SEL_WORLD_STATE, + CHAR_REP_WORLD_STATE, + MAX_CHARACTERDATABASE_STATEMENTS }; diff --git a/src/server/game/World/WorldState.cpp b/src/server/game/World/WorldState.cpp index cbdf4a91e..0ca7ea0c0 100644 --- a/src/server/game/World/WorldState.cpp +++ b/src/server/game/World/WorldState.cpp @@ -47,7 +47,9 @@ WorldState::~WorldState() void WorldState::Load() { - QueryResult result = CharacterDatabase.Query("SELECT Id, Data FROM world_state"); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_WORLD_STATE); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + if (result) { do @@ -205,8 +207,10 @@ void WorldState::Save(WorldStateSaveIds saveId) 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()); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_WORLD_STATE); + stmt->SetData(0, saveId); + stmt->SetData(1, stringToSave); + CharacterDatabase.Execute(stmt); } bool WorldState::IsConditionFulfilled(uint32 conditionId, uint32 state) const From cb580b986570fc526f2740aa26b0d2e344ac0602 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Tue, 29 Jul 2025 05:08:48 -0700 Subject: [PATCH 19/45] =?UTF-8?q?fix(Core/Spell):=20skip=20Guardian=20pets?= =?UTF-8?q?=20when=20searching=20for=20Party/R=E2=80=A6=20(#22587)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/Spells/Spell.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 6ea6d5d62..af13154c8 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -9040,7 +9040,7 @@ namespace Acore case TARGET_CHECK_PARTY: if (unitTarget->IsTotem()) return false; - if (unitTarget->IsGuardian()) + if (unitTarget->IsGuardian() && !unitTarget->IsControllableGuardian()) return false; if (!_caster->_IsValidAssistTarget(unitTarget, _spellInfo)) return false; @@ -9054,7 +9054,7 @@ namespace Acore case TARGET_CHECK_RAID: if (unitTarget->IsTotem()) return false; - if (unitTarget->IsGuardian()) + if (unitTarget->IsGuardian() && !unitTarget->IsControllableGuardian()) return false; if (!_caster->_IsValidAssistTarget(unitTarget, _spellInfo)) return false; From f31643c72cc20853fdba96fb8da4aa9b514c9f0d Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 29 Jul 2025 05:10:13 -0700 Subject: [PATCH 20/45] feat(Core/Unit): Optimize AuraEffectList container (#22584) --- src/server/game/Entities/Object/Object.cpp | 4 ++-- src/server/game/Entities/Unit/Unit.cpp | 10 +++++----- src/server/game/Entities/Unit/Unit.h | 2 +- src/server/scripts/Spells/spell_warrior.cpp | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index f458ba8a6..1a9d54c82 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1944,8 +1944,8 @@ bool WorldObject::CanDetectInvisibilityOf(WorldObject const* obj) const bool isPermInvisibleCreature = false; if (Creature const* baseObj = ToCreature()) { - auto auraEffects = baseObj->GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY); - for (auto const effect : auraEffects) + Unit::AuraEffectList const& auraEffects = baseObj->GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY); + for (AuraEffect* const effect : auraEffects) { if (SpellInfo const* spell = effect->GetSpellInfo()) { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f6c3e92b3..efc71eab7 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -2256,7 +2256,7 @@ void Unit::CalcAbsorbResist(DamageInfo& dmgInfo, bool Splited) // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation AuraEffectList vSchoolAbsorbCopy(victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB)); - vSchoolAbsorbCopy.sort(Acore::AbsorbAuraOrderPred()); + std::sort(vSchoolAbsorbCopy.begin(), vSchoolAbsorbCopy.end(), Acore::AbsorbAuraOrderPred()); // absorb without mana cost for (AuraEffectList::iterator itr = vSchoolAbsorbCopy.begin(); (itr != vSchoolAbsorbCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr) @@ -2439,7 +2439,7 @@ void Unit::CalcAbsorbResist(DamageInfo& dmgInfo, bool Splited) // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation AuraEffectList vSplitDamagePctCopy(victim->GetAuraEffectsByType(SPELL_AURA_SPLIT_DAMAGE_PCT)); - for (AuraEffectList::iterator itr = vSplitDamagePctCopy.begin(), next; (itr != vSplitDamagePctCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr) + for (AuraEffectList::iterator itr = vSplitDamagePctCopy.begin(); (itr != vSplitDamagePctCopy.end()) && (dmgInfo.GetDamage() > 0); ++itr) { // Check if aura was removed during iteration - we don't need to work on such auras AuraApplication const* aurApp = (*itr)->GetBase()->GetApplicationOfTarget(victim->GetGUID()); @@ -2565,7 +2565,7 @@ void Unit::CalcHealAbsorb(HealInfo& healInfo) { uint32 removedAuras = healInfo.GetTarget()->m_removedAurasCount; auraEff->GetBase()->Remove(AURA_REMOVE_BY_ENEMY_SPELL); - if (removedAuras + 1 < healInfo.GetTarget()->m_removedAurasCount) + if (healInfo.GetTarget()->m_removedAurasCount > removedAuras) i = vHealAbsorb.begin(); } } @@ -4732,7 +4732,7 @@ void Unit::_RegisterAuraEffect(AuraEffect* aurEff, bool apply) if (apply) m_modAuras[aurEff->GetAuraType()].push_back(aurEff); else - m_modAuras[aurEff->GetAuraType()].remove(aurEff); + m_modAuras[aurEff->GetAuraType()].erase(std::remove(m_modAuras[aurEff->GetAuraType()].begin(), m_modAuras[aurEff->GetAuraType()].end(), aurEff), m_modAuras[aurEff->GetAuraType()].end()); } // All aura base removes should go threw this function! @@ -5158,7 +5158,7 @@ void Unit::RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID, Aura* exc { uint32 removedAuras = m_removedAurasCount; RemoveAura(aurApp); - if (m_removedAurasCount > removedAuras + 1) + if (m_removedAurasCount > removedAuras) iter = m_modAuras[auraType].begin(); } } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index ae48d1926..c5d655406 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -633,7 +633,7 @@ public: typedef std::multimap AuraStateAurasMap; typedef std::pair AuraStateAurasMapBounds; - typedef std::list AuraEffectList; + typedef std::vector AuraEffectList; typedef std::list AuraList; typedef std::list AuraApplicationList; typedef std::list Diminishing; diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index faf4af9be..7552b5029 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -910,7 +910,7 @@ class spell_warr_heroic_strike : public SpellScript Unit* target = GetHitUnit(); if (!target) return; - std::list AuraEffectList = target->GetAuraEffectsByType(SPELL_AURA_MOD_DECREASE_SPEED); + Unit::AuraEffectList const& AuraEffectList = target->GetAuraEffectsByType(SPELL_AURA_MOD_DECREASE_SPEED); bool bonusDamage = false; for (AuraEffect* eff : AuraEffectList) { From 57dacae38b37f1230fa6bea03252d345cb7cf4dd Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 29 Jul 2025 05:10:46 -0700 Subject: [PATCH 21/45] feat(Core/Maps): Multithread startup map preloading (#22580) --- src/server/game/Maps/MapUpdater.cpp | 28 ++++++++++++++++++++++++++++ src/server/game/Maps/MapUpdater.h | 1 + src/server/game/World/World.cpp | 18 +++++++++++++----- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/server/game/Maps/MapUpdater.cpp b/src/server/game/Maps/MapUpdater.cpp index 8f8704da5..ea5fe9602 100644 --- a/src/server/game/Maps/MapUpdater.cpp +++ b/src/server/game/Maps/MapUpdater.cpp @@ -18,7 +18,9 @@ #include "MapUpdater.h" #include "DatabaseEnv.h" #include "LFGMgr.h" +#include "Log.h" #include "Map.h" +#include "MapMgr.h" #include "Metric.h" class UpdateRequest @@ -52,6 +54,27 @@ private: uint32 s_diff; }; +class MapPreloadRequest : public UpdateRequest +{ +public: + MapPreloadRequest(uint32 mapId, MapUpdater& updater) + : _mapId(mapId), _updater(updater) + { + } + + void call() override + { + Map* map = sMapMgr->CreateBaseMap(_mapId); + LOG_INFO("server.loading", ">> Loading All Grids For Map {} ({})", map->GetId(), map->GetMapName()); + map->LoadAllGrids(); + _updater.update_finished(); + } + +private: + uint32 _mapId; + MapUpdater& _updater; +}; + class LFGUpdateRequest : public UpdateRequest { public: @@ -120,6 +143,11 @@ void MapUpdater::schedule_update(Map& map, uint32 diff, uint32 s_diff) schedule_task(new MapUpdateRequest(map, *this, diff, s_diff)); } +void MapUpdater::schedule_map_preload(uint32 mapid) +{ + schedule_task(new MapPreloadRequest(mapid, *this)); +} + void MapUpdater::schedule_lfg_update(uint32 diff) { schedule_task(new LFGUpdateRequest(*this, diff)); diff --git a/src/server/game/Maps/MapUpdater.h b/src/server/game/Maps/MapUpdater.h index 174cef977..9576e7e1f 100644 --- a/src/server/game/Maps/MapUpdater.h +++ b/src/server/game/Maps/MapUpdater.h @@ -35,6 +35,7 @@ public: void schedule_task(UpdateRequest* request); void schedule_update(Map& map, uint32 diff, uint32 s_diff); + void schedule_map_preload(uint32 mapid); void schedule_lfg_update(uint32 diff); void wait(); void activate(std::size_t num_threads); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 0ef09fdf2..4a7044993 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1010,15 +1010,23 @@ void World::SetInitialWorldSettings() if (mapEntry && !mapEntry->Instanceable()) { - Map* map = sMapMgr->CreateBaseMap(mapEntry->MapID); - - if (map) + if (sMapMgr->GetMapUpdater()->activated()) + sMapMgr->GetMapUpdater()->schedule_map_preload(mapEntry->MapID); + else { - LOG_INFO("server.loading", ">> Loading All Grids For Map {}", map->GetId()); - map->LoadAllGrids(); + Map* map = sMapMgr->CreateBaseMap(mapEntry->MapID); + + if (map) + { + LOG_INFO("server.loading", ">> Loading All Grids For Map {}", map->GetId()); + map->LoadAllGrids(); + } } } } + + if (sMapMgr->GetMapUpdater()->activated()) + sMapMgr->GetMapUpdater()->wait(); } uint32 startupDuration = GetMSTimeDiffToNow(startupBegin); From 0abb99852932d6e7a1b04d8ad8fc0e3902a1f5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=B9=AD?= <18535853+PkllonG@users.noreply.github.com> Date: Tue, 29 Jul 2025 20:11:25 +0800 Subject: [PATCH 22/45] refactor(Core): Clean (#22583) --- .../game/AuctionHouse/AuctionHouseMgr.cpp | 4 +- src/server/game/Events/GameEventMgr.cpp | 2 +- src/server/game/Globals/ObjectMgr.cpp | 62 +++++++++---------- src/server/game/Maps/MapMgr.cpp | 2 +- src/server/game/Misc/GameGraveyard.cpp | 8 +-- src/server/game/Spells/SpellMgr.cpp | 10 +-- src/server/game/Tickets/TicketMgr.cpp | 2 +- src/server/game/World/World.cpp | 26 ++++---- 8 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index f922e77fc..736fd46e1 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -155,7 +155,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabas } } else - sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans); + RemoveAItem(auction->item_guid, true, &trans); } void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail) @@ -256,7 +256,7 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDat } } else - sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans); + RemoveAItem(auction->item_guid, true, &trans); } //this function sends mail to old bidder diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index 29b813deb..4e98280fc 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -1934,7 +1934,7 @@ void GameEventMgr::SetHolidayEventTime(GameEventData& event) uint32 GameEventMgr::GetHolidayEventId(uint32 holidayId) const { - auto const& events = sGameEventMgr->GetEventMap(); + auto const& events = GetEventMap(); for (auto const& eventEntry : events) { diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 911749df0..d3c07ed21 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -871,7 +871,7 @@ void ObjectMgr::LoadCreatureTemplateAddons() uint32 entry = fields[0].Get(); - if (!sObjectMgr->GetCreatureTemplate(entry)) + if (!GetCreatureTemplate(entry)) { LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_addon`", entry); continue; @@ -1501,7 +1501,7 @@ void ObjectMgr::LoadEquipmentTemplates() uint32 entry = fields[0].Get(); - if (!sObjectMgr->GetCreatureTemplate(entry)) + if (!GetCreatureTemplate(entry)) { LOG_ERROR("sql.sql", "Creature template (CreatureID: {}) does not exist but has a record in `creature_equip_template`", entry); continue; @@ -2455,7 +2455,7 @@ void ObjectMgr::LoadCreatureSparring() ObjectGuid::LowType spawnId = fields[0].Get(); float sparringHealthPct = fields[1].Get(); - if (!sObjectMgr->GetCreatureData(spawnId)) + if (!GetCreatureData(spawnId)) { LOG_ERROR("sql.sql", "Entry {} has a record in `creature_sparring` but doesn't exist in `creatures` table"); continue; @@ -2535,7 +2535,7 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, fl // We use spawn coords to spawn if (!map->Instanceable() && map->IsGridLoaded(x, y)) { - GameObject* go = sObjectMgr->IsGameObjectStaticTransport(data.id) ? new StaticTransport() : new GameObject(); + GameObject* go = IsGameObjectStaticTransport(data.id) ? new StaticTransport() : new GameObject(); if (!go->LoadGameObjectFromDB(spawnId, map)) { LOG_ERROR("sql.sql", "AddGOData: cannot add gameobject entry {} to map", entry); @@ -3012,7 +3012,7 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.Duration = fields[129].Get(); itemTemplate.ItemLimitCategory = uint32(fields[130].Get()); itemTemplate.HolidayId = fields[131].Get(); - itemTemplate.ScriptId = sObjectMgr->GetScriptId(fields[132].Get()); + itemTemplate.ScriptId = GetScriptId(fields[132].Get()); itemTemplate.DisenchantID = fields[133].Get(); itemTemplate.FoodType = uint32(fields[134].Get()); itemTemplate.MinMoneyLoot = fields[135].Get(); @@ -3570,7 +3570,7 @@ void ObjectMgr::LoadItemSetNames() { uint32 entry = *itr; // add data from item_template if available - pProto = sObjectMgr->GetItemTemplate(entry); + pProto = GetItemTemplate(entry); if (pProto) { LOG_ERROR("sql.sql", "Item set part (Entry: {}) does not have entry in `item_set_names`, adding data from `item_template`.", entry); @@ -3617,13 +3617,13 @@ void ObjectMgr::LoadVehicleTemplateAccessories() uint8 uiSummonType = fields[4].Get(); uint32 uiSummonTimer = fields[5].Get(); - if (!sObjectMgr->GetCreatureTemplate(uiEntry)) + if (!GetCreatureTemplate(uiEntry)) { LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} does not exist.", uiEntry); continue; } - if (!sObjectMgr->GetCreatureTemplate(uiAccessory)) + if (!GetCreatureTemplate(uiAccessory)) { LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory {} does not exist.", uiAccessory); continue; @@ -3673,7 +3673,7 @@ void ObjectMgr::LoadVehicleAccessories() uint8 uiSummonType = fields[4].Get(); uint32 uiSummonTimer = fields[5].Get(); - if (!sObjectMgr->GetCreatureTemplate(uiAccessory)) + if (!GetCreatureTemplate(uiAccessory)) { LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory {} does not exist.", uiAccessory); continue; @@ -3765,7 +3765,7 @@ void ObjectMgr::LoadPetLevelInfo() Field* fields = result->Fetch(); uint32 creature_id = fields[0].Get(); - if (!sObjectMgr->GetCreatureTemplate(creature_id)) + if (!GetCreatureTemplate(creature_id)) { LOG_ERROR("sql.sql", "Wrong creature id {} in `pet_levelstats` table, ignoring.", creature_id); continue; @@ -4998,7 +4998,7 @@ void ObjectMgr::LoadQuests() if (qinfo->StartItem) { - if (!sObjectMgr->GetItemTemplate(qinfo->StartItem)) + if (!GetItemTemplate(qinfo->StartItem)) { LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but item with entry {} does not exist, quest can't be done.", qinfo->GetQuestId(), qinfo->StartItem, qinfo->StartItem); @@ -5049,7 +5049,7 @@ void ObjectMgr::LoadQuests() qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER); - if (!sObjectMgr->GetItemTemplate(id)) + if (!GetItemTemplate(id)) { LOG_ERROR("sql.sql", "Quest {} has `RequiredItemId{}` = {} but item with entry {} does not exist, quest can't be done.", qinfo->GetQuestId(), j + 1, id, id); @@ -5069,7 +5069,7 @@ void ObjectMgr::LoadQuests() uint32 id = qinfo->ItemDrop[j]; if (id) { - if (!sObjectMgr->GetItemTemplate(id)) + if (!GetItemTemplate(id)) { LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = {} but item with entry {} does not exist, quest can't be done.", qinfo->GetQuestId(), j + 1, id, id); @@ -5090,14 +5090,14 @@ void ObjectMgr::LoadQuests() for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j) { int32 id = qinfo->RequiredNpcOrGo[j]; - if (id < 0 && !sObjectMgr->GetGameObjectTemplate(-id)) + if (id < 0 && !GetGameObjectTemplate(-id)) { LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but gameobject {} does not exist, quest can't be done.", qinfo->GetQuestId(), j + 1, id, uint32(-id)); qinfo->RequiredNpcOrGo[j] = 0; // quest can't be done for this requirement } - if (id > 0 && !sObjectMgr->GetCreatureTemplate(id)) + if (id > 0 && !GetCreatureTemplate(id)) { LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but creature with entry {} does not exist, quest can't be done.", qinfo->GetQuestId(), j + 1, id, uint32(id)); @@ -5130,7 +5130,7 @@ void ObjectMgr::LoadQuests() uint32 id = qinfo->RewardChoiceItemId[j]; if (id) { - if (!sObjectMgr->GetItemTemplate(id)) + if (!GetItemTemplate(id)) { LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.", qinfo->GetQuestId(), j + 1, id, id); @@ -5176,7 +5176,7 @@ void ObjectMgr::LoadQuests() uint32 id = qinfo->RewardItemId[j]; if (id) { - if (!sObjectMgr->GetItemTemplate(id)) + if (!GetItemTemplate(id)) { LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.", qinfo->GetQuestId(), j + 1, id, id); @@ -5760,7 +5760,7 @@ void ObjectMgr::LoadEventScripts() std::set evt_scripts; // Load all possible script entries from gameobjects - GameObjectTemplateContainer const* gotc = sObjectMgr->GetGameObjectTemplates(); + GameObjectTemplateContainer const* gotc = GetGameObjectTemplates(); for (GameObjectTemplateContainer::const_iterator itr = gotc->begin(); itr != gotc->end(); ++itr) if (uint32 eventId = itr->second.GetEventScriptId()) evt_scripts.insert(eventId); @@ -6069,7 +6069,7 @@ void ObjectMgr::LoadInstanceTemplate() instanceTemplate.AllowMount = fields[3].Get(); instanceTemplate.Parent = uint32(fields[1].Get()); - instanceTemplate.ScriptId = sObjectMgr->GetScriptId(fields[2].Get()); + instanceTemplate.ScriptId = GetScriptId(fields[2].Get()); _instanceTemplateStore[mapID] = instanceTemplate; @@ -6515,14 +6515,14 @@ void ObjectMgr::LoadQuestGreetings() switch (type) { case 0: // Creature - if (!sObjectMgr->GetCreatureTemplate(id)) + if (!GetCreatureTemplate(id)) { LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template entry {} does not exist.", id); continue; } break; case 1: // GameObject - if (!sObjectMgr->GetGameObjectTemplate(id)) + if (!GetGameObjectTemplate(id)) { LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template entry {} does not exist.", id); continue; @@ -6568,14 +6568,14 @@ void ObjectMgr::LoadQuestGreetingsLocales() switch (type) { case 0: // Creature - if (!sObjectMgr->GetCreatureTemplate(id)) + if (!GetCreatureTemplate(id)) { LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: creature template entry {} does not exist.", id); continue; } break; case 1: // GameObject - if (!sObjectMgr->GetGameObjectTemplate(id)) + if (!GetGameObjectTemplate(id)) { LOG_ERROR("sql.sql", "Table `quest_greeting_locale`: gameobject template entry {} does not exist.", id); continue; @@ -7577,7 +7577,7 @@ void ObjectMgr::LoadGameObjectTemplateAddons() uint32 entry = fields[0].Get(); - GameObjectTemplate const* got = sObjectMgr->GetGameObjectTemplate(entry); + GameObjectTemplate const* got = GetGameObjectTemplate(entry); if (!got) { LOG_ERROR("sql.sql", @@ -8222,7 +8222,7 @@ void ObjectMgr::LoadNPCSpellClickSpells() // all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data // NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories - CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates(); + CreatureTemplateContainer const* ctc = GetCreatureTemplates(); for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr) { if ((itr->second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && _spellClickInfoStore.find(itr->second.Entry) == _spellClickInfoStore.end()) @@ -8747,7 +8747,7 @@ void ObjectMgr::LoadGameObjectForQuests() { uint32 oldMSTime = getMSTime(); - if (sObjectMgr->GetGameObjectTemplates()->empty()) + if (GetGameObjectTemplates()->empty()) { LOG_WARN("server.loading", ">> Loaded 0 GameObjects for quests"); LOG_INFO("server.loading", " "); @@ -8757,7 +8757,7 @@ void ObjectMgr::LoadGameObjectForQuests() uint32 count = 0; // collect GO entries for GO that must activated - GameObjectTemplateContainer* gotc = const_cast(sObjectMgr->GetGameObjectTemplates()); + GameObjectTemplateContainer* gotc = const_cast(GetGameObjectTemplates()); for (GameObjectTemplateContainer::iterator itr = gotc->begin(); itr != gotc->end(); ++itr) { itr->second.IsForQuests = false; @@ -9666,7 +9666,7 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= tru bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 item_id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* player, std::set* /*skip_vendors*/, uint32 /*ORnpcflag*/) const { /* - CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry); + CreatureTemplate const* cInfo = GetCreatureTemplate(vendor_entry); if (!cInfo) { if (player) @@ -10040,7 +10040,7 @@ void ObjectMgr::LoadCreatureClassLevelStats() ++count; } while (result->NextRow()); - CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates(); + CreatureTemplateContainer const* ctc = GetCreatureTemplates(); for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr) { for (uint16 lvl = itr->second.minlevel; lvl <= itr->second.maxlevel; ++lvl) @@ -10148,9 +10148,9 @@ void ObjectMgr::LoadFactionChangeQuests() uint32 alliance = fields[0].Get(); uint32 horde = fields[1].Get(); - if (!sObjectMgr->GetQuestTemplate(alliance)) + if (!GetQuestTemplate(alliance)) LOG_ERROR("sql.sql", "Quest {} (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance); - else if (!sObjectMgr->GetQuestTemplate(horde)) + else if (!GetQuestTemplate(horde)) LOG_ERROR("sql.sql", "Quest {} (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde); else FactionChangeQuests[alliance] = horde; diff --git a/src/server/game/Maps/MapMgr.cpp b/src/server/game/Maps/MapMgr.cpp index f6e78a40a..e113c93d2 100644 --- a/src/server/game/Maps/MapMgr.cpp +++ b/src/server/game/Maps/MapMgr.cpp @@ -223,7 +223,7 @@ Map::EnterState MapMgr::PlayerCannotEnter(uint32 mapid, Player* player, bool log { uint32 destInstId = sInstanceSaveMgr->PlayerGetDestinationInstanceId(player, mapid, targetDifficulty); if (destInstId) - if (Map* boundMap = sMapMgr->FindMap(mapid, destInstId)) + if (Map* boundMap = FindMap(mapid, destInstId)) if (Map::EnterState denyReason = boundMap->CannotEnter(player, loginCheck)) return denyReason; } diff --git a/src/server/game/Misc/GameGraveyard.cpp b/src/server/game/Misc/GameGraveyard.cpp index 108c2fb64..338e31ff4 100644 --- a/src/server/game/Misc/GameGraveyard.cpp +++ b/src/server/game/Misc/GameGraveyard.cpp @@ -91,7 +91,7 @@ GraveyardStruct const* Graveyard::GetDefaultGraveyard(TeamId teamId) ALLIANCE_GRAVEYARD = 4, // Westfall }; - return sGraveyard->GetGraveyard(teamId == TEAM_HORDE ? HORDE_GRAVEYARD : ALLIANCE_GRAVEYARD); + return GetGraveyard(teamId == TEAM_HORDE ? HORDE_GRAVEYARD : ALLIANCE_GRAVEYARD); } GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId teamId, bool nearCorpse) @@ -100,7 +100,7 @@ GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId tea sScriptMgr->OnPlayerBeforeChooseGraveyard(player, teamId, nearCorpse, graveyardOverride); if (graveyardOverride) { - return sGraveyard->GetGraveyard(graveyardOverride); + return GetGraveyard(graveyardOverride); } WorldLocation loc = player->GetWorldLocation(); @@ -182,7 +182,7 @@ GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId tea for (; range.first != range.second; ++range.first) { GraveyardData const& graveyardLink = range.first->second; - GraveyardStruct const* entry = sGraveyard->GetGraveyard(graveyardLink.safeLocId); + GraveyardStruct const* entry = GetGraveyard(graveyardLink.safeLocId); if (!entry) { LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing `game_graveyard` table {}, skipped.", graveyardLink.safeLocId); @@ -388,7 +388,7 @@ void Graveyard::LoadGraveyardZones() uint32 team = fields[2].Get(); TeamId teamId = team == 0 ? TEAM_NEUTRAL : (team == ALLIANCE ? TEAM_ALLIANCE : TEAM_HORDE); - GraveyardStruct const* entry = sGraveyard->GetGraveyard(safeLocId); + GraveyardStruct const* entry = GetGraveyard(safeLocId); if (!entry) { LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for not existing `game_graveyard` table {}, skipped.", safeLocId); diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 3c5a4ccf6..534388fd4 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -1614,7 +1614,7 @@ void SpellMgr::LoadSpellTargetPositions() } if (found) { - if (!sSpellMgr->GetSpellTargetPosition(i)) + if (!GetSpellTargetPosition(i)) LOG_DEBUG("spells.aura", "Spell (ID: {}) does not have record in `spell_target_position`", i); } }*/ @@ -2348,7 +2348,7 @@ void SpellMgr::LoadPetLevelupSpellMap() LOG_INFO("server.loading", " "); } -bool LoadPetDefaultSpells_helper(CreatureTemplate const* cInfo, PetDefaultSpellsEntry& petDefSpells) +static bool LoadPetDefaultSpells_helper(CreatureTemplate const* cInfo, PetDefaultSpellsEntry& petDefSpells) { // skip empty list; bool have_spell = false; @@ -3448,9 +3448,9 @@ void SpellMgr::LoadSpellInfoCustomAttributes() spellInfo->_InitializeExplicitTargetMask(); - if (sSpellMgr->HasSpellCooldownOverride(spellInfo->Id)) + if (HasSpellCooldownOverride(spellInfo->Id)) { - SpellCooldownOverride spellOverride = sSpellMgr->GetSpellCooldownOverride(spellInfo->Id); + SpellCooldownOverride spellOverride = GetSpellCooldownOverride(spellInfo->Id); if (spellInfo->RecoveryTime != spellOverride.RecoveryTime) { @@ -3497,7 +3497,7 @@ void SpellMgr::LoadSpellInfoCustomAttributes() case SPELL_AURA_PERIODIC_TRIGGER_SPELL: case SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT: case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE: - if (SpellInfo const* triggerSpell = sSpellMgr->GetSpellInfo(spellInfo->Effects[j].TriggerSpell)) + if (SpellInfo const* triggerSpell = GetSpellInfo(spellInfo->Effects[j].TriggerSpell)) { overrideAttr = true; if (triggerSpell->AttributesCu & SPELL_ATTR0_CU_BINARY_SPELL) diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp index f339be0e1..543b40cd5 100644 --- a/src/server/game/Tickets/TicketMgr.cpp +++ b/src/server/game/Tickets/TicketMgr.cpp @@ -286,7 +286,7 @@ void TicketMgr::ResetTickets() { uint32 ticketId = itr->second->GetId(); ++itr; - sTicketMgr->RemoveTicket(ticketId); + RemoveTicket(ticketId); } else ++itr; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 4a7044993..c8f330abd 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -217,10 +217,10 @@ void World::LoadConfigSettings(bool reload) //visibility on continents _maxVisibleDistanceOnContinents = sConfigMgr->GetOption("Visibility.Distance.Continents", DEFAULT_VISIBILITY_DISTANCE); - if (_maxVisibleDistanceOnContinents < 45 * sWorld->getRate(RATE_CREATURE_AGGRO)) + if (_maxVisibleDistanceOnContinents < 45 * getRate(RATE_CREATURE_AGGRO)) { - LOG_ERROR("server.loading", "Visibility.Distance.Continents can't be less max aggro radius {}", 45 * sWorld->getRate(RATE_CREATURE_AGGRO)); - _maxVisibleDistanceOnContinents = 45 * sWorld->getRate(RATE_CREATURE_AGGRO); + LOG_ERROR("server.loading", "Visibility.Distance.Continents can't be less max aggro radius {}", 45 * getRate(RATE_CREATURE_AGGRO)); + _maxVisibleDistanceOnContinents = 45 * getRate(RATE_CREATURE_AGGRO); } else if (_maxVisibleDistanceOnContinents > MAX_VISIBILITY_DISTANCE) { @@ -230,10 +230,10 @@ void World::LoadConfigSettings(bool reload) //visibility in instances _maxVisibleDistanceInInstances = sConfigMgr->GetOption("Visibility.Distance.Instances", DEFAULT_VISIBILITY_INSTANCE); - if (_maxVisibleDistanceInInstances < 45 * sWorld->getRate(RATE_CREATURE_AGGRO)) + if (_maxVisibleDistanceInInstances < 45 * getRate(RATE_CREATURE_AGGRO)) { - LOG_ERROR("server.loading", "Visibility.Distance.Instances can't be less max aggro radius {}", 45 * sWorld->getRate(RATE_CREATURE_AGGRO)); - _maxVisibleDistanceInInstances = 45 * sWorld->getRate(RATE_CREATURE_AGGRO); + LOG_ERROR("server.loading", "Visibility.Distance.Instances can't be less max aggro radius {}", 45 * getRate(RATE_CREATURE_AGGRO)); + _maxVisibleDistanceInInstances = 45 * getRate(RATE_CREATURE_AGGRO); } else if (_maxVisibleDistanceInInstances > MAX_VISIBILITY_DISTANCE) { @@ -243,10 +243,10 @@ void World::LoadConfigSettings(bool reload) //visibility in BG/Arenas _maxVisibleDistanceInBGArenas = sConfigMgr->GetOption("Visibility.Distance.BGArenas", DEFAULT_VISIBILITY_BGARENAS); - if (_maxVisibleDistanceInBGArenas < 45 * sWorld->getRate(RATE_CREATURE_AGGRO)) + if (_maxVisibleDistanceInBGArenas < 45 * getRate(RATE_CREATURE_AGGRO)) { - LOG_ERROR("server.loading", "Visibility.Distance.BGArenas can't be less max aggro radius {}", 45 * sWorld->getRate(RATE_CREATURE_AGGRO)); - _maxVisibleDistanceInBGArenas = 45 * sWorld->getRate(RATE_CREATURE_AGGRO); + LOG_ERROR("server.loading", "Visibility.Distance.BGArenas can't be less max aggro radius {}", 45 * getRate(RATE_CREATURE_AGGRO)); + _maxVisibleDistanceInBGArenas = 45 * getRate(RATE_CREATURE_AGGRO); } else if (_maxVisibleDistanceInBGArenas > MAX_VISIBILITY_DISTANCE) { @@ -1000,7 +1000,7 @@ void World::SetInitialWorldSettings() sScriptMgr->OnBeforeWorldInitialized(); - if (sWorld->getBoolConfig(CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS)) + if (getBoolConfig(CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS)) { LOG_INFO("server.loading", "Loading All Grids For All Non-Instanced Maps..."); @@ -1196,7 +1196,7 @@ void World::Update(uint32 diff) } ///
  • Clean logs table - if (sWorld->getIntConfig(CONFIG_LOGDB_CLEARTIME) > 0) // if not enabled, ignore the timer + if (getIntConfig(CONFIG_LOGDB_CLEARTIME) > 0) // if not enabled, ignore the timer { if (_timers[WUPDATE_CLEANDB].Passed()) { @@ -1205,7 +1205,7 @@ void World::Update(uint32 diff) _timers[WUPDATE_CLEANDB].Reset(); LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_OLD_LOGS); - stmt->SetData(0, sWorld->getIntConfig(CONFIG_LOGDB_CLEARTIME)); + stmt->SetData(0, getIntConfig(CONFIG_LOGDB_CLEARTIME)); stmt->SetData(1, uint32(currentGameTime.count())); LoginDatabase.Execute(stmt); } @@ -1222,7 +1222,7 @@ void World::Update(uint32 diff) sMapMgr->Update(diff); } - if (sWorld->getBoolConfig(CONFIG_AUTOBROADCAST)) + if (getBoolConfig(CONFIG_AUTOBROADCAST)) { if (_timers[WUPDATE_AUTOBROADCAST].Passed()) { From 5031483ad1852c9c514832195cd5dea6097cd612 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 29 Jul 2025 09:12:51 -0300 Subject: [PATCH 23/45] fix(DB/SAI): Fix Poisonous Mushroom cast logic and move script to SAI (#22567) --- .../rev_1753473167596633100.sql | 35 ++++++ .../AzjolNerub/ahnkahet/boss_amanitar.cpp | 117 +----------------- 2 files changed, 41 insertions(+), 111 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1753473167596633100.sql diff --git a/data/sql/updates/pending_db_world/rev_1753473167596633100.sql b/data/sql/updates/pending_db_world/rev_1753473167596633100.sql new file mode 100644 index 000000000..9a93c9f49 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1753473167596633100.sql @@ -0,0 +1,35 @@ +-- +DELETE FROM `creature_template_addon` WHERE `entry` IN (31461, 31462); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(31461, 0, 0, 0, 0, 0, 0, '31690 56740'), +(31462, 0, 0, 0, 0, 0, 0, '31690 56741'); + +UPDATE `creature_template` SET `unit_flags` = `unit_flags` &~2&~33554432, `flags_extra` = `flags_extra` &~128, `ScriptName` = '' WHERE `entry` IN (30435, 30391, 31461, 31462); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 30435; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 30435); +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 +(30435, 0, 0, 0, 60, 0, 100, 1, 1000, 1000, 0, 0, 0, 0, 11, 57059, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On Update - Cast \'Serverside - Grow\' (No Repeat)'), +(30435, 0, 1, 0, 26, 0, 100, 1, 0, 3, 0, 0, 1, 0, 223, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - In Combat LoS - Do Action ID 1 (No Repeat)'), +(30435, 0, 2, 3, 2, 0, 100, 0, 0, 5, 0, 0, 0, 0, 11, 31691, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Between 0-5% Health - Cast \'Serverside - Shrink\''), +(30435, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 4000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Between 0-5% Health - Despawn In 4000 ms'), +(30435, 0, 4, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On Respawn - Set Reactstate Passive'), +(30435, 0, 5, 0, 32, 0, 100, 1, 1, 150, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On Damaged Between 1-150 - Do Action ID 1 (No Repeat)'), +(30435, 0, 6, 0, 72, 0, 100, 1, 1, 0, 0, 0, 0, 0, 80, 3043500, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On Action 1 Done - Run Script (No Repeat)'), +(30435, 0, 7, 0, 101, 0, 100, 0, 1, 0, 4000, 4000, 4000, 0, 28, 56648, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - On 1 or More Players in Range - Remove Aura \'Potent Fungus\''); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 3043500) AND (`source_type` = 9) AND (`id` IN (0, 1, 2)); +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 +(3043500, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 57061, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Actionlist - Cast \'Poison Cloud\''), +(3043500, 9, 1, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 0, 11, 31691, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Actionlist - Cast \'Serverside - Shrink\''), +(3043500, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 3000, 3000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Poisonous Mushroom - Actionlist - Despawn In 3000 ms'); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 30391; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 30391); +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 +(30391, 0, 0, 0, 60, 0, 100, 0, 1000, 1000, 0, 0, 0, 0, 11, 57059, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Healthy Mushroom - On Update - Cast \'Serverside - Grow\''), +(30391, 0, 1, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Healthy Mushroom - On Respawn - Set Reactstate Passive'), +(30391, 0, 2, 3, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 56648, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Healthy Mushroom - On Just Died - Cast \'Potent Fungus\''), +(30391, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 31691, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Healthy Mushroom - On Just Died - Cast \'Serverside - Shrink\''); diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp index 15a562623..fcebd8c01 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_amanitar.cpp @@ -31,14 +31,7 @@ enum Spells SPELL_REMOVE_MUSHROOM_POWER = 57283, // Mushroom - SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS = 56648, - SPELL_POISONOUS_MUSHROOM_POISON_CLOUD = 57061, - SPELL_POISONOUS_MUSHROOM_VISUAL_AURA = 56741, - SPELL_POISONOUS_MUSHROOM_VISUAL_AREA = 61566, // Self - SPELL_HEALTHY_MUSHROOM_VISUAL_AURA = 56740, - SPELL_PUTRID_MUSHROOM = 31690, - SPELL_GROW = 57059, - SPELL_SHRINK = 31691, + SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS = 56648 }; enum Creatures @@ -94,13 +87,12 @@ Position const MushroomPositions[MAX_MUSHROOMS_COUNT] = struct boss_amanitar : public BossAI { - boss_amanitar(Creature* creature) : BossAI(creature, DATA_AMANITAR), mushroomsSummoned(false) { } + boss_amanitar(Creature* creature) : BossAI(creature, DATA_AMANITAR) { } void Reset() override { _Reset(); _mushroomsDeque.clear(); - mushroomsSummoned = false; } void JustEngagedWith(Unit* /*attacker*/) override @@ -110,7 +102,7 @@ struct boss_amanitar : public BossAI }, 10s, 15s); ScheduleTimedEvent(10s, 14s, [&] { - DoCastVictim(SPELL_BASH, false); + DoCastVictim(SPELL_BASH); }, 15s, 20s); ScheduleTimedEvent(15s, 20s, [&] { @@ -153,10 +145,10 @@ struct boss_amanitar : public BossAI instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI); } - void SummonedCreatureDies(Creature* summon, Unit* killer) override + void SummonedCreatureDespawn(Creature* summon) override { _mushroomsDeque.push_back(summon->GetPosition()); - BossAI::SummonedCreatureDies(summon, killer); + BossAI::SummonedCreatureDespawn(summon); } void EnterEvadeMode(EvadeReason why) override @@ -167,7 +159,6 @@ struct boss_amanitar : public BossAI private: std::deque _mushroomsDeque; - bool mushroomsSummoned; void SummonMushroom(Position const& pos) { @@ -175,98 +166,6 @@ private: } }; -struct npc_amanitar_mushrooms : public ScriptedAI -{ - npc_amanitar_mushrooms(Creature* pCreature) : ScriptedAI(pCreature) - { - me->SetCombatMovement(false); - - //TODO: this prolly needs to be done in database - pCreature->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - pCreature->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - pCreature->SetRegeneratingHealth(false); - } - - // Disabled events - void JustEngagedWith(Unit* /*who*/) override {} - void AttackStart(Unit* /*victim*/) override {} - void EnterEvadeMode(EvadeReason /*why*/) override {} - - void Reset() override - { - me->SetReactState(REACT_PASSIVE); - DoCastSelf(SPELL_PUTRID_MUSHROOM); - - if (me->GetEntry() == NPC_POISONOUS_MUSHROOM) - { - DoCastSelf(SPELL_POISONOUS_MUSHROOM_VISUAL_AURA, true); - } - else - { - DoCastSelf(SPELL_HEALTHY_MUSHROOM_VISUAL_AURA, true); - } - - events.ScheduleEvent(EVENT_GROW, 800ms); - - if (me->GetEntry() == NPC_POISONOUS_MUSHROOM) - { - events.ScheduleEvent(EVENT_CHECK_PLAYER, 250ms); - } - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override - { - if (me->GetEntry() == NPC_HEALTHY_MUSHROOM && damage >= me->GetHealth()) - { - DoCastSelf(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS, true); - } - } - - void UpdateAI(uint32 diff) override - { - if (events.Empty()) - return; - - events.Update(diff); - while (uint32 const eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_GROW: - { - DoCastSelf(SPELL_GROW); - break; - } - case EVENT_CHECK_PLAYER: - { - if (Player* plr = me->SelectNearestPlayer(2.0f)) - { - plr->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS); - DoCastSelf(SPELL_POISONOUS_MUSHROOM_VISUAL_AREA); - DoCastSelf(SPELL_POISONOUS_MUSHROOM_POISON_CLOUD); - DoCastSelf(SPELL_SHRINK); - events.ScheduleEvent(EVENT_KILLSELF, 4s); - } - else - { - events.Repeat(250ms); - } - - break; - } - case EVENT_KILLSELF: - { - me->DisappearAndDie(); - break; - } - } - } - } - -private: - EventMap events; -}; - // 57283 Remove Mushroom Power class spell_amanitar_remove_mushroom_power : public AuraScript { @@ -274,8 +173,7 @@ class spell_amanitar_remove_mushroom_power : public AuraScript void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (Unit* target = GetTarget()) - target->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS); + GetTarget()->RemoveAurasDueToSpell(SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS); } void Register() override @@ -287,8 +185,5 @@ class spell_amanitar_remove_mushroom_power : public AuraScript void AddSC_boss_amanitar() { RegisterAhnKahetCreatureAI(boss_amanitar); - RegisterAhnKahetCreatureAI(npc_amanitar_mushrooms); - - // Spells RegisterSpellScript(spell_amanitar_remove_mushroom_power); } From 956f4b07515898382c3ce95d666bdab7e697c794 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 29 Jul 2025 12:13:56 +0000 Subject: [PATCH 24/45] chore(DB): import pending files Referenced commit(s): 5031483ad1852c9c514832195cd5dea6097cd612 --- .../rev_1753473167596633100.sql => db_world/2025_07_29_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1753473167596633100.sql => db_world/2025_07_29_00.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1753473167596633100.sql b/data/sql/updates/db_world/2025_07_29_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1753473167596633100.sql rename to data/sql/updates/db_world/2025_07_29_00.sql index 9a93c9f49..a159d4be9 100644 --- a/data/sql/updates/pending_db_world/rev_1753473167596633100.sql +++ b/data/sql/updates/db_world/2025_07_29_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_27_02 -> 2025_07_29_00 -- DELETE FROM `creature_template_addon` WHERE `entry` IN (31461, 31462); INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES From a77bd191043cdfc4155c484b70e4421823c13218 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 29 Jul 2025 05:18:07 -0700 Subject: [PATCH 25/45] feat(Core/Maps): Remove worldobject container switch functionality (#22586) --- .../game/Entities/Creature/Creature.cpp | 1 - src/server/game/Entities/Creature/Creature.h | 2 - src/server/game/Entities/Object/Object.cpp | 19 --- src/server/game/Entities/Object/Object.h | 4 +- src/server/game/Entities/Unit/Unit.cpp | 9 +- src/server/game/Grids/GridDefines.h | 2 +- src/server/game/Maps/Map.cpp | 118 ------------------ src/server/game/Maps/Map.h | 3 - src/server/game/Spells/SpellEffects.cpp | 2 +- 9 files changed, 6 insertions(+), 154 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 11952e02c..54f6abdb0 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -296,7 +296,6 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(), ResetLootMode(); // restore default loot mode TriggerJustRespawned = false; - m_isTempWorldObject = false; _focusSpell = nullptr; m_respawnedTime = time_t(0); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 49a7854cc..06444ef46 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -376,8 +376,6 @@ public: float m_SightDistance, m_CombatDistance; - bool m_isTempWorldObject; //true when possessed - // Handling caster facing during spellcast void SetTarget(ObjectGuid guid = ObjectGuid::Empty) override; void ClearTarget() { SetTarget(); }; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 1a9d54c82..c87395169 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1072,25 +1072,6 @@ void WorldObject::Update(uint32 diff) sScriptMgr->OnWorldObjectUpdate(this, diff); } -void WorldObject::SetWorldObject(bool on) -{ - if (!IsInWorld()) - return; - - GetMap()->AddObjectToSwitchList(this, on); -} - -bool WorldObject::IsWorldObject() const -{ - if (m_isWorldObject) - return true; - - if (ToCreature() && ToCreature()->m_isTempWorldObject) - return true; - - return false; -} - void WorldObject::setActive(bool on) { if (m_isActive == on) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index db634761a..c432d8575 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -635,9 +635,7 @@ public: [[nodiscard]] bool IsFarVisible() const { return m_isFarVisible; } [[nodiscard]] bool IsVisibilityOverridden() const { return m_visibilityDistanceOverride.has_value(); } void SetVisibilityDistanceOverride(VisibilityDistanceType type); - void SetWorldObject(bool apply); - [[nodiscard]] bool IsPermanentWorldObject() const { return m_isWorldObject; } - [[nodiscard]] bool IsWorldObject() const; + [[nodiscard]] bool IsWorldObject() const { return m_isWorldObject; } [[nodiscard]] bool IsInWintergrasp() const { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index efc71eab7..5532033dd 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -11212,10 +11212,8 @@ Unit* Unit::GetNextRandomRaidMemberOrPet(float radius) void Unit::AddPlayerToVision(Player* player) { if (m_sharedVision.empty()) - { setActive(true); - SetWorldObject(true); - } + m_sharedVision.push_back(player); player->m_isInSharedVisionOf.insert(this); } @@ -11225,11 +11223,10 @@ void Unit::RemovePlayerFromVision(Player* player) { m_sharedVision.remove(player); player->m_isInSharedVisionOf.erase(this); + + /// @todo: This isn't right, if a previously active object was set to active with e.g. Mind Vision this will make them no longer active if (m_sharedVision.empty()) - { setActive(false); - SetWorldObject(false); - } } void Unit::RemoveBindSightAuras() diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index eebdb3fd3..b70353112 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -52,7 +52,7 @@ class ObjectGuid; #define MAP_HALFSIZE (MAP_SIZE/2) // Creature used instead pet to simplify *::Visit templates (not required duplicate code for Creature->Pet case) -typedef TYPELIST_5(GameObject, Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/) AllWorldObjectTypes; +typedef TYPELIST_4(GameObject, Player, Creature/*pets*/, Corpse/*resurrectable*/) AllWorldObjectTypes; typedef TYPELIST_4(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/) AllGridObjectTypes; typedef TYPELIST_5(Creature, GameObject, DynamicObject, Pet, Corpse) AllMapStoredObjectTypes; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index cf1e22543..bed34b9a5 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -175,81 +175,6 @@ void Map::AddToGrid(Corpse* obj, Cell const& cell) } } -template -void Map::SwitchGridContainers(T* /*obj*/, bool /*on*/) -{ -} - -template<> -void Map::SwitchGridContainers(Creature* obj, bool on) -{ - ASSERT(!obj->IsPermanentWorldObject()); - CellCoord p = Acore::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); - if (!p.IsCoordValid()) - { - LOG_ERROR("maps", "Map::SwitchGridContainers: Object {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]", - obj->GetGUID().ToString(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord); - return; - } - - Cell cell(p); - if (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y))) - return; - - LOG_DEBUG("maps", "Switch object {} from grid[{}, {}] {}", obj->GetGUID().ToString(), cell.GridX(), cell.GridY(), on); - MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY()); - ASSERT(grid); - - obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add - - if (on) - { - grid->AddWorldObject(cell.CellX(), cell.CellY(), obj); - AddWorldObject(obj); - } - else - { - grid->AddGridObject(cell.CellX(), cell.CellY(), obj); - RemoveWorldObject(obj); - } - - obj->m_isTempWorldObject = on; -} - -template<> -void Map::SwitchGridContainers(GameObject* obj, bool on) -{ - ASSERT(!obj->IsPermanentWorldObject()); - CellCoord p = Acore::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); - if (!p.IsCoordValid()) - { - LOG_ERROR("maps", "Map::SwitchGridContainers: Object {} has invalid coordinates X:{} Y:{} grid cell [{}:{}]", - obj->GetGUID().ToString(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord); - return; - } - - Cell cell(p); - if (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y))) - return; - - //LOG_DEBUG(LOG_FILTER_MAPS, "Switch object {} from grid[{}, {}] {}", obj->GetGUID().ToString(), cell.data.Part.grid_x, cell.data.Part.grid_y, on); - MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY()); - ASSERT(grid); - - obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add - - if (on) - { - grid->AddWorldObject(cell.CellX(), cell.CellY(), obj); - AddWorldObject(obj); - } - else - { - grid->AddGridObject(cell.CellX(), cell.CellY(), obj); - RemoveWorldObject(obj); - } -} - template void Map::DeleteFromWorld(T* obj) { @@ -1843,49 +1768,8 @@ void Map::AddObjectToRemoveList(WorldObject* obj) //LOG_DEBUG("maps", "Object ({}) added to removing list.", obj->GetGUID().ToString()); } -void Map::AddObjectToSwitchList(WorldObject* obj, bool on) -{ - ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId()); - // i_objectsToSwitch is iterated only in Map::RemoveAllObjectsInRemoveList() and it uses - // the contained objects only if IsCreature() , so we can return in all other cases - if (!obj->IsCreature() && !obj->IsGameObject()) - return; - - std::map::iterator itr = i_objectsToSwitch.find(obj); - if (itr == i_objectsToSwitch.end()) - i_objectsToSwitch.insert(itr, std::make_pair(obj, on)); - else if (itr->second != on) - i_objectsToSwitch.erase(itr); - else - ABORT(); -} - void Map::RemoveAllObjectsInRemoveList() { - while (!i_objectsToSwitch.empty()) - { - std::map::iterator itr = i_objectsToSwitch.begin(); - WorldObject* obj = itr->first; - bool on = itr->second; - i_objectsToSwitch.erase(itr); - - if (!obj->IsPermanentWorldObject()) - { - switch (obj->GetTypeId()) - { - case TYPEID_UNIT: - SwitchGridContainers(obj->ToCreature(), on); - break; - case TYPEID_GAMEOBJECT: - SwitchGridContainers(obj->ToGameObject(), on); - break; - default: - break; - } - } - } - - //LOG_DEBUG("maps", "Object remover 1 check."); while (!i_objectsToRemove.empty()) { std::unordered_set::iterator itr = i_objectsToRemove.begin(); @@ -1923,8 +1807,6 @@ void Map::RemoveAllObjectsInRemoveList() break; } } - - //LOG_DEBUG("maps", "Object remover 2 check."); } uint32 Map::GetPlayersCountExceptGMs() const diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 7f70020dc..c92c52cc6 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -306,7 +306,6 @@ public: } void AddObjectToRemoveList(WorldObject* obj); - void AddObjectToSwitchList(WorldObject* obj, bool on); virtual void DelayedUpdate(const uint32 diff); void resetMarkedCells() { marked_cells.reset(); } @@ -336,7 +335,6 @@ public: template void RemoveFromActive(T* obj); - template void SwitchGridContainers(T* obj, bool on); CreatureGroupHolderType CreatureGroupHolder; void UpdateIteratorBack(Player* player); @@ -577,7 +575,6 @@ private: bool i_scriptLock; std::unordered_set i_objectsToRemove; - std::map i_objectsToSwitch; std::unordered_set i_worldObjects; typedef std::multimap ScriptScheduleMap; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 74a1a1bba..dbacbb56b 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2731,7 +2731,7 @@ void Spell::EffectAddFarsight(SpellEffIndex effIndex) // Remove old farsight if exist bool updateViewerVisibility = m_caster->RemoveDynObject(m_spellInfo->Id); - DynamicObject* dynObj = new DynamicObject(true); + DynamicObject* dynObj = new DynamicObject(false); if (!dynObj->CreateDynamicObject(m_caster->GetMap()->GenerateLowGuid(), m_caster, m_spellInfo->Id, *destTarget, radius, DYNAMIC_OBJECT_FARSIGHT_FOCUS)) { delete dynObj; From 98eda3684d7dfa0da160c88539ac762336b7dc70 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Tue, 29 Jul 2025 05:36:36 -0700 Subject: [PATCH 26/45] feat(Core/Spell): implement `SPELL_ATTR2_CHAIN_FROM_CASTER` (#22515) Co-authored-by: Shauren --- src/server/game/Spells/Spell.cpp | 25 +++++++++++-------------- src/server/shared/SharedDefines.h | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index af13154c8..a0f46e9a8 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2238,21 +2238,15 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar if (isBouncingFar) searchRadius *= chainTargets; + WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target; std::list tempTargets; - SearchAreaTargets(tempTargets, searchRadius, target, m_caster, objectType, selectType, condList); + SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList); tempTargets.remove(target); // remove targets which are always invalid for chain spells // for some spells allow only chain targets in front of caster (swipe for example) if (!isBouncingFar) - { - for (std::list::iterator itr = tempTargets.begin(); itr != tempTargets.end();) - { - std::list::iterator checkItr = itr++; - if (!m_caster->HasInArc(static_cast(M_PI), *checkItr)) - tempTargets.erase(checkItr); - } - } + tempTargets.remove_if([this](WorldObject* target) { return !m_caster->HasInArc(static_cast(M_PI), target); }); while (chainTargets) { @@ -2267,7 +2261,7 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar if (Unit* unit = (*itr)->ToUnit()) { uint32 deficit = unit->GetMaxHealth() - unit->GetHealth(); - if (deficit > maxHPDeficit && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2)) + if (deficit > maxHPDeficit && chainSource->IsWithinDist(unit, jumpRadius) && chainSource->IsWithinLOSInMap(unit, VMAP::ModelIgnoreFlags::M2)) { foundItr = itr; maxHPDeficit = deficit; @@ -2282,19 +2276,22 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar { if (foundItr == tempTargets.end()) { - if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) + if ((!isBouncingFar || chainSource->IsWithinDist(*itr, jumpRadius)) && chainSource->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } - else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) + else if (chainSource->GetDistanceOrder(*itr, *foundItr) && chainSource->IsWithinLOSInMap(*itr, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } } // not found any valid target - chain ends if (foundItr == tempTargets.end()) break; - target = *foundItr; + + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER)) + chainSource = *foundItr; + + targets.push_back(*foundItr); tempTargets.erase(foundItr); - targets.push_back(target); --chainTargets; } } diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 2788167c4..e114c997d 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -465,7 +465,7 @@ enum SpellAttr2 : uint32 SPELL_ATTR2_ALWAYS_CAST_AS_UNIT = 0x00000200, // TITLE Unknown attribute 9@Attr2 SPELL_ATTR2_SPECIAL_TAMING_FLAG = 0x00000400, // TITLE Unknown attribute 10@Attr2 DESCRIPTION Related to taming? SPELL_ATTR2_NO_TARGET_PER_SECOND_COST = 0x00000800, // TITLE Health Funnel - SPELL_ATTR2_CHAIN_FROM_CASTER = 0x00001000, // TITLE Unknown attribute 12@Attr2 DESCRIPTION Cleave, Heart Strike, Maul, Sunder Armor, Swipe + SPELL_ATTR2_CHAIN_FROM_CASTER = 0x00001000, // TITLE Chain from caster DESCRIPTION Cleave, Heart Strike, Maul, Sunder Armor, Swipe SPELL_ATTR2_ENCHANT_OWN_ITEM_ONLY = 0x00002000, // TITLE Enchant persists when entering arena SPELL_ATTR2_ALLOW_WHILE_INVISIBLE = 0x00004000, // TITLE Unknown attribute 14@Attr2 SPELL_ATTR2_DO_NOT_CONSUME_IF_GAINED_DURING_CAST = 0x00008000, // TITLE Unused attribute 15@Attr2 DESCRIPTION not set in 3.3.5a From 40c58123b148b6ba140851ac1e46b0cd92b3d1e8 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Tue, 29 Jul 2025 05:38:49 -0700 Subject: [PATCH 27/45] =?UTF-8?q?fix(Core/Player):=20allow=20attacking=20t?= =?UTF-8?q?arget=20within=20boundary=20radius=20when=E2=80=A6=20(#22500)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kito --- src/server/game/Entities/Player/PlayerUpdates.cpp | 8 ++++---- src/server/game/Entities/Unit/Unit.cpp | 10 ++++++++++ src/server/game/Entities/Unit/Unit.h | 2 ++ src/server/game/Spells/Spell.cpp | 4 ++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 9c2cde694..5291256a7 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -181,8 +181,8 @@ void Player::Update(uint32 p_time) m_swingErrorMsg = 1; } } - // 120 degrees of radiant range - else if (!HasInArc(2 * M_PI / 3, victim)) + // 120 degrees of radiant range, if player is not in boundary radius + else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim)) { setAttackTimer(BASE_ATTACK, 100); if (m_swingErrorMsg != 2) // send single time (client auto repeat) @@ -211,8 +211,8 @@ void Player::Update(uint32 p_time) { if (!IsWithinMeleeRange(victim)) setAttackTimer(OFF_ATTACK, 100); - else if (!HasInArc(2 * M_PI / 3, victim)) - setAttackTimer(OFF_ATTACK, 100); + else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim)) + setAttackTimer(BASE_ATTACK, 100); else { // prevent base and off attack in same time, delay attack at diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 5532033dd..318e50bf9 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -698,6 +698,16 @@ bool Unit::IsWithinRange(Unit const* obj, float dist) const return distsq <= dist * dist; } +bool Unit::IsWithinBoundaryRadius(const Unit* obj) const +{ + if (!obj || !IsInMap(obj) || !InSamePhase(obj)) + return false; + + float objBoundaryRadius = std::max(obj->GetBoundaryRadius(), MIN_MELEE_REACH); + + return IsInDist(obj, objBoundaryRadius); +} + bool Unit::GetRandomContactPoint(Unit const* obj, float& x, float& y, float& z, bool force) const { float combat_reach = GetCombatReach(); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index c5d655406..29344cb76 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -818,9 +818,11 @@ public: bool _IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const; // Combat range + [[nodiscard]] float GetBoundaryRadius() const { return m_floatValues[UNIT_FIELD_BOUNDINGRADIUS]; } [[nodiscard]] float GetCombatReach() const override { return m_floatValues[UNIT_FIELD_COMBATREACH]; } [[nodiscard]] float GetMeleeReach() const { float reach = m_floatValues[UNIT_FIELD_COMBATREACH]; return reach > MIN_MELEE_REACH ? reach : MIN_MELEE_REACH; } [[nodiscard]] bool IsWithinRange(Unit const* obj, float dist) const; + bool IsWithinBoundaryRadius(const Unit* obj) const; bool IsWithinCombatRange(Unit const* obj, float dist2compare) const; bool IsWithinMeleeRange(Unit const* obj, float dist = 0.f) const; float GetMeleeRange(Unit const* target) const; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index a0f46e9a8..ca543c804 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -7126,7 +7126,7 @@ SpellCastResult Spell::CheckRange(bool strict) return SPELL_FAILED_TOO_CLOSE; } - if (m_caster->IsPlayer() && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast(M_PI), target)) + if (m_caster->IsPlayer() && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast(M_PI), target) && !m_caster->IsWithinBoundaryRadius(target)) return SPELL_FAILED_UNIT_NOT_INFRONT; } @@ -9129,7 +9129,7 @@ namespace Acore } else { - if (!_caster->isInFront(target, _coneAngle)) + if (!_caster->IsWithinBoundaryRadius(target->ToUnit()) && !_caster->isInFront(target, _coneAngle)) return false; } return WorldObjectSpellAreaTargetCheck::operator ()(target); From 67aa022dbf4c303aa7457b10b6c9dee9049b0d67 Mon Sep 17 00:00:00 2001 From: Anton Popovichenko Date: Tue, 29 Jul 2025 14:55:56 +0200 Subject: [PATCH 28/45] fix(Core/Leash): Improve leashing behavior and timer handling (#22525) --- .../game/Entities/Creature/Creature.cpp | 9 +++--- src/server/game/Entities/Unit/Unit.cpp | 32 ++++++++++++++++++- src/server/game/Entities/Unit/Unit.h | 1 + .../TargetedMovementGenerator.cpp | 16 ++++++++-- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 54f6abdb0..34f5267d7 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -801,7 +801,7 @@ void Creature::Update(uint32 diff) // Periodically check if able to move, if not, extend leash timer if (diff >= m_extendLeashTime) { - if (!CanFreeMove()) + if (HasUnitState(UNIT_STATE_LOST_CONTROL)) UpdateLeashExtensionTime(); m_extendLeashTime = EXTEND_LEASH_CHECK_INTERVAL; } @@ -2685,10 +2685,11 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const float dist = sWorld->getFloatConfig(CONFIG_CREATURE_LEASH_RADIUS); - if (GetCharmerOrOwner()) + if (Unit* unit = GetCharmerOrOwner()) { - dist = std::min(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f); - return IsWithinDist(victim, dist); + float visibilityDist = std::min(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f); + if (!victim->IsWithinDist(unit, visibilityDist)) + return false; } if (!dist) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 318e50bf9..96a10b1c7 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -4185,6 +4185,15 @@ void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id, bool withI InterruptSpell(CURRENT_CHANNELED_SPELL, true, true, bySelf); } +Spell* Unit::GetFirstCurrentCastingSpell() const +{ + for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++) + if (m_currentSpells[i] && m_currentSpells[i]->GetCastTimeRemaining() > 0) + return m_currentSpells[i]; + + return nullptr; +} + Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const { for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++) @@ -10412,8 +10421,29 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) if (meleeAttack) AddUnitState(UNIT_STATE_MELEE_ATTACKING); + Unit* owner = GetCharmerOrOwner(); + Creature* ownerCreature = owner ? owner->ToCreature() : nullptr; + Creature* controlledCreatureWithSameVictim = nullptr; + if (creature && !m_Controlled.empty()) + { + for (ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) + { + if ((*itr)->ToCreature() && (*itr)->GetVictim() == victim) + { + controlledCreatureWithSameVictim = (*itr)->ToCreature(); + break; + } + } + } + + // Share leash timer with controlled unit + if (controlledCreatureWithSameVictim) + creature->SetLastLeashExtensionTimePtr(controlledCreatureWithSameVictim->GetLastLeashExtensionTimePtr()); + // Share leash timer with owner + else if (creature && ownerCreature && ownerCreature->GetVictim() == victim) + creature->SetLastLeashExtensionTimePtr(ownerCreature->GetLastLeashExtensionTimePtr()); // Update leash timer when attacking creatures - if (victim->IsCreature()) + else if (victim->IsCreature()) victim->ToCreature()->UpdateLeashExtensionTime(); // set position before any AI calls/assistance diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 29344cb76..f83e2a623 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1494,6 +1494,7 @@ public: [[nodiscard]] Player* GetSpellModOwner() const; [[nodiscard]] Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; } [[nodiscard]] Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; } + [[nodiscard]] Spell* GetFirstCurrentCastingSpell() const; [[nodiscard]] Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; [[nodiscard]] int32 GetCurrentSpellCastTime(uint32 spell_id) const; diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index 7dec2d8ce..76fb0840a 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -72,15 +72,27 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) return false; Creature* cOwner = owner->ToCreature(); + bool isStoppedBecauseOfCasting = cOwner && cOwner->IsMovementPreventedByCasting(); // the owner might be unable to move (rooted or casting), or we have lost the target, pause movement - if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || HasLostTarget(owner) || (cOwner && cOwner->IsMovementPreventedByCasting())) + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || HasLostTarget(owner) || isStoppedBecauseOfCasting) { owner->StopMoving(); _lastTargetPosition.reset(); if (cOwner) { - cOwner->UpdateLeashExtensionTime(); + if (isStoppedBecauseOfCasting) + { + // Don't reset leash timer if it's a spell like Shoot with a short cast time. + /// @todo: Research how it should actually work. + Spell *spell = cOwner->GetFirstCurrentCastingSpell(); + bool spellHasLongCast = spell && spell->GetCastTime() > 1 * SECOND * IN_MILLISECONDS; + if (spellHasLongCast) + cOwner->UpdateLeashExtensionTime(); + } + else + cOwner->UpdateLeashExtensionTime(); + cOwner->SetCannotReachTarget(); } return true; From 2e1f848f09e14a81e469909f4507a37d218adfcb Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Tue, 29 Jul 2025 05:58:30 -0700 Subject: [PATCH 29/45] fix(Core/Unit): Add melee leeway for auto attacks (#22566) --- src/server/game/Entities/Object/ObjectDefines.h | 2 ++ src/server/game/Entities/Unit/Unit.cpp | 3 +++ src/server/game/Entities/Unit/Unit.h | 6 ++++++ src/server/game/Spells/Spell.cpp | 2 +- 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/server/game/Entities/Object/ObjectDefines.h b/src/server/game/Entities/Object/ObjectDefines.h index 2913e587b..bfae02203 100644 --- a/src/server/game/Entities/Object/ObjectDefines.h +++ b/src/server/game/Entities/Object/ObjectDefines.h @@ -48,6 +48,8 @@ #define NOMINAL_MELEE_RANGE 5.0f #define MELEE_RANGE (NOMINAL_MELEE_RANGE - MIN_MELEE_REACH * 2) //center to center for players #define DEFAULT_COLLISION_HEIGHT 2.03128f // Most common value in dbc +#define LEEWAY_MIN_MOVE_SPEED 4.97f // NYI +#define LEEWAY_BONUS_RANGE 2.66f // used for creating values for respawn for example inline uint32 PAIR64_HIPART(uint64 x); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 96a10b1c7..e1cd1735c 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -674,6 +674,9 @@ bool Unit::IsWithinMeleeRange(Unit const* obj, float dist) const float maxdist = dist + GetMeleeRange(obj); + if ((IsPlayer() || obj->IsPlayer()) && HasLeewayMovement() && obj->HasLeewayMovement()) + maxdist += LEEWAY_BONUS_RANGE; + return distsq < maxdist * maxdist; } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index f83e2a623..4dc7cb585 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1639,6 +1639,12 @@ public: UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED) && !GetOwnerGUID(); } + [[nodiscard]] bool HasLeewayMovement() const + { + return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT | MOVEMENTFLAG_FALLING) + && !IsWalking(); + } + void KnockbackFrom(float x, float y, float speedXY, float speedZ); void JumpTo(float speedXY, float speedZ, bool forward = true); void JumpTo(WorldObject* obj, float speedZ); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index ca543c804..439713ee7 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -7109,7 +7109,7 @@ SpellCastResult Spell::CheckRange(bool strict) if (range_type == SPELL_RANGE_MELEE) { float real_max_range = max_range; - if (!m_caster->IsCreature() && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking()) + if (!m_caster->IsCreature() && m_caster->HasLeewayMovement() && target->HasLeewayMovement()) real_max_range -= MIN_MELEE_REACH; // Because of lag, we can not check too strictly here (is only used if both caster and target are moving) else real_max_range -= 2 * MIN_MELEE_REACH; From 231a03d7855dc9e01cec45b004b749b2fbb1783c Mon Sep 17 00:00:00 2001 From: avarishd <46330494+avarishd@users.noreply.github.com> Date: Tue, 29 Jul 2025 16:00:00 +0300 Subject: [PATCH 30/45] fix(Spells/Druid): Moonglade Raiment 2-piece set bonus (#22148) --- .../rev_1747581578778876200.sql | 3 + src/server/scripts/Spells/spell_druid.cpp | 56 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1747581578778876200.sql diff --git a/data/sql/updates/pending_db_world/rev_1747581578778876200.sql b/data/sql/updates/pending_db_world/rev_1747581578778876200.sql new file mode 100644 index 000000000..7c80d0f87 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1747581578778876200.sql @@ -0,0 +1,3 @@ +-- Moonglade Raiment 2 set +DELETE FROM `spell_script_names` WHERE `spell_id`=-774 AND `ScriptName`='spell_dru_rejuvenation_moonglade_2_set'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (-774, 'spell_dru_rejuvenation_moonglade_2_set'); diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index 0813b9eca..ededde81e 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -60,6 +60,7 @@ enum DruidSpells SPELL_DRUID_ENRAGE = 5229, SPELL_DRUID_ENRAGED_DEFENSE = 70725, SPELL_DRUID_ITEM_T10_FERAL_4P_BONUS = 70726, + SPELL_DRUID_MOONGLADE_2P_BONUS = 37286 }; enum DruidIcons @@ -1194,6 +1195,60 @@ class spell_dru_moonkin_form_passive_proc : public AuraScript } }; +// -774 - Rejuvenation +class spell_dru_rejuvenation_moonglade_2_set : public AuraScript +{ + PrepareAuraScript(spell_dru_rejuvenation_moonglade_2_set); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DRUID_MOONGLADE_2P_BONUS }); + } + + bool Load() override + { + _casterGUID.Clear(); + return true; + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Player* caster = ObjectAccessor::FindPlayer(GetCasterGUID())) + if (caster->HasAura(SPELL_DRUID_MOONGLADE_2P_BONUS)) + { + Player* target = GetTarget()->ToPlayer(); + if (!target) + return; + + _casterGUID = GetCasterGUID(); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_DRUID_MOONGLADE_2P_BONUS); + target->ApplyRatingMod(CR_DODGE, spellInfo->Effects[EFFECT_0].CalcValue(), true); // 35 rating + } + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (_casterGUID) + { + Player* target = GetTarget()->ToPlayer(); + if (!target) + return; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_DRUID_MOONGLADE_2P_BONUS); + target->ApplyRatingMod(CR_DODGE, spellInfo->Effects[EFFECT_0].CalcValue(), false); // 35 rating + } + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_dru_rejuvenation_moonglade_2_set::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_dru_rejuvenation_moonglade_2_set::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_HEAL, AURA_EFFECT_HANDLE_REAL); + } + +private: + ObjectGuid _casterGUID; +}; + void AddSC_druid_spell_scripts() { RegisterSpellScript(spell_dru_bear_form_passive); @@ -1229,4 +1284,5 @@ void AddSC_druid_spell_scripts() RegisterSpellScript(spell_dru_t10_restoration_4p_bonus); RegisterSpellScript(spell_dru_wild_growth); RegisterSpellScript(spell_dru_moonkin_form_passive_proc); + RegisterSpellScript(spell_dru_rejuvenation_moonglade_2_set); } From 33a71434c99f0a74b25ba2a09884bb661ef6a8d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 29 Jul 2025 13:00:21 +0000 Subject: [PATCH 31/45] chore(DB): import pending files Referenced commit(s): 67aa022dbf4c303aa7457b10b6c9dee9049b0d67 --- .../rev_1747581578778876200.sql => db_world/2025_07_29_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1747581578778876200.sql => db_world/2025_07_29_01.sql} (85%) diff --git a/data/sql/updates/pending_db_world/rev_1747581578778876200.sql b/data/sql/updates/db_world/2025_07_29_01.sql similarity index 85% rename from data/sql/updates/pending_db_world/rev_1747581578778876200.sql rename to data/sql/updates/db_world/2025_07_29_01.sql index 7c80d0f87..91ca903fb 100644 --- a/data/sql/updates/pending_db_world/rev_1747581578778876200.sql +++ b/data/sql/updates/db_world/2025_07_29_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_29_00 -> 2025_07_29_01 -- Moonglade Raiment 2 set DELETE FROM `spell_script_names` WHERE `spell_id`=-774 AND `ScriptName`='spell_dru_rejuvenation_moonglade_2_set'; INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (-774, 'spell_dru_rejuvenation_moonglade_2_set'); From 35d8d49ce37b19b9630472c1d842ca9fe8aeebb1 Mon Sep 17 00:00:00 2001 From: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Date: Tue, 29 Jul 2025 15:01:16 +0200 Subject: [PATCH 32/45] =?UTF-8?q?feat(Core/ServerMail):=20Add=20AccountFla?= =?UTF-8?q?gs=20condition=20to=20server=20mail=20temp=E2=80=A6=20(#22549)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rev_1753373508738396400.sql | 3 +++ src/common/Common.h | 13 +++++++++++++ src/server/game/Mails/ServerMailMgr.cpp | 17 ++++++++++++++--- src/server/game/Mails/ServerMailMgr.h | 4 +++- 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 data/sql/updates/pending_db_characters/rev_1753373508738396400.sql diff --git a/data/sql/updates/pending_db_characters/rev_1753373508738396400.sql b/data/sql/updates/pending_db_characters/rev_1753373508738396400.sql new file mode 100644 index 000000000..1c4dfcb49 --- /dev/null +++ b/data/sql/updates/pending_db_characters/rev_1753373508738396400.sql @@ -0,0 +1,3 @@ +-- +ALTER TABLE `mail_server_template_conditions` + CHANGE COLUMN `conditionType` `conditionType` ENUM('Level','PlayTime','Quest','Achievement','Reputation','Faction','Race','Class','AccountFlags') NOT NULL COLLATE 'utf8mb4_unicode_ci' AFTER `templateID`; diff --git a/src/common/Common.h b/src/common/Common.h index 8a28414c0..c8cd5e789 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -100,6 +100,19 @@ enum AccountFlag // ACCOUNT_FLAG_S2_RESTRICTED = 0xFFFFFFFF, // NYI UNK }; +constexpr uint32 ACCOUNT_FLAGS_ALL = + ACCOUNT_FLAG_GM | ACCOUNT_FLAG_NOKICK | ACCOUNT_FLAG_COLLECTOR | + ACCOUNT_FLAG_TRIAL | ACCOUNT_FLAG_CANCELLED | ACCOUNT_FLAG_IGR | + ACCOUNT_FLAG_WHOLESALER | ACCOUNT_FLAG_PRIVILEGED | ACCOUNT_FLAG_EU_FORBID_ELV | + ACCOUNT_FLAG_EU_FORBID_BILLING | ACCOUNT_FLAG_RESTRICTED | ACCOUNT_FLAG_REFERRAL | + ACCOUNT_FLAG_BLIZZARD | ACCOUNT_FLAG_RECURRING_BILLING | ACCOUNT_FLAG_NOELECTUP | + ACCOUNT_FLAG_KR_CERTIFICATE | ACCOUNT_FLAG_EXPANSION_COLLECTOR | ACCOUNT_FLAG_DISABLE_VOICE | + ACCOUNT_FLAG_DISABLE_VOICE_SPEAK | ACCOUNT_FLAG_REFERRAL_RESURRECT | ACCOUNT_FLAG_EU_FORBID_CC | + ACCOUNT_FLAG_OPENBETA_DELL | ACCOUNT_FLAG_PROPASS | ACCOUNT_FLAG_PROPASS_LOCK | + ACCOUNT_FLAG_PENDING_UPGRADE | ACCOUNT_FLAG_RETAIL_FROM_TRIAL | ACCOUNT_FLAG_EXPANSION2_COLLECTOR | + ACCOUNT_FLAG_OVERMIND_LINKED | ACCOUNT_FLAG_DEMOS | ACCOUNT_FLAG_DEATH_KNIGHT_OK | + ACCOUNT_FLAG_S2_REQUIRE_IGR | ACCOUNT_FLAG_S2_TRIAL; + enum LocaleConstant { LOCALE_enUS = 0, diff --git a/src/server/game/Mails/ServerMailMgr.cpp b/src/server/game/Mails/ServerMailMgr.cpp index d11595066..8ed35ffc1 100644 --- a/src/server/game/Mails/ServerMailMgr.cpp +++ b/src/server/game/Mails/ServerMailMgr.cpp @@ -16,7 +16,9 @@ */ #include "ServerMailMgr.h" +#include "AccountMgr.h" #include "AchievementMgr.h" +#include "Common.h" #include "DatabaseEnv.h" #include "Item.h" #include "Log.h" @@ -240,21 +242,28 @@ void ServerMailMgr::LoadMailServerTemplatesConditions() case ServerMailConditionType::Faction: if (conditionValue < TEAM_ALLIANCE || conditionValue > TEAM_HORDE) { - LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Faction' with invalid conditionValue ({}) for templateID {}, skipped.", conditionState, templateID); + LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Faction' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID); continue; } break; case ServerMailConditionType::Race: if (conditionValue & ~RACEMASK_ALL_PLAYABLE) { - LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Race' with invalid conditionValue ({}) for templateID {}, skipped.", conditionState, templateID); + LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Race' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID); continue; } break; case ServerMailConditionType::Class: if (conditionValue & ~CLASSMASK_ALL_PLAYABLE) { - LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Class' with invalid conditionValue ({}) for templateID {}, skipped.", conditionState, templateID); + LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'Class' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID); + continue; + } + break; + case ServerMailConditionType::AccountFlags: + if ((conditionValue & ~ACCOUNT_FLAGS_ALL) != 0) + { + LOG_ERROR("sql.sql", "Table `mail_server_template_conditions` has conditionType 'AccountFlags' with invalid conditionValue ({}) for templateID {}, skipped.", conditionValue, templateID); continue; } break; @@ -344,6 +353,8 @@ bool ServerMailCondition::CheckCondition(Player* player) const return (player->getRaceMask() & value) != 0; case ServerMailConditionType::Class: return (player->getClassMask() & value) != 0; + case ServerMailConditionType::AccountFlags: + return player->GetSession()->HasAccountFlag(value); default: [[unlikely]] LOG_ERROR("server.mail", "Unknown server mail condition type '{}'", static_cast(type)); return false; diff --git a/src/server/game/Mails/ServerMailMgr.h b/src/server/game/Mails/ServerMailMgr.h index 9cf70a805..12eee5113 100644 --- a/src/server/game/Mails/ServerMailMgr.h +++ b/src/server/game/Mails/ServerMailMgr.h @@ -53,6 +53,7 @@ enum class ServerMailConditionType : uint8 Faction = 6, ///< Requires the player to be a part of a specific faction. Horde/Alliance. Race = 7, ///< Requires the player to be a specific race. Class = 8, ///< Requires the player to be a specific class. + AccountFlags = 9, ///< Requires the player to have a specific AccountFlag (bit) }; /** @@ -67,7 +68,8 @@ constexpr std::pair ServerMailConditi { "Reputation", ServerMailConditionType::Reputation }, { "Faction", ServerMailConditionType::Faction }, { "Race", ServerMailConditionType::Race }, - { "Class", ServerMailConditionType::Class } + { "Class", ServerMailConditionType::Class }, + { "AccountFlags", ServerMailConditionType::AccountFlags } }; /** From 2bc6e0f7478dc6282f3df62aa9ea84fd830653c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 29 Jul 2025 13:01:32 +0000 Subject: [PATCH 33/45] chore(DB): import pending files Referenced commit(s): 33a71434c99f0a74b25ba2a09884bb661ef6a8d7 --- .../2025_07_29_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_characters/rev_1753373508738396400.sql => db_characters/2025_07_29_00.sql} (85%) diff --git a/data/sql/updates/pending_db_characters/rev_1753373508738396400.sql b/data/sql/updates/db_characters/2025_07_29_00.sql similarity index 85% rename from data/sql/updates/pending_db_characters/rev_1753373508738396400.sql rename to data/sql/updates/db_characters/2025_07_29_00.sql index 1c4dfcb49..faf92897e 100644 --- a/data/sql/updates/pending_db_characters/rev_1753373508738396400.sql +++ b/data/sql/updates/db_characters/2025_07_29_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_11_00 -> 2025_07_29_00 -- ALTER TABLE `mail_server_template_conditions` CHANGE COLUMN `conditionType` `conditionType` ENUM('Level','PlayTime','Quest','Achievement','Reputation','Faction','Race','Class','AccountFlags') NOT NULL COLLATE 'utf8mb4_unicode_ci' AFTER `templateID`; From f0054bd197d24e22c9104bfd4d5f4c250b4441d9 Mon Sep 17 00:00:00 2001 From: Tereneckla Date: Tue, 29 Jul 2025 13:04:35 +0000 Subject: [PATCH 34/45] fix(DB/Spells): add various spells to spell_groups (#22075) --- .../rev_1746620573935826954.sql | 35 +++++++++++++++++++ .../game/Spells/SpellInfoCorrections.cpp | 6 ++++ 2 files changed, 41 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1746620573935826954.sql diff --git a/data/sql/updates/pending_db_world/rev_1746620573935826954.sql b/data/sql/updates/pending_db_world/rev_1746620573935826954.sql new file mode 100644 index 000000000..61f9d2be7 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1746620573935826954.sql @@ -0,0 +1,35 @@ +-- improved shadow bolt into spell crit debuff +DELETE FROM `spell_group` WHERE `id` = 1010 AND `spell_id` IN (17800); +INSERT INTO `spell_group` VALUES (1010, 17800, 0); + +-- curse of weakness and spore cloud into low armor% debuff, remove curse of recklessness +-- combine low armor% and attack power debuff because of curse of weakness +-- add vindication and demoralizing screech +DELETE FROM `spell_group` WHERE `id` = 1004 AND `spell_id` IN (702, 50274, 16231, 99, 1160, 67, 24423); +INSERT INTO `spell_group` VALUES (1004, 702, 0), (1004, 50274, 0), (1004, 99, 0), (1004, 1160, 0), (1004, 67, 0), (1004, 24423, 0); + +-- rename +UPDATE `spell_group_stack_rules` SET `description` = 'Group of minor Armor reducing, hit increase and AP reducing debuffs, effect exclusive' WHERE `group_id` = 1004; + +-- remove old ap debuff group +DELETE FROM `spell_group` WHERE `id` = 1017; +DELETE FROM `spell_group_stack_rules` WHERE `group_id` = 1017; + +-- stampede into bleed debuff +DELETE FROM `spell_group` WHERE `id` = 1008 AND `spell_id` IN (57386); +INSERT INTO `spell_group` VALUES (1008, 57386, 0); + +-- poison spit, lava breath into spell haste debuff +DELETE FROM `spell_group` WHERE `id` = 1022 AND `spell_id` IN (35387, 58604); +INSERT INTO `spell_group` VALUES (1022, 35387, 0), (1022, 58604, 0); + +-- master poisoner into crit taken debuff (untested) +DELETE FROM `spell_group` WHERE `id` = 1013 AND `spell_id` IN (45176); +INSERT INTO `spell_group` VALUES (1013, 45176, 0); + +-- physical damage taken group with savage combat and blood frenzy +DELETE FROM `spell_group_stack_rules` WHERE `group_id` = 1024; +INSERT INTO `spell_group_stack_rules` VALUES (1024, 17, 'Group of physical damage taken increasing debuffs'); + +DELETE FROM `spell_group` WHERE `id` = 1024 AND `spell_id` IN (30069, 58684); +INSERT INTO `spell_group` VALUES (1024, 30069, 0), (1024, 58684, 0); diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index a61c81b02..4ba8f62c6 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -592,6 +592,12 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->AttributesEx3 &= ~SPELL_ATTR3_SUPPRESS_CASTER_PROCS; }); + // Vindication + ApplySpellFix({ 67, 26017}, [](SpellInfo* spellInfo) + { + spellInfo->Effects[EFFECT_0].MiscValue = 0; + }); + // Arcane Missiles ApplySpellFix({ 5143, 5144, 5145, 8416, 8417, 10211, 10212, 25345, 27075, 38699, 38704, 42843, 42846 }, [](SpellInfo* spellInfo) { From ddefd87fef0112b9b457f3c701e1f675167421b0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 29 Jul 2025 13:05:40 +0000 Subject: [PATCH 35/45] chore(DB): import pending files Referenced commit(s): 2bc6e0f7478dc6282f3df62aa9ea84fd830653c1 --- .../rev_1746620573935826954.sql => db_world/2025_07_29_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1746620573935826954.sql => db_world/2025_07_29_02.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1746620573935826954.sql b/data/sql/updates/db_world/2025_07_29_02.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1746620573935826954.sql rename to data/sql/updates/db_world/2025_07_29_02.sql index 61f9d2be7..17934d00c 100644 --- a/data/sql/updates/pending_db_world/rev_1746620573935826954.sql +++ b/data/sql/updates/db_world/2025_07_29_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_29_01 -> 2025_07_29_02 -- improved shadow bolt into spell crit debuff DELETE FROM `spell_group` WHERE `id` = 1010 AND `spell_id` IN (17800); INSERT INTO `spell_group` VALUES (1010, 17800, 0); From 9cf742a04bd407cd7a93ffb2d6029baabb57a7f0 Mon Sep 17 00:00:00 2001 From: Tereneckla Date: Tue, 29 Jul 2025 13:07:52 +0000 Subject: [PATCH 36/45] fix(Scripts/Spell): improve handling of refreshing diseases with Glyph of Disease (#22434) --- src/server/scripts/Spells/spell_dk.cpp | 66 ++++++++++++-------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index a880e5dc8..d1d371710 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -74,6 +74,9 @@ enum DeathKnightSpells SPELL_DK_UNHOLY_PRESENCE_TRIGGERED = 49772, SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1 = 49189, SPELL_DK_WILL_OF_THE_NECROPOLIS_AURA_R1 = 52284, + SPELL_DK_ICY_TALONS_TALENT_R1 = 50880, + SPELL_DK_CRYPT_FEVER_R1 = 50508, + SPELL_DK_EBON_PLAGUE_R1 = 51726, // Risen Ally SPELL_DK_RAISE_ALLY = 46619, SPELL_DK_THRASH = 47480, @@ -1746,7 +1749,8 @@ class spell_dk_pestilence : public SpellScript { SPELL_DK_GLYPH_OF_DISEASE, SPELL_DK_BLOOD_PLAGUE, - SPELL_DK_FROST_FEVER + SPELL_DK_FROST_FEVER, + SPELL_DK_ICY_TALONS_TALENT_R1 }); } @@ -1758,46 +1762,36 @@ class spell_dk_pestilence : public SpellScript if (!target) return; - if (target != hitUnit || caster->GetAura(SPELL_DK_GLYPH_OF_DISEASE)) + // Spread on others + if (target != hitUnit) { - // xinef: checked in target selection - //if (!m_targets.GetUnitTarget()->IsWithinLOSInMap(unitTarget)) - // return; - - // And spread them on target // Blood Plague - if (Aura* disOld = target->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID())) - if (AuraEffect* effOld = disOld->GetEffect(EFFECT_0)) - { - float pctMods = effOld->GetPctMods(); - float crit = effOld->GetCritChance(); - caster->CastSpell(hitUnit, SPELL_DK_BLOOD_PLAGUE, true); - - if (Aura* disNew = hitUnit->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID())) - if (AuraEffect* effNew = disNew->GetEffect(EFFECT_0)) - { - effNew->SetPctMods(pctMods); - effNew->SetCritChance(crit); - effNew->SetAmount(effNew->CalculateAmount(effNew->GetCaster())); - } - } + if (target->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID())) + caster->CastSpell(hitUnit, SPELL_DK_BLOOD_PLAGUE, true); // Frost Fever - if (Aura* disOld = target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID())) - if (AuraEffect* effOld = disOld->GetEffect(EFFECT_0)) - { - float pctMods = effOld->GetPctMods(); - float crit = effOld->GetCritChance(); - caster->CastSpell(hitUnit, SPELL_DK_FROST_FEVER, true); + if (target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID())) + caster->CastSpell(hitUnit, SPELL_DK_FROST_FEVER, true); + } + // Refresh on target + else if (caster->GetAura(SPELL_DK_GLYPH_OF_DISEASE)) + { + // Blood Plague + if (Aura* disease = target->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID())) + disease->RefreshDuration(); - if (Aura* disNew = hitUnit->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID())) - if (AuraEffect* effNew = disNew->GetEffect(EFFECT_0)) - { - effNew->SetPctMods(pctMods); - effNew->SetCritChance(crit); - effNew->SetAmount(effNew->CalculateAmount(effNew->GetCaster())); - } - } + // Frost Fever + if (Aura* disease = target->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID())) + { + disease->RefreshDuration(); + if (Aura const* talons = caster->GetAuraOfRankedSpell(SPELL_DK_ICY_TALONS_TALENT_R1)) + caster->CastSpell(caster, talons->GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true); + } + + if (Aura* disease = target->GetAuraOfRankedSpell(SPELL_DK_EBON_PLAGUE_R1, caster->GetGUID())) + disease->RefreshDuration(); + else if (Aura* disease = target->GetAuraOfRankedSpell(SPELL_DK_CRYPT_FEVER_R1, caster->GetGUID())) + disease->RefreshDuration(); } } From e526623d1d1b0e0641ed31e5206aa90d86af8d39 Mon Sep 17 00:00:00 2001 From: Ryan Turner Date: Tue, 29 Jul 2025 21:32:41 +0100 Subject: [PATCH 37/45] fix(DB/Commands): Added and updated commands for go objects, go creatures and group revive (#22229) --- .../updates/pending_db_world/rev_1748445403931598000.sql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1748445403931598000.sql diff --git a/data/sql/updates/pending_db_world/rev_1748445403931598000.sql b/data/sql/updates/pending_db_world/rev_1748445403931598000.sql new file mode 100644 index 000000000..5d4f02622 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1748445403931598000.sql @@ -0,0 +1,8 @@ +-- +DELETE FROM `command` WHERE `name` = "go gameobject"; +INSERT INTO `command` (`name`, `security`, `help`) VALUES +("go gameobject", 1, "Syntax: .go gameobject $gameobject.guid\r\nTeleports you to the gameobject using `guid` value of `gameobject` table."); + +UPDATE `command` SET `help` = "Syntax: .go creature $creature.guid \r\nTeleports you to the creature using `guid` value of `creature` table." WHERE `name` LIKE "go creature"; +UPDATE `command` SET `help` = "Syntax: .go creature name $creature_template.name \r\nTeleports you to a creature using the `name` value of `creature_template` table. \r\nIn the case of multiple creature of the same `name` existing in the world, you will be teleported to the lowest `guid` creature.\r\nWhen running the command for names with spaces don't break the string, example: .go creature name Ruby Scalebane" WHERE `name` LIKE "go creature name"; +UPDATE `command` SET `help` = "Syntax: .group revive $characterName \r\nRevives all group members of the given character or self if not provided." WHERE `name` LIKE "group revive"; From 4e8d0f565f8f67885f11aad824dd50f67315245a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 29 Jul 2025 20:33:42 +0000 Subject: [PATCH 38/45] chore(DB): import pending files Referenced commit(s): e526623d1d1b0e0641ed31e5206aa90d86af8d39 --- .../rev_1748445403931598000.sql => db_world/2025_07_29_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1748445403931598000.sql => db_world/2025_07_29_03.sql} (96%) diff --git a/data/sql/updates/pending_db_world/rev_1748445403931598000.sql b/data/sql/updates/db_world/2025_07_29_03.sql similarity index 96% rename from data/sql/updates/pending_db_world/rev_1748445403931598000.sql rename to data/sql/updates/db_world/2025_07_29_03.sql index 5d4f02622..f35ca3802 100644 --- a/data/sql/updates/pending_db_world/rev_1748445403931598000.sql +++ b/data/sql/updates/db_world/2025_07_29_03.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_29_02 -> 2025_07_29_03 -- DELETE FROM `command` WHERE `name` = "go gameobject"; INSERT INTO `command` (`name`, `security`, `help`) VALUES From cd87350a17bf5ee4a94c5f054eb234ed7d51bdae Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Wed, 30 Jul 2025 18:49:09 -0700 Subject: [PATCH 39/45] fix(Core/Grids): Fix corpse loading after a server restart (#22594) --- src/server/game/Grids/GridObjectLoader.cpp | 10 ++-------- src/server/game/Maps/Map.cpp | 16 ++++++++++++---- src/server/game/Maps/Map.h | 8 ++++---- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/server/game/Grids/GridObjectLoader.cpp b/src/server/game/Grids/GridObjectLoader.cpp index 10172a4a1..ff960db65 100644 --- a/src/server/game/Grids/GridObjectLoader.cpp +++ b/src/server/game/Grids/GridObjectLoader.cpp @@ -84,20 +84,14 @@ void GridObjectLoader::LoadAllCellsInGrid() LoadGameObjects(cell_guids.gameobjects, _map); LoadCreatures(cell_guids.creatures, _map); - if (std::unordered_set const* corpses = _map->GetCorpsesInCell(_grid.GetId())) + if (std::unordered_set const* corpses = _map->GetCorpsesInGrid(_grid.GetId())) { for (Corpse* corpse : *corpses) { if (corpse->IsInGrid()) continue; - CellCoord cellCoord = Acore::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY()); - Cell cell(cellCoord); - - if (corpse->IsWorldObject()) - _grid.AddWorldObject(cell.CellX(), cell.CellY(), corpse); - else - _grid.AddGridObject(cell.CellX(), cell.CellY(), corpse); + AddObjectHelper(_map, corpse); } } } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index bed34b9a5..4e731fc30 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1033,7 +1033,7 @@ void Map::UnloadAll() _transports.clear(); - for (auto& cellCorpsePair : _corpsesByCell) + for (auto& cellCorpsePair : _corpsesByGrid) { for (Corpse* corpse : cellCorpsePair.second) { @@ -1043,7 +1043,7 @@ void Map::UnloadAll() } } - _corpsesByCell.clear(); + _corpsesByGrid.clear(); _corpsesByPlayer.clear(); _corpseBones.clear(); } @@ -1848,6 +1848,12 @@ void Map::AddToActive(GameObject* d) AddToActiveHelper(d); } +template<> +void Map::AddToActive(Corpse* /*c*/) +{ + // do nothing for corpses +} + template void Map::RemoveFromActive(T* obj) { @@ -2667,7 +2673,8 @@ void Map::AddCorpse(Corpse* corpse) { corpse->SetMap(this); - _corpsesByCell[corpse->GetCellCoord().GetId()].insert(corpse); + GridCoord const gridCoord = Acore::ComputeGridCoord(corpse->GetPositionX(), corpse->GetPositionY()); + _corpsesByGrid[gridCoord.GetId()].insert(corpse); if (corpse->GetType() != CORPSE_BONES) _corpsesByPlayer[corpse->GetOwnerGUID()] = corpse; else @@ -2677,6 +2684,7 @@ void Map::AddCorpse(Corpse* corpse) void Map::RemoveCorpse(Corpse* corpse) { ASSERT(corpse); + GridCoord const gridCoord = Acore::ComputeGridCoord(corpse->GetPositionX(), corpse->GetPositionY()); corpse->DestroyForNearbyPlayers(); if (corpse->IsInGrid()) @@ -2687,7 +2695,7 @@ void Map::RemoveCorpse(Corpse* corpse) corpse->ResetMap(); } - _corpsesByCell[corpse->GetCellCoord().GetId()].erase(corpse); + _corpsesByGrid[gridCoord.GetId()].erase(corpse); if (corpse->GetType() != CORPSE_BONES) _corpsesByPlayer.erase(corpse->GetOwnerGUID()); else diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index c92c52cc6..53a35efb9 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -359,10 +359,10 @@ public: typedef std::unordered_multimap GameObjectBySpawnIdContainer; GameObjectBySpawnIdContainer& GetGameObjectBySpawnIdStore() { return _gameobjectBySpawnIdStore; } - [[nodiscard]] std::unordered_set const* GetCorpsesInCell(uint32 cellId) const + [[nodiscard]] std::unordered_set const* GetCorpsesInGrid(uint32 gridId) const { - auto itr = _corpsesByCell.find(cellId); - if (itr != _corpsesByCell.end()) + auto itr = _corpsesByGrid.find(gridId); + if (itr != _corpsesByGrid.end()) return &itr->second; return nullptr; @@ -631,7 +631,7 @@ private: MapStoredObjectTypesContainer _objectsStore; CreatureBySpawnIdContainer _creatureBySpawnIdStore; GameObjectBySpawnIdContainer _gameobjectBySpawnIdStore; - std::unordered_map> _corpsesByCell; + std::unordered_map> _corpsesByGrid; std::unordered_map _corpsesByPlayer; std::unordered_set _corpseBones; From 548447ffb02ddd357bf61774436c136c6efa9f20 Mon Sep 17 00:00:00 2001 From: Anton Popovichenko Date: Thu, 31 Jul 2025 19:45:24 +0200 Subject: [PATCH 40/45] fix(Core/Pet): Fix player pets attack (#22601) Co-authored-by: PkllonG --- src/server/game/Entities/Creature/Creature.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 34f5267d7..7a7025296 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2683,15 +2683,13 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const if (skipDistCheck) return true; - float dist = sWorld->getFloatConfig(CONFIG_CREATURE_LEASH_RADIUS); - if (Unit* unit = GetCharmerOrOwner()) { - float visibilityDist = std::min(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f); - if (!victim->IsWithinDist(unit, visibilityDist)) - return false; + float visibilityDist = std::min(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, DEFAULT_VISIBILITY_DISTANCE); + return victim->IsWithinDist(unit, visibilityDist); } + float dist = sWorld->getFloatConfig(CONFIG_CREATURE_LEASH_RADIUS); if (!dist) return true; From b711c55c1ea1b2d3ccc21d00cf8ba1a102c9f3f5 Mon Sep 17 00:00:00 2001 From: Vanna White <43800467+wetbrownsauce@users.noreply.github.com> Date: Fri, 1 Aug 2025 02:55:22 -0600 Subject: [PATCH 41/45] fix(CORE/lfg): Correct random LFG reward bug (#22599) Co-authored-by: Kito --- src/server/game/Entities/Player/PlayerQuest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Player/PlayerQuest.cpp b/src/server/game/Entities/Player/PlayerQuest.cpp index 3dbb7231c..9f663ff2f 100644 --- a/src/server/game/Entities/Player/PlayerQuest.cpp +++ b/src/server/game/Entities/Player/PlayerQuest.cpp @@ -1272,7 +1272,7 @@ bool Player::SatisfyQuestDay(Quest const* qInfo, bool msg) const if (qInfo->IsDFQuest()) { - if (!m_DFQuests.empty()) + if (m_DFQuests.find(qInfo->GetQuestId()) != m_DFQuests.end()) return false; return true; From 9ad7cef3c474faacd2999b8575cbfc79a4d58852 Mon Sep 17 00:00:00 2001 From: Tereneckla Date: Fri, 1 Aug 2025 13:36:01 +0000 Subject: [PATCH 42/45] fix(Core/Items): count stats programatically instead of manually set (#22564) Co-authored-by: heyitsbench --- .../rev_1753528647979968682.sql | 22 +++ src/server/game/Entities/Player/Player.cpp | 5 + src/server/game/Entities/Player/Player.h | 6 + .../game/Entities/Player/PlayerStorage.cpp | 11 +- src/server/game/Entities/Unit/StatSystem.cpp | 17 ++ src/server/game/Entities/Unit/Unit.cpp | 2 + src/server/game/Globals/ObjectMgr.cpp | 168 +++++++++--------- 7 files changed, 146 insertions(+), 85 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1753528647979968682.sql diff --git a/data/sql/updates/pending_db_world/rev_1753528647979968682.sql b/data/sql/updates/pending_db_world/rev_1753528647979968682.sql new file mode 100644 index 000000000..357a10fe9 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1753528647979968682.sql @@ -0,0 +1,22 @@ +-- +ALTER TABLE `item_template` DROP COLUMN `StatsCount`; + +UPDATE `item_template` SET `fire_res` = `stat_value1`, `frost_res` = `stat_value2`, `shadow_res` = `stat_value3`, `arcane_res` = `stat_value4`, `nature_res` = `stat_value5`, `stat_type1` = 0, `stat_value1` = 0, `stat_type2` = 0, `stat_value2` = 0, `stat_type3` = 0, `stat_value3` = 0, `stat_type4` = 0, `stat_value4` = 0, `stat_type5` = 0, `stat_value5` = 0, `VerifiedBuild` = 0 WHERE `entry` = 5828; +UPDATE `item_template` SET `fire_res` = `stat_value3`, `nature_res` = `stat_value4`, `stat_type3` = 0, `stat_value3` = 0, `stat_type4` = 0, `stat_value4` = 0, `VerifiedBuild` = 0 WHERE `entry` = 17802; +UPDATE `item_template` SET `fire_res` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 18881; +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 19065; +UPDATE `item_template` SET `fire_res` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 19158; +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value3`, `fire_res` = `stat_value4`, `frost_res` = `stat_value5`, `shadow_res` = `stat_value6`, `arcane_res` = `stat_value7`, `nature_res` = `stat_value8`, `stat_type3` = 0, `stat_value3` = 0, `stat_type4` = 0, `stat_value4` = 0, `stat_type5` = 0, `stat_value5` = 0, `stat_type6` = 0, `stat_value6` = 0, `stat_type7` = 0, `stat_value7` = 0, `stat_type8` = 0, `stat_value8` = 0, `VerifiedBuild` = 0 WHERE `entry` = 20142; +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 20522; +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `shadow_res` = `stat_value4`, `stat_type4` = 0, `stat_value4` = 0, `VerifiedBuild` = 0 WHERE `entry` = 20524; +UPDATE `item_template` SET `nature_res` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 21614; +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value2`, `stat_type2` = 0, `stat_value2` = 0, `VerifiedBuild` = 0 WHERE `entry` = 22230; +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value5`, `fire_res` = `stat_value6`, `shadow_res` = `stat_value7`,`stat_type5` = 0, `stat_value5` = 0, `stat_type6` = 0, `stat_value6` = 0, `stat_type7` = 0, `stat_value7` = 0, `VerifiedBuild` = 0 WHERE `entry` = 23362; +UPDATE `item_template` SET `nature_res` = `stat_value5`, `arcane_res` = `stat_value6`, `stat_type5` = 0, `stat_value5` = 0, `stat_type6` = 0, `stat_value6` = 0, `VerifiedBuild` = 0 WHERE `entry` = 23363; +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value5`, `stat_type5` = 0, `stat_value5` = 0, `VerifiedBuild` = 0 WHERE `entry` IN (32113, 32114, 32115, 32116, 32117, 32118, 32119, 32121, 32122, 32123, 32128, 32129, 32130, 32131, 32132); +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value1`, `stat_type1` = 0, `stat_value1` = 0, `VerifiedBuild` = 0 WHERE `entry` = 34187; +UPDATE `item_template` SET `fire_res` = `stat_value5`, `stat_type5` = 0, `stat_value5` = 0, `VerifiedBuild` = 0 WHERE `entry` = 40762; +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value1`, `stat_type1` = 0, `stat_value1` = 0, `VerifiedBuild` = 0 WHERE `entry` = 45860; +UPDATE `item_template` SET `ArmorDamageModifier` = `stat_value2`, `stat_type2` = 0, `stat_value2` = 0, `VerifiedBuild` = 0 WHERE `entry` = 48945; +UPDATE `item_template` SET `fire_res` = `stat_value3`, `stat_type3` = 0, `stat_value3` = 0, `VerifiedBuild` = 0 WHERE `entry` = 49312; +UPDATE `item_template` SET `fire_res` = `stat_value5`, `stat_type5` = 0, `stat_value5` = 0, `VerifiedBuild` = 0 WHERE `entry` = 49314; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 5fb5f85be..25c2f7271 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -318,6 +318,8 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this) m_baseRatingValue[i] = 0; m_baseSpellPower = 0; + m_baseSpellDamage = 0; + m_baseSpellHealing = 0; m_baseFeralAP = 0; m_baseManaRegen = 0; m_baseHealthRegen = 0; @@ -6836,7 +6838,10 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply break; /// @deprecated item mods case ITEM_MOD_SPELL_HEALING_DONE: + ApplySpellHealingBonus(int32(val), apply); + break; case ITEM_MOD_SPELL_DAMAGE_DONE: + ApplySpellDamageBonus(int32(val), apply); break; } } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index a098eaf1c..abf5e1958 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1964,6 +1964,8 @@ public: void UpdateAttackPowerAndDamage(bool ranged = false) override; void UpdateShieldBlockValue(); void ApplySpellPowerBonus(int32 amount, bool apply); + void ApplySpellDamageBonus(int32 amount, bool apply); + void ApplySpellHealingBonus(int32 amount, bool apply); void UpdateSpellDamageAndHealingBonus(); void ApplyRatingMod(CombatRating cr, int32 value, bool apply); void UpdateRating(CombatRating cr); @@ -1982,6 +1984,8 @@ public: [[nodiscard]] float GetRatingMultiplier(CombatRating cr) const; [[nodiscard]] float GetRatingBonusValue(CombatRating cr) const; uint32 GetBaseSpellPowerBonus() { return m_baseSpellPower; } + uint32 GetBaseSpellDamageBonus() { return m_baseSpellDamage; } + uint32 GetBaseSpellHealingBonus() { return m_baseSpellHealing; } [[nodiscard]] int32 GetSpellPenetrationItemMod() const { return m_spellPenetrationItemMod; } [[nodiscard]] float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const; @@ -2838,6 +2842,8 @@ protected: float m_auraBaseMod[BASEMOD_END][MOD_END]; int32 m_baseRatingValue[MAX_COMBAT_RATING]; uint32 m_baseSpellPower; + uint32 m_baseSpellDamage; + uint32 m_baseSpellHealing; uint32 m_baseFeralAP; uint32 m_baseManaRegen; uint32 m_baseHealthRegen; diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 43f11d14e..01595faa5 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -4616,8 +4616,15 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(enchant_amount), apply); LOG_DEBUG("entities.player.items", "+ {} BLOCK_VALUE", enchant_amount); break; - case ITEM_MOD_SPELL_HEALING_DONE: // deprecated - case ITEM_MOD_SPELL_DAMAGE_DONE: // deprecated + /// @deprecated item mods + case ITEM_MOD_SPELL_HEALING_DONE: + ApplySpellHealingBonus(enchant_amount, apply); + LOG_DEBUG("entities.player.items", "+ {} SPELL_HEALING", enchant_amount); + break; + case ITEM_MOD_SPELL_DAMAGE_DONE: + ApplySpellDamageBonus(enchant_amount, apply); + LOG_DEBUG("entities.player.items", "+ {} SPELL_DAMAGE", enchant_amount); + break; default: break; } diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index bf1b1464e..d9dce3c37 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -174,6 +174,23 @@ void Player::ApplySpellPowerBonus(int32 amount, bool apply) ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, amount, apply); } +void Player::ApplySpellDamageBonus(int32 amount, bool apply) +{ + apply = _ModifyUInt32(apply, m_baseSpellDamage, amount); + + // For speed just update for client + for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) + ApplyModInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + i, amount, apply); +} + +void Player::ApplySpellHealingBonus(int32 amount, bool apply) +{ + apply = _ModifyUInt32(apply, m_baseSpellHealing, amount); + + // For speed just update for client + ApplyModUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, amount, apply); +} + void Player::UpdateSpellDamageAndHealingBonus() { // Magic damage modifiers implemented in Unit::SpellDamageBonusDone diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index e1cd1735c..c433603fe 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -12034,6 +12034,7 @@ int32 Unit::SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) { // Base value DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus(); + DoneAdvertisedBenefit += ToPlayer()->GetBaseSpellDamageBonus(); // Damage bonus from stats AuraEffectList const& mDamageDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT); @@ -12796,6 +12797,7 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask) { // Base value AdvertisedBenefit += ToPlayer()->GetBaseSpellPowerBonus(); + AdvertisedBenefit += ToPlayer()->GetBaseSpellHealingBonus(); // Healing bonus from stats AuraEffectList const& mHealingDoneOfStatPercent = GetAuraEffectsByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index d3c07ed21..d6cecdc5d 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2862,35 +2862,35 @@ void ObjectMgr::LoadItemTemplates() // 0 1 2 3 4 5 6 7 8 9 10 11 12 QueryResult result = WorldDatabase.Query("SELECT entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, BuyPrice, SellPrice, InventoryType, " - // 13 14 15 16 17 18 19 20 + // 13 14 15 16 17 18 19 20 "AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, " - // 21 22 23 24 25 26 27 28 - "RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, stat_type1, " - // 29 30 31 32 33 34 35 36 37 38 + // 21 22 23 24 25 26 27 + "RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, stat_type1, " + // 28 29 30 31 32 33 34 35 36 37 "stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, stat_type5, stat_value5, stat_type6, " - // 39 40 41 42 43 44 45 46 47 + // 38 39 40 41 42 43 44 45 46 "stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, stat_type9, stat_value9, stat_type10, stat_value10, " - // 48 49 50 51 52 53 54 55 56 57 58 + // 47 48 49 50 51 52 53 54 55 56 57 "ScalingStatDistribution, ScalingStatValue, dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, " - // 59 60 61 62 63 64 65 66 67 68 + // 58 59 60 61 62 63 64 65 66 67 "nature_res, frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, spellcharges_1, " - // 69 70 71 72 73 74 75 + // 68 69 70 71 72 73 74 "spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, spelltrigger_2, spellcharges_2, " - // 76 77 78 79 80 81 82 + // 75 76 77 78 79 80 81 "spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, spellid_3, spelltrigger_3, spellcharges_3, " - // 83 84 85 86 87 88 89 + // 82 83 84 85 86 87 88 "spellppmRate_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, " - // 90 91 92 93 94 95 96 + // 89 90 91 92 93 94 95 "spellppmRate_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, " - // 97 98 99 100 101 102 103 104 105 + // 96 97 98 99 100 101 102 103 104 "spellppmRate_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, PageMaterial, " - // 106 107 108 109 110 111 112 113 114 115 116 117 + // 105 106 107 108 109 110 111 112 113 114 115 116 "startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, MaxDurability, area, Map, BagFamily, " - // 118 119 120 121 122 123 124 125 + // 117 118 119 120 121 122 123 124 "TotemCategory, socketColor_1, socketContent_1, socketColor_2, socketContent_2, socketColor_3, socketContent_3, socketBonus, " - // 126 127 128 129 130 131 132 133 + // 125 126 127 128 129 130 131 132 "GemProperties, RequiredDisenchantSkill, ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, " - // 134 135 136 + // 133 134 135 136 "FoodType, minMoneyLoot, maxMoneyLoot, flagsCustom FROM item_template"); if (!result) @@ -2940,84 +2940,91 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.MaxCount = fields[24].Get(); itemTemplate.Stackable = fields[25].Get(); itemTemplate.ContainerSlots = uint32(fields[26].Get()); - itemTemplate.StatsCount = uint32(fields[27].Get()); - for (uint8 i = 0; i < itemTemplate.StatsCount; ++i) + uint8 statsCount = 0; + while (statsCount < MAX_ITEM_PROTO_STATS) { - itemTemplate.ItemStat[i].ItemStatType = uint32(fields[28 + i * 2].Get()); - itemTemplate.ItemStat[i].ItemStatValue = fields[29 + i * 2].Get(); - } + uint32 statType = uint32(fields[27 + statsCount * 2].Get()); + int32 statValue = fields[28 + statsCount * 2].Get(); + if (statType == 0) + break; - itemTemplate.ScalingStatDistribution = uint32(fields[48].Get()); - itemTemplate.ScalingStatValue = fields[49].Get(); + itemTemplate.ItemStat[statsCount].ItemStatType = statType; + itemTemplate.ItemStat[statsCount].ItemStatValue = statValue; + statsCount++; + } + itemTemplate.StatsCount = statsCount; + + itemTemplate.ScalingStatDistribution = uint32(fields[47].Get()); + itemTemplate.ScalingStatValue = fields[48].Get(); for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) { - itemTemplate.Damage[i].DamageMin = fields[50 + i * 3].Get(); - itemTemplate.Damage[i].DamageMax = fields[51 + i * 3].Get(); - itemTemplate.Damage[i].DamageType = uint32(fields[52 + i * 3].Get()); + itemTemplate.Damage[i].DamageMin = fields[49 + i * 3].Get(); + itemTemplate.Damage[i].DamageMax = fields[50 + i * 3].Get(); + itemTemplate.Damage[i].DamageType = uint32(fields[51 + i * 3].Get()); } - itemTemplate.Armor = fields[56].Get(); - itemTemplate.HolyRes = fields[57].Get(); - itemTemplate.FireRes = fields[58].Get(); - itemTemplate.NatureRes = fields[59].Get(); - itemTemplate.FrostRes = fields[60].Get(); - itemTemplate.ShadowRes = fields[61].Get(); - itemTemplate.ArcaneRes = fields[62].Get(); - itemTemplate.Delay = uint32(fields[63].Get()); - itemTemplate.AmmoType = uint32(fields[64].Get()); - itemTemplate.RangedModRange = fields[65].Get(); + itemTemplate.Armor = fields[55].Get(); + itemTemplate.HolyRes = fields[56].Get(); + itemTemplate.FireRes = fields[57].Get(); + itemTemplate.NatureRes = fields[58].Get(); + itemTemplate.FrostRes = fields[59].Get(); + itemTemplate.ShadowRes = fields[60].Get(); + itemTemplate.ArcaneRes = fields[61].Get(); + itemTemplate.Delay = uint32(fields[62].Get()); + itemTemplate.AmmoType = uint32(fields[63].Get()); + itemTemplate.RangedModRange = fields[64].Get(); for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) { - itemTemplate.Spells[i].SpellId = fields[66 + i * 7 ].Get(); - itemTemplate.Spells[i].SpellTrigger = uint32(fields[67 + i * 7].Get()); - itemTemplate.Spells[i].SpellCharges = int32(fields[68 + i * 7].Get()); - itemTemplate.Spells[i].SpellPPMRate = fields[69 + i * 7].Get(); - itemTemplate.Spells[i].SpellCooldown = fields[70 + i * 7].Get(); - itemTemplate.Spells[i].SpellCategory = uint32(fields[71 + i * 7].Get()); - itemTemplate.Spells[i].SpellCategoryCooldown = fields[72 + i * 7].Get(); + itemTemplate.Spells[i].SpellId = fields[65 + i * 7 ].Get(); + itemTemplate.Spells[i].SpellTrigger = uint32(fields[66 + i * 7].Get()); + itemTemplate.Spells[i].SpellCharges = int32(fields[67 + i * 7].Get()); + itemTemplate.Spells[i].SpellPPMRate = fields[68 + i * 7].Get(); + itemTemplate.Spells[i].SpellCooldown = fields[69 + i * 7].Get(); + itemTemplate.Spells[i].SpellCategory = uint32(fields[70 + i * 7].Get()); + itemTemplate.Spells[i].SpellCategoryCooldown = fields[71 + i * 7].Get(); } - itemTemplate.Bonding = uint32(fields[101].Get()); - itemTemplate.Description = fields[102].Get(); - itemTemplate.PageText = fields[103].Get(); - itemTemplate.LanguageID = uint32(fields[104].Get()); - itemTemplate.PageMaterial = uint32(fields[105].Get()); - itemTemplate.StartQuest = fields[106].Get(); - itemTemplate.LockID = fields[107].Get(); - itemTemplate.Material = int32(fields[108].Get()); - itemTemplate.Sheath = uint32(fields[109].Get()); - itemTemplate.RandomProperty = fields[110].Get(); - itemTemplate.RandomSuffix = fields[111].Get(); - itemTemplate.Block = fields[112].Get(); - itemTemplate.ItemSet = fields[113].Get(); - itemTemplate.MaxDurability = uint32(fields[114].Get()); - itemTemplate.Area = fields[115].Get(); - itemTemplate.Map = uint32(fields[116].Get()); - itemTemplate.BagFamily = fields[117].Get(); - itemTemplate.TotemCategory = fields[118].Get(); + itemTemplate.Bonding = uint32(fields[100].Get()); + itemTemplate.Description = fields[101].Get(); + itemTemplate.PageText = fields[102].Get(); + itemTemplate.LanguageID = uint32(fields[103].Get()); + itemTemplate.PageMaterial = uint32(fields[104].Get()); + itemTemplate.StartQuest = fields[105].Get(); + itemTemplate.LockID = fields[106].Get(); + itemTemplate.Material = int32(fields[107].Get()); + itemTemplate.Sheath = uint32(fields[108].Get()); + itemTemplate.RandomProperty = fields[109].Get(); + itemTemplate.RandomSuffix = fields[110].Get(); + itemTemplate.Block = fields[111].Get(); + itemTemplate.ItemSet = fields[112].Get(); + itemTemplate.MaxDurability = uint32(fields[113].Get()); + itemTemplate.Area = fields[114].Get(); + itemTemplate.Map = uint32(fields[115].Get()); + itemTemplate.BagFamily = fields[116].Get(); + itemTemplate.TotemCategory = fields[117].Get(); for (uint8 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i) { - itemTemplate.Socket[i].Color = uint32(fields[119 + i * 2].Get()); - itemTemplate.Socket[i].Content = fields[120 + i * 2].Get(); + itemTemplate.Socket[i].Color = uint32(fields[118 + i * 2].Get()); + itemTemplate.Socket[i].Content = fields[119 + i * 2].Get(); } - itemTemplate.socketBonus = fields[125].Get(); - itemTemplate.GemProperties = fields[126].Get(); - itemTemplate.RequiredDisenchantSkill = uint32(fields[127].Get()); - itemTemplate.ArmorDamageModifier = fields[128].Get(); - itemTemplate.Duration = fields[129].Get(); - itemTemplate.ItemLimitCategory = uint32(fields[130].Get()); - itemTemplate.HolidayId = fields[131].Get(); - itemTemplate.ScriptId = GetScriptId(fields[132].Get()); - itemTemplate.DisenchantID = fields[133].Get(); - itemTemplate.FoodType = uint32(fields[134].Get()); - itemTemplate.MinMoneyLoot = fields[135].Get(); - itemTemplate.MaxMoneyLoot = fields[136].Get(); - itemTemplate.FlagsCu = ItemFlagsCustom(fields[137].Get()); + itemTemplate.socketBonus = fields[124].Get(); + itemTemplate.GemProperties = fields[125].Get(); + itemTemplate.RequiredDisenchantSkill = uint32(fields[126].Get()); + itemTemplate.ArmorDamageModifier = fields[127].Get(); + itemTemplate.Duration = fields[128].Get(); + itemTemplate.ItemLimitCategory = uint32(fields[129].Get()); + itemTemplate.HolidayId = fields[130].Get(); + itemTemplate.ScriptId = GetScriptId(fields[131].Get()); + itemTemplate.DisenchantID = fields[132].Get(); + itemTemplate.FoodType = uint32(fields[133].Get()); + itemTemplate.MinMoneyLoot = fields[134].Get(); + itemTemplate.MaxMoneyLoot = fields[135].Get(); + itemTemplate.FlagsCu = ItemFlagsCustom(fields[136].Get()); // Checks ItemEntry const* dbcitem = sItemStore.LookupEntry(entry); @@ -3171,12 +3178,6 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.ContainerSlots = MAX_BAG_SIZE; } - if (itemTemplate.StatsCount > MAX_ITEM_PROTO_STATS) - { - LOG_ERROR("sql.sql", "Item (Entry: {}) has too large value in statscount ({}), replace by hardcoded limit ({}).", entry, itemTemplate.StatsCount, MAX_ITEM_PROTO_STATS); - itemTemplate.StatsCount = MAX_ITEM_PROTO_STATS; - } - for (uint8 j = 0; j < itemTemplate.StatsCount; ++j) { // for ItemStatValue != 0 @@ -3189,7 +3190,8 @@ void ObjectMgr::LoadItemTemplates() switch (itemTemplate.ItemStat[j].ItemStatType) { case ITEM_MOD_SPELL_HEALING_DONE: - LOG_ERROR("sql.sql", "Item (Entry: {}) has deprecated stat_type{} ({})", entry, j + 1, itemTemplate.ItemStat[j].ItemStatType); + case ITEM_MOD_SPELL_DAMAGE_DONE: + LOG_WARN("sql.sql", "Item (Entry: {}) has deprecated stat_type{} ({})", entry, j + 1, itemTemplate.ItemStat[j].ItemStatType); break; default: break; From 239a641bd7e6ccd4c9ba9c55ba19b80179b4dd14 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Aug 2025 13:40:43 +0000 Subject: [PATCH 43/45] chore(DB): import pending files Referenced commit(s): 9ad7cef3c474faacd2999b8575cbfc79a4d58852 --- .../rev_1753528647979968682.sql => db_world/2025_08_01_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1753528647979968682.sql => db_world/2025_08_01_00.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1753528647979968682.sql b/data/sql/updates/db_world/2025_08_01_00.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1753528647979968682.sql rename to data/sql/updates/db_world/2025_08_01_00.sql index 357a10fe9..8762b4e7d 100644 --- a/data/sql/updates/pending_db_world/rev_1753528647979968682.sql +++ b/data/sql/updates/db_world/2025_08_01_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_29_03 -> 2025_08_01_00 -- ALTER TABLE `item_template` DROP COLUMN `StatsCount`; From 219942e8f87e04704a5ad22ecaabbe73ca3cb93a Mon Sep 17 00:00:00 2001 From: sudlud Date: Fri, 1 Aug 2025 17:03:41 +0200 Subject: [PATCH 44/45] fix(DB/SAI): fix scourge invasion quest 12616 'Chamber of Secrets' (#22606) --- .../pending_db_world/rev_1753980908982010900.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1753980908982010900.sql diff --git a/data/sql/updates/pending_db_world/rev_1753980908982010900.sql b/data/sql/updates/pending_db_world/rev_1753980908982010900.sql new file mode 100644 index 000000000..d8f45d505 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1753980908982010900.sql @@ -0,0 +1,11 @@ +-- fix quest 12616 'Chamber of Secrets' +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 190610; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 1 AND `entryorguid` = 190610); +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 +(190610, 1, 0, 0, 70, 0, 100, 0, 2, 0, 0, 0, 0, 0, 56, 38629, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Orders from the Lich King - On Gameobject State Changed - Add Item \'Orders from the Lich King\' 1 Time'); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 1) AND (`SourceEntry` = 190610) AND (`ConditionTypeOrReference` IN (2, 9)) AND (`ConditionValue1` IN (38629, 12616)); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 1, 190610, 1, 0, 9, 0, 12616, 0, 0, 0, 0, 0, '', '\'Orders from the Lich King\' requires quest \'Chamber of Secrets\''), +(22, 1, 190610, 1, 0, 2, 0, 38629, 1, 0, 1, 0, 0, '', 'Can\'t receive more than one \'Orders from the Lich King\''); From 8a5098861ee1122993baf9250a7133acad18d68a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 Aug 2025 15:04:50 +0000 Subject: [PATCH 45/45] chore(DB): import pending files Referenced commit(s): 219942e8f87e04704a5ad22ecaabbe73ca3cb93a --- .../rev_1753980908982010900.sql => db_world/2025_08_01_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1753980908982010900.sql => db_world/2025_08_01_01.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1753980908982010900.sql b/data/sql/updates/db_world/2025_08_01_01.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1753980908982010900.sql rename to data/sql/updates/db_world/2025_08_01_01.sql index d8f45d505..4bd47948c 100644 --- a/data/sql/updates/pending_db_world/rev_1753980908982010900.sql +++ b/data/sql/updates/db_world/2025_08_01_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_01_00 -> 2025_08_01_01 -- fix quest 12616 'Chamber of Secrets' UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 190610;