Merge pull request #208 from mod-playerbots/test-staging

Test staging
This commit is contained in:
Keleborn
2026-05-15 22:26:52 -07:00
committed by GitHub
25 changed files with 160 additions and 131 deletions

View File

@@ -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');

View File

@@ -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');

View File

@@ -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;

View File

@@ -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;

View File

@@ -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.

View File

@@ -364,6 +364,7 @@ void Battlefield::EndBattle(bool endByTimer)
DoPlaySoundToAll(BF_HORDE_WINS);
OnBattleEnd(endByTimer);
sScriptMgr->OnBattlefieldWarEnd(this, endByTimer);
// Reset battlefield timer
Timer = NoWarBattleTime;

View File

@@ -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())
{

View File

@@ -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());
}

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 (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())
{

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<uint16> enabledHooks) :
ScriptObject(name, BATTLEFIELDHOOK_END)
{

View File

@@ -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_

View File

@@ -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);

View File

@@ -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

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_ARENA, "AllowTwoSide.Interaction.Arena", 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_CHARTER_NAME, "MinCharterName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_CHARTER_NAME; }, "> 0 && <= MAX_CHARTER_NAME");

View File

@@ -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,

View File

@@ -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();

View File

@@ -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())

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);