From ae6e8e0462d3ec6b2650edbb1765a38933e85937 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 28 Mar 2026 07:55:16 -0300 Subject: [PATCH 01/47] fix(Scripts/HoR): clear IMMUNE_TO_PC before engaging Falric and Marwyn (#25273) Co-authored-by: Claude Opus 4.6 --- .../Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp | 1 + .../Northrend/FrozenHalls/HallsOfReflection/boss_marwyn.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp index 699581697..db3f3b96e 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp @@ -78,6 +78,7 @@ struct boss_falric : public BossAI me->m_Events.AddEventAtOffset([this]() { _startingFight = false; + me->SetImmuneToPC(false); me->SetInCombatWithZone(); }, 8s); } diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_marwyn.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_marwyn.cpp index 2f4b0784f..813e85084 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_marwyn.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/boss_marwyn.cpp @@ -78,6 +78,7 @@ struct boss_marwyn : public BossAI me->m_Events.AddEventAtOffset([this]() { _startingFight = false; + me->SetImmuneToPC(false); me->SetInCombatWithZone(); }, 8s); } From 531da1d0b4bd04bfe22cba0c3dd6a1d5cadfa1bf Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:30:02 -0300 Subject: [PATCH 02/47] feat(Scripts/Commands): allow achievement add from console for offline players (#25283) Co-authored-by: Claude Opus 4.6 --- .../rev_achievement_add_strings.sql | 5 ++++ src/server/game/Miscellaneous/Language.h | 6 ++++- .../scripts/Commands/cs_achievement.cpp | 26 ++++++++++++++----- 3 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_achievement_add_strings.sql diff --git a/data/sql/updates/pending_db_world/rev_achievement_add_strings.sql b/data/sql/updates/pending_db_world/rev_achievement_add_strings.sql new file mode 100644 index 000000000..fbe386a68 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_achievement_add_strings.sql @@ -0,0 +1,5 @@ +-- Achievement add command feedback strings +DELETE FROM `acore_string` WHERE `entry` IN (30126, 30127); +INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES +(30126, 'Achievement {} ({}) added to player {}.', '업적 {} ({})이(가) 플레이어 {}에게 추가되었습니다.', 'Le haut fait {} ({}) a été ajouté au joueur {}.', 'Erfolg {} ({}) wurde Spieler {} hinzugefügt.', '成就 {} ({}) 已添加给玩家 {}。', '成就 {} ({}) 已新增給玩家 {}。', 'El logro {} ({}) ha sido añadido al jugador {}.', 'El logro {} ({}) ha sido añadido al jugador {}.', 'Достижение {} ({}) добавлено игроку {}.'), +(30127, 'Achievement {} ({}) will be added to player {} on next login.', '업적 {} ({})이(가) 다음 로그인 시 플레이어 {}에게 추가됩니다.', 'Le haut fait {} ({}) sera ajouté au joueur {} à la prochaine connexion.', 'Erfolg {} ({}) wird Spieler {} beim nächsten Login hinzugefügt.', '成就 {} ({}) 将在玩家 {} 下次登录时添加。', '成就 {} ({}) 將在玩家 {} 下次登入時新增。', 'El logro {} ({}) será añadido al jugador {} en el próximo inicio de sesión.', 'El logro {} ({}) será añadido al jugador {} en el próximo inicio de sesión.', 'Достижение {} ({}) будет добавлено игроку {} при следующем входе.'); diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 5fc80fca9..2172cf6ec 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1445,6 +1445,10 @@ enum AcoreStrings LANG_DEBUG_FACTIONCHANGE_GOLD_FAIL = 30122, LANG_DEBUG_FACTIONCHANGE_GOLD_OK = 30123, LANG_DEBUG_FACTIONCHANGE_GOLD_NOLIMIT = 30124, - LANG_DEBUG_FACTIONCHANGE_NA = 30125 + LANG_DEBUG_FACTIONCHANGE_NA = 30125, + + // Achievement commands + LANG_ACHIEVEMENT_ADD_ONLINE = 30126, + LANG_ACHIEVEMENT_ADD_OFFLINE = 30127 }; #endif diff --git a/src/server/scripts/Commands/cs_achievement.cpp b/src/server/scripts/Commands/cs_achievement.cpp index be1ea77f2..8557f46ae 100644 --- a/src/server/scripts/Commands/cs_achievement.cpp +++ b/src/server/scripts/Commands/cs_achievement.cpp @@ -15,8 +15,10 @@ * with this program. If not, see . */ +#include "AchievementMgr.h" #include "Chat.h" #include "CommandScript.h" +#include "Language.h" #include "Player.h" using namespace Acore::ChatCommands; @@ -30,7 +32,7 @@ public: { static ChatCommandTable achievementCommandTable = { - { "add", HandleAchievementAddCommand, SEC_GAMEMASTER, Console::No }, + { "add", HandleAchievementAddCommand, SEC_GAMEMASTER, Console::Yes }, { "checkall", HandleAchievementCheckAllCommand, SEC_ADMINISTRATOR, Console::Yes } }; static ChatCommandTable commandTable = @@ -40,15 +42,27 @@ public: return commandTable; } - static bool HandleAchievementAddCommand(ChatHandler* handler, AchievementEntry const* achievementEntry) + static bool HandleAchievementAddCommand(ChatHandler* handler, AchievementEntry const* achievementEntry, Optional player) { - Player* target = handler->getSelectedPlayer(); - if (!target) + if (!player) + player = PlayerIdentifier::FromTargetOrSelf(handler); + + if (!player) { - handler->SendErrorMessage(LANG_NO_CHAR_SELECTED); + handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND); return false; } - target->CompletedAchievement(achievementEntry); + + if (player->IsConnected()) + { + player->GetConnectedPlayer()->CompletedAchievement(achievementEntry); + handler->PSendSysMessage(LANG_ACHIEVEMENT_ADD_ONLINE, achievementEntry->ID, achievementEntry->name[0], player->GetName()); + } + else + { + sAchievementMgr->CompletedAchievementForOfflinePlayer(player->GetGUID().GetCounter(), achievementEntry); + handler->PSendSysMessage(LANG_ACHIEVEMENT_ADD_OFFLINE, achievementEntry->ID, achievementEntry->name[0], player->GetName()); + } return true; } From 82eaf7506b7b00789781dec74defc8b6899d30c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 28 Mar 2026 20:31:09 +0000 Subject: [PATCH 03/47] chore(DB): import pending files Referenced commit(s): 531da1d0b4bd04bfe22cba0c3dd6a1d5cadfa1bf --- .../2026_03_28_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_achievement_add_strings.sql => db_world/2026_03_28_01.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_achievement_add_strings.sql b/data/sql/updates/db_world/2026_03_28_01.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_achievement_add_strings.sql rename to data/sql/updates/db_world/2026_03_28_01.sql index fbe386a68..5ba25ce61 100644 --- a/data/sql/updates/pending_db_world/rev_achievement_add_strings.sql +++ b/data/sql/updates/db_world/2026_03_28_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_03_28_00 -> 2026_03_28_01 -- Achievement add command feedback strings DELETE FROM `acore_string` WHERE `entry` IN (30126, 30127); INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES From 3f0c60c1dd64a2c104a63502a2a62d49cc14fc0d Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 28 Mar 2026 18:16:51 -0300 Subject: [PATCH 04/47] fix(Core/LFG): prevent RDF anti-kick exploit via loot rolls (#25112) Co-authored-by: Claude Opus 4.6 --- src/server/game/Entities/Player/Player.cpp | 2 +- src/server/game/Groups/Group.cpp | 61 ++++++++++++---------- src/server/game/Groups/Group.h | 1 + src/server/game/Server/WorldSession.cpp | 4 ++ 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a5d5a0c13..c32f0402a 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -13040,7 +13040,7 @@ PartyResult Player::CanUninviteFromGroup(ObjectGuid targetPlayerGUID) const if (state == lfg::LFG_STATE_FINISHED_DUNGEON) return ERR_PARTY_LFG_BOOT_DUNGEON_COMPLETE; - if (grp->isRollLootActive()) + if (grp->isRollLootActive() && ObjectAccessor::FindConnectedPlayer(targetPlayerGUID)) return ERR_PARTY_LFG_BOOT_LOOT_ROLLS; /// @todo: Should also be sent when anyone has recently left combat, with an aprox ~5 seconds timer. diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 548c4787f..5ddc2b133 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -600,35 +600,7 @@ bool Group::RemoveMember(ObjectGuid guid, const RemoveMethod& method /*= GROUP_R } // Remove player from loot rolls - for (Rolls::iterator it = RollId.begin(); it != RollId.end();) - { - Roll* roll = *it; - Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid); - if (itr2 == roll->playerVote.end()) - { - ++it; - continue; - } - - if (itr2->second == GREED || itr2->second == DISENCHANT) - --roll->totalGreed; - else if (itr2->second == NEED) - --roll->totalNeed; - else if (itr2->second == PASS) - --roll->totalPass; - - if (itr2->second != NOT_VALID) - --roll->totalPlayersRolling; - - roll->playerVote.erase(itr2); - - // Xinef: itr can be erased inside - // Xinef: player is removed from all vote lists so it will not pass above playerVote == playerVote.end statement during second iteration - if (CountRollVote(guid, roll->itemGUID, MAX_ROLL_TYPE)) - it = RollId.begin(); - else - ++it; - } + RemovePlayerFromRolls(guid); // Update subgroups member_witerator slot = _getMemberWSlot(guid); @@ -1422,6 +1394,37 @@ void Group::EndRoll(Loot* pLoot, Map* allowedMap) } } +void Group::RemovePlayerFromRolls(ObjectGuid guid) +{ + for (Rolls::iterator it = RollId.begin(); it != RollId.end();) + { + Roll* roll = *it; + Roll::PlayerVote::iterator itr2 = roll->playerVote.find(guid); + if (itr2 == roll->playerVote.end()) + { + ++it; + continue; + } + + if (itr2->second == GREED || itr2->second == DISENCHANT) + --roll->totalGreed; + else if (itr2->second == NEED) + --roll->totalNeed; + else if (itr2->second == PASS) + --roll->totalPass; + + if (itr2->second != NOT_VALID) + --roll->totalPlayersRolling; + + roll->playerVote.erase(itr2); + + if (CountRollVote(guid, roll->itemGUID, MAX_ROLL_TYPE)) + it = RollId.begin(); + else + ++it; + } +} + void Group::CountTheRoll(Rolls::iterator rollI, Map* allowedMap) { Roll* roll = *rollI; diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index c4a9b9262..5d2ca9e75 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -299,6 +299,7 @@ public: void CountTheRoll(Rolls::iterator roll, Map* allowedMap); bool CountRollVote(ObjectGuid playerGUID, ObjectGuid Guid, uint8 Choise); void EndRoll(Loot* loot, Map* allowedMap); + void RemovePlayerFromRolls(ObjectGuid guid); // related to disenchant rolls void ResetMaxEnchantingLevel(); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index ef81cbb86..131e39a30 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -702,6 +702,10 @@ void WorldSession::LogoutPlayer(bool save) // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected) d) LeaveGroupOnLogout is enabled if (_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && !_player->GetGroup()->isLFGGroup() && m_Socket && sWorld->getBoolConfig(CONFIG_LEAVE_GROUP_ON_LOGOUT)) _player->RemoveFromGroup(); + // Remove player from active loot rolls in LFG groups (player stays in group but should not block rolls) + else if (Group* group = _player->GetGroup()) + if (group->isLFGGroup()) + group->RemovePlayerFromRolls(_player->GetGUID()); // pussywizard: checked second time after being removed from a group if (!_player->IsBeingTeleportedFar() && !_player->m_InstanceValid && !_player->IsGameMaster()) From 28833e12dccbcf122d00bfb98a647f36cdff1720 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:12:55 -0300 Subject: [PATCH 05/47] fix(Scripts/HoR): fix HoR escape event outro and refactor tracking (#25287) Co-authored-by: Claude Opus 4.6 --- .../HallsOfReflection/halls_of_reflection.cpp | 70 +++++++------------ .../HallsOfReflection/halls_of_reflection.h | 19 ++--- .../instance_halls_of_reflection.cpp | 5 +- 3 files changed, 40 insertions(+), 54 deletions(-) diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp index 95b2c4f5c..c6fbed94b 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -1499,7 +1499,6 @@ enum eFightEvents EVENT_LK_SUMMON_LA, EVENT_LK_SUMMON_NEXT_ICE_WALL, EVENT_SAY_OPENING, - EVENT_DECREASE_REQ_COUNT_BY_100, }; struct npc_hor_lich_king : public NullCreatureAI @@ -1515,13 +1514,14 @@ struct npc_hor_lich_king : public NullCreatureAI EventMap events; SummonList summons; uint8 currentWall; - uint8 reqKillCount; + uint8 summonsCount; uint8 div2; void Reset() override { currentWall = 0; - reqKillCount = 0; + summonsCount = 0; + div2 = 0; events.Reset(); events.RescheduleEvent(EVENT_LK_CHECK_COMBAT, 1s); } @@ -1529,24 +1529,21 @@ struct npc_hor_lich_king : public NullCreatureAI { if (action == ACTION_START_LK_FIGHT_REAL) events.ScheduleEvent(EVENT_LK_START_FOLLOWING, 1500ms); - else if ((action == ACTION_INFORM_TRASH_DIED && reqKillCount) || action == ACTION_CHECK_TRASH_DIED) + } + + void WallCompleted() + { + ++currentWall; + instance->SetData(ACTION_DELETE_ICE_WALL, 1); + if (currentWall <= 3) { - if ((action == ACTION_CHECK_TRASH_DIED && reqKillCount == 0) || (action == ACTION_INFORM_TRASH_DIED && (--reqKillCount) == 0)) - { - events.CancelEvent(EVENT_DECREASE_REQ_COUNT_BY_100); // just in case, magic happens sometimes - ++currentWall; - instance->SetData(ACTION_DELETE_ICE_WALL, 1); - if (currentWall <= 3) - { - events.ScheduleEvent(EVENT_LK_SUMMON_NEXT_ICE_WALL, 1s); - events.ScheduleEvent(EVENT_LK_SUMMON, currentWall == 3 ? 11s : 7500ms); - } - else - me->RemoveAura(SPELL_REMORSELESS_WINTER); - if (Creature* c = instance->GetCreature(NPC_SYLVANAS_PART2)) - c->AI()->DoAction(ACTION_INFORM_WALL_DESTROYED); - } + events.ScheduleEvent(EVENT_LK_SUMMON_NEXT_ICE_WALL, 1s); + events.ScheduleEvent(EVENT_LK_SUMMON, currentWall == 3 ? 11s : 7500ms); } + else + me->RemoveAura(SPELL_REMORSELESS_WINTER); + if (Creature* c = instance->GetCreature(NPC_SYLVANAS_PART2)) + c->AI()->DoAction(ACTION_INFORM_WALL_DESTROYED); } void MovementInform(uint32 type, uint32 /*id*/) override @@ -1569,17 +1566,14 @@ struct npc_hor_lich_king : public NullCreatureAI else if (currentWall == 4) { Talk(SAY_LK_NOWHERE_TO_RUN); - instance->SetData(DATA_LICH_KING, DONE); + instance->SetBossState(DATA_LICH_KING, DONE); } } } void JustSummoned(Creature* s) override { - - ++reqKillCount; - if (events.HasTimeUntilEvent(EVENT_DECREASE_REQ_COUNT_BY_100)) - events.RescheduleEvent(EVENT_DECREASE_REQ_COUNT_BY_100, 10s); + ++summonsCount; summons.Summon(s); s->SetHomePosition(PathWaypoints[WP_STOP[currentWall + 1]]); s->GetMotionMaster()->MovePoint(0, PathWaypoints[WP_STOP[currentWall + 1]]); @@ -1592,6 +1586,14 @@ struct npc_hor_lich_king : public NullCreatureAI s->SetHomePosition(PathWaypoints[WP_STOP[currentWall + 1]]); } + void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) override + { + if (!summonsCount) + return; + if (--summonsCount == 0) + WallCompleted(); + } + void SummonedCreatureDespawn(Creature* s) override { summons.Despawn(s); @@ -1623,7 +1625,7 @@ struct npc_hor_lich_king : public NullCreatureAI { me->GetMotionMaster()->MovementExpired(); me->StopMoving(); - reqKillCount = 255; + summonsCount = 255; leader->InterruptNonMeleeSpells(true); me->CastSpell(leader, SPELL_HARVEST_SOUL); events.ScheduleEvent(EVENT_LK_KILL_LEADER, 3s); @@ -1720,15 +1722,6 @@ struct npc_hor_lich_king : public NullCreatureAI events.ScheduleEvent(EVENT_LK_SUMMON_LA, 17s + 700ms); break; } - if (currentWall <= 3) - { - reqKillCount = 100; - events.RescheduleEvent(EVENT_DECREASE_REQ_COUNT_BY_100, 10s); - } - break; - case EVENT_DECREASE_REQ_COUNT_BY_100: - reqKillCount = (reqKillCount <= 100 ? 0 : reqKillCount - 100); - DoAction(ACTION_CHECK_TRASH_DIED); break; case EVENT_LK_SUMMON_GHOULS: DoCastAOE(SPELL_SUMMON_RAGING_GHOULS); @@ -2003,9 +1996,6 @@ struct npc_hor_raging_ghoul : public ScriptedAI void JustDied(Unit* /*killer*/) override { me->SetCorpseDelay(10); - if (InstanceScript* instance = me->GetInstanceScript()) - if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) - lichKing->AI()->DoAction(ACTION_INFORM_TRASH_DIED); } }; @@ -2059,9 +2049,6 @@ struct npc_hor_risen_witch_doctor : public ScriptedAI void JustDied(Unit* /*killer*/) override { me->SetCorpseDelay(10); - if (InstanceScript* instance = me->GetInstanceScript()) - if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) - lichKing->AI()->DoAction(ACTION_INFORM_TRASH_DIED); } }; @@ -2110,9 +2097,6 @@ struct npc_hor_lumbering_abomination : public ScriptedAI void JustDied(Unit* /*killer*/) override { me->SetCorpseDelay(10); - if (InstanceScript* instance = me->GetInstanceScript()) - if (Creature* lichKing = instance->GetCreature(NPC_LICH_KING_BOSS)) - lichKing->AI()->DoAction(ACTION_INFORM_TRASH_DIED); } }; diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h index dfd4c93c5..363d1d17e 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h @@ -27,14 +27,22 @@ enum Data { - DATA_INTRO, + // Boss encounters (indices for SetBossState) DATA_FALRIC, DATA_MARWYN, + DATA_LICH_KING, + MAX_ENCOUNTER, + + // Non-boss data (SetData/GetData only) + DATA_INTRO, DATA_FROSTSWORN_GENERAL, DATA_LK_INTRO, - DATA_LICH_KING, DATA_BATTERED_HILT, - MAX_ENCOUNTER, + DATA_WAVE_NUMBER, + DATA_LK_BATTLE, + DATA_SHIP_CAPTAIN, + + // Actions ACTION_SHOW_TRASH, ACTION_SPIRITUAL_REFLECTIONS_COPY, ACTION_SPIRITUAL_REFLECTIONS_ACTIVATE, @@ -42,9 +50,6 @@ enum Data ACTION_START_LK_FIGHT, ACTION_STOP_LK_FIGHT, ACTION_DELETE_ICE_WALL, - DATA_WAVE_NUMBER, - DATA_LK_BATTLE,// in progress - DATA_SHIP_CAPTAIN, }; enum Creatures @@ -249,8 +254,6 @@ enum hMisc ACTION_START_INTRO, ACTION_SKIP_INTRO, ACTION_START_LK_FIGHT_REAL, - ACTION_INFORM_TRASH_DIED, - ACTION_CHECK_TRASH_DIED, ACTION_INFORM_WALL_DESTROYED, QUEST_DELIVRANCE_FROM_THE_PIT_A2 = 24710, diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp index a1bd4554e..9d235c7b9 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp @@ -468,6 +468,7 @@ public: leader->AI()->DoAction(ACTION_START_INTRO); break; case ACTION_START_LK_FIGHT: + SetBossState(DATA_LICH_KING, IN_PROGRESS); _isLichKingFightActive = true; DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_RETREATING_TIMED_EVENT); DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_RETREATING_TIMED_EVENT); @@ -516,15 +517,13 @@ public: lichKing->SetSpeed(MOVE_RUN, lichKing->GetCreatureTemplate()->speed_run); } _isLichKingFightActive = false; + SetBossState(DATA_LICH_KING, FAIL); _outroTimer = 0; _outroStep = 0; [[fallthrough]]; case ACTION_DELETE_ICE_WALL: HandleGameObject(GO_ICE_WALL, true); break; - case DATA_LICH_KING: - SetBossState(DATA_LICH_KING, static_cast(data)); - break; case DATA_BATTERED_HILT: { if (GetPersistentData(PERSISTENT_DATA_BATTERED_HILT)) From c10e19e2c6e35aa58b9d4b7ce253442fd3360134 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:14:18 -0300 Subject: [PATCH 06/47] refactor(Core/Scripts): move Outland gossip handlers to database (#25274) Co-authored-by: Claude Opus 4.6 --- .../rev_1774580000_gossip_outland.sql | 118 ++++++++++++++++++ .../Outland/zone_shadowmoon_valley.cpp | 95 -------------- .../scripts/Outland/zone_shattrath_city.cpp | 97 -------------- 3 files changed, 118 insertions(+), 192 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1774580000_gossip_outland.sql diff --git a/data/sql/updates/pending_db_world/rev_1774580000_gossip_outland.sql b/data/sql/updates/pending_db_world/rev_1774580000_gossip_outland.sql new file mode 100644 index 000000000..77ba9ea5e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1774580000_gossip_outland.sql @@ -0,0 +1,118 @@ +-- Move Outland gossip handlers from C++ to database +-- NPCs: Shattrath Flask Vendors (23484, 23483), Zephyr (25967), Drake Dealer Hurlunk (23489) + +-- ===================================================== +-- 1. npc_shattrathflaskvendors - Aldor vendor (23484), menu 8751 +-- Show vendor option only if Exalted with Aldor (932), Sha'tar (935), Cenarion Expedition (942) +-- Text 11085 = qualified, Text 11083 = rejection (default) +-- ===================================================== + +-- Condition on gossip_menu: show text 11085 if Exalted with all 3 factions +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=14 AND `SourceGroup`=8751 AND `SourceEntry`=11085; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 8751, 11085, 0, 0, 5, 0, 932, 128, 0, 0, 0, 0, '', 'Haldor the Compulsive - Show text 11085 if Exalted with Aldor'), +(14, 8751, 11085, 0, 0, 5, 0, 935, 128, 0, 0, 0, 0, '', 'Haldor the Compulsive - Show text 11085 if Exalted with Sha''tar'), +(14, 8751, 11085, 0, 0, 5, 0, 942, 128, 0, 0, 0, 0, '', 'Haldor the Compulsive - Show text 11085 if Exalted with Cenarion Expedition'); + +-- Condition on gossip_menu_option: show vendor option only if Exalted with all 3 factions +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8751 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8751, 0, 0, 0, 5, 0, 932, 128, 0, 0, 0, 0, '', 'Haldor the Compulsive - Show vendor option if Exalted with Aldor'), +(15, 8751, 0, 0, 0, 5, 0, 935, 128, 0, 0, 0, 0, '', 'Haldor the Compulsive - Show vendor option if Exalted with Sha''tar'), +(15, 8751, 0, 0, 0, 5, 0, 942, 128, 0, 0, 0, 0, '', 'Haldor the Compulsive - Show vendor option if Exalted with Cenarion Expedition'); + +-- Remove ScriptName +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=23484; + +-- ===================================================== +-- 2. npc_shattrathflaskvendors - Scryers vendor (23483), menu 8752 +-- Show vendor option only if Exalted with Scryers (934), Sha'tar (935), Cenarion Expedition (942) +-- Text 11085 = qualified, Text 11084 = rejection (default) +-- ===================================================== + +-- Add qualified text 11085 to menu 8752 (only rejection text 11084 exists) +DELETE FROM `gossip_menu` WHERE `MenuID`=8752 AND `TextID`=11085; +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(8752, 11085); + +-- Condition on gossip_menu: show text 11085 if Exalted with all 3 factions +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=14 AND `SourceGroup`=8752 AND `SourceEntry`=11085; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 8752, 11085, 0, 0, 5, 0, 934, 128, 0, 0, 0, 0, '', 'Arcanist Xorith - Show text 11085 if Exalted with Scryers'), +(14, 8752, 11085, 0, 0, 5, 0, 935, 128, 0, 0, 0, 0, '', 'Arcanist Xorith - Show text 11085 if Exalted with Sha''tar'), +(14, 8752, 11085, 0, 0, 5, 0, 942, 128, 0, 0, 0, 0, '', 'Arcanist Xorith - Show text 11085 if Exalted with Cenarion Expedition'); + +-- Condition on gossip_menu_option: show vendor option only if Exalted with all 3 factions +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8752 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8752, 0, 0, 0, 5, 0, 934, 128, 0, 0, 0, 0, '', 'Arcanist Xorith - Show vendor option if Exalted with Scryers'), +(15, 8752, 0, 0, 0, 5, 0, 935, 128, 0, 0, 0, 0, '', 'Arcanist Xorith - Show vendor option if Exalted with Sha''tar'), +(15, 8752, 0, 0, 0, 5, 0, 942, 128, 0, 0, 0, 0, '', 'Arcanist Xorith - Show vendor option if Exalted with Cenarion Expedition'); + +-- Remove ScriptName +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=23483; + +-- ===================================================== +-- 3. npc_zephyr (25967), menu 9205 +-- Show teleport option if Revered+ with Keepers of Time (989) +-- On select: player casts spell 37778 (Teleport: Caverns of Time) +-- ===================================================== + +-- Condition on gossip_menu_option: show option if Revered or Exalted with Keepers of Time +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=9205 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 9205, 0, 0, 0, 5, 0, 989, 192, 0, 0, 0, 0, '', 'Zephyr - Show teleport option if Revered+ with Keepers of Time'); + +-- SAI: on gossip select (menu 9205, option 0) -> invoker self-casts spell 37778 +DELETE FROM `smart_scripts` WHERE `entryorguid`=25967 AND `source_type`=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 +(25967, 0, 0, 0, 62, 0, 100, 512, 9205, 0, 0, 0, 0, 0, 134, 37778, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Zephyr - On Gossip Select - Invoker Cast Teleport Caverns of Time'); + +-- Update creature_template: set SmartAI, remove ScriptName +UPDATE `creature_template` SET `ScriptName`='', `AIName`='SmartAI' WHERE `entry`=25967; + +-- ===================================================== +-- 4. npc_drake_dealer_hurlunk (23489), menu 8754 +-- Show vendor option only if Exalted with Netherwing (1015) +-- ===================================================== + +-- Condition on gossip_menu_option: show vendor option if Exalted with Netherwing +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8754 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8754, 0, 0, 0, 5, 0, 1015, 128, 0, 0, 0, 0, '', 'Drake Dealer Hurlunk - Show vendor option if Exalted with Netherwing'); + +-- Remove ScriptName +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=23489; + +-- ===================================================== +-- 5. npcs_flanis_swiftwing_and_kagrosh +-- Flanis Swiftwing (21727), menu 8356 - give item 30658 if quest 10583 incomplete +-- Kagrosh (21725), menu 8371 - give item 30659 if quest 10601 incomplete +-- ===================================================== + +-- Condition on menu 8356 option 0: quest 10583 incomplete AND player doesn't have item 30658 +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8356 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8356, 0, 0, 0, 47, 0, 10583, 8, 0, 0, 0, 0, '', 'Flanis Swiftwing - Show option if quest 10583 is in progress'), +(15, 8356, 0, 0, 0, 2, 0, 30658, 1, 0, 1, 0, 0, '', 'Flanis Swiftwing - Show option if player does not have item 30658'); + +-- Condition on menu 8371 option 0: quest 10601 incomplete AND player doesn't have item 30659 +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8371 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8371, 0, 0, 0, 47, 0, 10601, 8, 0, 0, 0, 0, '', 'Kagrosh - Show option if quest 10601 is in progress'), +(15, 8371, 0, 0, 0, 2, 0, 30659, 1, 0, 1, 0, 0, '', 'Kagrosh - Show option if player does not have item 30659'); + +-- SAI for Flanis (21727): on gossip select -> close gossip + add item 30658 +DELETE FROM `smart_scripts` WHERE `entryorguid`=21727 AND `source_type`=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 +(21727, 0, 0, 1, 62, 0, 100, 512, 8356, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Flanis Swiftwing - On Gossip Select - Close Gossip'), +(21727, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 56, 30658, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Flanis Swiftwing - Linked - Add Item 30658'); + +-- SAI for Kagrosh (21725): on gossip select -> close gossip + add item 30659 +DELETE FROM `smart_scripts` WHERE `entryorguid`=21725 AND `source_type`=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 +(21725, 0, 0, 1, 62, 0, 100, 512, 8371, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Kagrosh - On Gossip Select - Close Gossip'), +(21725, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 56, 30659, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Kagrosh - Linked - Add Item 30659'); + +-- Update creature_template: set SmartAI, remove ScriptName +UPDATE `creature_template` SET `ScriptName`='', `AIName`='SmartAI' WHERE `entry` IN (21725, 21727); diff --git a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp index e53f34e32..88de0fe4f 100644 --- a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp +++ b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp @@ -20,7 +20,6 @@ #include "Group.h" #include "Player.h" #include "ScriptedCreature.h" -#include "ScriptedGossip.h" #include "SpellScript.h" #include "SpellScriptLoader.h" @@ -657,98 +656,6 @@ public: }; }; -/*###### -## npc_drake_dealer_hurlunk -######*/ - -class npc_drake_dealer_hurlunk : public CreatureScript -{ -public: - npc_drake_dealer_hurlunk() : CreatureScript("npc_drake_dealer_hurlunk") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsVendor() && player->GetReputationRank(1015) == REP_EXALTED) - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } -}; - -/*###### -## npc_flanis_swiftwing_and_kagrosh -######*/ - -enum Flanis : uint32 -{ - QUEST_THE_FATE_OF_FLANIS = 10583, - ITEM_FLAUNISS_PACK = 30658, - GOSSIP_MENU_FLANIS = 8356, -}; - -enum Kagrosh : uint32 -{ - QUEST_THE_FATE_OF_KAGROSH = 10601, - ITEM_KAGROSHS_PACK = 30659, - GOSSIP_MENU_KAGROSH = 8371, -}; - -class npcs_flanis_swiftwing_and_kagrosh : public CreatureScript -{ -public: - npcs_flanis_swiftwing_and_kagrosh() : CreatureScript("npcs_flanis_swiftwing_and_kagrosh") { } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, ITEM_FLAUNISS_PACK, 1, nullptr); - if (msg == EQUIP_ERR_OK) - { - player->StoreNewItem(dest, ITEM_FLAUNISS_PACK, true); - } - } - if (action == GOSSIP_ACTION_INFO_DEF + 2) - { - ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, ITEM_KAGROSHS_PACK, 1, nullptr); - if (msg == EQUIP_ERR_OK) - { - player->StoreNewItem(dest, ITEM_KAGROSHS_PACK, true); - } - } - - CloseGossipMenuFor(player); - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->GetQuestStatus(QUEST_THE_FATE_OF_FLANIS) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(ITEM_FLAUNISS_PACK, 1, true)) - AddGossipItemFor(player, GOSSIP_MENU_FLANIS, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - if (player->GetQuestStatus(QUEST_THE_FATE_OF_KAGROSH) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(ITEM_KAGROSHS_PACK, 1, true)) - AddGossipItemFor(player, GOSSIP_MENU_KAGROSH, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } -}; - /*#### # npc_karynaku ####*/ @@ -2219,8 +2126,6 @@ void AddSC_shadowmoon_valley() new npc_mature_netherwing_drake(); RegisterCreatureAI(npc_enslaved_netherwing_drake); new npc_dragonmaw_peon(); - new npc_drake_dealer_hurlunk(); - new npcs_flanis_swiftwing_and_kagrosh(); new npc_karynaku(); new npc_lord_illidan_stormrage(); new go_crystal_prison(); diff --git a/src/server/scripts/Outland/zone_shattrath_city.cpp b/src/server/scripts/Outland/zone_shattrath_city.cpp index 4447b2797..83f93dc96 100644 --- a/src/server/scripts/Outland/zone_shattrath_city.cpp +++ b/src/server/scripts/Outland/zone_shattrath_city.cpp @@ -20,101 +20,6 @@ #include "Player.h" #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" -#include "ScriptedGossip.h" - -/* -################################################## -Shattrath City Flask Vendors provides flasks to people exalted with 3 fActions: -Haldor the Compulsive -Arcanist Xorith -Both sell special flasks for use in Outlands 25man raids only, -purchasable for one Mark of Illidari each -Purchase requires exalted reputation with Scryers/Aldor, Cenarion Expedition and The Sha'tar -################################################## -*/ - -class npc_shattrathflaskvendors : public CreatureScript -{ -public: - npc_shattrathflaskvendors() : CreatureScript("npc_shattrathflaskvendors") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->GetEntry() == 23484) - { - // Aldor vendor - if (creature->IsVendor() && (player->GetReputationRank(932) == REP_EXALTED) && (player->GetReputationRank(935) == REP_EXALTED) && (player->GetReputationRank(942) == REP_EXALTED)) - { - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - SendGossipMenuFor(player, 11085, creature->GetGUID()); - } - else - { - SendGossipMenuFor(player, 11083, creature->GetGUID()); - } - } - - if (creature->GetEntry() == 23483) - { - // Scryers vendor - if (creature->IsVendor() && (player->GetReputationRank(934) == REP_EXALTED) && (player->GetReputationRank(935) == REP_EXALTED) && (player->GetReputationRank(942) == REP_EXALTED)) - { - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - SendGossipMenuFor(player, 11085, creature->GetGUID()); - } - else - { - SendGossipMenuFor(player, 11084, creature->GetGUID()); - } - } - - return true; - } -}; - -/*###### -# npc_zephyr -######*/ - -enum Zephyr : int32 -{ - GOSSIP_MENU_ZEPHYR = 9205, - SPELL_TELEPORT_CAVERNS_OF_TIME = 37778, -}; - -class npc_zephyr : public CreatureScript -{ -public: - npc_zephyr() : CreatureScript("npc_zephyr") { } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - player->CastSpell(player, SPELL_TELEPORT_CAVERNS_OF_TIME, false); - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->GetReputationRank(989) >= REP_REVERED) - AddGossipItemFor(player, GOSSIP_MENU_ZEPHYR, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } -}; /*###### # npc_kservant @@ -415,8 +320,6 @@ struct npc_shattrath_daily_quest : public NullCreatureAI void AddSC_shattrath_city() { - new npc_shattrathflaskvendors(); - new npc_zephyr(); new npc_kservant(); RegisterCreatureAI(npc_shattrath_daily_quest); } From 0803e77488119d1fa492473b04aa2118f1b7ad88 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:14:31 -0300 Subject: [PATCH 07/47] refactor(Core/Scripts): move Northrend gossip handlers to database (#25275) Co-authored-by: Claude Opus 4.6 --- .../rev_1774590000_gossip_northrend.sql | 96 +++++++++++++++++++ .../scripts/Northrend/zone_borean_tundra.cpp | 45 --------- .../scripts/Northrend/zone_howling_fjord.cpp | 69 ------------- .../scripts/Northrend/zone_sholazar_basin.cpp | 59 ------------ .../scripts/Northrend/zone_storm_peaks.cpp | 52 ---------- 5 files changed, 96 insertions(+), 225 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1774590000_gossip_northrend.sql diff --git a/data/sql/updates/pending_db_world/rev_1774590000_gossip_northrend.sql b/data/sql/updates/pending_db_world/rev_1774590000_gossip_northrend.sql new file mode 100644 index 000000000..3b0d0bb39 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1774590000_gossip_northrend.sql @@ -0,0 +1,96 @@ +-- Move Northrend gossip handlers from C++ to database +-- NPCs: Iruk (26219), Razael & Lyana (23998, 23778), Roxi Ramrocket (31247), Avatar of Freya (27801) + +-- ===================================================== +-- 1. npc_iruk (26219) - zone_borean_tundra +-- Show gossip option if quest 11961 is incomplete +-- On select: player self-casts spell 46816 (Create Totem of Issliruk) +-- ===================================================== + +-- Condition on gossip_menu_option: show option if quest 11961 is in progress +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=9280 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 9280, 0, 0, 0, 47, 0, 11961, 8, 0, 0, 0, 0, '', 'Iruk - Show option if quest 11961 is in progress'); + +-- SAI: on gossip select (menu 9280, option 0) -> close gossip + invoker self-cast spell 46816 +DELETE FROM `smart_scripts` WHERE `entryorguid`=26219 AND `source_type`=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 +(26219, 0, 0, 1, 62, 0, 100, 512, 9280, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Iruk - On Gossip Select - Close Gossip'), +(26219, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 134, 46816, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Iruk - Linked - Invoker Cast Create Totem of Issliruk'); + +-- Update creature_template +UPDATE `creature_template` SET `ScriptName`='', `AIName`='SmartAI' WHERE `entry`=26219; + +-- ===================================================== +-- 2. npc_razael_and_lyana - zone_howling_fjord +-- Razael (23998), menu 8870 -> ActionMenuID 8869 (already wired) +-- Lyana (23778), menu 8879 -> ActionMenuID 8878 (already wired) +-- Both require quest 11221 incomplete, give TalkedToCreature credit on select +-- ===================================================== + +-- Condition on menu 8870 option 0: quest 11221 in progress +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8870 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8870, 0, 0, 0, 47, 0, 11221, 8, 0, 0, 0, 0, '', 'Razael - Show gossip option if quest 11221 is in progress'); + +-- Condition on menu 8879 option 0: quest 11221 in progress +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=8879 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 8879, 0, 0, 0, 47, 0, 11221, 8, 0, 0, 0, 0, '', 'Lyana - Show gossip option if quest 11221 is in progress'); + +-- SAI for Razael (23998): on gossip select (menu 8870, option 0) -> quest credit 23998 +DELETE FROM `smart_scripts` WHERE `entryorguid`=23998 AND `source_type`=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 +(23998, 0, 0, 0, 62, 0, 100, 512, 8870, 0, 0, 0, 0, 0, 33, 23998, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Razael - On Gossip Select - Quest Credit 23998'); + +-- Update Razael creature_template +UPDATE `creature_template` SET `ScriptName`='', `AIName`='SmartAI' WHERE `entry`=23998; + +-- SAI for Lyana (23778): append gossip handler to existing SAI (id 0 = OOC say already exists) +DELETE FROM `smart_scripts` WHERE `entryorguid`=23778 AND `source_type`=0 AND `id`=1; +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 +(23778, 0, 1, 0, 62, 0, 100, 512, 8879, 0, 0, 0, 0, 0, 33, 23778, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Lyana - On Gossip Select - Quest Credit 23778'); + +-- Update Lyana creature_template (already has SmartAI) +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=23778; + +-- ===================================================== +-- 3. npc_roxi_ramrocket (31247) - zone_storm_peaks +-- Trainer option already in DB (menu 10210, OptionID 0, type 5) +-- Add vendor option gated by HasSpell (60866 Mechano-Hog OR 60867 Mekgineer's Chopper) +-- ===================================================== + +-- Add vendor gossip_menu_option +DELETE FROM `gossip_menu_option` WHERE `MenuID`=10210 AND `OptionID`=1; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(10210, 1, 1, 'I want to browse your goods.', 3370, 3, 128, 0, 0, 0, 0, '', 0, 0); + +-- Condition on vendor option: HasSpell 60866 (Mechano-Hog) OR HasSpell 60867 (Mekgineer's Chopper) +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=10210 AND `SourceEntry`=1; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 10210, 1, 0, 0, 25, 0, 60866, 0, 0, 0, 0, 0, '', 'Roxi Ramrocket - Show vendor option if player has Mechano-Hog'), +(15, 10210, 1, 0, 1, 25, 0, 60867, 0, 0, 0, 0, 0, '', 'Roxi Ramrocket - Show vendor option if player has Mekgineer''s Chopper'); + +-- Remove ScriptName +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=31247; + +-- ===================================================== +-- 4. npc_avatar_of_freya (27801) - zone_sholazar_basin +-- 3-step gossip chain already wired: 9720 -> 9721 -> 9722 -> close +-- Condition on first option: quest 12621 incomplete +-- SAI on final select: invoker self-cast spell 52045 +-- ===================================================== + +-- Condition on menu 9720 option 0: quest 12621 in progress +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=15 AND `SourceGroup`=9720 AND `SourceEntry`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 9720, 0, 0, 0, 47, 0, 12621, 8, 0, 0, 0, 0, '', 'Avatar of Freya - Show gossip option if quest 12621 is in progress'); + +-- SAI: on gossip select (menu 9722, option 0) -> close gossip + invoker self-cast spell 52045 +DELETE FROM `smart_scripts` WHERE `entryorguid`=27801 AND `source_type`=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 +(27801, 0, 0, 1, 62, 0, 100, 512, 9722, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Avatar of Freya - On Gossip Select - Close Gossip'), +(27801, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 134, 52045, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Avatar of Freya - Linked - Invoker Cast Freya Conversation'); + +-- Update creature_template +UPDATE `creature_template` SET `ScriptName`='', `AIName`='SmartAI' WHERE `entry`=27801; diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index a3cab28af..ce6a3a79b 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -239,50 +239,6 @@ public: } }; -/*###### -## npc_iruk -######*/ - -enum Iruk -{ - GOSSIP_MENU_ID_NPC_IRUK = 9280, - GOSSIP_OPTION_SEARCH_CORPSE = 0, - NPC_TEXT_THIS_YOUNG_TUSKARR = 12585, - - QUEST_SPIRITS_WATCH_OVER_US = 11961, - - SPELL_CREATE_TOTEM_OF_ISSLIRUK = 46816 -}; - -class npc_iruk : public CreatureScript -{ -public: - npc_iruk() : CreatureScript("npc_iruk") { } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->GetQuestStatus(QUEST_SPIRITS_WATCH_OVER_US) == QUEST_STATUS_INCOMPLETE) - AddGossipItemFor(player, GOSSIP_MENU_ID_NPC_IRUK, GOSSIP_OPTION_SEARCH_CORPSE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - SendGossipMenuFor(player, NPC_TEXT_THIS_YOUNG_TUSKARR, creature->GetGUID()); - - return true; - } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - player->CastSpell(player, SPELL_CREATE_TOTEM_OF_ISSLIRUK, true); - CloseGossipMenuFor(player); - } - - return true; - } -}; - /*###### ## npc_nerubar_victim ######*/ @@ -1379,7 +1335,6 @@ void AddSC_borean_tundra() RegisterSpellScript(spell_q11919_q11940_drake_hunt_aura); new npc_sinkhole_kill_credit(); new npc_khunok_the_behemoth(); - new npc_iruk(); new npc_nerubar_victim(); new npc_lurgglbr(); RegisterSpellScript(spell_arcane_chains_character_force_cast); diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index 731f022e9..57f698386 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -20,7 +20,6 @@ #include "Player.h" #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" -#include "ScriptedGossip.h" #include "SpellInfo.h" #include "SpellScript.h" @@ -227,73 +226,6 @@ public: } }; -/*###### -## npc_razael_and_lyana -######*/ - -enum Razael -{ - QUEST_REPORTS_FROM_THE_FIELD = 11221, - NPC_RAZAEL = 23998, - NPC_LYANA = 23778, - GOSSIP_TEXTID_RAZAEL1 = 11562, - GOSSIP_TEXTID_RAZAEL2 = 11564, - GOSSIP_TEXTID_LYANA1 = 11586, - GOSSIP_TEXTID_LYANA2 = 11588 -}; - -class npc_razael_and_lyana : public CreatureScript -{ -public: - npc_razael_and_lyana() : CreatureScript("npc_razael_and_lyana") { } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(QUEST_REPORTS_FROM_THE_FIELD) == QUEST_STATUS_INCOMPLETE) - switch (creature->GetEntry()) - { - case NPC_RAZAEL: - if (!player->GetReqKillOrCastCurrentCount(QUEST_REPORTS_FROM_THE_FIELD, NPC_RAZAEL)) - { - AddGossipItemFor(player, 8870, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, GOSSIP_TEXTID_RAZAEL1, creature->GetGUID()); - return true; - } - break; - case NPC_LYANA: - if (!player->GetReqKillOrCastCurrentCount(QUEST_REPORTS_FROM_THE_FIELD, NPC_LYANA)) - { - AddGossipItemFor(player, 8879, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, GOSSIP_TEXTID_LYANA1, creature->GetGUID()); - return true; - } - break; - } - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF + 1: - SendGossipMenuFor(player, GOSSIP_TEXTID_RAZAEL2, creature->GetGUID()); - player->TalkedToCreature(NPC_RAZAEL, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - SendGossipMenuFor(player, GOSSIP_TEXTID_LYANA2, creature->GetGUID()); - player->TalkedToCreature(NPC_LYANA, creature->GetGUID()); - break; - } - return true; - } -}; - enum RodinLightningSpells { SPELL_RODIN_LIGHTNING_START = 44787, @@ -490,7 +422,6 @@ void AddSC_howling_fjord() new npc_attracted_reef_bull(); new npc_apothecary_hanes(); new npc_plaguehound_tracker(); - new npc_razael_and_lyana(); RegisterCreatureAI(npc_rodin_lightning_enabler); RegisterSpellScript(spell_hawk_hunting); RegisterSpellScript(spell_the_cleansing_shrine_cast); diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index a9de0bf08..c97f094e7 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -387,64 +387,6 @@ public: } }; -/*###### -## avatar_of_freya -######*/ - -enum Freya -{ - QUEST_FREYA_PACT = 12621, - - SPELL_FREYA_CONVERSATION = 52045, - - GOSSIP_AVATAR_MENU_1 = 9720, - GOSSIP_AVATAR_MENU_2 = 9721, - GOSSIP_AVATAR_MENU_3 = 9722, - - GOSSIP_TEXTID_AVATAR_1 = 13303, - GOSSIP_TEXTID_AVATAR_2 = 13304, - GOSSIP_TEXTID_AVATAR_3 = 13305, -}; - -class npc_avatar_of_freya : public CreatureScript -{ -public: - npc_avatar_of_freya() : CreatureScript("npc_avatar_of_freya") { } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(QUEST_FREYA_PACT) == QUEST_STATUS_INCOMPLETE) - AddGossipItemFor(player, GOSSIP_AVATAR_MENU_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - SendGossipMenuFor(player, GOSSIP_TEXTID_AVATAR_1, creature); - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF+1: - AddGossipItemFor(player, GOSSIP_AVATAR_MENU_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, GOSSIP_TEXTID_AVATAR_2, creature); - break; - case GOSSIP_ACTION_INFO_DEF+2: - AddGossipItemFor(player, GOSSIP_AVATAR_MENU_3, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - SendGossipMenuFor(player, GOSSIP_TEXTID_AVATAR_3, creature); - break; - case GOSSIP_ACTION_INFO_DEF+3: - player->CastSpell(player, SPELL_FREYA_CONVERSATION, true); - CloseGossipMenuFor(player); - break; - } - return true; - } -}; - /*###### ## npc_bushwhacker ######*/ @@ -1187,7 +1129,6 @@ void AddSC_sholazar_basin() new go_pressure_valve(); new go_brazier(); new npc_vekjik(); - new npc_avatar_of_freya(); new npc_bushwhacker(); new npc_engineer_helice(); new npc_adventurous_dwarf(); diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index b94dabc73..853043860 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -22,7 +22,6 @@ #include "ScriptedCreature.h" #include "SpellMgr.h" #include "ScriptedEscortAI.h" -#include "ScriptedGossip.h" #include "SpellAuraEffects.h" #include "SpellScript.h" #include "SpellScriptLoader.h" @@ -339,56 +338,6 @@ class spell_q13007_iron_colossus : public SpellScript } }; -/*###### -## npc_roxi_ramrocket -######*/ - -enum RoxiRamrocket -{ - SPELL_MECHANO_HOG = 60866, - SPELL_MEKGINEERS_CHOPPER = 60867 -}; - -class npc_roxi_ramrocket : public CreatureScript -{ -public: - npc_roxi_ramrocket() : CreatureScript("npc_roxi_ramrocket") { } - - bool OnGossipHello(Player* player, Creature* creature) override - { - //Quest Menu - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - //Trainer Menu - if (creature->IsTrainer()) - AddGossipItemFor(player, GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN); - - //Vendor Menu - if (creature->IsVendor()) - if (player->HasSpell(SPELL_MECHANO_HOG) || player->HasSpell(SPELL_MEKGINEERS_CHOPPER)) - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_TRAIN: - player->GetSession()->SendTrainerList(creature); - break; - case GOSSIP_ACTION_TRADE: - player->GetSession()->SendListInventory(creature->GetGUID()); - break; - } - return true; - } -}; - /*###### ## npc_brunnhildar_prisoner ######*/ @@ -1386,7 +1335,6 @@ void AddSC_storm_peaks() new npc_iron_watcher(); new npc_time_lost_proto_drake(); RegisterSpellScript(spell_q13007_iron_colossus); - new npc_roxi_ramrocket(); new npc_brunnhildar_prisoner(); new npc_freed_protodrake(); new npc_icefang(); From a19c4f9476808cacb47727b5b9f7476b39c2ae77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 28 Mar 2026 22:15:31 +0000 Subject: [PATCH 08/47] chore(DB): import pending files Referenced commit(s): c10e19e2c6e35aa58b9d4b7ce253442fd3360134 --- .../2026_03_28_02.sql} | 1 + .../2026_03_28_03.sql} | 1 + 2 files changed, 2 insertions(+) rename data/sql/updates/{pending_db_world/rev_1774580000_gossip_outland.sql => db_world/2026_03_28_02.sql} (99%) rename data/sql/updates/{pending_db_world/rev_1774590000_gossip_northrend.sql => db_world/2026_03_28_03.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1774580000_gossip_outland.sql b/data/sql/updates/db_world/2026_03_28_02.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1774580000_gossip_outland.sql rename to data/sql/updates/db_world/2026_03_28_02.sql index 77ba9ea5e..77b737812 100644 --- a/data/sql/updates/pending_db_world/rev_1774580000_gossip_outland.sql +++ b/data/sql/updates/db_world/2026_03_28_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_03_28_01 -> 2026_03_28_02 -- Move Outland gossip handlers from C++ to database -- NPCs: Shattrath Flask Vendors (23484, 23483), Zephyr (25967), Drake Dealer Hurlunk (23489) diff --git a/data/sql/updates/pending_db_world/rev_1774590000_gossip_northrend.sql b/data/sql/updates/db_world/2026_03_28_03.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1774590000_gossip_northrend.sql rename to data/sql/updates/db_world/2026_03_28_03.sql index 3b0d0bb39..ad2dab95b 100644 --- a/data/sql/updates/pending_db_world/rev_1774590000_gossip_northrend.sql +++ b/data/sql/updates/db_world/2026_03_28_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_03_28_02 -> 2026_03_28_03 -- Move Northrend gossip handlers from C++ to database -- NPCs: Iruk (26219), Razael & Lyana (23998, 23778), Roxi Ramrocket (31247), Avatar of Freya (27801) From 3fa012f5ef182108fdb0572165d697d23e545be3 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:20:23 -0300 Subject: [PATCH 09/47] fix(Core/Battlegrounds): Remove siege damage buffs on node assault in IoC (#25155) Co-authored-by: Claude Opus 4.6 --- src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp index d4924f7f2..2d43dbdf4 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp @@ -745,6 +745,13 @@ void BattlegroundIC::HandleContestedNodes(ICNodePoint* nodePoint) (*itr)->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); } } + else if (nodePoint->nodeType == NODE_TYPE_REFINERY || nodePoint->nodeType == NODE_TYPE_QUARRY) + { + // nodePoint->faction is the assaulting team (set before this call); + // remove the siege damage buff from the team that previously controlled the node. + uint32 auraSpellId = (nodePoint->nodeType == NODE_TYPE_REFINERY) ? SPELL_OIL_REFINERY : SPELL_QUARRY; + RemoveAuraOnTeam(auraSpellId, GetOtherTeamId(nodePoint->faction)); + } else if (nodePoint->nodeType == NODE_TYPE_WORKSHOP) { DelObject(BG_IC_GO_SEAFORIUM_BOMBS_1); From 4988b2a6838c2e04a8ec9958a8ee90361822b888 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 28 Mar 2026 21:11:34 -0300 Subject: [PATCH 10/47] fix(Core/Scripts): Move EasternKingdoms gossip to database (#25208) Co-authored-by: dr-j Co-authored-by: offl Co-authored-by: Claude Opus 4.6 --- .../rev_1774395836122258800.sql | 116 ++++++++++++++++++ .../BlackrockDepths/blackrock_depths.cpp | 54 -------- .../zone_eastern_plaguelands.cpp | 31 ----- .../EasternKingdoms/zone_undercity.cpp | 58 --------- 4 files changed, 116 insertions(+), 143 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1774395836122258800.sql diff --git a/data/sql/updates/pending_db_world/rev_1774395836122258800.sql b/data/sql/updates/pending_db_world/rev_1774395836122258800.sql new file mode 100644 index 000000000..fde0f43af --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1774395836122258800.sql @@ -0,0 +1,116 @@ +-- Move gossip handling from C++ scripts to database for Augustus the Touched, +-- Parqual Fintallas, and Lokhtos Darkbargainer. +-- Augustus migration based on TC work by dr-j (commit cefed89c38bc) +-- Parqual migration based on TC work by offl (issue #24993, commit 7908b00311) + +-- ============================================================================= +-- 1. Augustus the Touched (entry 12384) +-- Conditional vendor option if quest 6164 (Augustus' Receipt Book) rewarded. +-- Two gossip texts: 4979 before quest, 4980 after quest. +-- ============================================================================= + +-- Remove C++ ScriptName +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` = 12384; + +-- Add gossip_menu entry for post-quest text 4980 +DELETE FROM `gossip_menu` WHERE `MenuID` = 4085 AND `TextID` = 4980; +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES (4085, 4980); + +-- Conditions on gossip_menu text: 4979 before quest, 4980 after quest +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 14 AND `SourceGroup` = 4085 AND `SourceEntry` IN (4979, 4980); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 4085, 4979, 0, 0, 8, 0, 6164, 0, 0, 1, 0, 0, '', 'Augustus the Touched - Show text 4979 if quest 6164 NOT rewarded'), +(14, 4085, 4980, 0, 0, 8, 0, 6164, 0, 0, 0, 0, 0, '', 'Augustus the Touched - Show text 4980 if quest 6164 rewarded'); + +-- Condition: show vendor option (MenuID 4085, OptionID 0) only if quest 6164 rewarded +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 15 AND `SourceGroup` = 4085 AND `SourceEntry` = 0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 4085, 0, 0, 0, 8, 0, 6164, 0, 0, 0, 0, 0, '', 'Augustus the Touched - Show vendor option only if quest 6164 rewarded'); + +-- ============================================================================= +-- 2. Parqual Fintallas (entry 4488) +-- Test of Lore quiz: 4 answers, wrong ones cast Shame, correct completes quest. +-- ============================================================================= + +-- Remove C++ ScriptName, set SmartAI +UPDATE `creature_template` SET `ScriptName` = '', `AIName` = 'SmartAI' WHERE `entry` = 4488; + +-- Add gossip_menu entry for quiz text (5822) shown during quiz +DELETE FROM `gossip_menu` WHERE `MenuID` = 4764 AND `TextID` = 5822; +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES (4764, 5822); + +-- Condition on gossip_menu text 5822: quest 6628 taken + no Shame aura +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 14 AND `SourceGroup` = 4764 AND `SourceEntry` = 5822; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 4764, 5822, 0, 0, 9, 0, 6628, 0, 0, 0, 0, 0, '', 'Parqual Fintallas - Show quiz text 5822 if quest 6628 taken'), +(14, 4764, 5822, 0, 0, 1, 0, 6767, 0, 0, 1, 0, 0, '', 'Parqual Fintallas - Show quiz text 5822 if player does not have Mark of Shame aura'); + +-- Conditions on quiz options 0, 1, 2, 3: quest 6628 taken + no Shame aura +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 15 AND `SourceGroup` = 4764 AND `SourceEntry` IN (0, 1, 2, 3); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +-- Option 0: Kel'Thuzad (wrong answer) +(15, 4764, 0, 0, 0, 9, 0, 6628, 0, 0, 0, 0, 0, '', 'Parqual Fintallas - Show option 0 if quest 6628 taken'), +(15, 4764, 0, 0, 0, 1, 0, 6767, 0, 0, 1, 0, 0, '', 'Parqual Fintallas - Show option 0 if no Mark of Shame aura'), +-- Option 1: Gul'dan (wrong answer) +(15, 4764, 1, 0, 0, 9, 0, 6628, 0, 0, 0, 0, 0, '', 'Parqual Fintallas - Show option 1 if quest 6628 taken'), +(15, 4764, 1, 0, 0, 1, 0, 6767, 0, 0, 1, 0, 0, '', 'Parqual Fintallas - Show option 1 if no Mark of Shame aura'), +-- Option 2: Kil'jaeden (wrong answer) +(15, 4764, 2, 0, 0, 9, 0, 6628, 0, 0, 0, 0, 0, '', 'Parqual Fintallas - Show option 2 if quest 6628 taken'), +(15, 4764, 2, 0, 0, 1, 0, 6767, 0, 0, 1, 0, 0, '', 'Parqual Fintallas - Show option 2 if no Mark of Shame aura'), +-- Option 3: Ner'zhul (correct answer) +(15, 4764, 3, 0, 0, 9, 0, 6628, 0, 0, 0, 0, 0, '', 'Parqual Fintallas - Show option 3 if quest 6628 taken'), +(15, 4764, 3, 0, 0, 1, 0, 6767, 0, 0, 1, 0, 0, '', 'Parqual Fintallas - Show option 3 if no Mark of Shame aura'); + +-- SAI for Parqual Fintallas +DELETE FROM `smart_scripts` WHERE `entryorguid` = 4488 AND `source_type` = 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 +-- Option 0 (Kel'Thuzad - wrong): close gossip, then cast Mark of Shame +(4488, 0, 0, 1, 62, 0, 100, 0, 4764, 0, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Parqual Fintallas - On Gossip Select 0 (Kel''Thuzad) - Close Gossip'), +(4488, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 6767, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Parqual Fintallas - Linked - Cast Mark of Shame on Invoker'), +-- Option 1 (Gul'dan - wrong): close gossip, then cast Mark of Shame +(4488, 0, 2, 3, 62, 0, 100, 0, 4764, 1, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Parqual Fintallas - On Gossip Select 1 (Gul''dan) - Close Gossip'), +(4488, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 6767, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Parqual Fintallas - Linked - Cast Mark of Shame on Invoker'), +-- Option 2 (Kil'jaeden - wrong): close gossip, then cast Mark of Shame +(4488, 0, 4, 5, 62, 0, 100, 0, 4764, 2, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Parqual Fintallas - On Gossip Select 2 (Kil''jaeden) - Close Gossip'), +(4488, 0, 5, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 6767, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Parqual Fintallas - Linked - Cast Mark of Shame on Invoker'), +-- Option 3 (Ner'zhul - correct): close gossip, then credit quest 6628 +(4488, 0, 6, 7, 62, 0, 100, 0, 4764, 3, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Parqual Fintallas - On Gossip Select 3 (Ner''zhul) - Close Gossip'), +(4488, 0, 7, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 15, 6628, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Parqual Fintallas - Linked - Area Explored Or Event Happens Quest 6628'); + +-- ============================================================================= +-- 3. Lokhtos Darkbargainer (entry 12944) +-- Rep-gated vendor + conditional Thorium Brotherhood contract creation. +-- ============================================================================= + +-- Remove C++ ScriptName, set SmartAI +UPDATE `creature_template` SET `ScriptName` = '', `AIName` = 'SmartAI' WHERE `entry` = 12944; + +-- Replace gossip_menu text: remove default 5834, add rep-conditional texts 3673/3677 +DELETE FROM `gossip_menu` WHERE `MenuID` = 4781 AND `TextID` IN (5834, 3673, 3677); +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(4781, 3673), +(4781, 3677); + +-- Conditions on gossip_menu text: 3673 below Friendly, 3677 at Friendly+ +-- Friendly(4)=16, Honored(5)=32, Revered(6)=64, Exalted(7)=128 => mask 240 +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 14 AND `SourceGroup` = 4781 AND `SourceEntry` IN (3673, 3677); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 4781, 3673, 0, 0, 5, 0, 59, 240, 0, 1, 0, 0, '', 'Lokhtos Darkbargainer - Show text 3673 if Thorium Brotherhood rep < Friendly'), +(14, 4781, 3677, 0, 0, 5, 0, 59, 240, 0, 0, 0, 0, '', 'Lokhtos Darkbargainer - Show text 3677 if Thorium Brotherhood rep >= Friendly'); + +-- Condition on vendor option (MenuID 4781, OptionID 0): rep >= Friendly +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 15 AND `SourceGroup` = 4781 AND `SourceEntry` IN (0, 1); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +-- Option 0 (vendor): requires Friendly rep +(15, 4781, 0, 0, 0, 5, 0, 59, 240, 0, 0, 0, 0, '', 'Lokhtos Darkbargainer - Show vendor option if Thorium Brotherhood rep >= Friendly'), +-- Option 1 (contract): requires quest 7604 NOT rewarded + no item 18628 (incl. bank) + has item 17203 +(15, 4781, 1, 0, 0, 8, 0, 7604, 0, 0, 1, 0, 0, '', 'Lokhtos Darkbargainer - Show contract option if quest 7604 NOT rewarded'), +(15, 4781, 1, 0, 0, 2, 0, 18628, 1, 1, 1, 0, 0, '', 'Lokhtos Darkbargainer - Show contract option if player does NOT have Thorium Brotherhood Contract'), +(15, 4781, 1, 0, 0, 2, 0, 17203, 1, 0, 0, 0, 0, '', 'Lokhtos Darkbargainer - Show contract option if player has Sulfuron Ingot'); + +-- SAI for Lokhtos Darkbargainer: contract spell on gossip select option 1 +DELETE FROM `smart_scripts` WHERE `entryorguid` = 12944 AND `source_type` = 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 +-- Option 1 (Get contract): close gossip, then invoker casts contract creation spell +(12944, 0, 0, 1, 62, 0, 100, 0, 4781, 1, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Lokhtos Darkbargainer - On Gossip Select 1 (Contract) - Close Gossip'), +(12944, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 134, 23059, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Lokhtos Darkbargainer - Linked - Invoker Cast Create Thorium Brotherhood Contract'); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp index 55f33f152..8333c5abb 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp @@ -452,59 +452,6 @@ private: uint32 _mightyBlowTimer; }; -// npc_lokhtos_darkbargainer -enum LokhtosItems -{ - ITEM_THRORIUM_BROTHERHOOD_CONTRACT = 18628, - ITEM_SULFURON_INGOT = 17203 -}; - -enum LokhtosQuests -{ - QUEST_A_BINDING_CONTRACT = 7604 -}; - -enum LokhtosSpells -{ - SPELL_CREATE_THORIUM_BROTHERHOOD_CONTRACT_DND = 23059 -}; - -struct npc_lokhtos_darkbargainer : public ScriptedAI -{ - npc_lokhtos_darkbargainer(Creature* creature) : ScriptedAI(creature) { } - - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - CloseGossipMenuFor(player); - player->CastSpell(player, SPELL_CREATE_THORIUM_BROTHERHOOD_CONTRACT_DND, false); - } - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(me->GetGUID()); - } - - void sGossipHello(Player* player) override - { - if (me->IsQuestGiver()) - player->PrepareQuestMenu(me->GetGUID()); - - if (me->IsVendor() && player->GetReputationRank(59) >= REP_FRIENDLY) - AddGossipItemFor(player, 4781, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - if (player->GetQuestRewardStatus(QUEST_A_BINDING_CONTRACT) != 1 && - !player->HasItemCount(ITEM_THRORIUM_BROTHERHOOD_CONTRACT, 1, true) && - player->HasItemCount(ITEM_SULFURON_INGOT)) - AddGossipItemFor(player, 4781, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - if (player->GetReputationRank(59) < REP_FRIENDLY) - SendGossipMenuFor(player, 3673, me->GetGUID()); - else - SendGossipMenuFor(player, 3677, me->GetGUID()); - } -}; - // npc_rocknot enum RocknotSays { @@ -641,7 +588,6 @@ void AddSC_blackrock_depths() new at_ring_of_law(); RegisterBlackrockDepthsCreatureAI(npc_grimstone); RegisterBlackrockDepthsCreatureAI(npc_phalanx); - RegisterBlackrockDepthsCreatureAI(npc_lokhtos_darkbargainer); RegisterBlackrockDepthsCreatureAI(npc_rocknot); RegisterBlackrockDepthsCreatureAI(brd_ironhand_guardian); } diff --git a/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp b/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp index 276c8115b..8a8066c90 100644 --- a/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_eastern_plaguelands.cpp @@ -296,39 +296,8 @@ public: }; }; -/*###### -## npc_augustus_the_touched -######*/ - -class npc_augustus_the_touched : public CreatureScript -{ -public: - npc_augustus_the_touched() : CreatureScript("npc_augustus_the_touched") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (creature->IsVendor() && player->GetQuestRewardStatus(6164)) - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID()); - return true; - } -}; - void AddSC_eastern_plaguelands() { new npc_eris_hevenfire(); new npc_balance_of_light_and_shadow(); - new npc_augustus_the_touched(); } diff --git a/src/server/scripts/EasternKingdoms/zone_undercity.cpp b/src/server/scripts/EasternKingdoms/zone_undercity.cpp index eab33d378..cbc19b7c0 100644 --- a/src/server/scripts/EasternKingdoms/zone_undercity.cpp +++ b/src/server/scripts/EasternKingdoms/zone_undercity.cpp @@ -293,63 +293,6 @@ public: }; }; -/*###### -## npc_parqual_fintallas -######*/ - -enum ParqualFintallas -{ - SPELL_MARK_OF_SHAME = 6767, - QUEST_ID_TEST_OF_LORE = 6628, - GOSSIP_MENU_ID_TEST_OF_LORE = 4764, - GOSSIP_TEXTID_PARQUAL_FINTALLAS = 5821, - GOSSIP_TEXTID_TEST_OF_LORE = 5822, -}; - -class npc_parqual_fintallas : public CreatureScript -{ -public: - npc_parqual_fintallas() : CreatureScript("npc_parqual_fintallas") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - CloseGossipMenuFor(player); - creature->CastSpell(player, SPELL_MARK_OF_SHAME, false); - } - if (action == GOSSIP_ACTION_INFO_DEF + 2) - { - CloseGossipMenuFor(player); - player->AreaExploredOrEventHappens(6628); - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - { - player->PrepareQuestMenu(creature->GetGUID()); - } - - if (player->GetQuestStatus(QUEST_ID_TEST_OF_LORE) == QUEST_STATUS_INCOMPLETE && !player->HasAura(SPELL_MARK_OF_SHAME)) - { - AddGossipItemFor(player, GOSSIP_MENU_ID_TEST_OF_LORE, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - AddGossipItemFor(player, GOSSIP_MENU_ID_TEST_OF_LORE, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - AddGossipItemFor(player, GOSSIP_MENU_ID_TEST_OF_LORE, 3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, GOSSIP_TEXTID_TEST_OF_LORE, creature->GetGUID()); - } - else - { - SendGossipMenuFor(player, GOSSIP_TEXTID_PARQUAL_FINTALLAS, creature->GetGUID()); - } - - return true; - } -}; - /*###### ## ALLIANCE #######*/ @@ -4037,7 +3980,6 @@ void AddSC_undercity() { new npc_lady_sylvanas_windrunner(); new npc_highborne_lamenter(); - new npc_parqual_fintallas(); new npc_varian_wrynn(); new npc_thrall_bfu(); From f644e4522e6ef9a8dc5af09bf570a11ae0541b16 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Mar 2026 00:12:41 +0000 Subject: [PATCH 11/47] chore(DB): import pending files Referenced commit(s): 4988b2a6838c2e04a8ec9958a8ee90361822b888 --- .../rev_1774395836122258800.sql => db_world/2026_03_29_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1774395836122258800.sql => db_world/2026_03_29_00.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1774395836122258800.sql b/data/sql/updates/db_world/2026_03_29_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1774395836122258800.sql rename to data/sql/updates/db_world/2026_03_29_00.sql index fde0f43af..484508b59 100644 --- a/data/sql/updates/pending_db_world/rev_1774395836122258800.sql +++ b/data/sql/updates/db_world/2026_03_29_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_03_28_03 -> 2026_03_29_00 -- Move gossip handling from C++ scripts to database for Augustus the Touched, -- Parqual Fintallas, and Lokhtos Darkbargainer. -- Augustus migration based on TC work by dr-j (commit cefed89c38bc) From 0246faa95a39ab92a294cf69fe7aecfeff1d77c2 Mon Sep 17 00:00:00 2001 From: Axel Cocat Date: Sun, 29 Mar 2026 01:20:27 +0100 Subject: [PATCH 12/47] feat(Core/Scripting): Add `OnPlayerLearnTaxiNode` hook (#25288) --- src/server/game/Handlers/TaxiHandler.cpp | 4 ++++ src/server/game/Scripting/ScriptDefines/PlayerScript.cpp | 5 +++++ src/server/game/Scripting/ScriptDefines/PlayerScript.h | 9 +++++++++ src/server/game/Scripting/ScriptMgr.h | 1 + 4 files changed, 19 insertions(+) diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index c4f21afed..1a493c7d3 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -142,6 +142,8 @@ bool WorldSession::SendLearnNewTaxiNode(Creature* unit) update << uint8(1); SendPacket(&update); + sScriptMgr->OnPlayerLearnTaxiNode(GetPlayer(), curloc); + return true; } else @@ -154,6 +156,8 @@ void WorldSession::SendDiscoverNewTaxiNode(uint32 nodeid) { WorldPacket msg(SMSG_NEW_TAXI_PATH, 0); SendPacket(&msg); + + sScriptMgr->OnPlayerLearnTaxiNode(GetPlayer(), nodeid); } } diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp index 72dcb83e8..c33c13d7c 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp @@ -925,6 +925,11 @@ void ScriptMgr::OnPlayerGetReputationPriceDiscount(Player const* player, Faction CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_GET_REPUTATION_PRICE_DISCOUNT, script->OnPlayerGetReputationPriceDiscount(player, factionTemplate, discount)); } +void ScriptMgr::OnPlayerLearnTaxiNode(Player const* player, uint32 nodeId) +{ + CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LEARN_TAXI_NODE, script->OnPlayerLearnTaxiNode(player, nodeId)); +} + PlayerScript::PlayerScript(const char* name, std::vector enabledHooks) : ScriptObject(name, PLAYERHOOK_END) { diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.h b/src/server/game/Scripting/ScriptDefines/PlayerScript.h index e0418eb87..e25fc82b8 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.h +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.h @@ -210,6 +210,7 @@ enum PlayerHook PLAYERHOOK_ON_SEND_LIST_INVENTORY, PLAYERHOOK_ON_GIVE_REPUTATION, PLAYERHOOK_ON_GET_REPUTATION_PRICE_DISCOUNT, + PLAYERHOOK_ON_LEARN_TAXI_NODE, PLAYERHOOK_END }; @@ -820,6 +821,14 @@ public: * @param discount Float value of the discount, as a multiplier of the base price */ virtual void OnPlayerGetReputationPriceDiscount(Player const* /*player*/, FactionTemplateEntry const* /*factionTemplate*/, float& /*discount*/) {} + + /** + * @brief This hook is called when a player learns a new flight path node. + * + * @param player Contains information about the Player + * @param nodeId The id of the learned taxi node + */ + virtual void OnPlayerLearnTaxiNode(Player const* /*player*/, uint32 /*nodeId*/) {} }; #endif diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 6f139bbd0..070a97c09 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -466,6 +466,7 @@ public: /* PlayerScript */ void OnPlayerSendListInventory(Player* player, ObjectGuid vendorGuid, uint32& vendorEntry); void OnPlayerGetReputationPriceDiscount(Player const* player, Creature const* creature, float& discount); void OnPlayerGetReputationPriceDiscount(Player const* player, FactionTemplateEntry const* factionTemplate, float& discount); + void OnPlayerLearnTaxiNode(Player const* player, uint32 nodeId); // Anti cheat void AnticheatSetCanFlybyServer(Player* player, bool apply); From 295507c9cec83af5148cb45783493b6340e6a52a Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Sun, 29 Mar 2026 04:27:47 -0500 Subject: [PATCH 13/47] fix(Core/Movement): resume waypoint movement after casting interruption (#25269) Co-authored-by: blinkysc --- .../Movement/MovementGenerators/WaypointMovementGenerator.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 272a1f823..c40d5a0d0 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -367,6 +367,7 @@ bool WaypointMovementGenerator::DoUpdate(Creature* creature, uint32 di creature->StopMoving(); _lastSplineId = 0; _smoothSplineLaunched = false; + _hasBeenStalled = true; } // Set home position to current position. From 74988d345262985624ddc4d86a590207981d0a97 Mon Sep 17 00:00:00 2001 From: kissingers <33601959+kissingers@users.noreply.github.com> Date: Sun, 29 Mar 2026 17:28:00 +0800 Subject: [PATCH 14/47] fix(Scripts/SunwellPlateau)More fix for boss Kalecgos resetting during death (#25284) --- .../scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp index f32644ea0..c23569e4e 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp @@ -187,7 +187,6 @@ struct boss_kalecgos : public BossAI me->SetRegeneratingHealth(false); me->RemoveAllAuras(); me->SetReactState(REACT_PASSIVE); - me->CombatStop(); me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); me->SetFaction(FACTION_FRIENDLY); }, 1s); From 2b56061cb7c6c4e1435a3d0020ad6b250d770d78 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Sun, 29 Mar 2026 04:28:33 -0500 Subject: [PATCH 15/47] fix(Core/Combat): Restrict NullCreatureAI combat disallow to triggers only (#25289) Co-authored-by: blinkysc --- .../updates/pending_db_world/rev_1774759571712435204.sql | 2 ++ src/server/game/AI/CoreAI/PassiveAI.cpp | 5 +++-- src/server/scripts/World/npcs_special.cpp | 8 +------- 3 files changed, 6 insertions(+), 9 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1774759571712435204.sql diff --git a/data/sql/updates/pending_db_world/rev_1774759571712435204.sql b/data/sql/updates/pending_db_world/rev_1774759571712435204.sql new file mode 100644 index 000000000..b12f5a30b --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1774759571712435204.sql @@ -0,0 +1,2 @@ +-- Fix Ashtongue Channeler saved health (was incorrectly 1) +UPDATE `creature` SET `curhealth` = 87973 WHERE `id1` = 23421; diff --git a/src/server/game/AI/CoreAI/PassiveAI.cpp b/src/server/game/AI/CoreAI/PassiveAI.cpp index 79ac51fa8..69fa69de0 100644 --- a/src/server/game/AI/CoreAI/PassiveAI.cpp +++ b/src/server/game/AI/CoreAI/PassiveAI.cpp @@ -23,8 +23,9 @@ PossessedAI::PossessedAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_ NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); - // TODO: Remove once WorldObject casting is ported (triggers won't create combat refs from spell casts) - me->SetIsCombatDisallowed(true); + // TODO: Remove once WorldObject spell casting is ported (triggers won't create combat refs from spell casts) + if (me->IsTrigger()) + me->SetIsCombatDisallowed(true); } int32 NullCreatureAI::Permissible(Creature const* creature) diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index 3ddb27669..3e7ebb34e 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -314,11 +314,7 @@ public: struct npc_training_dummy : NullCreatureAI { - npc_training_dummy(Creature* creature) : NullCreatureAI(creature) - { - // TODO: Remove once WorldObject casting is ported - me->SetIsCombatDisallowed(false); - } + npc_training_dummy(Creature* creature) : NullCreatureAI(creature) { } void JustEnteredCombat(Unit* who) override { @@ -363,8 +359,6 @@ struct npc_target_dummy : NullCreatureAI { npc_target_dummy(Creature* creature) : NullCreatureAI(creature) { - // TODO: Remove once WorldObject casting is ported - me->SetIsCombatDisallowed(false); _deathTimer = 15s; } From 4cedf09ff3676e180b660f1586dc0f86e133b963 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Mar 2026 09:28:58 +0000 Subject: [PATCH 16/47] chore(DB): import pending files Referenced commit(s): 295507c9cec83af5148cb45783493b6340e6a52a --- .../rev_1774759571712435204.sql => db_world/2026_03_29_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1774759571712435204.sql => db_world/2026_03_29_01.sql} (73%) diff --git a/data/sql/updates/pending_db_world/rev_1774759571712435204.sql b/data/sql/updates/db_world/2026_03_29_01.sql similarity index 73% rename from data/sql/updates/pending_db_world/rev_1774759571712435204.sql rename to data/sql/updates/db_world/2026_03_29_01.sql index b12f5a30b..209f630e0 100644 --- a/data/sql/updates/pending_db_world/rev_1774759571712435204.sql +++ b/data/sql/updates/db_world/2026_03_29_01.sql @@ -1,2 +1,3 @@ +-- DB update 2026_03_29_00 -> 2026_03_29_01 -- Fix Ashtongue Channeler saved health (was incorrectly 1) UPDATE `creature` SET `curhealth` = 87973 WHERE `id1` = 23421; From 433b9afe9b83fab2066462bbf5c0eb796d803d2f Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Sun, 29 Mar 2026 04:31:18 -0500 Subject: [PATCH 17/47] fix(Core/Scripts): Fix Aeranas reset loop during quest Avruu's Orb (#25290) Co-authored-by: blinkysc --- .../Outland/zone_hellfire_peninsula.cpp | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index 140ef72fe..8953d3298 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -83,6 +83,9 @@ public: void Reset() override { + if (_defeated) + return; + faction_Timer = 8000; envelopingWinds_Timer = 9000; shock_Timer = 5000; @@ -93,6 +96,15 @@ public: Talk(SAY_SUMMON); } + void JustReachedHome() override + { + if (_defeated) + { + Talk(SAY_FREE); + me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); + } + } + void UpdateAI(uint32 diff) override { if (faction_Timer) @@ -110,12 +122,11 @@ public: if (HealthBelowPct(30)) { - me->SetFaction(FACTION_FRIENDLY); - me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); + _defeated = true; me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->SetFaction(FACTION_FRIENDLY); me->CombatStop(true); - Talk(SAY_FREE); + me->GetThreatMgr().ClearAllThreat(); return; } @@ -140,6 +151,7 @@ public: uint32 faction_Timer; uint32 envelopingWinds_Timer; uint32 shock_Timer; + bool _defeated = false; }; CreatureAI* GetAI(Creature* creature) const override From e32fc30cef826503afffe3d6c21925eef67caefa Mon Sep 17 00:00:00 2001 From: sogladev Date: Sun, 29 Mar 2026 13:31:43 +0200 Subject: [PATCH 18/47] fix(DB/Immunities): add missing effects to knockback immunities (#25264) --- .../rev_1774621259631560378.sql | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1774621259631560378.sql diff --git a/data/sql/updates/pending_db_world/rev_1774621259631560378.sql b/data/sql/updates/pending_db_world/rev_1774621259631560378.sql new file mode 100644 index 000000000..d04285ade --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1774621259631560378.sql @@ -0,0 +1,45 @@ +-- +-- Set EFFECT_PULL_TOWARDS, EFFECT_PULL_TOWARDS_DEST knockback effects +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x3CCB3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-410; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x3CCB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-407; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CF3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-372; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB7F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BLEED|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-369; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='school=0x10(FROST), mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-366; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='school=0x4(FIRE), mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-364; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=AVOID_AOE|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-363; +UPDATE `creature_immunities` SET `Effects`='98,114,124,144,145', `Comment`='mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),114(ATTACK_ME),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST), auras=11(BIND)' WHERE `ID`=-362; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-361; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB3F7E(DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-359; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB3F7D(CHARM|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-357; +UPDATE `creature_immunities` SET `Effects`='98,114,124,144,145', `Comment`='mech=0x26CB3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),114(ATTACK_ME),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST), auras=11(BIND)' WHERE `ID`=-356; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-355; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB3F77(CHARM|DISORIENTED|DISARM|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-353; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB3F5F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-350; +UPDATE `creature_immunities` SET `Effects`='98,114,124,144,145', `Comment`='mech=0x26CB3B7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=NO_TAUNT|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),114(ATTACK_ME),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST), auras=11(BIND)' WHERE `ID`=-342; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB377F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-340; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB375F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-339; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB031F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-336; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x26CB031D(CHARM|DISARM|DISTRACT|FEAR|SILENCE|SLEEP|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-335; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x268B3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|INTERRUPT|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-327; +UPDATE `creature_immunities` SET `Effects`='98,114,124,144,145', `Comment`='mech=0x24CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=NO_TAUNT|IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),114(ATTACK_ME),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST), auras=11(BIND)' WHERE `ID`=-287; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x24CB3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-286; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x24CB3F5B(CHARM|DISORIENTED|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-275; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x24CB375F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|ROOT|SILENCE|SLEEP|SNARE|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-267; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x24CB207F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-264; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x248B3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-256; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x24812E7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|STUN|KNOCKOUT|POLYMORPH|HORROR|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-236; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x2401267F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SLEEP|SNARE|KNOCKOUT|POLYMORPH|DAZE|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-223; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x22CB337F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|INTERRUPT|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-219; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x20CF3F7F(CHARM|DISORIENTED|DISARM|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHIELD|SHACKLE|TURN|HORROR|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-211; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x20C10A11(CHARM|FEAR|SLEEP|STUN|POLYMORPH|TURN|HORROR|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-200; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x20011B51(CHARM|FEAR|ROOT|SILENCE|SLEEP|STUN|FREEZE|POLYMORPH|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-177; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x20000022(DISORIENTED|GRIP|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-173; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x6030005(CHARM|DISARM|POLYMORPH|BANISH|INTERRUPT|DAZE), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-166; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x4030005(CHARM|DISARM|POLYMORPH|BANISH|DAZE), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-151; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x2C23F57(CHARM|DISORIENTED|DISARM|FEAR|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|BANISH|TURN|HORROR|INTERRUPT), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-143; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x2811D50(FEAR|ROOT|SILENCE|SNARE|STUN|FREEZE|POLYMORPH|HORROR|INTERRUPT), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-138; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x800010(FEAR|HORROR), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-96; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x20720(GRIP|SILENCE|SLEEP|SNARE|BANISH), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-87; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x900(SILENCE|STUN), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-50; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x20(GRIP), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-27; +UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-3; From a6d8fa9f394da28e6c87e887b676ed32c9487949 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Mar 2026 11:32:51 +0000 Subject: [PATCH 19/47] chore(DB): import pending files Referenced commit(s): e32fc30cef826503afffe3d6c21925eef67caefa --- .../rev_1774621259631560378.sql => db_world/2026_03_29_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1774621259631560378.sql => db_world/2026_03_29_02.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1774621259631560378.sql b/data/sql/updates/db_world/2026_03_29_02.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1774621259631560378.sql rename to data/sql/updates/db_world/2026_03_29_02.sql index d04285ade..f21943fe9 100644 --- a/data/sql/updates/pending_db_world/rev_1774621259631560378.sql +++ b/data/sql/updates/db_world/2026_03_29_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_03_29_01 -> 2026_03_29_02 -- -- Set EFFECT_PULL_TOWARDS, EFFECT_PULL_TOWARDS_DEST knockback effects UPDATE `creature_immunities` SET `Effects`='98,124,144,145', `Comment`='mech=0x3CCB3F7B(CHARM|DISORIENTED|DISTRACT|FEAR|GRIP|ROOT|SILENCE|SLEEP|SNARE|STUN|FREEZE|KNOCKOUT|POLYMORPH|BANISH|SHACKLE|TURN|HORROR|DAZE|DISCOVERY|IMMUNE_SHIELD|SAPPED), flags=IMMUNITY_KNOCKBACK, effects=98(KNOCK_BACK),124(PULL_TOWARDS),144(KNOCK_BACK_DEST),145(PULL_TOWARDS_DEST)' WHERE `ID`=-410; From 77de36bb9ea4c6b011070fab241f5c7ae35fe0d2 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Sun, 29 Mar 2026 09:44:20 -0500 Subject: [PATCH 20/47] fix(Core/SmartAI): Suppress evade during SMART_ACTION_COMBAT_STOP (#25249) Co-authored-by: blinkysc --- src/server/game/AI/SmartScripts/SmartAI.cpp | 4 ++++ src/server/game/AI/SmartScripts/SmartAI.h | 2 ++ src/server/game/AI/SmartScripts/SmartScript.cpp | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 80a65b113..df978f02e 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -44,6 +44,7 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c) mCanRepeatPath = false; mEvadeDisabled = false; + mSuppressEvade = false; mCanAutoAttack = true; @@ -698,6 +699,9 @@ void SmartAI::MovementInform(uint32 MovementType, uint32 Data) void SmartAI::EnterEvadeMode(EvadeReason /*why*/) { + if (mSuppressEvade) + return; + if (mEvadeDisabled) { GetScript()->ProcessEventsFor(SMART_EVENT_EVADE); diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index 5449e4970..e36a258b7 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -181,6 +181,7 @@ public: void SetSwim(bool swim = true); void SetEvadeDisabled(bool disable = true); + void SetSuppressEvade(bool suppress) { mSuppressEvade = suppress; } void SetInvincibilityHpLevel(uint32 level) { mInvincibilityHpLevel = level; } @@ -249,6 +250,7 @@ private: uint32 GetWPCount() { return mWayPoints ? mWayPoints->Nodes.size() : 0; } bool mCanRepeatPath; bool mEvadeDisabled; + bool mSuppressEvade; bool mCanAutoAttack; bool mForcedPaused; uint32 mInvincibilityHpLevel; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 4024052b4..27b390bd0 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -1039,7 +1039,16 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (!me) break; + // Suppress evade during script-initiated combat stop so + // JustExitedCombat does not trigger EnterEvadeMode. + if (SmartAI* sai = CAST_AI(SmartAI, me->AI())) + sai->SetSuppressEvade(true); + me->CombatStop(true); + + if (SmartAI* sai = CAST_AI(SmartAI, me->AI())) + sai->SetSuppressEvade(false); + break; } case SMART_ACTION_CALL_GROUPEVENTHAPPENS: From 7352910a59756065bbf36165d3f3ff4312de7302 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Sun, 29 Mar 2026 18:29:44 -0500 Subject: [PATCH 21/47] fix(Scripts/MoltenCore): Fix Majordomo Executus gossip for Ragnaros summoning (#25307) Co-authored-by: blinkysc --- .../BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp index 7498b88fc..41c84b0cd 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp @@ -509,12 +509,14 @@ struct boss_majordomo : public BossAI void sGossipHello(Player* player) override { + ClearGossipMenuFor(player); AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); SendGossipMenuFor(player, TEXT_ID_SUMMON_1, me->GetGUID()); } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override { + uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); ClearGossipMenuFor(player); switch (action) { From 331393df93863567c6475cdc2fa80e4857be97a6 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Mon, 30 Mar 2026 07:33:23 -0300 Subject: [PATCH 22/47] fix(Scripts/MoltenCore): Move Majordomo Executus gossip to database (#25309) Co-authored-by: Claude Opus 4.6 --- .../rev_majordomo_gossip_to_db.sql | 9 ++++ .../MoltenCore/boss_majordomo_executus.cpp | 54 +++---------------- 2 files changed, 16 insertions(+), 47 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_majordomo_gossip_to_db.sql diff --git a/data/sql/updates/pending_db_world/rev_majordomo_gossip_to_db.sql b/data/sql/updates/pending_db_world/rev_majordomo_gossip_to_db.sql new file mode 100644 index 000000000..5668e37e2 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_majordomo_gossip_to_db.sql @@ -0,0 +1,9 @@ +-- Move Majordomo Executus Ragnaros summoning gossip chain to database +-- Previously handled entirely in C++ script (sGossipHello/sGossipSelect) + +-- Add gossip_menu entries (MenuID -> TextID) for the gossip chain +DELETE FROM `gossip_menu` WHERE `MenuID` IN (4093, 4108, 4109); +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(4093, 4995), +(4109, 5011), +(4108, 5012); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp index 41c84b0cd..bb563de0a 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp @@ -95,13 +95,7 @@ enum Events enum Misc { - TEXT_ID_SUMMON_1 = 4995, - TEXT_ID_SUMMON_2 = 5011, - TEXT_ID_SUMMON_3 = 5012, - - GOSSIP_ITEM_SUMMON_1 = 4093, - GOSSIP_ITEM_SUMMON_2 = 4109, - GOSSIP_ITEM_SUMMON_3 = 4108, + MENU_ID_RAGNAROS_SUMMON = 4108, FACTION_MAJORDOMO_FRIENDLY = 1080, SUMMON_GROUP_ADDS = 1, @@ -507,48 +501,14 @@ struct boss_majordomo : public BossAI } } - void sGossipHello(Player* player) override + void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override { - ClearGossipMenuFor(player); - AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_1, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - SendGossipMenuFor(player, TEXT_ID_SUMMON_1, me->GetGUID()); - } - - void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - switch (action) + if (menuId == MENU_ID_RAGNAROS_SUMMON) { - case GOSSIP_ACTION_INFO_DEF: - { - AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, TEXT_ID_SUMMON_2, me->GetGUID()); - break; - } - case GOSSIP_ACTION_INFO_DEF+1: - { - AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_2, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, TEXT_ID_SUMMON_2, me->GetGUID()); - break; - } - case GOSSIP_ACTION_INFO_DEF+2: - { - AddGossipItemFor(player, GOSSIP_ITEM_SUMMON_3, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - SendGossipMenuFor(player, TEXT_ID_SUMMON_3, me->GetGUID()); - break; - } - case GOSSIP_ACTION_INFO_DEF+3: - { - CloseGossipMenuFor(player); - me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - Talk(SAY_RAG_SUM_1, player); - DoAction(ACTION_START_RAGNAROS_INTRO); - break; - } - default: - CloseGossipMenuFor(player); - break; + CloseGossipMenuFor(player); + me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); + Talk(SAY_RAG_SUM_1, player); + DoAction(ACTION_START_RAGNAROS_INTRO); } } From 268ff79bc3907d1c654fc145b8ad13243b4a0868 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 30 Mar 2026 10:34:28 +0000 Subject: [PATCH 23/47] chore(DB): import pending files Referenced commit(s): 331393df93863567c6475cdc2fa80e4857be97a6 --- .../2026_03_30_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_majordomo_gossip_to_db.sql => db_world/2026_03_30_00.sql} (89%) diff --git a/data/sql/updates/pending_db_world/rev_majordomo_gossip_to_db.sql b/data/sql/updates/db_world/2026_03_30_00.sql similarity index 89% rename from data/sql/updates/pending_db_world/rev_majordomo_gossip_to_db.sql rename to data/sql/updates/db_world/2026_03_30_00.sql index 5668e37e2..148c98785 100644 --- a/data/sql/updates/pending_db_world/rev_majordomo_gossip_to_db.sql +++ b/data/sql/updates/db_world/2026_03_30_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_03_29_02 -> 2026_03_30_00 -- Move Majordomo Executus Ragnaros summoning gossip chain to database -- Previously handled entirely in C++ script (sGossipHello/sGossipSelect) From 0b9395531b22cbbf9fedbdc3d27b5562c6a87ad2 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Mon, 30 Mar 2026 09:52:10 -0500 Subject: [PATCH 24/47] fix(Core/Scripts): Fix multiple ICC encounter regressions after threat system port (#25303) Co-authored-by: blinkysc Co-authored-by: Treeston Co-authored-by: Shauren Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Co-authored-by: ccrs --- .../boss_icecrown_gunship_battle.cpp | 4 +- .../IcecrownCitadel/boss_the_lich_king.cpp | 8 + .../boss_valithria_dreamwalker.cpp | 115 +++++-------- .../IcecrownCitadel/icecrown_citadel.cpp | 160 +++++++++++------- 4 files changed, 153 insertions(+), 134 deletions(-) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index fc562f39b..2256c4e12 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -688,14 +688,14 @@ public: if (_instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) != IN_PROGRESS) return; + // Blizzlike: ship wipes when all players are in cannons (vehicles) with nobody on deck + // See: azerothcore/azerothcore-wotlk#17856 bool playerOnDeck = false; me->GetMap()->DoForAllPlayers([&](Player* player) { if (!player->GetVehicle() && player->IsAlive()) playerOnDeck = true; }); - - // Wipe if no player is on the deck if (!playerOnDeck) { // Script runs on enemy ship. We want to kill our ship. diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index 346997c48..b27e9008b 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -653,6 +653,8 @@ public: _lastTalkTimeBuff = 0; _bFrostmournePhase = false; _bFordringMustFallYell = false; + me->SetRegeneratingHealth(true); + me->SetIsCombatDisallowed(false); _Reset(); DoAction(ACTION_RESTORE_LIGHT); @@ -792,12 +794,15 @@ public: if (_phase == PHASE_THREE && HealthBelowPct(10) && !me->HasUnitState(UNIT_STATE_CASTING)) { _phase = PHASE_OUTRO; + me->SetRegeneratingHealth(false); + me->SetIsCombatDisallowed(true); EntryCheckPredicate pred(NPC_STRANGULATE_VEHICLE); summons.DoAction(ACTION_TELEPORT_BACK, pred); events.Reset(); summons.DespawnAll(); me->SetReactState(REACT_PASSIVE); me->AttackStop(); + me->CombatStop(true); me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_FURY_OF_FROSTMOURNE); me->InterruptNonMeleeSpells(true); me->CastSpell((Unit*)nullptr, SPELL_FURY_OF_FROSTMOURNE, false); @@ -1253,6 +1258,9 @@ public: void EnterEvadeMode(EvadeReason why) override { + if (_phase == PHASE_OUTRO || _phase == PHASE_FROSTMOURNE) + return; + EntryCheckPredicate pred(NPC_STRANGULATE_VEHICLE); summons.DoAction(ACTION_TELEPORT_BACK, pred); instance->SetBossState(DATA_THE_LICH_KING, FAIL); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp index d700cb08c..6d497061c 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp @@ -508,31 +508,26 @@ public: void Reset() override { - events.Reset(); - summons.DespawnAll(); - if (instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) != DONE) - instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, NOT_STARTED); + _Reset(); me->SetReactState(REACT_PASSIVE); - checkTimer = 5000; } - void JustEngagedWith(Unit* target) override + void JustEnteredCombat(Unit* target) override { + if (IsEngaged()) + return; + if (!instance->CheckRequiredBosses(DATA_VALITHRIA_DREAMWALKER, target->ToPlayer())) { - me->setActive(true); - EnterEvadeMode(); + EnterEvadeMode(EVADE_REASON_SEQUENCE_BREAK); instance->DoCastSpellOnPlayers(LIGHT_S_HAMMER_TELEPORT); return; } - if (instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) == DONE) - { - me->CombatStop(); - return; - } + + EngagementStart(target); me->setActive(true); - me->SetInCombatWithZone(); + DoZoneInCombat(); instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, IN_PROGRESS); if (Creature* valithria = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VALITHRIA_DREAMWALKER))) valithria->AI()->DoAction(ACTION_ENTER_COMBAT); @@ -547,17 +542,6 @@ public: (*itr)->AI()->DoAction(ACTION_ENTER_COMBAT); } - void AttackStart(Unit* target) override - { - if (target->IsPlayer()) - BossAI::AttackStart(target); - } - - void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER) override - { - CreatureAI::EnterEvadeMode(why); - } - void MoveInLineOfSight(Unit* /*who*/) override {} bool CanAIAttack(Unit const* target) const override @@ -565,48 +549,27 @@ public: return target->IsPlayer(); } - void JustReachedHome() override + void JustExitedCombat() override { - if (instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) != DONE) - DoAction(ACTION_DEATH); // setActive(false) in ValithriaDespawner - else - _JustReachedHome(); + EngagementOver(); + + me->setActive(false); + + if (!me->IsAlive()) + return; + DoAction(ACTION_DEATH); } void DoAction(int32 action) override { if (action == ACTION_DEATH) + { + instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, NOT_STARTED); me->m_Events.AddEventAtOffset(new ValithriaDespawner(me), 5s); - else if (action == ACTION_ENTER_COMBAT) - { - if (!me->IsInCombat()) - me->SetInCombatWithZone(); + if (Creature* lichKing = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VALITHRIA_LICH_KING))) + lichKing->AI()->EnterEvadeMode(); } } - - void UpdateAI(uint32 diff) override - { - if (!me->IsInCombat()) - return; - - if (checkTimer <= diff) - { - checkTimer = 3000; - me->SetInCombatWithZone(); - for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList()) - { - if (Unit* target = ref->GetVictim()) - if (target->IsAlive() && target->IsPlayer() && me->GetExactDist(target) < 200.0f && !target->IsImmunedToDamageOrSchool(SPELL_SCHOOL_MASK_ALL)) - return; - } - EnterEvadeMode(); - } - else - checkTimer -= diff; - } - - private: - uint16 checkTimer; }; CreatureAI* GetAI(Creature* creature) const override @@ -712,7 +675,7 @@ public: struct npc_risen_archmageAI : public ScriptedAI { npc_risen_archmageAI(Creature* creature) : ScriptedAI(creature), - _instance(creature->GetInstanceScript()) + _instance(creature->GetInstanceScript()), _isInitialArchmage(false) { } @@ -725,23 +688,34 @@ public: { _events.Reset(); _events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, 5s, 15s); - _events.ScheduleEvent(EVENT_MANA_VOID, 15s, 25s); + _events.ScheduleEvent(EVENT_MANA_VOID, 20s, 25s); _events.ScheduleEvent(EVENT_COLUMN_OF_FROST, 10s, 20s); + _isInitialArchmage = me->GetSpawnId() != 0; } - void JustEngagedWith(Unit* /*target*/) override + void JustEnteredCombat(Unit* who) override { - me->FinishSpell(CURRENT_CHANNELED_SPELL, false); - me->SetInCombatWithZone(); - if (me->GetSpawnId()) + if (IsEngaged()) + return; + + me->InterruptNonMeleeSpells(false); + + EngagementStart(who); + + if (_isInitialArchmage) + { + if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_LICH_KING))) + DoZoneInCombat(lichKing); + if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER))) - trigger->AI()->DoAction(ACTION_ENTER_COMBAT); + DoZoneInCombat(trigger); + } } void DoAction(int32 action) override { - if (action == ACTION_ENTER_COMBAT && !me->IsInCombat()) - me->SetInCombatWithZone(); + if (action == ACTION_ENTER_COMBAT) + DoZoneInCombat(); } void JustSummoned(Creature* summon) override @@ -754,10 +728,8 @@ public: void UpdateAI(uint32 diff) override { - if (!me->IsInCombat()) - if (me->GetSpawnId()) - if (!me->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) - me->CastSpell(me, SPELL_CORRUPTION, true); + if (!me->IsInCombat() && me->IsAlive() && _isInitialArchmage && !me->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + DoCastSelf(SPELL_CORRUPTION); if (!UpdateVictim()) return; @@ -796,6 +768,7 @@ public: private: EventMap _events; InstanceScript* _instance; + bool _isInitialArchmage; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index 77f6c7069..f471555c4 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -345,11 +345,17 @@ public: return; } + uint32 corpseDelay = creature->GetCorpseDelay(); + uint32 respawnDelay = creature->GetRespawnDelay(); + creature->SetCorpseDelay(1); + creature->SetRespawnDelay(2); + if (CreatureData const* data = creature->GetCreatureData()) creature->SetPosition(data->posX, data->posY, data->posZ, data->orientation); creature->DespawnOrUnsummon(); - creature->SetRespawnTime(5); + creature->SetCorpseDelay(corpseDelay); + creature->SetRespawnDelay(respawnDelay); } }; @@ -730,7 +736,7 @@ public: { me->SetReactState(REACT_DEFENSIVE); _didUnderTenPercentText = false; - _wipeCheckTimer = 3000; + _wipeCheckTimer = 1000; _handledWP4 = false; _events.Reset(); @@ -868,6 +874,27 @@ public: void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override { + if (!_wipeCheckTimer) + { + _wipeCheckTimer = 1000; + Player* player = nullptr; + Acore::AnyPlayerInObjectRangeCheck check(me, 60.0f); + Acore::PlayerSearcher searcher(me, player, check); + Cell::VisitObjects(me, searcher, 60.0f); + // wipe + if (!player) + { + damage *= 100; + if (damage >= me->GetHealth()) + { + FrostwingGauntletRespawner respawner; + Acore::CreatureWorker worker(me, respawner); + Cell::VisitObjects(me, worker, 333.0f); + return; + } + } + } + if (HealthBelowPct(10)) { if (!_didUnderTenPercentText) @@ -885,37 +912,21 @@ public: } } - void UpdateEscortAI(uint32 /*diff*/) override {} + void UpdateEscortAI(uint32 diff) override + { + if (_wipeCheckTimer <= diff) + _wipeCheckTimer = 0; + else + _wipeCheckTimer -= diff; + } void UpdateAI(uint32 diff) override { npc_escortAI::UpdateAI(diff); - //Position pos = me->GetHomePosition(); - if (!me->isActiveObject()/* && me->GetExactDist(&pos) < 5.0f*/) // during event + if (!me->isActiveObject()) return; - if (_wipeCheckTimer <= diff) - { - _wipeCheckTimer = 3000; - - Player* player = nullptr; - Acore::AnyPlayerInObjectRangeCheck check(me, 140.0f); - Acore::PlayerSearcher searcher(me, player, check); - Cell::VisitObjects(me, searcher, 140.0f); - // wipe - if (!player || me->GetExactDist(4357.0f, 2606.0f, 350.0f) > 125.0f) - { - //Talk(SAY_CROK_DEATH); - FrostwingGauntletRespawner respawner; - Acore::CreatureWorker worker(me, respawner); - Cell::VisitObjects(me, worker, 333.0f); - return; - } - } - else - _wipeCheckTimer -= diff; - UpdateVictim(); _events.Update(diff); @@ -968,7 +979,7 @@ public: bool CanAIAttack(Unit const* target) const override { // do not see targets inside Frostwing Halls when we are not there - return !target->IsPlayer() && (me->GetPositionY() > 2660.0f) == (target->GetPositionY() > 2660.0f) && target->GetEntry() != NPC_SINDRAGOSA; + return (me->GetPositionY() > 2660.0f) == (target->GetPositionY() > 2660.0f); } private: @@ -995,18 +1006,30 @@ public: struct boss_sister_svalnaAI : public BossAI { - boss_sister_svalnaAI(Creature* creature) : BossAI(creature, DATA_SISTER_SVALNA) + boss_sister_svalnaAI(Creature* creature) : BossAI(creature, DATA_SISTER_SVALNA), + _isEventInProgress(false) { } + void InitializeAI() override + { + if (!me->isDead()) + Reset(); + me->SetReactState(REACT_PASSIVE); + } + void Reset() override { _Reset(); - me->SetImmuneToAll(true); + me->SetReactState(REACT_DEFENSIVE); + _isEventInProgress = false; + } + + void JustReachedHome() override + { + _JustReachedHome(); me->SetReactState(REACT_PASSIVE); - me->SetCanFly(true); - me->SetDisableGravity(true); - me->SendMovementFlagUpdate(); + me->SetDisableGravity(false); } void JustDied(Unit* /*killer*/) override @@ -1080,6 +1103,8 @@ public: break; case ACTION_START_GAUNTLET: me->setActive(true); + me->SetImmuneToAll(true); + _isEventInProgress = true; events.ScheduleEvent(EVENT_SVALNA_START, 25s); break; case ACTION_RESURRECT_CAPTAINS: @@ -1106,16 +1131,21 @@ public: } } + void JustExitedCombat() override + { + if (_isEventInProgress) + return; + CreatureAI::JustExitedCombat(); + } + void MovementInform(uint32 type, uint32 id) override { if (type != EFFECT_MOTION_TYPE || id != POINT_LAND) return; - + _isEventInProgress = false; + me->setActive(false); me->SetImmuneToAll(false); - me->SetCanFly(false); me->SetDisableGravity(false); - me->SetReactState(REACT_AGGRESSIVE); - DoZoneInCombat(nullptr, 150.0f); } void SpellHitTarget(Unit* target, SpellInfo const* spell) override @@ -1140,43 +1170,51 @@ public: void UpdateAI(uint32 diff) override { - if (!me->isActiveObject()) + if (!UpdateVictim() && !_isEventInProgress) return; - UpdateVictim(); - events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - switch (events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) { - case EVENT_SVALNA_START: - Talk(SAY_SVALNA_EVENT_START); - break; - case EVENT_SVALNA_RESURRECT: - Talk(SAY_SVALNA_RESURRECT_CAPTAINS); - me->CastSpell(me, SPELL_REVIVE_CHAMPION, false); - break; - case EVENT_SVALNA_COMBAT: - Talk(SAY_SVALNA_AGGRO); - break; - case EVENT_IMPALING_SPEAR: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false, -SPELL_IMPALING_SPEAR)) - { - DoCast(me, SPELL_AETHER_SHIELD); - me->AddAura(70203, me); - DoCast(target, SPELL_IMPALING_SPEAR); - } - events.ScheduleEvent(EVENT_IMPALING_SPEAR, 20s, 25s); - break; - default: - break; + switch (eventId) + { + case EVENT_SVALNA_START: + Talk(SAY_SVALNA_EVENT_START); + break; + case EVENT_SVALNA_RESURRECT: + Talk(SAY_SVALNA_RESURRECT_CAPTAINS); + me->CastSpell(me, SPELL_REVIVE_CHAMPION, false); + break; + case EVENT_SVALNA_COMBAT: + me->SetReactState(REACT_DEFENSIVE); + Talk(SAY_SVALNA_AGGRO); + break; + case EVENT_IMPALING_SPEAR: + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false, -SPELL_IMPALING_SPEAR)) + { + DoCast(me, SPELL_AETHER_SHIELD); + me->AddAura(70203, me); + DoCast(target, SPELL_IMPALING_SPEAR); + } + events.ScheduleEvent(EVENT_IMPALING_SPEAR, 20s, 25s); + break; + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } DoMeleeAttackIfReady(); } + + private: + bool _isEventInProgress; }; CreatureAI* GetAI(Creature* creature) const override From 263802381cb1e3cf027e2612e924b92305f74f4b Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:03:32 -0500 Subject: [PATCH 25/47] fix(Core/Unit): Skip evade aura removal for player-owned creatures (#25311) Co-authored-by: blinkysc Co-authored-by: Rushor --- src/server/game/Entities/Unit/Unit.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index c1657cfb0..ad38ef15a 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -5697,6 +5697,9 @@ void Unit::RemoveAllAurasExceptType(AuraType type) // Xinef: We should not remove passive auras on evade, if npc has player owner (scripted one cast auras) void Unit::RemoveEvadeAuras() { + if (IsCharmedOwnedByPlayerOrPlayer()) + return; + for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();) { Aura const* aura = iter->second->GetBase(); From 037588ed03ed475aadf62b027610cde2eee3c424 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:22:36 -0500 Subject: [PATCH 26/47] fix(Core/Scripts): Fix Valithria Dreamwalker encounter and default JustExitedCombat (#25315) Co-authored-by: ForesterDev <11771800+ForesterDev@users.noreply.github.com> --- src/server/game/AI/CreatureAI.cpp | 8 ++--- .../boss_valithria_dreamwalker.cpp | 33 +++++-------------- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index 665b915cb..4cfbc1ea3 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -309,12 +309,8 @@ void CreatureAI::EngagementOver() void CreatureAI::JustExitedCombat() { - EngagementOver(); - - // If creature is alive, in world, and not already evading, trigger evade to return home - // Check IsInWorld to avoid evade during server shutdown/cleanup - if (me->IsAlive() && me->IsInWorld() && !me->IsInEvadeMode()) - EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + // Creatures evade through UpdateVictim() detecting out-of-combat state. + // Scripts that need custom combat-exit behavior should override this. } /*void CreatureAI::AttackedBy(Unit* attacker) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp index 6d497061c..cd7811ba0 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp @@ -341,18 +341,12 @@ public: _done = true; Talk(SAY_VALITHRIA_SUCCESS); _instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - _instance->DoRemoveAurasDueToSpellOnPlayers(70766); me->RemoveAurasDueToSpell(SPELL_CORRUPTION_VALITHRIA); me->CastSpell(me, SPELL_ACHIEVEMENT_CHECK, true); me->CastSpell((Unit*)nullptr, SPELL_DREAMWALKERS_RAGE, false); - _events.Reset(); _events.ScheduleEvent(EVENT_DREAM_SLIP, 3500ms); - _instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, DONE); - - if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER))) - trigger->AI()->EnterEvadeMode(); if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_LICH_KING))) - lichKing->AI()->Reset(); + lichKing->AI()->EnterEvadeMode(); } else if (!_over75PercentTalkDone && me->HealthAbovePctHealed(75, heal)) { @@ -361,11 +355,8 @@ public: } else if (_instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) == NOT_STARTED) { - if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER))) - { - trigger->AI()->DoAction(ACTION_ENTER_COMBAT); - _instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, IN_PROGRESS); - } + if (Creature* archmage = me->FindNearestCreature(NPC_RISEN_ARCHMAGE, 30.0f)) + DoZoneInCombat(archmage); // archmage chain will put trigger in combat } } @@ -388,7 +379,7 @@ public: Talk(SAY_VALITHRIA_DEATH); _instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER))) - trigger->AI()->EnterEvadeMode(); + trigger->AI()->DoAction(ACTION_DEATH); } } } @@ -404,6 +395,8 @@ public: me->SetDisplayId(11686); me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); me->DespawnOrUnsummon(4s); + if (Creature* trigger = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_TRIGGER))) + Unit::Kill(me, trigger); if (Creature* lichKing = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_VALITHRIA_LICH_KING))) lichKing->CastSpell(lichKing, SPELL_SPAWN_CHEST, false); _instance->SetData(DATA_WEEKLY_QUEST_ID, 0); // show hidden npc if necessary @@ -434,13 +427,8 @@ public: void UpdateAI(uint32 diff) override { // does not enter combat - if (_instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) == NOT_STARTED) - { - uint32 startingHealth = me->GetMaxHealth() * 0.5f; - if (me->GetHealth() != startingHealth) // healing when boss cannot be engaged (lower spire not finished, cheating) doesn't start the fight, prevent winning this way - me->SetHealth(startingHealth); + if (_instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) != IN_PROGRESS) return; - } _events.Update(diff); @@ -544,11 +532,6 @@ public: void MoveInLineOfSight(Unit* /*who*/) override {} - bool CanAIAttack(Unit const* target) const override - { - return target->IsPlayer(); - } - void JustExitedCombat() override { EngagementOver(); @@ -557,6 +540,8 @@ public: if (!me->IsAlive()) return; + if (instance->GetBossState(DATA_VALITHRIA_DREAMWALKER) == DONE) + return; DoAction(ACTION_DEATH); } From dc9efbdff156392d075756cc139a9541af5d043b Mon Sep 17 00:00:00 2001 From: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:56:14 +0200 Subject: [PATCH 27/47] feat(DB/Misc): Add state and assignee to bugreport (#25316) --- .../pending_db_characters/rev_1774895742399799600.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 data/sql/updates/pending_db_characters/rev_1774895742399799600.sql diff --git a/data/sql/updates/pending_db_characters/rev_1774895742399799600.sql b/data/sql/updates/pending_db_characters/rev_1774895742399799600.sql new file mode 100644 index 000000000..4396ee751 --- /dev/null +++ b/data/sql/updates/pending_db_characters/rev_1774895742399799600.sql @@ -0,0 +1,5 @@ +-- +ALTER TABLE `bugreport` + ADD COLUMN `State` TINYINT NOT NULL DEFAULT 1, + ADD COLUMN `Assignee` VARCHAR(255) DEFAULT NULL, + ADD COLUMN `Comment` LONGTEXT DEFAULT NULL; From d20b9db8b22089034d2283a73defed96739970ec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 30 Mar 2026 21:56:02 +0000 Subject: [PATCH 28/47] chore(DB): import pending files Referenced commit(s): dc9efbdff156392d075756cc139a9541af5d043b --- .../2026_03_30_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_characters/rev_1774895742399799600.sql => db_characters/2026_03_30_00.sql} (79%) diff --git a/data/sql/updates/pending_db_characters/rev_1774895742399799600.sql b/data/sql/updates/db_characters/2026_03_30_00.sql similarity index 79% rename from data/sql/updates/pending_db_characters/rev_1774895742399799600.sql rename to data/sql/updates/db_characters/2026_03_30_00.sql index 4396ee751..beb967ac3 100644 --- a/data/sql/updates/pending_db_characters/rev_1774895742399799600.sql +++ b/data/sql/updates/db_characters/2026_03_30_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_02_24_00 -> 2026_03_30_00 -- ALTER TABLE `bugreport` ADD COLUMN `State` TINYINT NOT NULL DEFAULT 1, From 19ccf2311c1867c57140c344b76e181e031327d4 Mon Sep 17 00:00:00 2001 From: youdeoo Date: Wed, 1 Apr 2026 04:44:19 +0800 Subject: [PATCH 29/47] fix(DB/Creatures): fix Valithria dream phase NPC not visible to players (#25320) --- data/sql/updates/pending_db_world/rev_1774904384329967200.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1774904384329967200.sql diff --git a/data/sql/updates/pending_db_world/rev_1774904384329967200.sql b/data/sql/updates/pending_db_world/rev_1774904384329967200.sql new file mode 100644 index 000000000..fd747838d --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1774904384329967200.sql @@ -0,0 +1,2 @@ +-- Valithria Dreamwalker +UPDATE `creature_template` SET `flags_extra` = `flags_extra` & ~128 WHERE `entry` = 37950; From 1b5db88ec870708256ecc2ebaf7dd70c2f4e15fd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 31 Mar 2026 20:45:30 +0000 Subject: [PATCH 30/47] chore(DB): import pending files Referenced commit(s): 19ccf2311c1867c57140c344b76e181e031327d4 --- .../rev_1774904384329967200.sql => db_world/2026_03_31_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1774904384329967200.sql => db_world/2026_03_31_00.sql} (72%) diff --git a/data/sql/updates/pending_db_world/rev_1774904384329967200.sql b/data/sql/updates/db_world/2026_03_31_00.sql similarity index 72% rename from data/sql/updates/pending_db_world/rev_1774904384329967200.sql rename to data/sql/updates/db_world/2026_03_31_00.sql index fd747838d..c7ca68b41 100644 --- a/data/sql/updates/pending_db_world/rev_1774904384329967200.sql +++ b/data/sql/updates/db_world/2026_03_31_00.sql @@ -1,2 +1,3 @@ +-- DB update 2026_03_30_00 -> 2026_03_31_00 -- Valithria Dreamwalker UPDATE `creature_template` SET `flags_extra` = `flags_extra` & ~128 WHERE `entry` = 37950; From 373e1d75c0831276f8b63b9c08131b9b6293f0d4 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:50:18 -0500 Subject: [PATCH 31/47] fix(Scripts/VioletHold): Fix Ethereal Sphere speed in Xevozz encounter (#25313) Co-authored-by: blinkysc --- src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp index 86bd871c0..28dbd974e 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_xevozz.cpp @@ -157,8 +157,7 @@ public: { if (pSummoned) { - pSummoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f); - pSummoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); + pSummoned->GetMotionMaster()->MoveFollow(me, 0.0f, 0.0f, MOTION_SLOT_ACTIVE, true, false); spheres.Summon(pSummoned); if (pInstance) pInstance->SetGuidData(DATA_ADD_TRASH_MOB, pSummoned->GetGUID()); From eb09ad5d60f6fad323774e567ab8e720c2fda52d Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:22:39 -0300 Subject: [PATCH 32/47] fix(Core/Movement): restore JustExitedCombat evade to fix waypoint resume (#25332) Co-authored-by: Claude Opus 4.6 --- src/server/game/AI/CreatureAI.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index 4cfbc1ea3..665b915cb 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -309,8 +309,12 @@ void CreatureAI::EngagementOver() void CreatureAI::JustExitedCombat() { - // Creatures evade through UpdateVictim() detecting out-of-combat state. - // Scripts that need custom combat-exit behavior should override this. + EngagementOver(); + + // If creature is alive, in world, and not already evading, trigger evade to return home + // Check IsInWorld to avoid evade during server shutdown/cleanup + if (me->IsAlive() && me->IsInWorld() && !me->IsInEvadeMode()) + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); } /*void CreatureAI::AttackedBy(Unit* attacker) From e144326f02f3c25533f66a8579a0bfbc91ceb0db Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 31 Mar 2026 22:22:02 -0300 Subject: [PATCH 33/47] fix(Scripts/Karazhan): Fix Moroes resetting during Vanish (#25333) Co-authored-by: Claude Opus 4.6 --- src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp index 8b6256ad8..dc300de98 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp @@ -149,7 +149,7 @@ struct boss_moroes : public BossAI _vanished = true; Talk(SAY_SPECIAL); DoCastSelf(SPELL_VANISH); - me->SetImmuneToAll(true); + me->SetImmuneToAll(true, true); scheduler.Schedule(5s, 7s, [this](TaskContext) { me->SetImmuneToAll(false); From e54952ca7bebb81a3c8e53cb2f442457363a6928 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Thu, 2 Apr 2026 03:03:07 -0500 Subject: [PATCH 34/47] fix(Core/Spells): Resolve pet to owner in EffectSummonType default path (#25322) Co-authored-by: blinkysc --- src/server/game/Spells/SpellEffects.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 40edc0576..46e674736 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2471,6 +2471,11 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) TempSummonType summonType = (duration <= 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; + Unit* summoner = m_originalCaster; + if (summoner->IsPet()) + if (Unit* owner = summoner->GetOwner()) + summoner = owner; + for (uint32 count = 0; count < numSummons; ++count) { Position pos; @@ -2480,7 +2485,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) // randomize position for multiple summons pos = m_caster->GetRandomPoint(*destTarget, radius); - summon = m_originalCaster->SummonCreature(entry, pos, summonType, duration, 0, nullptr, personalSpawn); + summon = summoner->SummonCreature(entry, pos, summonType, duration, 0, nullptr, personalSpawn); if (!summon) continue; From f8c5c9a46815be217be7d0a48e7dbed6960e8f7a Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Thu, 2 Apr 2026 03:05:05 -0500 Subject: [PATCH 35/47] fix(Core/Spells): Include absorbed damage in PROC_SPELL_TYPE_DAMAGE classification (#25314) Co-authored-by: blinkysc --- src/server/game/Entities/Unit/Unit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index ad38ef15a..ce7e5c5b9 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -6771,7 +6771,7 @@ void Unit::ProcSkillsAndAuras(Unit* actor, Unit* victim, uint32 procAttacker, ui } else if (healInfo && healInfo->GetHeal()) spellTypeMask = PROC_SPELL_TYPE_HEAL; - else if (damageInfo && damageInfo->GetDamage()) + else if (damageInfo && (damageInfo->GetDamage() || damageInfo->GetAbsorb())) spellTypeMask = PROC_SPELL_TYPE_DAMAGE; else if (procSpellInfo) spellTypeMask = PROC_SPELL_TYPE_NO_DMG_HEAL; From 0fb8fe3f4a5ac7e9f1375cb5b1b79384fe7a2858 Mon Sep 17 00:00:00 2001 From: sogladev Date: Fri, 3 Apr 2026 05:01:31 +0200 Subject: [PATCH 36/47] fix(DB/Immunities): set Water Elemental (mage pet) to Frost Immune only (#25352) --- data/sql/updates/pending_db_world/rev_1775163389363198948.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1775163389363198948.sql diff --git a/data/sql/updates/pending_db_world/rev_1775163389363198948.sql b/data/sql/updates/pending_db_world/rev_1775163389363198948.sql new file mode 100644 index 000000000..bcf924c02 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1775163389363198948.sql @@ -0,0 +1,3 @@ +-- +-- from mechanic_immune_mask 646000477 to school=0x10(FROST) +UPDATE `creature_template` SET `CreatureImmunitiesId`=-6 WHERE `entry`=510; From 965a60e71a0ccd1dca1b806cd6ab6321f7472ae2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Apr 2026 03:02:36 +0000 Subject: [PATCH 37/47] chore(DB): import pending files Referenced commit(s): 0fb8fe3f4a5ac7e9f1375cb5b1b79384fe7a2858 --- .../rev_1775163389363198948.sql => db_world/2026_04_03_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1775163389363198948.sql => db_world/2026_04_03_00.sql} (76%) diff --git a/data/sql/updates/pending_db_world/rev_1775163389363198948.sql b/data/sql/updates/db_world/2026_04_03_00.sql similarity index 76% rename from data/sql/updates/pending_db_world/rev_1775163389363198948.sql rename to data/sql/updates/db_world/2026_04_03_00.sql index bcf924c02..1f83a828a 100644 --- a/data/sql/updates/pending_db_world/rev_1775163389363198948.sql +++ b/data/sql/updates/db_world/2026_04_03_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_03_31_00 -> 2026_04_03_00 -- -- from mechanic_immune_mask 646000477 to school=0x10(FROST) UPDATE `creature_template` SET `CreatureImmunitiesId`=-6 WHERE `entry`=510; From 60f2b328764c9e6ebc4fa51aaf96a649b46b2057 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Fri, 3 Apr 2026 02:04:27 -0300 Subject: [PATCH 38/47] fix(Scripts/UtgardePinnacle): Fix Skadi gauntlet add movement and reset (#25359) Co-authored-by: Gustavo Co-authored-by: maximius <19688589+maximius@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 --- .../skadi_gauntlet_adds_movement.sql | 25 ++++++++ .../UtgardePinnacle/boss_skadi.cpp | 60 +++++++++++++------ 2 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 data/sql/updates/pending_db_world/skadi_gauntlet_adds_movement.sql diff --git a/data/sql/updates/pending_db_world/skadi_gauntlet_adds_movement.sql b/data/sql/updates/pending_db_world/skadi_gauntlet_adds_movement.sql new file mode 100644 index 000000000..fc6092c6e --- /dev/null +++ b/data/sql/updates/pending_db_world/skadi_gauntlet_adds_movement.sql @@ -0,0 +1,25 @@ +DELETE FROM `waypoint_data` WHERE `id` = 2669000; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(2669000, 1, 478.743, -505.576, 104.724, 0, 0, 1, 0, 100, 0), +(2669000, 2, 318.177, -503.8898, 104.5326, 0, 0, 1, 0, 100, 0); + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 26690 AND `source_type` = 0 AND `id` IN (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`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(26690, 0, 2, 0, 109, 0, 100, 0, 0, 2669000, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Ymirjar Warrior - On Path 2669000 Finished - Set In Combat With Zone'); + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 26691 AND `source_type` = 0 AND `id` IN (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`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(26691, 0, 2, 0, 109, 0, 100, 0, 0, 2669000, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Ymirjar Witch Doctor - On Path 2669000 Finished - Set In Combat With Zone'); + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 26692 AND `source_type` = 0 AND `id` IN (3); +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`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(26692, 0, 3, 0, 109, 0, 100, 0, 0, 2669000, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 'Ymirjar Harpooner - On Path 2669000 Finished - Set In Combat With Zone'); + +UPDATE `spell_area` SET `gender` = 2 WHERE `spell` = 47546; + +DELETE FROM `waypoint_data` WHERE `id` IN (2689303, 2689304); +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(2689303, 1, 520.483, -541.563, 119.842, 0, 0, 2, 0, 100, 0), +(2689303, 2, 496.434, -517.578, 120, 0, 0, 2, 0, 100, 0), +(2689304, 1, 520.483, -541.563, 119.842, 0, 0, 2, 0, 100, 0), +(2689304, 2, 500.243, -501.693, 120, 0, 0, 2, 0, 100, 0); diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp index c983d83a0..e3feb9bb9 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp @@ -113,8 +113,6 @@ Position const FirstWaveLocations[FIRST_WAVE_SIZE] = { 481.388f, -507.109f, 104.724f, 3.26377f }, }; -Position const SecondaryWavesInitialPoint = { 478.743f, -505.576f, 104.724f }; - enum GraufPoints { POINT_BREACH = 0, @@ -122,11 +120,19 @@ enum GraufPoints POINT_RIGHT = 2, }; -uint32 const PATH_INITIAL = 2689300; -uint32 const PATH_RIGHT = 2689301; -uint32 const PATH_LEFT = 2689302; +enum GraufPaths +{ + PATH_INITIAL = 2689300, + PATH_RIGHT = 2689301, + PATH_LEFT = 2689302, + PATH_BREACH_RIGHT = 2689303, + PATH_BREACH_LEFT = 2689304, + PATH_GAUNTLET_ADDS = 2669000, +}; float const BreachFacing = 2.670354f; +float const BreathFacingRight = 3.124139f; +float const BreathFacingLeft = 3.228859f; enum Events { @@ -139,6 +145,7 @@ enum Events // Grauf EVENT_GRAUF_START = 10, EVENT_GRAUF_LEAVE_BREACH = 11, + EVENT_GRAUF_BREATH_START = 12, EVENT_GRAUF_REMOVE_AURA = 13, }; @@ -193,7 +200,10 @@ public: case NPC_YMIRJAR_WITCH_DOCTOR: case NPC_YMIRJAR_HARPOONER: if (_firstWaveSummoned) - summon->GetMotionMaster()->MovePoint(1, SecondaryWavesInitialPoint); + { + summon->LoadPath(PATH_GAUNTLET_ADDS); + summon->GetMotionMaster()->MoveWaypoint(PATH_GAUNTLET_ADDS, false); + } break; default: break; @@ -404,6 +414,7 @@ public: _summons.DespawnAll(); me->SetReactState(REACT_PASSIVE); me->SetSpeedRate(MOVE_RUN, 2.5f); + _flyingToSide = false; } void DoAction(int32 param) override @@ -430,9 +441,19 @@ public: { if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized()) { - me->SetFacingTo(BreachFacing); - Talk(EMOTE_ON_RANGE); - _events.ScheduleEvent(EVENT_GRAUF_LEAVE_BREACH, 10s); + if (_flyingToSide) + { + _flyingToSide = false; + me->SetFacingTo(_lastSide == POINT_LEFT ? BreathFacingLeft : BreathFacingRight); + Talk(EMOTE_DEEP_BREATH); + _events.ScheduleEvent(EVENT_GRAUF_BREATH_START, 2s); + } + else + { + me->SetFacingTo(BreachFacing); + Talk(EMOTE_ON_RANGE); + _events.ScheduleEvent(EVENT_GRAUF_LEAVE_BREACH, 10s); + } } } @@ -443,16 +464,20 @@ public: { case EVENT_GRAUF_LEAVE_BREACH: _lastSide = RAND(POINT_LEFT, POINT_RIGHT); - me->GetMotionMaster()->MovePath(_lastSide == POINT_LEFT ? PATH_LEFT : PATH_RIGHT, FORCED_MOVEMENT_RUN); - - if (_lastSide == POINT_LEFT) - DoCast(me, SPELL_FREEZING_CLOUD_LEFT_PERIODIC); - else - DoCast(me, SPELL_FREEZING_CLOUD_RIGHT_PERIODIC); - + _flyingToSide = true; + me->GetMotionMaster()->MovePath( + _lastSide == POINT_LEFT ? PATH_BREACH_LEFT : PATH_BREACH_RIGHT, + FORCED_MOVEMENT_RUN); + break; + case EVENT_GRAUF_BREATH_START: + me->GetMotionMaster()->MovePath( + _lastSide == POINT_LEFT ? PATH_LEFT : PATH_RIGHT, + FORCED_MOVEMENT_RUN); + DoCast(me, _lastSide == POINT_LEFT + ? SPELL_FREEZING_CLOUD_LEFT_PERIODIC + : SPELL_FREEZING_CLOUD_RIGHT_PERIODIC); if (Creature* skadi = _instance->GetCreature(DATA_SKADI_THE_RUTHLESS)) skadi->AI()->DoAction(ACTION_DRAKE_BREATH); - _events.ScheduleEvent(EVENT_GRAUF_REMOVE_AURA, 10s); break; case EVENT_GRAUF_REMOVE_AURA: @@ -480,6 +505,7 @@ public: EventMap _events; SummonList _summons; uint8 _lastSide = POINT_LEFT; + bool _flyingToSide = false; }; }; From e349304dd985bd3ff3a1fb96ca6fc49c94cba81a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Apr 2026 05:05:30 +0000 Subject: [PATCH 39/47] chore(DB): import pending files Referenced commit(s): 60f2b328764c9e6ebc4fa51aaf96a649b46b2057 --- .../2026_04_03_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/skadi_gauntlet_adds_movement.sql => db_world/2026_04_03_01.sql} (98%) diff --git a/data/sql/updates/pending_db_world/skadi_gauntlet_adds_movement.sql b/data/sql/updates/db_world/2026_04_03_01.sql similarity index 98% rename from data/sql/updates/pending_db_world/skadi_gauntlet_adds_movement.sql rename to data/sql/updates/db_world/2026_04_03_01.sql index fc6092c6e..4b2f7b84b 100644 --- a/data/sql/updates/pending_db_world/skadi_gauntlet_adds_movement.sql +++ b/data/sql/updates/db_world/2026_04_03_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_03_00 -> 2026_04_03_01 DELETE FROM `waypoint_data` WHERE `id` = 2669000; INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES (2669000, 1, 478.743, -505.576, 104.724, 0, 0, 1, 0, 100, 0), From 51c6f348bb6e496e02dffd396c0a9ac5bb3b81f5 Mon Sep 17 00:00:00 2001 From: sogladev Date: Fri, 3 Apr 2026 07:18:55 +0200 Subject: [PATCH 40/47] fix(Core/Immunities): PvP trinkets no longer remove Silence or Disarm (#25354) --- src/server/shared/SharedDefines.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 23d31745d..9b081e7f5 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -1360,8 +1360,6 @@ inline constexpr uint64 IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK = (UI64LIT(1) << MECHANIC_SNARE) | (UI64LIT(1) << MECHANIC_STUN) | (UI64LIT(1) << MECHANIC_FREEZE) | - (UI64LIT(1) << MECHANIC_SILENCE) | - (UI64LIT(1) << MECHANIC_DISARM) | (UI64LIT(1) << MECHANIC_KNOCKOUT) | (UI64LIT(1) << MECHANIC_POLYMORPH) | (UI64LIT(1) << MECHANIC_BANISH) | From 177fa72101888b7cad689417af595cc534469655 Mon Sep 17 00:00:00 2001 From: sogladev Date: Fri, 3 Apr 2026 12:03:04 +0200 Subject: [PATCH 41/47] chore(Core/Loot): apply clang-tidy to use bool literal (#25356) --- src/server/game/Loot/LootMgr.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 2ef13a484..9a9804a7d 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -405,10 +405,10 @@ LootItem::LootItem(LootStoreItem const& li) randomSuffix = GenerateEnchSuffixFactor(itemid); randomPropertyId = Item::GenerateItemRandomPropertyId(itemid); count = 0; - is_looted = 0; - is_blocked = 0; - is_underthreshold = 0; - is_counted = 0; + is_looted = false; + is_blocked = false; + is_underthreshold = false; + is_counted = false; rollWinnerGUID = ObjectGuid::Empty; groupid = li.groupid; } From b7dfaf0fd649d5fb8ac9aa2668929498f6547269 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Fri, 3 Apr 2026 09:06:50 -0300 Subject: [PATCH 42/47] fix(Core/Creature): Implement CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT (#25363) Co-authored-by: Ovahlord Co-authored-by: offl Co-authored-by: Claude Opus 4.6 --- .../rev_1743710400_creature_cannot_enter_combat.sql | 2 ++ src/server/game/Entities/Creature/Creature.cpp | 2 ++ src/server/game/Entities/Creature/CreatureData.h | 2 +- src/server/game/Entities/Creature/enuminfo_CreatureData.cpp | 6 +++--- 4 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1743710400_creature_cannot_enter_combat.sql diff --git a/data/sql/updates/pending_db_world/rev_1743710400_creature_cannot_enter_combat.sql b/data/sql/updates/pending_db_world/rev_1743710400_creature_cannot_enter_combat.sql new file mode 100644 index 000000000..2657bcb25 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1743710400_creature_cannot_enter_combat.sql @@ -0,0 +1,2 @@ +-- Add CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT to Toxic Tunnel (Naxxramas) +UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 0x00002000 WHERE `entry` = 16400; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index b1ab32562..5d7804d01 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -663,6 +663,8 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); } + SetIsCombatDisallowed(cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT)); + SetDetectionDistance(cInfo->detection_range); // Update movement diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index a79d7f8f0..ec71df83a 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -56,7 +56,7 @@ enum CreatureFlagsExtra : uint32 CREATURE_FLAG_EXTRA_GHOST_VISIBILITY = 0x00000400, // creature will only be visible to dead players CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK = 0x00000800, // creature will use offhand attacks CREATURE_FLAG_EXTRA_NO_SELL_VENDOR = 0x00001000, // players can't sell items to this vendor - CREATURE_FLAG_EXTRA_IGNORE_COMBAT = 0x00002000, + CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT = 0x00002000, // creature is not allowed to enter combat CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging) CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH = 0x00010000, // creature ignores feign death diff --git a/src/server/game/Entities/Creature/enuminfo_CreatureData.cpp b/src/server/game/Entities/Creature/enuminfo_CreatureData.cpp index 2f0aebeba..15a361fd1 100644 --- a/src/server/game/Entities/Creature/enuminfo_CreatureData.cpp +++ b/src/server/game/Entities/Creature/enuminfo_CreatureData.cpp @@ -44,7 +44,7 @@ AC_API_EXPORT EnumText EnumUtils::ToString(CreatureFlagsExtr case CREATURE_FLAG_EXTRA_GHOST_VISIBILITY: return { "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "creature will only be visible to dead players" }; case CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK: return { "CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK", "CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK", "/ @todo: Implement CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK (creature will use offhand attacks)" }; case CREATURE_FLAG_EXTRA_NO_SELL_VENDOR: return { "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "players can't sell items to this vendor" }; - case CREATURE_FLAG_EXTRA_IGNORE_COMBAT: return { "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "" }; + case CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT: return { "CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT", "CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT", "creature is not allowed to enter combat" }; case CREATURE_FLAG_EXTRA_WORLDEVENT: return { "CREATURE_FLAG_EXTRA_WORLDEVENT", "CREATURE_FLAG_EXTRA_WORLDEVENT", "custom flag for world event creatures (left room for merging)" }; case CREATURE_FLAG_EXTRA_GUARD: return { "CREATURE_FLAG_EXTRA_GUARD", "CREATURE_FLAG_EXTRA_GUARD", "Creature is guard" }; case CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH: return { "CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH", "CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH", "creature ignores feign death" }; @@ -88,7 +88,7 @@ AC_API_EXPORT CreatureFlagsExtra EnumUtils::FromIndex(std::s case 10: return CREATURE_FLAG_EXTRA_GHOST_VISIBILITY; case 11: return CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK; case 12: return CREATURE_FLAG_EXTRA_NO_SELL_VENDOR; - case 13: return CREATURE_FLAG_EXTRA_IGNORE_COMBAT; + case 13: return CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT; case 14: return CREATURE_FLAG_EXTRA_WORLDEVENT; case 15: return CREATURE_FLAG_EXTRA_GUARD; case 16: return CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH; @@ -129,7 +129,7 @@ AC_API_EXPORT std::size_t EnumUtils::ToIndex(CreatureFlagsEx case CREATURE_FLAG_EXTRA_GHOST_VISIBILITY: return 10; case CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK: return 11; case CREATURE_FLAG_EXTRA_NO_SELL_VENDOR: return 12; - case CREATURE_FLAG_EXTRA_IGNORE_COMBAT: return 13; + case CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT: return 13; case CREATURE_FLAG_EXTRA_WORLDEVENT: return 14; case CREATURE_FLAG_EXTRA_GUARD: return 15; case CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH: return 16; From 161abfdc857cbec63fe27bfa5bef7e87c56ae2a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Apr 2026 12:09:28 +0000 Subject: [PATCH 43/47] chore(DB): import pending files Referenced commit(s): b7dfaf0fd649d5fb8ac9aa2668929498f6547269 --- .../2026_04_03_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1743710400_creature_cannot_enter_combat.sql => db_world/2026_04_03_02.sql} (79%) diff --git a/data/sql/updates/pending_db_world/rev_1743710400_creature_cannot_enter_combat.sql b/data/sql/updates/db_world/2026_04_03_02.sql similarity index 79% rename from data/sql/updates/pending_db_world/rev_1743710400_creature_cannot_enter_combat.sql rename to data/sql/updates/db_world/2026_04_03_02.sql index 2657bcb25..e5a988497 100644 --- a/data/sql/updates/pending_db_world/rev_1743710400_creature_cannot_enter_combat.sql +++ b/data/sql/updates/db_world/2026_04_03_02.sql @@ -1,2 +1,3 @@ +-- DB update 2026_04_03_01 -> 2026_04_03_02 -- Add CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT to Toxic Tunnel (Naxxramas) UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 0x00002000 WHERE `entry` = 16400; From ab2f9524c168178cf1af9a0e05c7cad2378e6d6c Mon Sep 17 00:00:00 2001 From: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Date: Fri, 3 Apr 2026 20:49:14 +0200 Subject: [PATCH 44/47] feat(Core/Opcodes): Implement CMSG_COMPLAIN & CMSG_CALENDAR_COMPLAIN (#25329) Co-authored-by: Dmitri Petmanson <53072+laizerox@users.noreply.github.com> Co-authored-by: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> --- .../rev_1774971400849620100.sql | 18 ++++++++++++++++++ .../apps/worldserver/worldserver.conf.dist | 9 +++++++++ .../Implementation/CharacterDatabase.cpp | 1 + .../Implementation/CharacterDatabase.h | 1 + src/server/game/Handlers/CalendarHandler.cpp | 16 +++++++++++++--- src/server/game/Handlers/MiscHandler.cpp | 17 +++++++++++++++-- .../game/Server/Packets/CalendarPackets.cpp | 1 + src/server/game/Server/Packets/MiscPackets.cpp | 10 +++++----- src/server/game/Server/Packets/MiscPackets.h | 12 ++++++------ src/server/game/World/WorldConfig.cpp | 2 ++ src/server/game/World/WorldConfig.h | 1 + 11 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 data/sql/updates/pending_db_characters/rev_1774971400849620100.sql diff --git a/data/sql/updates/pending_db_characters/rev_1774971400849620100.sql b/data/sql/updates/pending_db_characters/rev_1774971400849620100.sql new file mode 100644 index 000000000..7a560ef63 --- /dev/null +++ b/data/sql/updates/pending_db_characters/rev_1774971400849620100.sql @@ -0,0 +1,18 @@ +-- +CREATE TABLE `spam_reports` ( + `ID` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `SpamType` TINYINT UNSIGNED NOT NULL COMMENT '0 = mail, 1 = chat, 2 = calendar', + `SpammerGuid` INT UNSIGNED NOT NULL DEFAULT '0', + `Unk1` INT UNSIGNED NULL DEFAULT '0', + `MailIdOrMessageType` INT UNSIGNED NULL DEFAULT '0', + `ChannelId` INT UNSIGNED NULL COMMENT 'Only used if SpamType = 1', + `SecondsSinceMessage` INT UNSIGNED NULL COMMENT 'Only used if SpamType = 1', + `Description` LONGTEXT NULL DEFAULT NULL COLLATE 'utf8mb4_unicode_ci', + `Time` INT NULL DEFAULT NULL COMMENT 'Time of report', + PRIMARY KEY (`ID`) USING BTREE +) +CHARSET = utf8mb4 +COLLATE = utf8mb4_unicode_ci +ENGINE = InnoDB +ROW_FORMAT = DEFAULT +; diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 2e62d2829..0990fb4e6 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -612,6 +612,15 @@ AllowLoggingIPAddressesInDatabase = 1 Allow.IP.Based.Action.Logging = 0 +# +# LogSpamReports +# Description: Allow logging spam reports from players in the chat, mail or calendar into the database. +# Default: 1 - (Enabled) +# 0 - (Disabled) +# + +LogSpamReports = 1 + # # Appender config values: Given an appender "name" # Appender.name diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 11e27fed3..31664d06b 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -353,6 +353,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_REM_AT_LOGIN_FLAG, "UPDATE characters set at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ALL_AT_LOGIN_FLAGS, "UPDATE characters SET at_login = at_login | ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_BUG_REPORT, "INSERT INTO bugreport (type, content) VALUES(?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_SPAM_REPORT, "INSERT INTO spam_reports (SpamType, SpammerGuid, Unk1, MailIdOrMessageType, ChannelId, SecondsSinceMessage, Description, Time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petition_id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petition_id, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ACCOUNT_ONLINE, "UPDATE characters SET online = 0 WHERE account = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 6e5aa9154..968bafa69 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -277,6 +277,7 @@ enum CharacterDatabaseStatements : uint32 CHAR_UPD_REM_AT_LOGIN_FLAG, CHAR_UPD_ALL_AT_LOGIN_FLAGS, CHAR_INS_BUG_REPORT, + CHAR_INS_SPAM_REPORT, CHAR_UPD_PETITION_NAME, CHAR_INS_PETITION_SIGNATURE, CHAR_UPD_ACCOUNT_ONLINE, diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp index d5b41e41c..1a104e656 100644 --- a/src/server/game/Handlers/CalendarHandler.cpp +++ b/src/server/game/Handlers/CalendarHandler.cpp @@ -754,11 +754,21 @@ void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket& recvData) void WorldSession::HandleCalendarComplain(WorldPackets::Calendar::CalendarComplain& packet) { - ObjectGuid guid = _player->GetGUID(); + if (sWorld->getBoolConfig(CONFIG_LOGSPAMREPORTS)) + { + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_SPAM_REPORT); - LOG_DEBUG("network", "CMSG_CALENDAR_COMPLAIN [{}] EventId [{}] guid [{}]", guid.ToString(), packet.EventId, packet.ComplainGuid.ToString()); + stmt->SetData(0, 2); // SpamType 2 = Calendar + stmt->SetData(1, packet.ComplainGuid.GetCounter()); + stmt->SetData(2, 0); + stmt->SetData(3, 0); + stmt->SetData(4, 0); + stmt->SetData(5, 0); + stmt->SetData(6, "EventId: " + std::to_string(packet.EventId)); + stmt->SetData(7, GameTime::GetGameTime().count()); - // what to do with complains? + CharacterDatabase.Execute(stmt); + } } void WorldSession::HandleCalendarGetNumPending(WorldPacket& /*recvData*/) diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index e5ae429c0..56047089f 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1157,8 +1157,21 @@ void WorldSession::HandleComplainOpcode(WorldPackets::Misc::Complain& packet) // Complaint Received message SendPacket(WorldPackets::Misc::ComplainResult().Write()); - LOG_DEBUG("network", "REPORT SPAM: type {}, {}, unk1 {}, unk2 {}, unk3 {}, unk4 {}, message {}", - packet.SpamType, packet.SpammerGuid.ToString(), packet.Unk1, packet.Unk2, packet.Unk3, packet.Unk4, packet.Description); + if (sWorld->getBoolConfig(CONFIG_LOGSPAMREPORTS)) + { + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_SPAM_REPORT); + + stmt->SetData(0, packet.SpamType); + stmt->SetData(1, packet.SpammerGuid.GetCounter()); + stmt->SetData(2, packet.Unk1); + stmt->SetData(3, packet.MailIdOrMessageType); + stmt->SetData(4, packet.ChannelId); + stmt->SetData(5, packet.SecondsSinceMessage); + stmt->SetData(6, packet.Description); + stmt->SetData(7, GameTime::GetGameTime().count()); + + CharacterDatabase.Execute(stmt); + } } void WorldSession::HandleRealmSplitOpcode(WorldPacket& recv_data) diff --git a/src/server/game/Server/Packets/CalendarPackets.cpp b/src/server/game/Server/Packets/CalendarPackets.cpp index 2ff2cdd0d..59417727b 100644 --- a/src/server/game/Server/Packets/CalendarPackets.cpp +++ b/src/server/game/Server/Packets/CalendarPackets.cpp @@ -16,6 +16,7 @@ */ #include "CalendarPackets.h" +#include "Packet.h" void WorldPackets::Calendar::GetEvent::Read() { diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp index 64e248dd5..e20d422b9 100644 --- a/src/server/game/Server/Packets/MiscPackets.cpp +++ b/src/server/game/Server/Packets/MiscPackets.cpp @@ -149,14 +149,14 @@ void WorldPackets::Misc::Complain::Read() { case 0: _worldPacket >> Unk1; // const 0 - _worldPacket >> Unk2; // probably mail id - _worldPacket >> Unk3; // const 0 + _worldPacket >> MailIdOrMessageType; // probably mail id + _worldPacket >> ChannelId; // const 0 break; case 1: _worldPacket >> Unk1; // probably language - _worldPacket >> Unk2; // message type? - _worldPacket >> Unk3; // probably channel id - _worldPacket >> Unk4; // unk random value + _worldPacket >> MailIdOrMessageType; + _worldPacket >> ChannelId; + _worldPacket >> SecondsSinceMessage; _worldPacket >> Description; // spam description string (messagetype, channel name, player name, message) break; } diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h index 5b0689168..df2f70973 100644 --- a/src/server/game/Server/Packets/MiscPackets.h +++ b/src/server/game/Server/Packets/MiscPackets.h @@ -218,13 +218,13 @@ namespace WorldPackets void Read() override; - uint8 SpamType = 0; // 0 - mail, 1 - chat + uint8 SpamType = 0; // 0 - mail, 1 - chat ObjectGuid SpammerGuid; - uint32 Unk1 = 0; - uint32 Unk2 = 0; - uint32 Unk3 = 0; - uint32 Unk4 = 0; - std::string Description = ""; + uint32 Unk1 = 0; + uint32 MailIdOrMessageType = 0; + uint32 ChannelId = 0; + uint32 SecondsSinceMessage = 0; + std::string Description = ""; }; class ComplainResult final : public ServerPacket diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 131f734ad..530cef16c 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -603,6 +603,8 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_IP_BASED_ACTION_LOGGING, "Allow.IP.Based.Action.Logging", false); + SetConfigValue(CONFIG_LOGSPAMREPORTS, "LogSpamReports", true); + // Whether to use LoS from game objects SetConfigValue(CONFIG_CHECK_GOBJECT_LOS, "CheckGameObjectLoS", true); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 9626be8bf..66709d66b 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -112,6 +112,7 @@ enum ServerConfigs CONFIG_ENABLE_CONTINENT_TRANSPORT_PRELOADING, CONFIG_MINIGOB_MANABONK, CONFIG_IP_BASED_ACTION_LOGGING, + CONFIG_LOGSPAMREPORTS, CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA, CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA, CONFIG_CHECK_GOBJECT_LOS, From 8f474d8f4ca27cf268c1b63c7fab374201b9296b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Apr 2026 18:50:23 +0000 Subject: [PATCH 45/47] chore(DB): import pending files Referenced commit(s): ab2f9524c168178cf1af9a0e05c7cad2378e6d6c --- .../2026_04_03_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_characters/rev_1774971400849620100.sql => db_characters/2026_04_03_00.sql} (94%) diff --git a/data/sql/updates/pending_db_characters/rev_1774971400849620100.sql b/data/sql/updates/db_characters/2026_04_03_00.sql similarity index 94% rename from data/sql/updates/pending_db_characters/rev_1774971400849620100.sql rename to data/sql/updates/db_characters/2026_04_03_00.sql index 7a560ef63..329d255c5 100644 --- a/data/sql/updates/pending_db_characters/rev_1774971400849620100.sql +++ b/data/sql/updates/db_characters/2026_04_03_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_03_30_00 -> 2026_04_03_00 -- CREATE TABLE `spam_reports` ( `ID` INT UNSIGNED NOT NULL AUTO_INCREMENT, From 8ffbbe2c0873369ba9bbd48a2dfad4b1f907589a Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Fri, 3 Apr 2026 16:52:28 -0300 Subject: [PATCH 46/47] fix(Scripts/UtgardePinnacle): Fix Skadi Poisoned Spear not applying DOT in Heroic (#25364) Co-authored-by: Claude Opus 4.6 --- .../pending_db_world/fix_skadi_poisoned_spear_heroic.sql | 5 +++++ .../Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 data/sql/updates/pending_db_world/fix_skadi_poisoned_spear_heroic.sql diff --git a/data/sql/updates/pending_db_world/fix_skadi_poisoned_spear_heroic.sql b/data/sql/updates/pending_db_world/fix_skadi_poisoned_spear_heroic.sql new file mode 100644 index 000000000..aa4bb4ac8 --- /dev/null +++ b/data/sql/updates/pending_db_world/fix_skadi_poisoned_spear_heroic.sql @@ -0,0 +1,5 @@ +-- Fix Skadi Poisoned Spear not applying periodic DOT in Heroic mode. +-- Spell 59331 is the heroic variant of 50255 (mapped via spelldifficulty_dbc), +-- but the spell script was only registered for the normal version. +DELETE FROM `spell_script_names` WHERE `spell_id` = 59331 AND `ScriptName` = 'spell_skadi_poisoned_spear'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (59331, 'spell_skadi_poisoned_spear'); diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp index e3feb9bb9..eab8ec6d7 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp @@ -533,7 +533,7 @@ class spell_skadi_launch_harpoon : public SpellScript } }; -// 50255 - Poisoned Spear +// 50255, 59331 - Poisoned Spear class spell_skadi_poisoned_spear : public SpellScript { PrepareSpellScript(spell_skadi_poisoned_spear); From 71928da8d44aa8e1a982732c9a224d62d0d5cfba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 3 Apr 2026 20:03:21 +0000 Subject: [PATCH 47/47] chore(DB): import pending files Referenced commit(s): 8ffbbe2c0873369ba9bbd48a2dfad4b1f907589a --- .../2026_04_03_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/fix_skadi_poisoned_spear_heroic.sql => db_world/2026_04_03_03.sql} (90%) diff --git a/data/sql/updates/pending_db_world/fix_skadi_poisoned_spear_heroic.sql b/data/sql/updates/db_world/2026_04_03_03.sql similarity index 90% rename from data/sql/updates/pending_db_world/fix_skadi_poisoned_spear_heroic.sql rename to data/sql/updates/db_world/2026_04_03_03.sql index aa4bb4ac8..8b24fd5d8 100644 --- a/data/sql/updates/pending_db_world/fix_skadi_poisoned_spear_heroic.sql +++ b/data/sql/updates/db_world/2026_04_03_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_03_02 -> 2026_04_03_03 -- Fix Skadi Poisoned Spear not applying periodic DOT in Heroic mode. -- Spell 59331 is the heroic variant of 50255 (mapped via spelldifficulty_dbc), -- but the spell script was only registered for the normal version.