Merge remote-tracking branch 'acore/master' into test-staging

This commit is contained in:
Celandriel
2026-05-08 18:22:10 +00:00
25 changed files with 160 additions and 131 deletions
@@ -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');
@@ -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');
@@ -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;
@@ -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;
@@ -4360,14 +4360,6 @@ AllowTwoSide.Interaction.Calendar = 0
AllowTwoSide.Interaction.Chat = 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 # AllowTwoSide.Interaction.Channel
# Description: Allow channel chat between factions. # Description: Allow channel chat between factions.
@@ -4409,38 +4401,6 @@ AllowTwoSide.Interaction.Arena = 0
AllowTwoSide.Interaction.Auction = 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 # TalentsInspecting
# Description: Allow inspecting characters from the opposing faction. # Description: Allow inspecting characters from the opposing faction.
@@ -364,6 +364,7 @@ void Battlefield::EndBattle(bool endByTimer)
DoPlaySoundToAll(BF_HORDE_WINS); DoPlaySoundToAll(BF_HORDE_WINS);
OnBattleEnd(endByTimer); OnBattleEnd(endByTimer);
sScriptMgr->OnBattlefieldWarEnd(this, endByTimer);
// Reset battlefield timer // Reset battlefield timer
Timer = NoWarBattleTime; Timer = NoWarBattleTime;
+4 -9
View File
@@ -206,6 +206,10 @@ void ThreatReference::HeapNotifyDecreased()
if (tWho->GetSummonerGUID().IsPlayer()) if (tWho->GetSummonerGUID().IsPlayer())
return false; 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; return true;
} }
@@ -396,15 +400,6 @@ void ThreatManager::AddThreat(Unit* target, float amount, SpellInfo const* spell
return; 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 we cannot actually have a threat list, we instead just set combat state and avoid creating threat refs altogether
if (!CanHaveThreatList()) if (!CanHaveThreatList())
{ {
@@ -15426,6 +15426,15 @@ void Player::ActivateSpec(uint8 spec)
++iter; ++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()); sScriptMgr->OnPlayerAfterSpecSlotChanged(this, GetActiveSpec());
} }
+5 -2
View File
@@ -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 (TempSummon* accessory = _me->SummonCreature(entry, *_me, TempSummonType(type), summonTime))
{ {
if (minion) if (minion)
{
accessory->AddUnitTypeMask(UNIT_MASK_ACCESSORY); accessory->AddUnitTypeMask(UNIT_MASK_ACCESSORY);
accessory->GetThreatMgr().Initialize(); // reinitialize CanHaveThreatList cached value
}
if (!_me->HandleSpellClick(accessory, seatId)) if (!_me->HandleSpellClick(accessory, seatId))
{ {
@@ -455,9 +458,9 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId)
init.SetTransportEnter(); init.SetTransportEnter();
init.Launch(); 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()) 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()) if (_me->IsCreature())
{ {
+1 -1
View File
@@ -214,7 +214,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sCharacterCache->GetCharacterAccountIdByGuid(receiverGuid); 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); player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM);
return; return;
-1
View File
@@ -293,7 +293,6 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData)
for (auto const& target : sWhoListCacheMgr->GetWhoList()) 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)) if (target.GetTeamId() != team && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_WHO_LIST))
continue; continue;
+1 -1
View File
@@ -64,7 +64,7 @@ void WorldSession::HandleAddFriendOpcode(WorldPacket& recv_data)
{ {
if (friendGuid == GetPlayer()->GetGUID()) if (friendGuid == GetPlayer()->GetGUID())
friendResult = FRIEND_SELF; 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; friendResult = FRIEND_ENEMY;
else if (GetPlayer()->GetSocial()->HasFriend(friendGuid)) else if (GetPlayer()->GetSocial()->HasFriend(friendGuid))
friendResult = FRIEND_ALREADY; friendResult = FRIEND_ALREADY;
@@ -723,7 +723,6 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
} }
if (pOther->GetTeamId() != _player->GetTeamId() && if (pOther->GetTeamId() != _player->GetTeamId() &&
!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) &&
!GetPlayer()->GetSession()->HasPermission(rbac::RBAC_PERM_ALLOW_TWO_SIDE_TRADE)) !GetPlayer()->GetSession()->HasPermission(rbac::RBAC_PERM_ALLOW_TWO_SIDE_TRADE))
{ {
info.Status = TRADE_STATUS_WRONG_FACTION; info.Status = TRADE_STATUS_WRONG_FACTION;
@@ -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)); 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<uint16> enabledHooks) : BattlefieldScript::BattlefieldScript(char const* name, std::vector<uint16> enabledHooks) :
ScriptObject(name, BATTLEFIELDHOOK_END) ScriptObject(name, BATTLEFIELDHOOK_END)
{ {
@@ -28,6 +28,7 @@ enum BattlefieldHook
BATTLEFIELDHOOK_ON_PLAYER_JOIN_WAR, // 2 - fires after player is added to the active war 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_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_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 BATTLEFIELDHOOK_END
}; };
@@ -87,6 +88,17 @@ public:
* @param player The player being invited to war * @param player The player being invited to war
*/ */
virtual void OnBattlefieldBeforeInvitePlayerToWar(Battlefield* /*bf*/, Player* /*player*/) { } 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_ #endif // SCRIPT_OBJECT_BATTLEFIELD_SCRIPT_H_
+1
View File
@@ -608,6 +608,7 @@ public: /* BattlefieldScript */
void OnBattlefieldPlayerJoinWar(Battlefield* bf, Player* player); void OnBattlefieldPlayerJoinWar(Battlefield* bf, Player* player);
void OnBattlefieldPlayerLeaveWar(Battlefield* bf, Player* player); void OnBattlefieldPlayerLeaveWar(Battlefield* bf, Player* player);
void OnBattlefieldBeforeInvitePlayerToWar(Battlefield* bf, Player* player); void OnBattlefieldBeforeInvitePlayerToWar(Battlefield* bf, Player* player);
void OnBattlefieldWarEnd(Battlefield* bf, bool endByTimer);
public: /* BGScript */ public: /* BGScript */
void OnBattlegroundStart(Battleground* bg); void OnBattlegroundStart(Battleground* bg);
+4
View File
@@ -1532,7 +1532,11 @@ void Spell::EffectHeal(SpellEffIndex effIndex)
int32 tickheal = targetAura->GetAmount(); int32 tickheal = targetAura->GetAmount();
if (Unit* auraCaster = targetAura->GetCaster()) 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); tickheal = unitTarget->SpellHealingBonusTaken(auraCaster, targetAura->GetSpellInfo(), tickheal, DOT);
}
//int32 tickheal = targetAura->GetSpellInfo()->EffectBasePoints[idx] + 1; //int32 tickheal = targetAura->GetSpellInfo()->EffectBasePoints[idx] + 1;
//It is said that talent bonus should not be included //It is said that talent bonus should not be included
-5
View File
@@ -204,11 +204,6 @@ void WorldConfig::BuildConfigCache()
SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, "AllowTwoSide.Interaction.Guild", false); SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, "AllowTwoSide.Interaction.Guild", false);
SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA, "AllowTwoSide.Interaction.Arena", false); SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA, "AllowTwoSide.Interaction.Arena", false);
SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, "AllowTwoSide.Interaction.Auction", false); SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, "AllowTwoSide.Interaction.Auction", false);
SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL, "AllowTwoSide.Interaction.Mail", false);
SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_WHO_LIST, "AllowTwoSide.WhoList", false);
SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND, "AllowTwoSide.AddFriend", false);
SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_TRADE, "AllowTwoSide.Trade", false);
SetConfigValue<bool>(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE, "AllowTwoSide.Interaction.Emote", false);
SetConfigValue<uint32>(CONFIG_MIN_PLAYER_NAME, "MinPlayerName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_PLAYER_NAME; }, "> 0 && <= MAX_PLAYER_NAME"); SetConfigValue<uint32>(CONFIG_MIN_PLAYER_NAME, "MinPlayerName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_PLAYER_NAME; }, "> 0 && <= MAX_PLAYER_NAME");
SetConfigValue<uint32>(CONFIG_MIN_CHARTER_NAME, "MinCharterName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_CHARTER_NAME; }, "> 0 && <= MAX_CHARTER_NAME"); SetConfigValue<uint32>(CONFIG_MIN_CHARTER_NAME, "MinCharterName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_CHARTER_NAME; }, "> 0 && <= MAX_CHARTER_NAME");
-5
View File
@@ -35,10 +35,6 @@ enum ServerConfigs
CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA, CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, 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_ALL_TAXI_PATHS,
CONFIG_INSTANCE_IGNORE_LEVEL, CONFIG_INSTANCE_IGNORE_LEVEL,
CONFIG_INSTANCE_IGNORE_RAID, CONFIG_INSTANCE_IGNORE_RAID,
@@ -119,7 +115,6 @@ enum ServerConfigs
CONFIG_CLOSE_IDLE_CONNECTIONS, CONFIG_CLOSE_IDLE_CONNECTIONS,
CONFIG_LFG_LOCATION_ALL, CONFIG_LFG_LOCATION_ALL,
CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS, CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS,
CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE,
CONFIG_ITEMDELETE_METHOD, CONFIG_ITEMDELETE_METHOD,
CONFIG_ITEMDELETE_VENDOR, CONFIG_ITEMDELETE_VENDOR,
CONFIG_DEBUG_BATTLEGROUND, CONFIG_DEBUG_BATTLEGROUND,
+3 -1
View File
@@ -74,8 +74,10 @@ public:
static RBACCommandData GetRBACData(uint32 accountId, std::string const& accountName) static RBACCommandData GetRBACData(uint32 accountId, std::string const& accountName)
{ {
// session->GetRBACData() can be null after World::ReloadRBAC()
if (WorldSession* session = sWorldSessionMgr->FindSession(accountId)) 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::RBACData* rbac = new rbac::RBACData(accountId, accountName, realm.Id.Realm, AccountMgr::GetSecurity(accountId, realm.Id.Realm));
rbac->LoadFromDB(); rbac->LoadFromDB();
@@ -60,6 +60,7 @@ public:
case 5: case 5:
_owner.SetFacingTo(2.82f); _owner.SetFacingTo(2.82f);
_owner.SetStandState(UNIT_STAND_STATE_KNEEL); _owner.SetStandState(UNIT_STAND_STATE_KNEEL);
_owner.m_Events.AddEventAtOffset(new UtherBatteredHiltEvent(_owner, 6), 3s);
break; break;
case 6: case 6:
if (InstanceScript* inst = _owner.GetInstanceScript()) if (InstanceScript* inst = _owner.GetInstanceScript())
@@ -709,11 +709,10 @@ struct boss_malygos : public BossAI
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
} }
void JustDied(Unit* /*killer*/) override void JustDied(Unit* /*killer*/) override
{ {
_JustDied(); _JustDied();
Talk(SAY_DEATH); Talk(SAY_DEATH);
instance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, NPC_MALYGOS, 1);
} }
void KilledUnit(Unit* victim) override void KilledUnit(Unit* victim) override
@@ -3774,7 +3774,6 @@ private:
29435 - Capture Female Kaliri Hatchling 29435 - Capture Female Kaliri Hatchling
42268 - Quest - Mindless Abomination Explosion FX Master 42268 - Quest - Mindless Abomination Explosion FX Master
51592 - Pickup Primordial Hatchling 51592 - Pickup Primordial Hatchling
51910 - Kickin' Nass: Quest Completion
52267 - Despawn Horse 52267 - Despawn Horse
54420 - Deliver Gryphon */ 54420 - Deliver Gryphon */
class spell_gen_despawn_self : public SpellScript class spell_gen_despawn_self : public SpellScript
@@ -70,14 +70,14 @@ Player* NPCStaveQuestAI::GetGossipPlayer()
return ObjectAccessor::GetPlayer(*me, gossipPlayerGUID); 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 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); bool isAllowed = std::find(std::begin(allowedEntries), std::end(allowedEntries), entry) != std::end(allowedEntries);
return isAllowed; return isAllowed;
} }
bool NPCStaveQuestAI::UnitIsUnfair(Unit* unit) bool NPCStaveQuestAI::UnitIsUnfair(Unit* unit) const
{ {
if (!unit || playerGUID.IsEmpty()) if (!unit || playerGUID.IsEmpty())
{ {
@@ -108,7 +108,7 @@ bool NPCStaveQuestAI::UnitIsUnfair(Unit* unit)
return false; return false;
} }
bool NPCStaveQuestAI::IsFairFight() bool NPCStaveQuestAI::IsFairFight() const
{ {
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList()) for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
{ {
@@ -128,12 +128,10 @@ bool NPCStaveQuestAI::IsFairFight()
return true; return true;
} }
bool NPCStaveQuestAI::ValidThreatlist() bool NPCStaveQuestAI::ValidThreatlist() const
{ {
if (me->GetThreatMgr().GetThreatListSize() == 1) if (me->GetThreatMgr().GetThreatListSize() == 1)
{
return true; return true;
}
bool isFair = IsFairFight(); bool isFair = IsFairFight();
@@ -463,10 +461,10 @@ public:
struct npc_preciousAI : public NPCStaveQuestAI struct npc_preciousAI : public NPCStaveQuestAI
{ {
npc_preciousAI(Creature *creature) : NPCStaveQuestAI(creature) { } explicit npc_preciousAI(Creature *creature) : NPCStaveQuestAI(creature) { }
EventMap events; EventMap events;
bool flaggedForDespawn; bool flaggedForDespawn{};
void InitializeAI() override void InitializeAI() override
{ {
@@ -512,6 +510,37 @@ public:
{ {
flaggedForDespawn = true; 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 struct npc_simoneAI : public NPCStaveQuestAI
{ {
npc_simoneAI(Creature *creature) : NPCStaveQuestAI(creature) { } explicit npc_simoneAI(Creature *creature) : NPCStaveQuestAI(creature) { }
EventMap events; EventMap events;
ObjectGuid preciousGUID; ObjectGuid preciousGUID;
@@ -535,39 +564,18 @@ public:
void SetPreciousGUID() void SetPreciousGUID()
{ {
if (CreatureGroup* formation = me->GetFormation()) if (CreatureGroup* formation = me->GetFormation())
{ for ([[maybe_unused]] auto const& [member, info] : formation->GetMembers())
const CreatureGroup::CreatureGroupMemberType& members = formation->GetMembers(); if (member && member->GetOriginalEntry() == PRECIOUS_NORMAL_ENTRY)
for (CreatureGroup::CreatureGroupMemberType::const_iterator itr = members.begin(); itr != members.end(); ++itr) preciousGUID = member->GetGUID();
{
if (itr->first && itr->first->GetOriginalEntry() == PRECIOUS_NORMAL_ENTRY)
{
preciousGUID = itr->first->GetGUID();
}
}
}
} }
Creature* Precious() Creature* Precious()
{ {
if (preciousGUID.IsEmpty()) if (preciousGUID.IsEmpty())
{
SetPreciousGUID(); SetPreciousGUID();
}
if (!preciousGUID.IsEmpty()) if (!preciousGUID.IsEmpty())
{
return ObjectAccessor::GetCreature(*me, preciousGUID); 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; return nullptr;
} }
@@ -632,21 +640,20 @@ public:
void CorpseRemoved(uint32& /*respawnDelay*/) override void CorpseRemoved(uint32& /*respawnDelay*/) override
{ {
if (!Precious()) Creature* creature = Precious();
{ if (!creature)
return; return;
}
if (Precious()->IsInCombat()) if (creature->IsInCombat())
{ {
// If Simone corpse is removed but pet is InCombat, EnterEvadeMode and auto despawn on pet reaching home // If Simone corpse is removed but pet is InCombat, ask pet AI to enter evade and flag for despawn
PreciousAI()->EnterEvadeMode(); if (creature->AI())
PreciousAI()->FlagForDespawn(); creature->AI()->SetData(DATA_SIMONE_CORPSE_REMOVED, 1);
else
creature->DespawnOrUnsummon(0ms);
} }
else else
{ creature->DespawnOrUnsummon(0ms);
Precious()->DespawnOrUnsummon(0ms);
}
} }
void Reset() override void Reset() override
@@ -704,10 +711,9 @@ public:
break; break;
case EVENT_REVEAL: case EVENT_REVEAL:
RevealForm(); RevealForm();
if (PreciousAI()) if (Creature* creature = Precious())
{ if (creature->AI())
PreciousAI()->RevealForm(); creature->AI()->SetData(DATA_SIMONE_REVEAL, 1);
}
break; break;
// Prevent hunters from figthing Simone alone // Prevent hunters from figthing Simone alone
case SIMONE_EVENT_CHECK_PET_STATE: case SIMONE_EVENT_CHECK_PET_STATE:
@@ -721,6 +727,8 @@ public:
events.ScheduleEvent(SIMONE_EVENT_CHECK_PET_STATE, 1s); events.ScheduleEvent(SIMONE_EVENT_CHECK_PET_STATE, 1s);
} }
break; break;
default:
break;
} }
if (UpdateVictim()) if (UpdateVictim())
@@ -760,23 +768,32 @@ public:
} }
break; break;
case EVENT_UNFAIR_FIGHT: 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(); SetHomePosition();
PreciousAI()->SetHomePosition(); me->DespawnOrUnsummon(5s);
Precious()->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1);
Precious()->SetImmuneToAll(true);
me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1); me->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1);
me->SetImmuneToAll(true); 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; break;
} }
events.Repeat(2s); events.Repeat(2s);
break; break;
}
case SIMONE_EVENT_CHAIN_LIGHTNING: case SIMONE_EVENT_CHAIN_LIGHTNING:
me->CastSpell(me->GetVictim(), SIMONE_SPELL_CHAIN_LIGHTNING, false); me->CastSpell(me->GetVictim(), SIMONE_SPELL_CHAIN_LIGHTNING, false);
events.Repeat(7s); events.Repeat(7s);
@@ -785,6 +802,8 @@ public:
me->CastSpell(me->GetVictim(), SIMONE_SPELL_TEMPTRESS_KISS, false); me->CastSpell(me->GetVictim(), SIMONE_SPELL_TEMPTRESS_KISS, false);
events.Repeat(45s); events.Repeat(45s);
break; break;
default:
break;
} }
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
@@ -805,10 +824,10 @@ public:
void ScheduleEncounterStart(ObjectGuid playerGUID) void ScheduleEncounterStart(ObjectGuid playerGUID)
{ {
PrepareForEncounter(); PrepareForEncounter();
if (PreciousAI()) if (Creature* creature = Precious())
{ if (creature->AI())
PreciousAI()->PrepareForEncounter(); creature->AI()->SetData(DATA_SIMONE_PREPARE_ENCOUNTER, 1);
}
gossipPlayerGUID = playerGUID; gossipPlayerGUID = playerGUID;
events.ScheduleEvent(EVENT_ENCOUNTER_START, 1s); events.ScheduleEvent(EVENT_ENCOUNTER_START, 1s);
} }
@@ -88,6 +88,15 @@ enum NPCPrecious
PRECIOUS_EVIL_ENTRY = 14538 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 enum NPCNelson
{ {
NELSON_EVENT_DREADFUL_FRIGHT = 6, NELSON_EVENT_DREADFUL_FRIGHT = 6,
@@ -189,10 +198,10 @@ struct NPCStaveQuestAI : public ScriptedAI
void RevealForm(); void RevealForm();
void StorePlayerGUID(); void StorePlayerGUID();
Player* GetGossipPlayer(); Player* GetGossipPlayer();
bool IsAllowedEntry(uint32 /*entry*/); bool IsAllowedEntry(uint32 /*entry*/) const;
bool UnitIsUnfair(Unit* unit); bool UnitIsUnfair(Unit* unit) const;
bool IsFairFight(); bool IsFairFight() const;
bool ValidThreatlist(); bool ValidThreatlist() const;
void SetHomePosition(); void SetHomePosition();
void PrepareForEncounter(); void PrepareForEncounter();
void ClearLootIfUnfair(Unit* killer); void ClearLootIfUnfair(Unit* killer);