diff --git a/data/sql/updates/db_world/2026_05_06_00.sql b/data/sql/updates/db_world/2026_05_06_00.sql new file mode 100644 index 000000000..f7b5141bc --- /dev/null +++ b/data/sql/updates/db_world/2026_05_06_00.sql @@ -0,0 +1,8 @@ +-- DB update 2026_05_05_04 -> 2026_05_06_00 + +-- Set SAI +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 186565; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 1 AND `entryorguid` = 186565); +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 +(186565, 1, 0, 0, 64, 0, 100, 0, 1, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ceremonial Dragonflayer Harpoon - On Gossip Hello - Despawn Instant'); diff --git a/data/sql/updates/db_world/2026_05_07_00.sql b/data/sql/updates/db_world/2026_05_07_00.sql new file mode 100644 index 000000000..c43622ca1 --- /dev/null +++ b/data/sql/updates/db_world/2026_05_07_00.sql @@ -0,0 +1,4 @@ +-- DB update 2026_05_06_00 -> 2026_05_07_00 +DELETE FROM `spell_linked_spell` WHERE `spell_trigger` = -48323; +INSERT INTO `spell_linked_spell` (`spell_trigger`, `spell_effect`, `type`, `comment`) VALUES +(-48323, 48330, 0, 'On Indisposed Expiring - Cast Create Amberseeds'); diff --git a/data/sql/updates/db_world/2026_05_07_01.sql b/data/sql/updates/db_world/2026_05_07_01.sql new file mode 100644 index 000000000..f241cf513 --- /dev/null +++ b/data/sql/updates/db_world/2026_05_07_01.sql @@ -0,0 +1,7 @@ +-- DB update 2026_05_07_00 -> 2026_05_07_01 + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28521) AND (`source_type` = 0) AND (`id` IN (11)); +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 +(28521, 0, 11, 5, 8, 0, 100, 0, 51910, 0, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Nass - On Spellhit \'Kickin` Nass: Quest Completion\' - Despawn Instant'); + +DELETE FROM `spell_script_names` WHERE `spell_id` = 51910; diff --git a/data/sql/updates/db_world/2026_05_08_00.sql b/data/sql/updates/db_world/2026_05_08_00.sql new file mode 100644 index 000000000..9ee4f9871 --- /dev/null +++ b/data/sql/updates/db_world/2026_05_08_00.sql @@ -0,0 +1,3 @@ +-- DB update 2026_05_07_01 -> 2026_05_08_00 +UPDATE `conditions` SET `ConditionTypeOrReference` = 25 WHERE `SourceTypeOrReferenceId` = 14 AND `SourceGroup` = 1945 AND `SourceEntry` = 2605 AND `ConditionTypeOrReference` = 16 AND `ConditionValue1` = 14891 AND `NegativeCondition` = 1; +UPDATE `conditions` SET `ConditionTypeOrReference` = 25 WHERE `SourceTypeOrReferenceId` = 15 AND `SourceGroup` = 1945 AND `SourceEntry` = 0 AND `ConditionTypeOrReference` = 16 AND `ConditionValue1` = 14891 AND `NegativeCondition` = 1; diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index bc7ca2e57..8be788fe1 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -4360,14 +4360,6 @@ AllowTwoSide.Interaction.Calendar = 0 AllowTwoSide.Interaction.Chat = 0 -# -# AllowTwoSide.Interaction.Emote -# Description: Allow emote messages between factions (e.g. "/e looks into the sky") -# Default: 0 - (Disabled) -# 1 - (Enabled) - -AllowTwoSide.Interaction.Emote = 0 - # # AllowTwoSide.Interaction.Channel # Description: Allow channel chat between factions. @@ -4409,38 +4401,6 @@ AllowTwoSide.Interaction.Arena = 0 AllowTwoSide.Interaction.Auction = 0 -# -# AllowTwoSide.Interaction.Mail -# Description: Allow sending mails between factions. -# Default: 0 - (Disabled) -# 1 - (Enabled) - -AllowTwoSide.Interaction.Mail = 0 - -# -# AllowTwoSide.WhoList -# Description: Show characters from both factions in the /who list. -# Default: 0 - (Disabled) -# 1 - (Enabled) - -AllowTwoSide.WhoList = 0 - -# -# AllowTwoSide.AddFriend -# Description: Allow adding friends from other faction the friends list. -# Default: 0 - (Disabled) -# 1 - (Enabled) - -AllowTwoSide.AddFriend = 0 - -# -# AllowTwoSide.Trade -# Description: Allow trading between factions. -# Default: 0 - (Disabled) -# 1 - (Enabled) - -AllowTwoSide.Trade = 0 - # # TalentsInspecting # Description: Allow inspecting characters from the opposing faction. diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index a7494f1e6..a37534dbb 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -364,6 +364,7 @@ void Battlefield::EndBattle(bool endByTimer) DoPlaySoundToAll(BF_HORDE_WINS); OnBattleEnd(endByTimer); + sScriptMgr->OnBattlefieldWarEnd(this, endByTimer); // Reset battlefield timer Timer = NoWarBattleTime; diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 5559b8674..07e02ae0f 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -206,6 +206,10 @@ void ThreatReference::HeapNotifyDecreased() if (tWho->GetSummonerGUID().IsPlayer()) return false; + // accessories are fully treated as components of the parent and cannot have threat + if (cWho->HasUnitTypeMask(UNIT_MASK_ACCESSORY)) + return false; + return true; } @@ -396,15 +400,6 @@ void ThreatManager::AddThreat(Unit* target, float amount, SpellInfo const* spell return; } - // while riding a vehicle, all threat goes to the vehicle, not the pilot - if (Unit* vehicle = target->GetVehicleBase()) - { - AddThreat(vehicle, amount, spell, ignoreModifiers, ignoreRedirects); - if (target->HasUnitTypeMask(UNIT_MASK_ACCESSORY)) // accessories are fully treated as components of the parent and cannot have threat - return; - amount = 0.0f; - } - // if we cannot actually have a threat list, we instead just set combat state and avoid creating threat refs altogether if (!CanHaveThreatList()) { diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 639b1cc53..ee531531a 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -15426,6 +15426,15 @@ void Player::ActivateSpec(uint8 spec) ++iter; } + // Recheck shapeshift bonus auras: drop and re-apply form-tied passives + // so buffs from talents missing in the new spec (e.g. Master Shapeshifter) go away + Unit::AuraEffectList const& shapeshiftAuras = GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT); + for (AuraEffect* aurEff : shapeshiftAuras) + { + aurEff->HandleShapeshiftBoosts(this, false); + aurEff->HandleShapeshiftBoosts(this, true); + } + sScriptMgr->OnPlayerAfterSpecSlotChanged(this, GetActiveSpec()); } diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 09293b5a4..794016405 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -313,7 +313,10 @@ void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 typ if (TempSummon* accessory = _me->SummonCreature(entry, *_me, TempSummonType(type), summonTime)) { if (minion) + { accessory->AddUnitTypeMask(UNIT_MASK_ACCESSORY); + accessory->GetThreatMgr().Initialize(); // reinitialize CanHaveThreatList cached value + } if (!_me->HandleSpellClick(accessory, seatId)) { @@ -455,9 +458,9 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) init.SetTransportEnter(); init.Launch(); - // Transfer threat from passenger to vehicle + // Put the vehicle in combat with anything that was threatening the passenger; the threat itself stays on the passenger for (auto const& [guid, threatRef] : unit->GetThreatMgr().GetThreatenedByMeList()) - threatRef->GetOwner()->GetThreatMgr().AddThreat(_me, threatRef->GetThreat(), nullptr, true, true); + threatRef->GetOwner()->GetThreatMgr().AddThreat(_me, 0.0f, nullptr, true, true); if (_me->IsCreature()) { diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index b7083c0fa..90e04a0ea 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -214,7 +214,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sCharacterCache->GetCharacterAccountIdByGuid(receiverGuid); - if (/*!accountBound*/ GetAccountId() != rc_account && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && player->GetTeamId() != rc_teamId && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_MAIL)) + if (/*!accountBound*/ GetAccountId() != rc_account && player->GetTeamId() != rc_teamId && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_MAIL)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 30886a778..da9480a6b 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -293,7 +293,6 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData) for (auto const& target : sWhoListCacheMgr->GetWhoList()) { - // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST if (target.GetTeamId() != team && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_WHO_LIST)) continue; diff --git a/src/server/game/Handlers/Socialhandler.cpp b/src/server/game/Handlers/Socialhandler.cpp index 23038746e..ba2ebeae5 100644 --- a/src/server/game/Handlers/Socialhandler.cpp +++ b/src/server/game/Handlers/Socialhandler.cpp @@ -64,7 +64,7 @@ void WorldSession::HandleAddFriendOpcode(WorldPacket& recv_data) { if (friendGuid == GetPlayer()->GetGUID()) friendResult = FRIEND_SELF; - else if (GetPlayer()->GetTeamId() != teamId && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND) && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_ADD_FRIEND)) + else if (GetPlayer()->GetTeamId() != teamId && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_ADD_FRIEND)) friendResult = FRIEND_ENEMY; else if (GetPlayer()->GetSocial()->HasFriend(friendGuid)) friendResult = FRIEND_ALREADY; diff --git a/src/server/game/Handlers/TradeHandler.cpp b/src/server/game/Handlers/TradeHandler.cpp index 1cd6066d7..a6951df67 100644 --- a/src/server/game/Handlers/TradeHandler.cpp +++ b/src/server/game/Handlers/TradeHandler.cpp @@ -723,7 +723,6 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) } if (pOther->GetTeamId() != _player->GetTeamId() && - !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && !GetPlayer()->GetSession()->HasPermission(rbac::RBAC_PERM_ALLOW_TWO_SIDE_TRADE)) { info.Status = TRADE_STATUS_WRONG_FACTION; diff --git a/src/server/game/Scripting/ScriptDefines/BattlefieldScript.cpp b/src/server/game/Scripting/ScriptDefines/BattlefieldScript.cpp index d614f1a8d..18dabf3af 100644 --- a/src/server/game/Scripting/ScriptDefines/BattlefieldScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/BattlefieldScript.cpp @@ -44,6 +44,11 @@ void ScriptMgr::OnBattlefieldBeforeInvitePlayerToWar(Battlefield* bf, Player* pl CALL_ENABLED_HOOKS(BattlefieldScript, BATTLEFIELDHOOK_BEFORE_INVITE_PLAYER_TO_WAR, script->OnBattlefieldBeforeInvitePlayerToWar(bf, player)); } +void ScriptMgr::OnBattlefieldWarEnd(Battlefield* bf, bool endByTimer) +{ + CALL_ENABLED_HOOKS(BattlefieldScript, BATTLEFIELDHOOK_ON_WAR_END, script->OnBattlefieldWarEnd(bf, endByTimer)); +} + BattlefieldScript::BattlefieldScript(char const* name, std::vector enabledHooks) : ScriptObject(name, BATTLEFIELDHOOK_END) { diff --git a/src/server/game/Scripting/ScriptDefines/BattlefieldScript.h b/src/server/game/Scripting/ScriptDefines/BattlefieldScript.h index f2a4e19d9..df17e570f 100644 --- a/src/server/game/Scripting/ScriptDefines/BattlefieldScript.h +++ b/src/server/game/Scripting/ScriptDefines/BattlefieldScript.h @@ -28,6 +28,7 @@ enum BattlefieldHook BATTLEFIELDHOOK_ON_PLAYER_JOIN_WAR, // 2 - fires after player is added to the active war BATTLEFIELDHOOK_ON_PLAYER_LEAVE_WAR, // 3 - fires after player is removed from the active war BATTLEFIELDHOOK_BEFORE_INVITE_PLAYER_TO_WAR, // 4 - fires in InvitePlayerToWar before InvitedPlayers insert + BATTLEFIELDHOOK_ON_WAR_END, // 5 - fires in EndBattle after OnBattleEnd(), before timer reset BATTLEFIELDHOOK_END }; @@ -87,6 +88,17 @@ public: * @param player The player being invited to war */ virtual void OnBattlefieldBeforeInvitePlayerToWar(Battlefield* /*bf*/, Player* /*player*/) { } + + /** + * @brief Called in EndBattle() after OnBattleEnd() completes, before the timer is reset. + * All core PlayersInWar/InvitedPlayers structures have already been cleared. + * Modules that maintain their own per-war player tracking should use this hook + * to perform end-of-war cleanup (e.g. restoring cross-faction disguises). + * + * @param bf The Battlefield instance + * @param endByTimer True if the war ended by the countdown timer expiring + */ + virtual void OnBattlefieldWarEnd(Battlefield* /*bf*/, bool /*endByTimer*/) { } }; #endif // SCRIPT_OBJECT_BATTLEFIELD_SCRIPT_H_ diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index de3a038f7..d66145b10 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -608,6 +608,7 @@ public: /* BattlefieldScript */ void OnBattlefieldPlayerJoinWar(Battlefield* bf, Player* player); void OnBattlefieldPlayerLeaveWar(Battlefield* bf, Player* player); void OnBattlefieldBeforeInvitePlayerToWar(Battlefield* bf, Player* player); + void OnBattlefieldWarEnd(Battlefield* bf, bool endByTimer); public: /* BGScript */ void OnBattlegroundStart(Battleground* bg); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index b36c34d45..a66672753 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1532,7 +1532,11 @@ void Spell::EffectHeal(SpellEffIndex effIndex) int32 tickheal = targetAura->GetAmount(); if (Unit* auraCaster = targetAura->GetCaster()) + { + // MOD_HEALING_DONE_PERCENT is applied per-tick, not baked into GetAmount. + tickheal = int32(float(tickheal) * auraCaster->GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_DONE_PERCENT)); tickheal = unitTarget->SpellHealingBonusTaken(auraCaster, targetAura->GetSpellInfo(), tickheal, DOT); + } //int32 tickheal = targetAura->GetSpellInfo()->EffectBasePoints[idx] + 1; //It is said that talent bonus should not be included diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index f637cc372..b9a9fb473 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -204,11 +204,6 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, "AllowTwoSide.Interaction.Guild", false); SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA, "AllowTwoSide.Interaction.Arena", false); SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, "AllowTwoSide.Interaction.Auction", false); - SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL, "AllowTwoSide.Interaction.Mail", false); - SetConfigValue(CONFIG_ALLOW_TWO_SIDE_WHO_LIST, "AllowTwoSide.WhoList", false); - SetConfigValue(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND, "AllowTwoSide.AddFriend", false); - SetConfigValue(CONFIG_ALLOW_TWO_SIDE_TRADE, "AllowTwoSide.Trade", false); - SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE, "AllowTwoSide.Interaction.Emote", false); SetConfigValue(CONFIG_MIN_PLAYER_NAME, "MinPlayerName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_PLAYER_NAME; }, "> 0 && <= MAX_PLAYER_NAME"); SetConfigValue(CONFIG_MIN_CHARTER_NAME, "MinCharterName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_CHARTER_NAME; }, "> 0 && <= MAX_CHARTER_NAME"); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 499baba6c..20c5fa6e8 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -35,10 +35,6 @@ enum ServerConfigs CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA, CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL, - CONFIG_ALLOW_TWO_SIDE_WHO_LIST, - CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND, - CONFIG_ALLOW_TWO_SIDE_TRADE, CONFIG_ALL_TAXI_PATHS, CONFIG_INSTANCE_IGNORE_LEVEL, CONFIG_INSTANCE_IGNORE_RAID, @@ -119,7 +115,6 @@ enum ServerConfigs CONFIG_CLOSE_IDLE_CONNECTIONS, CONFIG_LFG_LOCATION_ALL, CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE, CONFIG_ITEMDELETE_METHOD, CONFIG_ITEMDELETE_VENDOR, CONFIG_DEBUG_BATTLEGROUND, diff --git a/src/server/scripts/Commands/cs_rbac.cpp b/src/server/scripts/Commands/cs_rbac.cpp index 7d80a33a7..21adb9dfe 100644 --- a/src/server/scripts/Commands/cs_rbac.cpp +++ b/src/server/scripts/Commands/cs_rbac.cpp @@ -74,8 +74,10 @@ public: static RBACCommandData GetRBACData(uint32 accountId, std::string const& accountName) { + // session->GetRBACData() can be null after World::ReloadRBAC() if (WorldSession* session = sWorldSessionMgr->FindSession(accountId)) - return { session->GetRBACData(), false }; + if (rbac::RBACData* sessionRbac = session->GetRBACData()) + return { sessionRbac, false }; rbac::RBACData* rbac = new rbac::RBACData(accountId, accountName, realm.Id.Realm, AccountMgr::GetSecurity(accountId, realm.Id.Realm)); rbac->LoadFromDB(); 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 9d235c7b9..c04e21ee3 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 @@ -60,6 +60,7 @@ public: case 5: _owner.SetFacingTo(2.82f); _owner.SetStandState(UNIT_STAND_STATE_KNEEL); + _owner.m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(_owner, 6), 3s); break; case 6: if (InstanceScript* inst = _owner.GetInstanceScript()) diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index dc2f37ff3..adb7cabba 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -709,11 +709,10 @@ struct boss_malygos : public BossAI DoMeleeAttackIfReady(); } - void JustDied(Unit* /*killer*/) override + void JustDied(Unit* /*killer*/) override { _JustDied(); Talk(SAY_DEATH); - instance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, NPC_MALYGOS, 1); } void KilledUnit(Unit* victim) override diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 8dff3bba9..fb63ef229 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -3774,7 +3774,6 @@ private: 29435 - Capture Female Kaliri Hatchling 42268 - Quest - Mindless Abomination Explosion FX Master 51592 - Pickup Primordial Hatchling - 51910 - Kickin' Nass: Quest Completion 52267 - Despawn Horse 54420 - Deliver Gryphon */ class spell_gen_despawn_self : public SpellScript diff --git a/src/server/scripts/World/npc_stave_of_ancients.cpp b/src/server/scripts/World/npc_stave_of_ancients.cpp index 2d45d2e7b..56d89382f 100644 --- a/src/server/scripts/World/npc_stave_of_ancients.cpp +++ b/src/server/scripts/World/npc_stave_of_ancients.cpp @@ -70,14 +70,14 @@ Player* NPCStaveQuestAI::GetGossipPlayer() return ObjectAccessor::GetPlayer(*me, gossipPlayerGUID); } -bool NPCStaveQuestAI::IsAllowedEntry(uint32 entry) +bool NPCStaveQuestAI::IsAllowedEntry(uint32 entry) const { uint32 allowedEntries[4] = { 0, 12999, 19833, 19921 }; //player, World Invisible Trigger(traps) and snake trap snakes bool isAllowed = std::find(std::begin(allowedEntries), std::end(allowedEntries), entry) != std::end(allowedEntries); return isAllowed; } -bool NPCStaveQuestAI::UnitIsUnfair(Unit* unit) +bool NPCStaveQuestAI::UnitIsUnfair(Unit* unit) const { if (!unit || playerGUID.IsEmpty()) { @@ -108,7 +108,7 @@ bool NPCStaveQuestAI::UnitIsUnfair(Unit* unit) return false; } -bool NPCStaveQuestAI::IsFairFight() +bool NPCStaveQuestAI::IsFairFight() const { for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList()) { @@ -128,12 +128,10 @@ bool NPCStaveQuestAI::IsFairFight() return true; } -bool NPCStaveQuestAI::ValidThreatlist() +bool NPCStaveQuestAI::ValidThreatlist() const { if (me->GetThreatMgr().GetThreatListSize() == 1) - { return true; - } bool isFair = IsFairFight(); @@ -463,10 +461,10 @@ public: struct npc_preciousAI : public NPCStaveQuestAI { - npc_preciousAI(Creature *creature) : NPCStaveQuestAI(creature) { } + explicit npc_preciousAI(Creature *creature) : NPCStaveQuestAI(creature) { } EventMap events; - bool flaggedForDespawn; + bool flaggedForDespawn{}; void InitializeAI() override { @@ -512,6 +510,37 @@ public: { flaggedForDespawn = true; } + + uint32 GetData(uint32 type) const override + { + if (type == DATA_SIMONE_VALID_THREATLIST) + return ValidThreatlist() ? 1 : 0; + + return 0; + } + + void SetData(uint32 type, uint32 data) override + { + switch (type) + { + case DATA_SIMONE_REVEAL: + if (data) + RevealForm(); + break; + case DATA_SIMONE_PREPARE_ENCOUNTER: + PrepareForEncounter(); + break; + case DATA_SIMONE_SET_HOME: + SetHomePosition(); + break; + case DATA_SIMONE_CORPSE_REMOVED: + EnterEvadeMode(); + FlagForDespawn(); + break; + default: + break; + } + } }; }; @@ -527,7 +556,7 @@ public: struct npc_simoneAI : public NPCStaveQuestAI { - npc_simoneAI(Creature *creature) : NPCStaveQuestAI(creature) { } + explicit npc_simoneAI(Creature *creature) : NPCStaveQuestAI(creature) { } EventMap events; ObjectGuid preciousGUID; @@ -535,39 +564,18 @@ public: void SetPreciousGUID() { if (CreatureGroup* formation = me->GetFormation()) - { - const CreatureGroup::CreatureGroupMemberType& members = formation->GetMembers(); - for (CreatureGroup::CreatureGroupMemberType::const_iterator itr = members.begin(); itr != members.end(); ++itr) - { - if (itr->first && itr->first->GetOriginalEntry() == PRECIOUS_NORMAL_ENTRY) - { - preciousGUID = itr->first->GetGUID(); - } - } - } + for ([[maybe_unused]] auto const& [member, info] : formation->GetMembers()) + if (member && member->GetOriginalEntry() == PRECIOUS_NORMAL_ENTRY) + preciousGUID = member->GetGUID(); } Creature* Precious() { if (preciousGUID.IsEmpty()) - { SetPreciousGUID(); - } if (!preciousGUID.IsEmpty()) - { return ObjectAccessor::GetCreature(*me, preciousGUID); - } - - return nullptr; - } - - npc_precious::npc_preciousAI* PreciousAI() - { - if (Precious()) - { - return CAST_AI(npc_precious::npc_preciousAI, Precious()->AI()); - } return nullptr; } @@ -632,21 +640,20 @@ public: void CorpseRemoved(uint32& /*respawnDelay*/) override { - if (!Precious()) - { + Creature* creature = Precious(); + if (!creature) return; - } - if (Precious()->IsInCombat()) + if (creature->IsInCombat()) { - // If Simone corpse is removed but pet is InCombat, EnterEvadeMode and auto despawn on pet reaching home - PreciousAI()->EnterEvadeMode(); - PreciousAI()->FlagForDespawn(); + // If Simone corpse is removed but pet is InCombat, ask pet AI to enter evade and flag for despawn + if (creature->AI()) + creature->AI()->SetData(DATA_SIMONE_CORPSE_REMOVED, 1); + else + creature->DespawnOrUnsummon(0ms); } else - { - Precious()->DespawnOrUnsummon(0ms); - } + creature->DespawnOrUnsummon(0ms); } void Reset() override @@ -704,10 +711,9 @@ public: break; case EVENT_REVEAL: RevealForm(); - if (PreciousAI()) - { - PreciousAI()->RevealForm(); - } + if (Creature* creature = Precious()) + if (creature->AI()) + creature->AI()->SetData(DATA_SIMONE_REVEAL, 1); break; // Prevent hunters from figthing Simone alone case SIMONE_EVENT_CHECK_PET_STATE: @@ -721,6 +727,8 @@ public: events.ScheduleEvent(SIMONE_EVENT_CHECK_PET_STATE, 1s); } break; + default: + break; } if (UpdateVictim()) @@ -760,23 +768,32 @@ public: } break; case EVENT_UNFAIR_FIGHT: - if (!ValidThreatlist() || (PreciousAI() && !PreciousAI()->ValidThreatlist())) + { + Creature* creature = Precious(); + bool isPreciousThreatListValid = creature && creature->AI() && creature->AI()->GetData(DATA_SIMONE_VALID_THREATLIST) == 1; + + if (!ValidThreatlist() || !isPreciousThreatListValid) { SetHomePosition(); - PreciousAI()->SetHomePosition(); - - Precious()->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1); - Precious()->SetImmuneToAll(true); + me->DespawnOrUnsummon(5s); me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1); me->SetImmuneToAll(true); - Precious()->DespawnOrUnsummon(5s); + if (creature) + { + if (creature->AI()) + creature->AI()->SetData(DATA_SIMONE_SET_HOME, 1); + + creature->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1); + creature->SetImmuneToAll(true); + creature->DespawnOrUnsummon(5s); + } - me->DespawnOrUnsummon(5s); break; } events.Repeat(2s); break; + } case SIMONE_EVENT_CHAIN_LIGHTNING: me->CastSpell(me->GetVictim(), SIMONE_SPELL_CHAIN_LIGHTNING, false); events.Repeat(7s); @@ -785,6 +802,8 @@ public: me->CastSpell(me->GetVictim(), SIMONE_SPELL_TEMPTRESS_KISS, false); events.Repeat(45s); break; + default: + break; } DoMeleeAttackIfReady(); @@ -805,10 +824,10 @@ public: void ScheduleEncounterStart(ObjectGuid playerGUID) { PrepareForEncounter(); - if (PreciousAI()) - { - PreciousAI()->PrepareForEncounter(); - } + if (Creature* creature = Precious()) + if (creature->AI()) + creature->AI()->SetData(DATA_SIMONE_PREPARE_ENCOUNTER, 1); + gossipPlayerGUID = playerGUID; events.ScheduleEvent(EVENT_ENCOUNTER_START, 1s); } diff --git a/src/server/scripts/World/npc_stave_of_ancients.h b/src/server/scripts/World/npc_stave_of_ancients.h index cb4ab6e2d..b391c69df 100644 --- a/src/server/scripts/World/npc_stave_of_ancients.h +++ b/src/server/scripts/World/npc_stave_of_ancients.h @@ -88,6 +88,15 @@ enum NPCPrecious PRECIOUS_EVIL_ENTRY = 14538 }; +enum SimoneAIData +{ + DATA_SIMONE_REVEAL = 1, + DATA_SIMONE_PREPARE_ENCOUNTER = 2, + DATA_SIMONE_VALID_THREATLIST = 3, + DATA_SIMONE_SET_HOME = 4, + DATA_SIMONE_CORPSE_REMOVED = 5 +}; + enum NPCNelson { NELSON_EVENT_DREADFUL_FRIGHT = 6, @@ -189,10 +198,10 @@ struct NPCStaveQuestAI : public ScriptedAI void RevealForm(); void StorePlayerGUID(); Player* GetGossipPlayer(); - bool IsAllowedEntry(uint32 /*entry*/); - bool UnitIsUnfair(Unit* unit); - bool IsFairFight(); - bool ValidThreatlist(); + bool IsAllowedEntry(uint32 /*entry*/) const; + bool UnitIsUnfair(Unit* unit) const; + bool IsFairFight() const; + bool ValidThreatlist() const; void SetHomePosition(); void PrepareForEncounter(); void ClearLootIfUnfair(Unit* killer);