diff options
Diffstat (limited to 'src/server')
357 files changed, 19613 insertions, 20787 deletions
diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index 49602119de0..a5db0e37928 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -576,7 +576,7 @@ bool AuthSession::HandleLogonProof() PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); stmt->setString(0, K.AsHexStr()); - stmt->setString(1, GetRemoteIpAddress().to_string().c_str()); + stmt->setString(1, GetRemoteIpAddress().to_string()); stmt->setUInt32(2, GetLocaleByName(_localizationName)); stmt->setString(3, _os); stmt->setString(4, _accountInfo.Login); @@ -885,6 +885,8 @@ void AuthSession::RealmListCallback(PreparedQueryResult result) hdr.append(RealmListSizeBuffer); // append RealmList's size buffer hdr.append(pkt); // append realms in the realmlist SendPacket(hdr); + + _status = STATUS_AUTHED; } // Make the SRP6 calculation from hash in dB diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index c64e7a9c2fc..654074d594b 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -92,6 +92,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_DEL_DISABLES, "DELETE FROM disables WHERE entry = ? AND sourceType = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA, "UPDATE creature SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA, "UPDATE gameobject SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(WORLD_DEL_SPAWNGROUP_MEMBER, "DELETE FROM spawn_group WHERE spawnType = ? AND spawnId = ?", CONNECTION_ASYNC); } WorldDatabaseConnection::WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h index b68f53bb173..f7784ad256f 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.h +++ b/src/server/database/Database/Implementation/WorldDatabase.h @@ -98,6 +98,7 @@ enum WorldDatabaseStatements : uint32 WORLD_DEL_DISABLES, WORLD_UPD_CREATURE_ZONE_AREA_DATA, WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA, + WORLD_DEL_SPAWNGROUP_MEMBER, MAX_WORLDDATABASE_STATEMENTS }; diff --git a/src/server/game/AI/CoreAI/PassiveAI.cpp b/src/server/game/AI/CoreAI/PassiveAI.cpp index 2eca6c64c9e..abbff7a870b 100644 --- a/src/server/game/AI/CoreAI/PassiveAI.cpp +++ b/src/server/game/AI/CoreAI/PassiveAI.cpp @@ -29,7 +29,7 @@ int32 NullCreatureAI::Permissible(Creature const* creature) return PERMIT_BASE_PROACTIVE + 50; if (creature->IsTrigger()) - return PERMIT_BASE_REACTIVE; + return PERMIT_BASE_PROACTIVE; return PERMIT_BASE_IDLE; } @@ -105,7 +105,7 @@ void TriggerAI::IsSummonedBy(Unit* summoner) int32 TriggerAI::Permissible(Creature const* creature) { if (creature->IsTrigger() && creature->m_spells[0]) - return PERMIT_BASE_PROACTIVE; + return PERMIT_BASE_SPECIAL; return PERMIT_BASE_NO; } diff --git a/src/server/game/AI/CoreAI/TotemAI.cpp b/src/server/game/AI/CoreAI/TotemAI.cpp index da484e20983..365f0ca5ce8 100644 --- a/src/server/game/AI/CoreAI/TotemAI.cpp +++ b/src/server/game/AI/CoreAI/TotemAI.cpp @@ -72,7 +72,7 @@ void TotemAI::UpdateAI(uint32 /*diff*/) me->IsFriendlyTo(victim) || !me->CanSeeOrDetect(victim)) { victim = nullptr; - Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me, max_range); + Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me->GetCharmerOrOwnerOrSelf(), max_range); Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(me, victim, u_check); Cell::VisitAllObjects(me, checker, max_range); } diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index 92c9c8e3fb6..244b0b5e047 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -190,7 +190,7 @@ void UnitAI::DoCastAOE(uint32 spellId, bool triggered) if (!triggered && me->HasUnitState(UNIT_STATE_CASTING)) return; - me->CastSpell((Unit*)nullptr, spellId, triggered); + me->CastSpell(nullptr, spellId, triggered); } uint32 UnitAI::GetDialogStatus(Player* /*player*/) diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 30a0575c3d2..1078c16f8de 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -330,6 +330,11 @@ class TC_GAME_API UnitAI // Called when the dialog status between a player and the creature is requested. virtual uint32 GetDialogStatus(Player* /*player*/); + virtual void WaypointPathStarted(uint32 /*nodeId*/, uint32 /*pathId*/) { } + virtual void WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) { } + virtual void WaypointReached(uint32 /*nodeId*/, uint32 /*pathId*/) { } + virtual void WaypointPathEnded(uint32 /*nodeId*/, uint32 /*pathId*/) { } + private: UnitAI(UnitAI const& right) = delete; UnitAI& operator=(UnitAI const& right) = delete; diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index bd7a6efab22..015e46cccdd 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -134,8 +134,8 @@ class TC_GAME_API CreatureAI : public UnitAI virtual bool IsEscorted() const { return false; } - // Called when creature is spawned or respawned - virtual void JustRespawned() { } + // Called when creature appears in the world (spawn, respawn, grid load etc...) + virtual void JustAppeared() { } // Called at waypoint reached or point movement finished virtual void MovementInform(uint32 /*type*/, uint32 /*id*/) { } @@ -187,6 +187,9 @@ class TC_GAME_API CreatureAI : public UnitAI // If a PlayerAI* is returned, that AI is placed on the player instead of the default charm AI // Object destruction is handled by Unit::RemoveCharmedBy virtual PlayerAI* GetAIForCharmedPlayer(Player* /*who*/) { return nullptr; } + // Should return true if the NPC is target of an escort quest + // If onlyIfActive is set, should return true only if the escort quest is currently active + virtual bool IsEscortNPC(bool /*onlyIfActive*/) const { return false; } // intended for encounter design/debugging. do not use for other purposes. expensive. int32 VisualizeBoundary(uint32 duration, Unit* owner = nullptr, bool fill = false) const; diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp index 92f1c6f06dc..a103b34ff56 100644 --- a/src/server/game/AI/PlayerAI/PlayerAI.cpp +++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp @@ -769,18 +769,30 @@ Unit* PlayerAI::SelectAttackTarget() const return me->GetCharmer() ? me->GetCharmer()->GetVictim() : nullptr; } -struct UncontrolledTargetSelectPredicate +struct ValidTargetSelectPredicate { + ValidTargetSelectPredicate(UnitAI const* ai) : _ai(ai) { } + UnitAI const* const _ai; bool operator()(Unit const* target) const { - return !target->HasBreakableByDamageCrowdControlAura(); + return _ai->CanAIAttack(target); } }; +bool SimpleCharmedPlayerAI::CanAIAttack(Unit const* who) const +{ + if (!me->IsValidAttackTarget(who) || who->HasBreakableByDamageCrowdControlAura()) + return false; + if (Unit* charmer = me->GetCharmer()) + if (!charmer->IsValidAttackTarget(who)) + return false; + return UnitAI::CanAIAttack(who); +} + Unit* SimpleCharmedPlayerAI::SelectAttackTarget() const { if (Unit* charmer = me->GetCharmer()) - return charmer->IsAIEnabled ? charmer->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, UncontrolledTargetSelectPredicate()) : charmer->GetVictim(); + return charmer->IsAIEnabled ? charmer->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0, ValidTargetSelectPredicate(this)) : charmer->GetVictim(); return nullptr; } @@ -1316,11 +1328,23 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff) if (charmer->IsEngaged()) { Unit* target = me->GetVictim(); - if (!target || !charmer->IsValidAttackTarget(target) || target->HasBreakableByDamageCrowdControlAura()) + if (!target || !CanAIAttack(target)) { target = SelectAttackTarget(); - if (!target) + if (!target || !CanAIAttack(target)) + { + if (!_isFollowing) + { + _isFollowing = true; + me->AttackStop(); + me->CastStop(); + me->StopMoving(); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + } return; + } + _isFollowing = false; if (IsRangedAttacker()) { @@ -1373,8 +1397,9 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff) DoAutoAttackIfReady(); } - else + else if (!_isFollowing) { + _isFollowing = true; me->AttackStop(); me->CastStop(); me->StopMoving(); diff --git a/src/server/game/AI/PlayerAI/PlayerAI.h b/src/server/game/AI/PlayerAI/PlayerAI.h index 8afc5ab3ce9..faff09b6b6e 100644 --- a/src/server/game/AI/PlayerAI/PlayerAI.h +++ b/src/server/game/AI/PlayerAI/PlayerAI.h @@ -97,11 +97,12 @@ class TC_GAME_API PlayerAI : public UnitAI class TC_GAME_API SimpleCharmedPlayerAI : public PlayerAI { public: - SimpleCharmedPlayerAI(Player* player) : PlayerAI(player), _castCheckTimer(500), _chaseCloser(false), _forceFacing(true) { } + SimpleCharmedPlayerAI(Player* player) : PlayerAI(player), _castCheckTimer(2500), _chaseCloser(false), _forceFacing(true), _isFollowing(false) { } void UpdateAI(uint32 diff) override; void OnCharmed(bool apply) override; protected: + bool CanAIAttack(Unit const* who) const override; Unit* SelectAttackTarget() const override; private: @@ -109,6 +110,7 @@ class TC_GAME_API SimpleCharmedPlayerAI : public PlayerAI uint32 _castCheckTimer; bool _chaseCloser; bool _forceFacing; + bool _isFollowing; }; #endif diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index e9e252bcf35..afa7e789b7a 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -478,7 +478,7 @@ void BossAI::_Reset() events.Reset(); summons.DespawnAll(); scheduler.CancelAll(); - if (instance) + if (instance && instance->GetBossState(_bossId) != DONE) instance->SetBossState(_bossId, NOT_STARTED); } @@ -564,12 +564,12 @@ bool BossAI::CanAIAttack(Unit const* target) const return CheckBoundary(target); } -void BossAI::_DespawnAtEvade(uint32 delayToRespawn /*= 30*/, Creature* who /*= nullptr*/) +void BossAI::_DespawnAtEvade(Seconds delayToRespawn, Creature* who) { - if (delayToRespawn < 2) + if (delayToRespawn < Seconds(2)) { - TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %u seconds, defaulting to 2.", delayToRespawn); - delayToRespawn = 2; + TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %ld seconds, defaulting to 2.", delayToRespawn.count()); + delayToRespawn = Seconds(2); } if (!who) diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 6fc85b6a81a..9f2e49c1ffb 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -366,8 +366,8 @@ class TC_GAME_API BossAI : public ScriptedAI void _EnterCombat(); void _JustDied(); void _JustReachedHome(); - void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr); - void _DespawnAtEvade(Seconds const& time, Creature* who = nullptr) { _DespawnAtEvade(uint32(time.count()), who); } + void _DespawnAtEvade(Seconds delayToRespawn, Creature* who = nullptr); + void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr) { _DespawnAtEvade(Seconds(delayToRespawn), who); } void TeleportCheaters(); diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index bcf060e2ad6..376aceba4ba 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -16,21 +16,17 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Npc_EscortAI -SD%Complete: 100 -SDComment: -SDCategory: Npc -EndScriptData */ - #include "ScriptedEscortAI.h" #include "Creature.h" #include "Group.h" #include "Log.h" #include "Map.h" #include "MotionMaster.h" +#include "MovementGenerator.h" #include "ObjectAccessor.h" #include "Player.h" +#include "ScriptSystem.h" +#include "World.h" enum Points { @@ -38,101 +34,75 @@ enum Points POINT_HOME = 0xFFFFFE }; -npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature), - m_uiWPWaitTimer(2500), - m_uiPlayerCheckTimer(1000), - m_uiEscortState(STATE_ESCORT_NONE), - MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE), - m_pQuestForEscort(nullptr), - m_bIsActiveAttacker(true), - m_bIsRunning(false), - m_bCanInstantRespawn(false), - m_bCanReturnToStart(false), - DespawnAtEnd(true), - DespawnAtFar(true), - ScriptWP(false), - HasImmuneToNPCFlags(false) -{ } - -void npc_escortAI::AttackStart(Unit* who) +EscortAI::EscortAI(Creature* creature) : ScriptedAI(creature), _pauseTimer(2500), _playerCheckTimer(1000), _escortState(STATE_ESCORT_NONE), _maxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE), + _escortQuest(nullptr), _activeAttacker(true), _running(false), _instantRespawn(false), _returnToStart(false), _despawnAtEnd(true), _despawnAtFar(true), _manualPath(false), + _hasImmuneToNPCFlags(false), _started(false), _ended(false), _resume(false) { - if (!who) - return; - - if (me->Attack(who, true)) - { - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) - me->GetMotionMaster()->MovementExpired(); - - if (IsCombatMovementAllowed()) - me->GetMotionMaster()->MoveChase(who); - } } -Player* npc_escortAI::GetPlayerForEscort() +Player* EscortAI::GetPlayerForEscort() { - return ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID); + return ObjectAccessor::GetPlayer(*me, _playerGUID); } -//see followerAI -bool npc_escortAI::AssistPlayerInCombatAgainst(Unit* who) +// see followerAI +bool EscortAI::AssistPlayerInCombatAgainst(Unit* who) { if (!who || !who->GetVictim()) return false; - //experimental (unknown) flag not present + if (me->HasReactState(REACT_PASSIVE)) + return false; + + // experimental (unknown) flag not present if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; - //not a player + // not a player if (!who->EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()) return false; - //never attack friendly - if (me->IsFriendlyTo(who)) + if (!who->isInAccessiblePlaceFor(me)) return false; - //too far away and no free sight? + if (!CanAIAttack(who)) + return false; + + // we cannot attack in evade mode + if (me->IsInEvadeMode()) + return false; + + // or if enemy is in evade mode + if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode()) + return false; + + if (!me->IsValidAssistTarget(who->GetVictim())) + return false; + + // too far away and no free sight if (me->IsWithinDistInMap(who, GetMaxPlayerDistance()) && me->IsWithinLOSInMap(who)) { - //already fighting someone? - if (!me->GetVictim()) - { - AttackStart(who); - return true; - } - else - { - me->EngageWithTarget(who); - return true; - } + me->EngageWithTarget(who); + return true; } return false; } -void npc_escortAI::MoveInLineOfSight(Unit* who) +void EscortAI::MoveInLineOfSight(Unit* who) { - if (me->HasReactState(REACT_AGGRESSIVE) && !me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me)) - { - if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombatAgainst(who)) - return; + if (!who) + return; - if (!me->CanFly() && me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE) - return; + if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombatAgainst(who)) + return; - if (me->IsHostileTo(who)) - { - float fAttackRadius = me->GetAttackDistance(who); - if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who)) - me->EngageWithTarget(who); - } - } + ScriptedAI::MoveInLineOfSight(who); } -void npc_escortAI::JustDied(Unit* /*killer*/) +void EscortAI::JustDied(Unit* /*killer*/) { - if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort) + if (!HasEscortState(STATE_ESCORT_ESCORTING) || !_playerGUID || !_escortQuest) return; if (Player* player = GetPlayerForEscort()) @@ -140,24 +110,26 @@ void npc_escortAI::JustDied(Unit* /*killer*/) if (Group* group = player->GetGroup()) { for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next()) + { if (Player* member = groupRef->GetSource()) if (member->IsInMap(player)) - member->FailQuest(m_pQuestForEscort->GetQuestId()); + member->FailQuest(_escortQuest->GetQuestId()); + } } else - player->FailQuest(m_pQuestForEscort->GetQuestId()); + player->FailQuest(_escortQuest->GetQuestId()); } } -void npc_escortAI::JustRespawned() +void EscortAI::JustAppeared() { - m_uiEscortState = STATE_ESCORT_NONE; + _escortState = STATE_ESCORT_NONE; if (!IsCombatMovementAllowed()) SetCombatMovement(true); - //add a small delay before going to first waypoint, normal in near all cases - m_uiWPWaitTimer = 2500; + // add a small delay before going to first waypoint, normal in near all cases + _pauseTimer = 2000; if (me->GetFaction() != me->GetCreatureTemplate()->faction) me->RestoreFaction(); @@ -165,14 +137,12 @@ void npc_escortAI::JustRespawned() Reset(); } -void npc_escortAI::ReturnToLastPoint() +void EscortAI::ReturnToLastPoint() { - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z); + me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, me->GetHomePosition()); } -void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/) +void EscortAI::EnterEvadeMode(EvadeReason /*why*/) { me->RemoveAllAuras(); me->GetThreatManager().ClearAllThreat(); @@ -183,27 +153,29 @@ void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/) { AddEscortState(STATE_ESCORT_RETURNING); ReturnToLastPoint(); - TC_LOG_DEBUG("scripts", "EscortAI has left combat and is now returning to last point"); + TC_LOG_DEBUG("scripts", "EscortAI::EnterEvadeMode: left combat and is now returning to last point"); } else { me->GetMotionMaster()->MoveTargetedHome(); - if (HasImmuneToNPCFlags) + if (_hasImmuneToNPCFlags) me->SetImmuneToNPC(true); Reset(); } } -bool npc_escortAI::IsPlayerOrGroupInRange() +bool EscortAI::IsPlayerOrGroupInRange() { if (Player* player = GetPlayerForEscort()) { if (Group* group = player->GetGroup()) { for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next()) + { if (Player* member = groupRef->GetSource()) if (me->IsWithinDistInMap(member, GetMaxPlayerDistance())) return true; + } } else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance())) return true; @@ -212,95 +184,94 @@ bool npc_escortAI::IsPlayerOrGroupInRange() return false; } -void npc_escortAI::UpdateAI(uint32 diff) +void EscortAI::UpdateAI(uint32 diff) { - //Waypoint Updating - if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->GetVictim() && m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_RETURNING)) + // Waypoint Updating + if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->IsEngaged() && !HasEscortState(STATE_ESCORT_RETURNING)) { - if (m_uiWPWaitTimer <= diff) + if (_pauseTimer <= diff) { - //End of the line - if (CurrentWP == WaypointList.end()) + if (!HasEscortState(STATE_ESCORT_PAUSED)) { - if (DespawnAtEnd) - { - TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints"); - - if (m_bCanReturnToStart) - { - float fRetX, fRetY, fRetZ; - me->GetRespawnPosition(fRetX, fRetY, fRetZ); - - me->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ); + _pauseTimer = 0; - m_uiWPWaitTimer = 0; - - TC_LOG_DEBUG("scripts", "EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ); - return; - } + if (_ended) + { + _ended = false; + me->GetMotionMaster()->MoveIdle(); - if (m_bCanInstantRespawn) + if (_despawnAtEnd) { - me->setDeathState(JUST_DIED); - me->Respawn(); + TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: reached end of waypoints, despawning at end"); + if (_returnToStart) + { + Position respawnPosition; + float orientation = 0.f; + me->GetRespawnPosition(respawnPosition.m_positionX, respawnPosition.m_positionY, respawnPosition.m_positionZ, &orientation); + respawnPosition.SetOrientation(orientation); + me->GetMotionMaster()->MovePoint(POINT_HOME, respawnPosition); + TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: returning to spawn location: %s", respawnPosition.ToString().c_str()); + } + else if (_instantRespawn) + me->Respawn(true); + else + me->DespawnOrUnsummon(); } - else - me->DespawnOrUnsummon(); - + TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: reached end of waypoints"); + RemoveEscortState(STATE_ESCORT_ESCORTING); return; } - else - { - TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints with Despawn off"); - return; + if (!_started) + { + _started = true; + me->GetMotionMaster()->MovePath(_path, false); + } + else if (_resume) + { + _resume = false; + if (MovementGenerator* movementGenerator = me->GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE)) + movementGenerator->Resume(0); } - } - - if (!HasEscortState(STATE_ESCORT_PAUSED)) - { - me->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z); - TC_LOG_DEBUG("scripts", "EscortAI start waypoint %u (%f, %f, %f).", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z); - - WaypointStart(CurrentWP->id); - - m_uiWPWaitTimer = 0; } } else - m_uiWPWaitTimer -= diff; + _pauseTimer -= diff; } - //Check if player or any member of his group is within range - if (HasEscortState(STATE_ESCORT_ESCORTING) && m_uiPlayerGUID && !me->GetVictim() && !HasEscortState(STATE_ESCORT_RETURNING)) + // Check if player or any member of his group is within range + if (_despawnAtFar && HasEscortState(STATE_ESCORT_ESCORTING) && _playerGUID && !me->GetVictim() && !HasEscortState(STATE_ESCORT_RETURNING)) { - if (m_uiPlayerCheckTimer <= diff) + if (_playerCheckTimer <= diff) { - if (DespawnAtFar && !IsPlayerOrGroupInRange()) + if (!IsPlayerOrGroupInRange()) { - TC_LOG_DEBUG("scripts", "EscortAI failed because player/group was to far away or not found"); + TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: failed because player/group was to far away or not found"); - if (m_bCanInstantRespawn) - { - me->setDeathState(JUST_DIED); - me->Respawn(); - } + bool isEscort = false; + if (CreatureData const* creatureData = me->GetCreatureData()) + isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (creatureData->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC)); + + if (_instantRespawn && !isEscort) + me->DespawnOrUnsummon(0, Seconds(1)); + else if (_instantRespawn && isEscort) + me->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, me->GetSpawnId(), true); else me->DespawnOrUnsummon(); return; } - m_uiPlayerCheckTimer = 1000; + _playerCheckTimer = 1000; } else - m_uiPlayerCheckTimer -= diff; + _playerCheckTimer -= diff; } UpdateEscortAI(diff); } -void npc_escortAI::UpdateEscortAI(uint32 /*diff*/) +void EscortAI::UpdateEscortAI(uint32 /*diff*/) { if (!UpdateVictim()) return; @@ -308,262 +279,197 @@ void npc_escortAI::UpdateEscortAI(uint32 /*diff*/) DoMeleeAttackIfReady(); } -void npc_escortAI::MovementInform(uint32 moveType, uint32 pointId) +void EscortAI::MovementInform(uint32 type, uint32 id) { - if (moveType != POINT_MOTION_TYPE || !HasEscortState(STATE_ESCORT_ESCORTING)) + // no action allowed if there is no escort + if (!HasEscortState(STATE_ESCORT_ESCORTING)) return; - //Combat start position reached, continue waypoint movement - if (pointId == POINT_LAST_POINT) + if (type == POINT_MOTION_TYPE) { - TC_LOG_DEBUG("scripts", "EscortAI has returned to original position before combat"); + if (!_pauseTimer) + _pauseTimer = 2000; - me->SetWalk(!m_bIsRunning); - RemoveEscortState(STATE_ESCORT_RETURNING); - - if (!m_uiWPWaitTimer) - m_uiWPWaitTimer = 1; + // continue waypoint movement + if (id == POINT_LAST_POINT) + { + TC_LOG_DEBUG("scripts", "EscortAI::MovementInform: returned to before combat position"); + me->SetWalk(!_running); + RemoveEscortState(STATE_ESCORT_RETURNING); + } + else if (id == POINT_HOME) + { + TC_LOG_DEBUG("scripts", "EscortAI::MovementInform: returned to home location and restarting waypoint path"); + _started = false; + } } - else if (pointId == POINT_HOME) + else if (type == WAYPOINT_MOTION_TYPE) { - TC_LOG_DEBUG("scripts", "EscortAI has returned to original home location and will continue from beginning of waypoint list."); + ASSERT(id < _path.nodes.size(), "EscortAI::MovementInform: referenced movement id (%u) points to non-existing node in loaded path", id); + WaypointNode waypoint = _path.nodes[id]; - CurrentWP = WaypointList.begin(); - m_uiWPWaitTimer = 1; - } - else if (CurrentWP != WaypointList.end()) - { - //Make sure that we are still on the right waypoint - if (CurrentWP->id != pointId) + TC_LOG_DEBUG("scripts", "EscortAI::MovementInform: waypoint node %u reached", waypoint.id); + + // last point + if (id == _path.nodes.size() - 1) { - TC_LOG_ERROR("misc", "TSCR ERROR: EscortAI reached waypoint out of order %u, expected %u, creature entry %u", pointId, CurrentWP->id, me->GetEntry()); - return; + _started = false; + _ended = true; + _pauseTimer = 1000; } - - TC_LOG_DEBUG("scripts", "EscortAI Waypoint %u reached", CurrentWP->id); - - //Call WP function - WaypointReached(CurrentWP->id); - - m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1; - - ++CurrentWP; } } +///@todo investigate whether if its necessary to handle anything on charm /* -void npc_escortAI::OnPossess(bool apply) +void EscortAI::OnCharmed(bool apply) { - // We got possessed in the middle of being escorted, store the point - // where we left off to come back to when possess is removed - if (HasEscortState(STATE_ESCORT_ESCORTING)) - { - if (apply) - me->GetPosition(LastPos.x, LastPos.y, LastPos.z); - else - { - Returning = true; - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->MovePoint(WP_LAST_POINT, LastPos.x, LastPos.y, LastPos.z); - } - } } */ -void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime) +void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, float orientation/* = 0*/, uint32 waitTime/* = 0*/) { - Escort_Waypoint t(id, x, y, z, waitTime); - - WaypointList.push_back(t); - - // i think SD2 no longer uses this function - ScriptWP = true; - /*PointMovement wp; - wp.m_uiCreatureEntry = me->GetEntry(); - wp.m_uiPointId = id; - wp.m_fX = x; - wp.m_fY = y; - wp.m_fZ = z; - wp.m_uiWaitTime = WaitTimeMs; - PointMovementMap[wp.m_uiCreatureEntry].push_back(wp);*/ + Trinity::NormalizeMapCoord(x); + Trinity::NormalizeMapCoord(y); + + WaypointNode waypoint; + waypoint.id = id; + waypoint.x = x; + waypoint.y = y; + waypoint.z = z; + waypoint.orientation = orientation; + waypoint.moveType = _running ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK; + waypoint.delay = waitTime; + waypoint.eventId = 0; + waypoint.eventChance = 100; + _path.nodes.push_back(std::move(waypoint)); + + _manualPath = true; } -void npc_escortAI::FillPointMovementListForCreature() +void EscortAI::FillPointMovementListForCreature() { - ScriptPointVector const* movePoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry()); - if (!movePoints) + WaypointPath const* path = sScriptSystemMgr->GetPath(me->GetEntry()); + if (!path) return; - for (ScriptPointVector::const_iterator itr = movePoints->begin(); itr != movePoints->end(); ++itr) + for (WaypointNode const& value : path->nodes) { - Escort_Waypoint point(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime); - WaypointList.push_back(point); + WaypointNode node = value; + Trinity::NormalizeMapCoord(node.x); + Trinity::NormalizeMapCoord(node.y); + node.moveType = _running ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK; + + _path.nodes.push_back(std::move(node)); } } -void npc_escortAI::SetRun(bool on) +void EscortAI::SetRun(bool on) { - if (on) - { - if (!m_bIsRunning) - me->SetWalk(false); - else - TC_LOG_DEBUG("scripts", "EscortAI attempt to set run mode, but is already running."); - } - else - { - if (m_bIsRunning) - me->SetWalk(true); - else - TC_LOG_DEBUG("scripts", "EscortAI attempt to set walk mode, but is already walking."); - } + if (on && !_running) + me->SetWalk(false); + else if (!on && _running) + me->SetWalk(true); - m_bIsRunning = on; + _running = on; } /// @todo get rid of this many variables passed in function. -void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */) +void EscortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */) { + // Queue respawn from the point it starts + if (Map* map = me->GetMap()) + { + if (CreatureData const* cdata = me->GetCreatureData()) + { + if (SpawnGroupTemplateData const* groupdata = cdata->spawnGroupData) + { + if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (groupdata->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC) && !map->GetCreatureRespawnTime(me->GetSpawnId())) + { + me->SetRespawnTime(me->GetRespawnDelay()); + me->SaveRespawnTime(); + } + } + } + } + if (me->GetVictim()) { - TC_LOG_ERROR("scripts.escortai", "TSCR ERROR: EscortAI (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry()); + TC_LOG_ERROR("scripts", "EscortAI::Start: (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry()); return; } if (HasEscortState(STATE_ESCORT_ESCORTING)) { - TC_LOG_ERROR("scripts.escortai", "EscortAI (script: %s, creature entry: %u) attempts to Start while already escorting", me->GetScriptName().c_str(), me->GetEntry()); + TC_LOG_ERROR("scripts", "EscortAI::Start: (script: %s, creature entry: %u) attempts to Start while already escorting", me->GetScriptName().c_str(), me->GetEntry()); return; } - if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does - { - if (!WaypointList.empty()) - WaypointList.clear(); + if (!_manualPath && resetWaypoints) FillPointMovementListForCreature(); - } - if (WaypointList.empty()) + if (_path.nodes.empty()) { - TC_LOG_ERROR("scripts", "EscortAI (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).", - me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0); + TC_LOG_ERROR("scripts", "EscortAI::Start: (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).", me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0); return; } - //set variables - m_bIsActiveAttacker = isActiveAttacker; - m_bIsRunning = run; - - m_uiPlayerGUID = playerGUID; - m_pQuestForEscort = quest; + // set variables + _activeAttacker = isActiveAttacker; + _running = run; + _playerGUID = playerGUID; + _escortQuest = quest; + _instantRespawn = instantRespawn; + _returnToStart = canLoopPath; - m_bCanInstantRespawn = instantRespawn; - m_bCanReturnToStart = canLoopPath; + if (_returnToStart && _instantRespawn) + TC_LOG_DEBUG("scripts", "EscortAI::Start: (script: %s, creature entry: %u) is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.", me->GetScriptName().c_str(), me->GetEntry()); - if (m_bCanReturnToStart && m_bCanInstantRespawn) - TC_LOG_DEBUG("scripts", "EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn."); + me->GetMotionMaster()->MoveIdle(); + me->GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE); - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) - { - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->MoveIdle(); - TC_LOG_DEBUG("scripts", "EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle."); - } - - //disable npcflags + // disable npcflags me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); if (me->IsImmuneToNPC()) { - HasImmuneToNPCFlags = true; + _hasImmuneToNPCFlags = true; me->SetImmuneToNPC(false); } - TC_LOG_DEBUG("scripts", "EscortAI started with " UI64FMTD " waypoints. ActiveAttacker = %d, Run = %d, %s", uint64(WaypointList.size()), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID.ToString().c_str()); + TC_LOG_DEBUG("scripts", "EscortAI::Start: (script: %s, creature entry: %u) started with %u waypoints. ActiveAttacker = %d, Run = %d, Player = %s", me->GetScriptName().c_str(), me->GetEntry(), uint32(_path.nodes.size()), _activeAttacker, _running, _playerGUID.ToString().c_str()); - CurrentWP = WaypointList.begin(); - - //Set initial speed - if (m_bIsRunning) - me->SetWalk(false); - else - me->SetWalk(true); + // set initial speed + me->SetWalk(!_running); + _started = false; AddEscortState(STATE_ESCORT_ESCORTING); } -void npc_escortAI::SetEscortPaused(bool on) +void EscortAI::SetEscortPaused(bool on) { if (!HasEscortState(STATE_ESCORT_ESCORTING)) return; if (on) - AddEscortState(STATE_ESCORT_PAUSED); - else - RemoveEscortState(STATE_ESCORT_PAUSED); -} - -bool npc_escortAI::SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation) -{ - me->UpdatePosition(x, y, z, orientation); - return SetNextWaypoint(pointId, false, true); -} - -bool npc_escortAI::SetNextWaypoint(uint32 pointId, bool setPosition, bool resetWaypointsOnFail) -{ - if (!WaypointList.empty()) - WaypointList.clear(); - - FillPointMovementListForCreature(); - - if (WaypointList.empty()) - return false; - - size_t const size = WaypointList.size(); - Escort_Waypoint waypoint(0, 0, 0, 0, 0); - do { - waypoint = WaypointList.front(); - WaypointList.pop_front(); - if (waypoint.id == pointId) - { - if (setPosition) - me->UpdatePosition(waypoint.x, waypoint.y, waypoint.z, me->GetOrientation()); - - CurrentWP = WaypointList.begin(); - return true; - } + AddEscortState(STATE_ESCORT_PAUSED); + if (MovementGenerator* movementGenerator = me->GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE)) + movementGenerator->Pause(0); } - while (!WaypointList.empty()); - - // we failed. - // we reset the waypoints in the start; if we pulled any, reset it again - if (resetWaypointsOnFail && size != WaypointList.size()) + else { - if (!WaypointList.empty()) - WaypointList.clear(); - - FillPointMovementListForCreature(); + RemoveEscortState(STATE_ESCORT_PAUSED); + _resume = true; } - - return false; } -bool npc_escortAI::GetWaypointPosition(uint32 pointId, float& x, float& y, float& z) +bool EscortAI::IsEscortNPC(bool onlyIfActive) const { - ScriptPointVector const* waypoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry()); - if (!waypoints) - return false; + if (!onlyIfActive) + return true; - for (ScriptPointVector::const_iterator itr = waypoints->begin(); itr != waypoints->end(); ++itr) - { - if (itr->uiPointId == pointId) - { - x = itr->fX; - y = itr->fY; - z = itr->fZ; - return true; - } - } + if (GetEventStarterGUID()) + return true; return false; } diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h index 754d96dced9..4a514af33cc 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h @@ -20,93 +20,60 @@ #define SC_ESCORTAI_H #include "ScriptedCreature.h" -#include "ScriptSystem.h" +#include "WaypointDefines.h" class Quest; #define DEFAULT_MAX_PLAYER_DISTANCE 50 -struct Escort_Waypoint +enum EscortState : uint32 { - Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w) - { - id = _id; - x = _x; - y = _y; - z = _z; - WaitTimeMs = _w; - } - - uint32 id; - float x; - float y; - float z; - uint32 WaitTimeMs; + STATE_ESCORT_NONE = 0x00, // nothing in progress + STATE_ESCORT_ESCORTING = 0x01, // escort is in progress + STATE_ESCORT_RETURNING = 0x02, // escort is returning after being in combat + STATE_ESCORT_PAUSED = 0x04 // escort is paused, wont continue with next waypoint }; -enum eEscortState -{ - STATE_ESCORT_NONE = 0x000, //nothing in progress - STATE_ESCORT_ESCORTING = 0x001, //escort are in progress - STATE_ESCORT_RETURNING = 0x002, //escort is returning after being in combat - STATE_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed -}; - -struct TC_GAME_API npc_escortAI : public ScriptedAI +struct TC_GAME_API EscortAI : public ScriptedAI { public: - explicit npc_escortAI(Creature* creature); - ~npc_escortAI() { } - - // CreatureAI functions - void AttackStart(Unit* who) override; + explicit EscortAI(Creature* creature); + ~EscortAI() { } + void UpdateAI(uint32 diff) override; // the "internal" update, calls UpdateEscortAI() void MoveInLineOfSight(Unit* who) override; - void JustDied(Unit*) override; - - void JustRespawned() override; - + void JustAppeared() override; void ReturnToLastPoint(); - void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override; - - void UpdateAI(uint32 diff) override; // the "internal" update, calls UpdateEscortAI() - virtual void UpdateEscortAI(uint32 diff); // used when it's needed to add code in update (abilities, scripted events, etc) - void MovementInform(uint32, uint32) override; - // EscortAI functions - void AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime = 0); // waitTime is in ms - - //this will set the current position to x/y/z/o, and the current WP to pointId. - bool SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation); + virtual void UpdateEscortAI(uint32 diff); // used when it's needed to add code in update (abilities, scripted events, etc) - //this will set the current position to WP start position (if setPosition == true), - //and the current WP to pointId - bool SetNextWaypoint(uint32 pointId, bool setPosition = true, bool resetWaypointsOnFail = true); - - bool GetWaypointPosition(uint32 pointId, float& x, float& y, float& z); - - virtual void WaypointReached(uint32 pointId) = 0; - virtual void WaypointStart(uint32 /*pointId*/) { } + void AddWaypoint(uint32 id, float x, float y, float z, float orientation = 0.f, uint32 waitTime = 0); // waitTime is in ms void Start(bool isActiveAttacker = true, bool run = false, ObjectGuid playerGUID = ObjectGuid::Empty, Quest const* quest = nullptr, bool instantRespawn = false, bool canLoopPath = false, bool resetWaypoints = true); void SetRun(bool on = true); + void SetEscortPaused(bool on); + void SetPauseTimer(uint32 Timer) { _pauseTimer = Timer; } + + bool HasEscortState(uint32 escortState) { return (_escortState & escortState) != 0; } + virtual bool IsEscorted() const override { return (_escortState & STATE_ESCORT_ESCORTING); } + + void SetMaxPlayerDistance(float newMax) { _maxPlayerDistance = newMax; } + float GetMaxPlayerDistance() const { return _maxPlayerDistance; } - bool HasEscortState(uint32 escortState) { return (m_uiEscortState & escortState) != 0; } - virtual bool IsEscorted() const override { return (m_uiEscortState & STATE_ESCORT_ESCORTING); } + void SetDespawnAtEnd(bool despawn) { _despawnAtEnd = despawn; } + void SetDespawnAtFar(bool despawn) { _despawnAtFar = despawn; } - void SetMaxPlayerDistance(float newMax) { MaxPlayerDistance = newMax; } - float GetMaxPlayerDistance() const { return MaxPlayerDistance; } + bool GetAttack() const { return _activeAttacker; } // used in EnterEvadeMode override + void SetCanAttack(bool attack) { _activeAttacker = attack; } - void SetDespawnAtEnd(bool despawn) { DespawnAtEnd = despawn; } - void SetDespawnAtFar(bool despawn) { DespawnAtFar = despawn; } - bool GetAttack() const { return m_bIsActiveAttacker; }//used in EnterEvadeMode override - void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; } - ObjectGuid GetEventStarterGUID() const { return m_uiPlayerGUID; } + ObjectGuid GetEventStarterGUID() const { return _playerGUID; } + + virtual bool IsEscortNPC(bool isEscorting) const override; protected: Player* GetPlayerForEscort(); @@ -116,27 +83,29 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI bool IsPlayerOrGroupInRange(); void FillPointMovementListForCreature(); - void AddEscortState(uint32 escortState) { m_uiEscortState |= escortState; } - void RemoveEscortState(uint32 escortState) { m_uiEscortState &= ~escortState; } - - ObjectGuid m_uiPlayerGUID; - uint32 m_uiWPWaitTimer; - uint32 m_uiPlayerCheckTimer; - uint32 m_uiEscortState; - float MaxPlayerDistance; - - Quest const* m_pQuestForEscort; //generally passed in Start() when regular escort script. - - std::list<Escort_Waypoint> WaypointList; - std::list<Escort_Waypoint>::iterator CurrentWP; - - bool m_bIsActiveAttacker; //obsolete, determined by faction. - bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK) - bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used) - bool m_bCanReturnToStart; //if creature can walk same path (loop) without despawn. Not for regular escort quests. - bool DespawnAtEnd; - bool DespawnAtFar; - bool ScriptWP; - bool HasImmuneToNPCFlags; + void AddEscortState(uint32 escortState) { _escortState |= escortState; } + void RemoveEscortState(uint32 escortState) { _escortState &= ~escortState; } + + ObjectGuid _playerGUID; + uint32 _pauseTimer; + uint32 _playerCheckTimer; + uint32 _escortState; + float _maxPlayerDistance; + + Quest const* _escortQuest; // generally passed in Start() when regular escort script. + + WaypointPath _path; + + bool _activeAttacker; // obsolete, determined by faction. + bool _running; // all creatures are walking by default (has flag MOVEMENTFLAG_WALK) + bool _instantRespawn; // if creature should respawn instantly after escort over (if not, database respawntime are used) + bool _returnToStart; // if creature can walk same path (loop) without despawn. Not for regular escort quests. + bool _despawnAtEnd; + bool _despawnAtFar; + bool _manualPath; + bool _hasImmuneToNPCFlags; + bool _started; + bool _ended; + bool _resume; }; #endif diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp index b0b332afecd..6f50cdaac10 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -145,7 +145,7 @@ void FollowerAI::JustDied(Unit* /*killer*/) } } -void FollowerAI::JustRespawned() +void FollowerAI::JustAppeared() { m_uiFollowState = STATE_FOLLOW_NONE; diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h index 7fe877a7589..1b13db8f7a3 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h @@ -41,8 +41,6 @@ class TC_GAME_API FollowerAI : public ScriptedAI explicit FollowerAI(Creature* creature); ~FollowerAI() { } - //virtual void WaypointReached(uint32 uiPointId) = 0; - void MovementInform(uint32 motionType, uint32 pointId) override; void AttackStart(Unit*) override; @@ -53,7 +51,7 @@ class TC_GAME_API FollowerAI : public ScriptedAI void JustDied(Unit*) override; - void JustRespawned() override; + void JustAppeared() override; void UpdateAI(uint32) override; //the "internal" update, calls UpdateFollowerAI() virtual void UpdateFollowerAI(uint32); //used when it's needed to add code in update (abilities, scripted events, etc) diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 5aa2550a704..24719902659 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -17,6 +17,7 @@ #include "SmartAI.h" #include "Creature.h" +#include "CreatureGroups.h" #include "DBCStructure.h" #include "GameObject.h" #include "Group.h" @@ -28,54 +29,12 @@ #include "ScriptMgr.h" #include "Vehicle.h" -SmartAI::SmartAI(Creature* c) : CreatureAI(c) +SmartAI::SmartAI(Creature* creature) : CreatureAI(creature), mIsCharmed(false), mFollowCreditType(0), mFollowArrivedTimer(0), mFollowCredit(0), mFollowArrivedEntry(0), mFollowDist(0.f), mFollowAngle(0.f), + _escortState(SMART_ESCORT_NONE), _escortNPCFlags(0), _escortInvokerCheckTimer(1000), _currentWaypointNode(0), _waypointReached(false), _waypointPauseTimer(0), _waypointPauseForced(false), _repeatWaypointPath(false), + _OOCReached(false), _waypointPathEnded(false), mRun(true), mEvadeDisabled(false), mCanAutoAttack(true), mCanCombatMove(true), mInvincibilityHpLevel(0), mDespawnTime(0), mDespawnState(0), mConditionsTimer(0), + _gossipReturn(false), mEscortQuestID(0) { - mIsCharmed = false; - // copy script to local (protection for table reload) - - mWayPoints = nullptr; - mEscortState = SMART_ESCORT_NONE; - mCurrentWPID = 0;//first wp id is 1 !! - mWPReached = false; - mWPPauseTimer = 0; - mEscortNPCFlags = 0; - mLastWP = nullptr; - - mCanRepeatPath = false; - - // Spawn in run mode - me->SetWalk(false); - mRun = false; - mEvadeDisabled = false; - - mLastOOCPos = me->GetPosition(); - - mCanAutoAttack = true; - mCanCombatMove = true; - - mForcedPaused = false; - mLastWPIDReached = 0; - - mEscortQuestID = 0; - - mDespawnTime = 0; - mDespawnState = 0; - - mEscortInvokerCheckTimer = 1000; - mFollowGuid.Clear(); - mFollowDist = 0; - mFollowAngle = 0; - mFollowCredit = 0; - mFollowArrivedEntry = 0; - mFollowCreditType = 0; - mFollowArrivedTimer = 0; - mInvincibilityHpLevel = 0; - - mJustReset = false; - mConditionsTimer = 0; - mHasConditions = sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, c->GetEntry()); - - _gossipReturn = false; + mHasConditions = sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, creature->GetEntry()); } bool SmartAI::IsAIControlled() const @@ -83,92 +42,68 @@ bool SmartAI::IsAIControlled() const return !mIsCharmed; } -void SmartAI::UpdateDespawn(uint32 diff) -{ - if (mDespawnState <= 1 || mDespawnState > 3) - return; - - if (mDespawnTime < diff) - { - if (mDespawnState == 2) - { - me->SetVisible(false); - mDespawnTime = 5000; - mDespawnState++; - } - else - me->DespawnOrUnsummon(); - } else mDespawnTime -= diff; -} - -WayPoint* SmartAI::GetNextWayPoint() -{ - if (!mWayPoints || mWayPoints->empty()) - return nullptr; - - mCurrentWPID++; - WPPath::const_iterator itr = mWayPoints->find(mCurrentWPID); - if (itr != mWayPoints->end()) - { - mLastWP = (*itr).second; - if (mLastWP->id != mCurrentWPID) - { - TC_LOG_ERROR("misc", "SmartAI::GetNextWayPoint: Got not expected waypoint id %u, expected %u", mLastWP->id, mCurrentWPID); - } - return (*itr).second; - } - return nullptr; -} - -void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* invoker) +void SmartAI::StartPath(bool run/* = false*/, uint32 pathId/* = 0*/, bool repeat/* = false*/, Unit* invoker/* = nullptr*/, uint32 nodeId/* = 1*/) { - if (me->IsInCombat())// no wp movement in combat + if (me->IsInCombat()) // no wp movement in combat { - TC_LOG_ERROR("misc", "SmartAI::StartPath: Creature entry %u wanted to start waypoint movement while in combat, ignoring.", me->GetEntry()); + TC_LOG_ERROR("misc", "SmartAI::StartPath: Creature entry %u wanted to start waypoint movement (%u) while in combat, ignoring.", me->GetEntry(), pathId); return; } if (HasEscortState(SMART_ESCORT_ESCORTING)) StopPath(); - if (path) + SetRun(run); + + if (pathId) { - if (!LoadPath(path)) + if (!LoadPath(pathId)) return; } - if (!mWayPoints || mWayPoints->empty()) + if (_path.nodes.empty()) return; - if (WayPoint* wp = GetNextWayPoint()) - { - AddEscortState(SMART_ESCORT_ESCORTING); - mCanRepeatPath = repeat; + _currentWaypointNode = nodeId; + _waypointPathEnded = false; - SetRun(run); + _repeatWaypointPath = repeat; - if (invoker && invoker->GetTypeId() == TYPEID_PLAYER) - { - mEscortNPCFlags = me->GetUInt32Value(UNIT_NPC_FLAGS); - me->SetUInt32Value(UNIT_NPC_FLAGS, 0); - } + // Do not use AddEscortState, removing everything from previous + _escortState = SMART_ESCORT_ESCORTING; - mLastOOCPos = me->GetPosition(); - me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z); - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, wp->id, GetScript()->GetPathId()); + if (invoker && invoker->GetTypeId() == TYPEID_PLAYER) + { + _escortNPCFlags = me->GetUInt32Value(UNIT_NPC_FLAGS); + me->SetFlag(UNIT_NPC_FLAGS, 0); } + + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, _currentWaypointNode, GetScript()->GetPathId()); + + me->GetMotionMaster()->MovePath(_path, _repeatWaypointPath); } bool SmartAI::LoadPath(uint32 entry) { if (HasEscortState(SMART_ESCORT_ESCORTING)) return false; - mWayPoints = sSmartWaypointMgr->GetPath(entry); - if (!mWayPoints) + + WaypointPath const* path = sSmartWaypointMgr->GetPath(entry); + if (!path || path->nodes.empty()) { GetScript()->SetPathId(0); return false; } + + _path.id = path->id; + _path.nodes = path->nodes; + for (WaypointNode& waypoint : _path.nodes) + { + Trinity::NormalizeMapCoord(waypoint.x); + Trinity::NormalizeMapCoord(waypoint.y); + waypoint.moveType = mRun ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK; + } + GetScript()->SetPathId(entry); return true; } @@ -176,65 +111,88 @@ bool SmartAI::LoadPath(uint32 entry) void SmartAI::PausePath(uint32 delay, bool forced) { if (!HasEscortState(SMART_ESCORT_ESCORTING)) + { + me->PauseMovement(delay, MOTION_SLOT_IDLE, forced); + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + { + std::pair<uint32, uint32> waypointInfo = me->GetCurrentWaypointInfo(); + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, waypointInfo.first, waypointInfo.second); + } return; + } + if (HasEscortState(SMART_ESCORT_PAUSED)) { - TC_LOG_ERROR("misc", "SmartAI::PausePath: Creature entry %u wanted to pause waypoint movement while already paused, ignoring.", me->GetEntry()); + TC_LOG_ERROR("misc", "SmartAI::PausePath: Creature entry %u wanted to pause waypoint (current waypoint: %u) movement while already paused, ignoring.", me->GetEntry(), _currentWaypointNode); return; } - mForcedPaused = forced; - mLastOOCPos = me->GetPosition(); - AddEscortState(SMART_ESCORT_PAUSED); - mWPPauseTimer = delay; + + _waypointPauseTimer = delay; + if (forced) { + _waypointPauseForced = forced; SetRun(mRun); - me->StopMoving();//force stop - me->GetMotionMaster()->MoveIdle();//force stop + me->PauseMovement(); + me->SetHomePosition(me->GetPosition()); } - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, mLastWP->id, GetScript()->GetPathId()); + else + _waypointReached = false; + + AddEscortState(SMART_ESCORT_PAUSED); + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, _currentWaypointNode, GetScript()->GetPathId()); } void SmartAI::StopPath(uint32 DespawnTime, uint32 quest, bool fail) { if (!HasEscortState(SMART_ESCORT_ESCORTING)) + { + std::pair<uint32, uint32> waypointInfo = { 0, 0 }; + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + waypointInfo = me->GetCurrentWaypointInfo(); + + if (mDespawnState != 2) + SetDespawnTime(DespawnTime); + + me->GetMotionMaster()->MoveIdle(); + + if (waypointInfo.first) + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, waypointInfo.first, waypointInfo.second); + + if (!fail) + { + if (waypointInfo.first) + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, waypointInfo.first, waypointInfo.second); + if (mDespawnState == 1) + StartDespawn(); + } return; + } if (quest) mEscortQuestID = quest; - SetDespawnTime(DespawnTime); - //mDespawnTime = DespawnTime; - mLastOOCPos = me->GetPosition(); - me->StopMoving();//force stop + if (mDespawnState != 2) + SetDespawnTime(DespawnTime); + me->GetMotionMaster()->MoveIdle(); - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, mLastWP->id, GetScript()->GetPathId()); + + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, _currentWaypointNode, GetScript()->GetPathId()); + EndPath(fail); } void SmartAI::EndPath(bool fail) { - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, mLastWP->id, GetScript()->GetPathId()); - RemoveEscortState(SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING); - mWayPoints = nullptr; - mCurrentWPID = 0; - mWPPauseTimer = 0; - mLastWP = nullptr; - - if (mEscortNPCFlags) - { - me->SetUInt32Value(UNIT_NPC_FLAGS, mEscortNPCFlags); - mEscortNPCFlags = 0; - } + _path.nodes.clear(); + _waypointPauseTimer = 0; - if (mCanRepeatPath) + if (_escortNPCFlags) { - if (IsAIControlled()) - StartPath(mRun, GetScript()->GetPathId(), true); + me->SetFlag(UNIT_NPC_FLAGS, _escortNPCFlags); + _escortNPCFlags = 0; } - else - GetScript()->SetPathId(0); ObjectVector const* targets = GetScript()->GetStoredTargetVector(SMART_ESCORT_TARGETS, *me); if (targets && mEscortQuestID) @@ -279,126 +237,57 @@ void SmartAI::EndPath(bool fail) } } + // End Path events should be only processed if it was SUCCESSFUL stop or stop called by SMART_ACTION_WAYPOINT_STOP + if (fail) + return; + + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, _currentWaypointNode, GetScript()->GetPathId()); + + if (_repeatWaypointPath) + { + if (IsAIControlled()) + StartPath(mRun, GetScript()->GetPathId(), _repeatWaypointPath); + } + else + GetScript()->SetPathId(0); + if (mDespawnState == 1) StartDespawn(); } void SmartAI::ResumePath() { - SetRun(mRun); - if (mLastWP) - me->GetMotionMaster()->MovePoint(mLastWP->id, mLastWP->x, mLastWP->y, mLastWP->z); -} + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_RESUMED, nullptr, _currentWaypointNode, GetScript()->GetPathId()); -void SmartAI::ReturnToLastOOCPos() -{ - if (!IsAIControlled()) - return; + RemoveEscortState(SMART_ESCORT_PAUSED); + + _waypointPauseForced = false; + _waypointReached = false; + _waypointPauseTimer = 0; SetRun(mRun); - me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, mLastOOCPos); + me->ResumeMovement(); } -void SmartAI::UpdatePath(const uint32 diff) +void SmartAI::ReturnToLastOOCPos() { - if (!HasEscortState(SMART_ESCORT_ESCORTING)) - return; - if (mEscortInvokerCheckTimer < diff) - { - // Escort failed, no players in range - if (!IsEscortInvokerInRange()) - { - StopPath(0, mEscortQuestID, true); - - // allow to properly hook out of range despawn action, which in most cases should perform the same operation as dying - GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, me); - me->DespawnOrUnsummon(1); - return; - } - mEscortInvokerCheckTimer = 1000; - } - else - mEscortInvokerCheckTimer -= diff; - - // handle pause - if (HasEscortState(SMART_ESCORT_PAUSED)) - { - if (mWPPauseTimer < diff) - { - if (!me->IsInCombat() && !HasEscortState(SMART_ESCORT_RETURNING) && (mWPReached || mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT || mForcedPaused)) - { - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_RESUMED, nullptr, mLastWP->id, GetScript()->GetPathId()); - RemoveEscortState(SMART_ESCORT_PAUSED); - if (mForcedPaused)// if paused between 2 wps resend movement - { - ResumePath(); - mWPReached = false; - mForcedPaused = false; - } - if (mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT) - mWPReached = true; - } - - mWPPauseTimer = 0; - } - else - mWPPauseTimer -= diff; - } - - if (HasEscortState(SMART_ESCORT_RETURNING)) - { - if (mWPReached)//reached OOC WP - { - RemoveEscortState(SMART_ESCORT_RETURNING); - if (!HasEscortState(SMART_ESCORT_PAUSED)) - ResumePath(); - mWPReached = false; - } - } - - if ((!me->HasReactState(REACT_PASSIVE) && me->IsInCombat()) || HasEscortState(SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING)) + if (!IsAIControlled()) return; - // handle next wp - if (mWPReached)//reached WP - { - mWPReached = false; - if (mCurrentWPID == GetWPCount()) - { - EndPath(); - } - else if (WayPoint* wp = GetNextWayPoint()) - { - SetRun(mRun); - me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z); - } - } + me->SetWalk(false); + me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, me->GetHomePosition()); } void SmartAI::UpdateAI(uint32 diff) { CheckConditions(diff); + GetScript()->OnUpdate(diff); + UpdatePath(diff); + UpdateFollow(diff); UpdateDespawn(diff); - /// @todo move to void - if (mFollowGuid) - { - if (mFollowArrivedTimer < diff) - { - if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, true)) - { - StopFollow(true); - return; - } - - mFollowArrivedTimer = 1000; - } - else - mFollowArrivedTimer -= diff; - } - if (!IsAIControlled()) return; @@ -450,24 +339,70 @@ bool SmartAI::IsEscortInvokerInRange() return true; } -void SmartAI::MovepointReached(uint32 id) +///@todo move escort related logic +void SmartAI::WaypointPathStarted(uint32 nodeId, uint32 pathId) +{ + if (!HasEscortState(SMART_ESCORT_ESCORTING)) + { + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, nodeId, pathId); + return; + } +} + +///@todo Implement new smart event SMART_EVENT_WAYPOINT_STARTED +void SmartAI::WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) +{ +} + +void SmartAI::WaypointReached(uint32 nodeId, uint32 pathId) { - if (id != SMART_ESCORT_LAST_OOC_POINT && mLastWPIDReached != id) - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, id); + if (!HasEscortState(SMART_ESCORT_ESCORTING)) + { + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, nodeId, pathId); + return; + } + + _currentWaypointNode = nodeId; + + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, _currentWaypointNode, pathId); + + if (_waypointPauseTimer && !_waypointPauseForced) + { + _waypointReached = true; + me->PauseMovement(); + me->SetHomePosition(me->GetPosition()); + } + else if (HasEscortState(SMART_ESCORT_ESCORTING) && me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + { + if (_currentWaypointNode == _path.nodes.size()) + _waypointPathEnded = true; + else + SetRun(mRun); + } +} - mLastWPIDReached = id; - mWPReached = true; +///@todo move escort related logic +void SmartAI::WaypointPathEnded(uint32 nodeId, uint32 pathId) +{ + if (!HasEscortState(SMART_ESCORT_ESCORTING)) + { + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, nodeId, pathId); + return; + } } -void SmartAI::MovementInform(uint32 MovementType, uint32 Data) +void SmartAI::MovementInform(uint32 type, uint32 id) { - if ((MovementType == POINT_MOTION_TYPE && Data == SMART_ESCORT_LAST_OOC_POINT) || MovementType == FOLLOW_MOTION_TYPE) + if (type == POINT_MOTION_TYPE && id == SMART_ESCORT_LAST_OOC_POINT) me->ClearUnitState(UNIT_STATE_EVADE); - GetScript()->ProcessEventsFor(SMART_EVENT_MOVEMENTINFORM, nullptr, MovementType, Data); - if (MovementType != POINT_MOTION_TYPE || !HasEscortState(SMART_ESCORT_ESCORTING)) + GetScript()->ProcessEventsFor(SMART_EVENT_MOVEMENTINFORM, nullptr, type, id); + + if (!HasEscortState(SMART_ESCORT_ESCORTING)) return; - MovepointReached(Data); + + if (type == POINT_MOTION_TYPE && id == SMART_ESCORT_LAST_OOC_POINT) + _OOCReached = true; } void SmartAI::EnterEvadeMode(EvadeReason /*why*/) @@ -489,10 +424,16 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/) me->AddUnitState(UNIT_STATE_EVADE); - GetScript()->ProcessEventsFor(SMART_EVENT_EVADE);//must be after aura clear so we can cast spells from db + GetScript()->ProcessEventsFor(SMART_EVENT_EVADE); // must be after _EnterEvadeMode (spells, auras, ...) SetRun(mRun); - if (HasEscortState(SMART_ESCORT_ESCORTING)) + + if (Unit* owner = me->GetCharmerOrOwner()) + { + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + me->ClearUnitState(UNIT_STATE_EVADE); + } + else if (HasEscortState(SMART_ESCORT_ESCORTING)) { AddEscortState(SMART_ESCORT_RETURNING); ReturnToLastOOCPos(); @@ -503,16 +444,11 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/) // evade is not cleared in MoveFollow, so we can't keep it me->ClearUnitState(UNIT_STATE_EVADE); } - else if (Unit* owner = me->GetCharmerOrOwner()) - { - me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); - me->ClearUnitState(UNIT_STATE_EVADE); - } else me->GetMotionMaster()->MoveTargetedHome(); - if (!HasEscortState(SMART_ESCORT_ESCORTING)) //dont mess up escort movement after combat - SetRun(mRun); + if (!me->HasUnitState(UNIT_STATE_EVADE)) + GetScript()->OnReset(); } void SmartAI::MoveInLineOfSight(Unit* who) @@ -525,7 +461,7 @@ void SmartAI::MoveInLineOfSight(Unit* who) if (!IsAIControlled()) return; - if (AssistPlayerInCombatAgainst(who)) + if (HasEscortState(SMART_ESCORT_ESCORTING) && AssistPlayerInCombatAgainst(who)) return; CreatureAI::MoveInLineOfSight(who); @@ -544,19 +480,32 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who) if (!who || !who->GetVictim()) return false; - //experimental (unknown) flag not present + // experimental (unknown) flag not present if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; - //not a player + // not a player if (!who->EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()) return false; - //never attack friendly + if (!who->isInAccessiblePlaceFor(me)) + return false; + + if (!CanAIAttack(who)) + return false; + + // we cannot attack in evade mode + if (me->IsInEvadeMode()) + return false; + + // or if enemy is in evade mode + if (who->GetTypeId() == TYPEID_UNIT && who->ToCreature()->IsInEvadeMode()) + return false; + if (!me->IsValidAssistTarget(who->GetVictim())) return false; - //too far away and no free sight? + // too far away and no free sight if (me->IsWithinDistInMap(who, SMART_MAX_AID_DIST) && me->IsWithinLOSInMap(who)) { me->EngageWithTarget(who); @@ -566,18 +515,21 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who) return false; } -void SmartAI::JustRespawned() +void SmartAI::JustAppeared() { mDespawnTime = 0; mDespawnState = 0; - mEscortState = SMART_ESCORT_NONE; + _escortState = SMART_ESCORT_NONE; + me->SetVisible(true); + if (me->GetFaction() != me->GetCreatureTemplate()->faction) me->RestoreFaction(); - mJustReset = true; - JustReachedHome(); + + GetScript()->OnReset(); GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN); - mFollowGuid.Clear();//do not reset follower on Reset(), we need it after combat evade + + mFollowGuid.Clear(); // do not reset follower on Reset(), we need it after combat evade mFollowDist = 0; mFollowAngle = 0; mFollowCredit = 0; @@ -589,16 +541,18 @@ void SmartAI::JustRespawned() void SmartAI::JustReachedHome() { GetScript()->OnReset(); + GetScript()->ProcessEventsFor(SMART_EVENT_REACHED_HOME); - if (!mJustReset) + CreatureGroup* formation = me->GetFormation(); + if (!formation || formation->getLeader() == me || !formation->isFormed()) { - GetScript()->ProcessEventsFor(SMART_EVENT_REACHED_HOME); - - if (!UpdateVictim() && me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE && me->GetWaypointPath()) + if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_IDLE) != WAYPOINT_MOTION_TYPE && me->GetWaypointPath()) me->GetMotionMaster()->MovePath(me->GetWaypointPath(), true); + else + me->ResumeMovement(); } - - mJustReset = false; + else if (formation->isFormed()) + me->GetMotionMaster()->MoveIdle(); // wait the order of leader } void SmartAI::EnterCombat(Unit* enemy) @@ -607,24 +561,14 @@ void SmartAI::EnterCombat(Unit* enemy) me->InterruptNonMeleeSpells(false); // must be before ProcessEvents GetScript()->ProcessEventsFor(SMART_EVENT_AGGRO, enemy); - - if (!IsAIControlled()) - return; - mLastOOCPos = me->GetPosition(); - SetRun(mRun); - if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == POINT_MOTION_TYPE) - me->GetMotionMaster()->MovementExpired(); } void SmartAI::JustDied(Unit* killer) { - GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer); if (HasEscortState(SMART_ESCORT_ESCORTING)) - { EndPath(true); - me->StopMoving();//force stop - me->GetMotionMaster()->MoveIdle(); - } + + GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer); } void SmartAI::KilledUnit(Unit* victim) @@ -642,15 +586,21 @@ void SmartAI::AttackStart(Unit* who) // dont allow charmed npcs to act on their own if (!IsAIControlled()) { - if (who && mCanAutoAttack) - me->Attack(who, true); + if (who) + me->Attack(who, mCanAutoAttack); return; } - if (who && me->Attack(who, me->IsWithinMeleeRange(who))) + if (who && me->Attack(who, mCanAutoAttack)) { + me->GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE); + me->PauseMovement(); + if (mCanCombatMove) + { + SetRun(mRun); me->GetMotionMaster()->MoveChase(who); + } } } @@ -713,12 +663,9 @@ void SmartAI::PassengerBoarded(Unit* who, int8 seatId, bool apply) void SmartAI::InitializeAI() { GetScript()->OnInitialize(me); + if (!me->isDead()) - { - mJustReset = true; - JustReachedHome(); - GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN); - } + GetScript()->OnReset(); } void SmartAI::OnCharmed(bool apply) @@ -727,13 +674,13 @@ void SmartAI::OnCharmed(bool apply) { if (HasEscortState(SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING)) EndPath(true); - me->StopMoving(); } + mIsCharmed = apply; if (!apply && !me->IsInEvadeMode()) { - if (mCanRepeatPath) + if (_repeatWaypointPath) StartPath(mRun, GetScript()->GetPathId(), true); else me->SetWalk(!mRun); @@ -826,30 +773,21 @@ void SmartAI::SetCombatMove(bool on) { if (mCanCombatMove == on) return; + mCanCombatMove = on; + if (!IsAIControlled()) return; - if (!HasEscortState(SMART_ESCORT_ESCORTING)) + + if (me->IsEngaged()) { - if (on && me->GetVictim()) + if (on && !me->HasReactState(REACT_PASSIVE) && me->GetVictim() && me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == MAX_MOTION_TYPE) { - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) - { - SetRun(mRun); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - me->CastStop(); - } - } - else - { - if (me->HasUnitState(UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE)) - return; - - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->Clear(true); - me->StopMoving(); - me->GetMotionMaster()->MoveIdle(); + SetRun(mRun); + me->GetMotionMaster()->MoveChase(me->GetVictim()); } + else if (!on && me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == CHASE_MOTION_TYPE) + me->GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE); } } @@ -881,7 +819,6 @@ void SmartAI::StopFollow(bool complete) mFollowArrivedTimer = 1000; mFollowArrivedEntry = 0; mFollowCreditType = 0; - me->StopMoving(); me->GetMotionMaster()->MoveIdle(); if (!complete) @@ -949,6 +886,96 @@ void SmartAI::CheckConditions(uint32 diff) mConditionsTimer -= diff; } +void SmartAI::UpdatePath(uint32 diff) +{ + if (!HasEscortState(SMART_ESCORT_ESCORTING)) + return; + + if (_escortInvokerCheckTimer < diff) + { + if (!IsEscortInvokerInRange()) + { + StopPath(0, mEscortQuestID, true); + + // allow to properly hook out of range despawn action, which in most cases should perform the same operation as dying + GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, me); + me->DespawnOrUnsummon(); + return; + } + _escortInvokerCheckTimer = 1000; + } + else + _escortInvokerCheckTimer -= diff; + + // handle pause + if (HasEscortState(SMART_ESCORT_PAUSED) && (_waypointReached || _waypointPauseForced)) + { + if (_waypointPauseTimer <= diff) + { + if (!me->IsInCombat() && !HasEscortState(SMART_ESCORT_RETURNING)) + ResumePath(); + } + else + _waypointPauseTimer -= diff; + } + else if (_waypointPathEnded) // end path + { + _waypointPathEnded = false; + StopPath(); + return; + } + + if (HasEscortState(SMART_ESCORT_RETURNING)) + { + if (_OOCReached) // reached OOC WP + { + _OOCReached = false; + RemoveEscortState(SMART_ESCORT_RETURNING); + if (!HasEscortState(SMART_ESCORT_PAUSED)) + ResumePath(); + } + } +} + +void SmartAI::UpdateFollow(uint32 diff) +{ + if (mFollowGuid) + { + if (mFollowArrivedTimer < diff) + { + if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, true)) + { + StopFollow(true); + return; + } + + mFollowArrivedTimer = 1000; + } + else + mFollowArrivedTimer -= diff; + } +} + +void SmartAI::UpdateDespawn(uint32 diff) +{ + if (mDespawnState <= 1 || mDespawnState > 3) + return; + + if (mDespawnTime < diff) + { + if (mDespawnState == 2) + { + me->SetVisible(false); + mDespawnTime = 5000; + mDespawnState++; + } + else + me->DespawnOrUnsummon(); + } + else + mDespawnTime -= diff; +} + void SmartGameObjectAI::UpdateAI(uint32 diff) { GetScript()->OnUpdate(diff); @@ -957,10 +984,6 @@ void SmartGameObjectAI::UpdateAI(uint32 diff) void SmartGameObjectAI::InitializeAI() { GetScript()->OnInitialize(me); - - // do not call respawn event if go is not spawned - if (me->isSpawned()) - GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN); //Reset(); } diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index b5ed74c4edb..ecf9aaeb97a 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -23,8 +23,7 @@ #include "GameObjectAI.h" #include "Position.h" #include "SmartScript.h" - -struct WayPoint; +#include "WaypointDefines.h" enum SmartEscortState { @@ -43,35 +42,42 @@ enum SmartEscortVars class TC_GAME_API SmartAI : public CreatureAI { public: - ~SmartAI(){ } + ~SmartAI() { } explicit SmartAI(Creature* c); + // core related + static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; } + // Check whether we are currently permitted to make the creature take action bool IsAIControlled() const; // Start moving to the desired MovePoint - void StartPath(bool run = false, uint32 path = 0, bool repeat = false, Unit* invoker = nullptr); + void StartPath(bool run = false, uint32 pathId = 0, bool repeat = false, Unit* invoker = nullptr, uint32 nodeId = 1); bool LoadPath(uint32 entry); void PausePath(uint32 delay, bool forced = false); void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false); void EndPath(bool fail = false); void ResumePath(); - WayPoint* GetNextWayPoint(); - bool HasEscortState(uint32 uiEscortState) const { return (mEscortState & uiEscortState) != 0; } - void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; } - void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; } + bool HasEscortState(uint32 uiEscortState) const { return (_escortState & uiEscortState) != 0; } + void AddEscortState(uint32 uiEscortState) { _escortState |= uiEscortState; } + void RemoveEscortState(uint32 uiEscortState) { _escortState &= ~uiEscortState; } void SetAutoAttack(bool on) { mCanAutoAttack = on; } void SetCombatMove(bool on); bool CanCombatMove() { return mCanCombatMove; } void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0); void StopFollow(bool complete); + bool IsEscortInvokerInRange(); + + void WaypointPathStarted(uint32 nodeId, uint32 pathId) override; + void WaypointStarted(uint32 nodeId, uint32 pathId) override; + void WaypointReached(uint32 nodeId, uint32 pathId) override; + void WaypointPathEnded(uint32 nodeId, uint32 pathId) override; void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker); SmartScript* GetScript() { return &mScript; } - bool IsEscortInvokerInRange(); // Called when creature is spawned or respawned - void JustRespawned() override; + void JustAppeared() override; // Called at reaching home after evade, InitializeAI(), EnterEvadeMode() for resetting variables void JustReachedHome() override; @@ -157,12 +163,6 @@ class TC_GAME_API SmartAI : public CreatureAI // Used in scripts to share variables ObjectGuid GetGUID(int32 id = 0) const override; - //core related - static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; } - - // Called at movepoint reached - void MovepointReached(uint32 id); - // Makes the creature run/walk void SetRun(bool run = true); @@ -183,8 +183,6 @@ class TC_GAME_API SmartAI : public CreatureAI void QuestReward(Player* player, Quest const* quest, uint32 opt) override; void OnGameEvent(bool start, uint16 eventId) override; - uint32 mEscortQuestID; - void SetDespawnTime (uint32 t) { mDespawnTime = t; @@ -194,11 +192,22 @@ class TC_GAME_API SmartAI : public CreatureAI void OnSpellClick(Unit* clicker, bool& result) override; - void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; } + void SetWPPauseTimer(uint32 time) { _waypointPauseTimer = time; } void SetGossipReturn(bool val) { _gossipReturn = val; } + void SetEscortQuest(uint32 questID) { mEscortQuestID = questID; } + private: + bool AssistPlayerInCombatAgainst(Unit* who); + void ReturnToLastOOCPos(); + void CheckConditions(uint32 diff); + void UpdatePath(uint32 diff); + void UpdateFollow(uint32 diff); + void UpdateDespawn(uint32 diff); + + SmartScript mScript; + bool mIsCharmed; uint32 mFollowCreditType; uint32 mFollowArrivedTimer; @@ -208,41 +217,35 @@ class TC_GAME_API SmartAI : public CreatureAI float mFollowDist; float mFollowAngle; - void ReturnToLastOOCPos(); - void UpdatePath(const uint32 diff); - SmartScript mScript; - WPPath* mWayPoints; - uint32 mEscortState; - uint32 mCurrentWPID; - uint32 mLastWPIDReached; - bool mWPReached; - uint32 mWPPauseTimer; - uint32 mEscortNPCFlags; - WayPoint* mLastWP; - Position mLastOOCPos;//set on enter combat - uint32 GetWPCount() const { return mWayPoints ? uint32(mWayPoints->size()) : 0; } - bool mCanRepeatPath; + uint32 _escortState; + uint32 _escortNPCFlags; + uint32 _escortInvokerCheckTimer; + WaypointPath _path; + uint32 _currentWaypointNode; + bool _waypointReached; + uint32 _waypointPauseTimer; + bool _waypointPauseForced; + bool _repeatWaypointPath; + bool _OOCReached; + bool _waypointPathEnded; + bool mRun; bool mEvadeDisabled; bool mCanAutoAttack; bool mCanCombatMove; - bool mForcedPaused; uint32 mInvincibilityHpLevel; - bool AssistPlayerInCombatAgainst(Unit* who); uint32 mDespawnTime; uint32 mDespawnState; - void UpdateDespawn(uint32 diff); - uint32 mEscortInvokerCheckTimer; - bool mJustReset; // Vehicle conditions - void CheckConditions(uint32 diff); bool mHasConditions; uint32 mConditionsTimer; // Gossip bool _gossipReturn; + + uint32 mEscortQuestID; }; class TC_GAME_API SmartGameObjectAI : public GameObjectAI diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 7b7bb00416d..30fa5deadc3 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -39,6 +39,7 @@ #include "SpellMgr.h" #include "TemporarySummon.h" #include "Vehicle.h" +#include "WaypointDefines.h" #include <G3D/Quat.h> SmartScript::SmartScript() @@ -309,7 +310,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { if (IsUnit(target)) { - target->PlayDirectSound(e.action.sound.sound, e.action.sound.onlySelf ? target->ToPlayer() : nullptr); + if (e.action.sound.distance == 1) + target->PlayDistanceSound(e.action.sound.sound, e.action.sound.onlySelf ? target->ToPlayer() : nullptr); + else + target->PlayDirectSound(e.action.sound.sound, e.action.sound.onlySelf ? target->ToPlayer() : nullptr); + TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_SOUND: target: %s (GuidLow: %u), sound: %u, onlyself: %u", target->GetName().c_str(), target->GetGUID().GetCounter(), e.action.sound.sound, e.action.sound.onlySelf); } @@ -936,7 +941,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u case 1: instance->SetBossState(e.action.setInstanceData.field, static_cast<EncounterState>(e.action.setInstanceData.data)); TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: SetBossState BossId: %u, State: %u (%s)", - e.action.setInstanceData.field, e.action.setInstanceData.data, InstanceScript::GetBossStateName(e.action.setInstanceData.data).c_str()); + e.action.setInstanceData.field, e.action.setInstanceData.data, InstanceScript::GetBossStateName(e.action.setInstanceData.data)); break; default: // Static analysis break; @@ -1039,8 +1044,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u else creature->DespawnOrUnsummon(respawnDelay); } - else if (GameObject* go = target->ToGameObject()) - go->SetRespawnTime(respawnDelay); + else if (GameObject* goTarget = target->ToGameObject()) + goTarget->SetRespawnTime(respawnDelay); } break; } @@ -1331,7 +1336,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u uint32 quest = e.action.wpStart.quest; uint32 DespawnTime = e.action.wpStart.despawnTime; - ENSURE_AI(SmartAI, me->AI())->mEscortQuestID = quest; + ENSURE_AI(SmartAI, me->AI())->SetEscortQuest(quest); ENSURE_AI(SmartAI, me->AI())->SetDespawnTime(DespawnTime); break; } @@ -1588,10 +1593,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (IsSmart(creature)) ENSURE_AI(SmartAI, creature->AI())->SetScript9(e, e.action.timedActionList.id, GetLastInvoker()); } - else if (GameObject* go = target->ToGameObject()) + else if (GameObject* goTarget = target->ToGameObject()) { - if (IsSmartGO(go)) - ENSURE_AI(SmartGameObjectAI, go->AI())->SetScript9(e, e.action.timedActionList.id, GetLastInvoker()); + if (IsSmartGO(goTarget)) + ENSURE_AI(SmartGameObjectAI, goTarget->AI())->SetScript9(e, e.action.timedActionList.id, GetLastInvoker()); } } break; @@ -1675,10 +1680,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (IsSmart(creature)) ENSURE_AI(SmartAI, creature->AI())->SetScript9(e, id, GetLastInvoker()); } - else if (GameObject* go = target->ToGameObject()) + else if (GameObject* goTarget = target->ToGameObject()) { - if (IsSmartGO(go)) - ENSURE_AI(SmartGameObjectAI, go->AI())->SetScript9(e, id, GetLastInvoker()); + if (IsSmartGO(goTarget)) + ENSURE_AI(SmartGameObjectAI, goTarget->AI())->SetScript9(e, id, GetLastInvoker()); } } break; @@ -1699,10 +1704,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (IsSmart(creature)) ENSURE_AI(SmartAI, creature->AI())->SetScript9(e, id, GetLastInvoker()); } - else if (GameObject* go = target->ToGameObject()) + else if (GameObject* goTarget = target->ToGameObject()) { - if (IsSmartGO(go)) - ENSURE_AI(SmartGameObjectAI, go->AI())->SetScript9(e, id, GetLastInvoker()); + if (IsSmartGO(goTarget)) + ENSURE_AI(SmartGameObjectAI, goTarget->AI())->SetScript9(e, id, GetLastInvoker()); } } break; @@ -1899,7 +1904,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { for (WorldObject* target : targets) if (IsCreature(target)) - target->ToCreature()->setRegeneratingHealth(e.action.setHealthRegen.regenHealth != 0); + target->ToCreature()->SetRegenerateHealth(e.action.setHealthRegen.regenHealth != 0); break; } case SMART_ACTION_SET_ROOT: @@ -1990,7 +1995,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u std::back_inserter(waypoints), [](uint32 wp) { return wp != 0; }); float distanceToClosest = std::numeric_limits<float>::max(); - WayPoint* closestWp = nullptr; + std::pair<uint32, uint32> closest = { 0, 0 }; for (WorldObject* target : targets) { @@ -1998,29 +2003,27 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { if (IsSmart(creature)) { - for (uint32 wp : waypoints) + for (uint32 pathId : waypoints) { - WPPath* path = sSmartWaypointMgr->GetPath(wp); - if (!path || path->empty()) + WaypointPath const* path = sSmartWaypointMgr->GetPath(pathId); + if (!path || path->nodes.empty()) continue; - auto itrWp = path->find(0); - if (itrWp != path->end()) + for (auto itr = path->nodes.begin(); itr != path->nodes.end(); ++itr) { - if (WayPoint* wp = itrWp->second) + WaypointNode const waypoint = *itr; + float distamceToThisNode = creature->GetDistance(waypoint.x, waypoint.y, waypoint.z); + if (distamceToThisNode < distanceToClosest) { - float distToThisPath = creature->GetDistance(wp->x, wp->y, wp->z); - if (distToThisPath < distanceToClosest) - { - distanceToClosest = distToThisPath; - closestWp = wp; - } + distanceToClosest = distamceToThisNode; + closest.first = pathId; + closest.second = waypoint.id; } } } - if (closestWp) - CAST_AI(SmartAI, creature->AI())->StartPath(false, closestWp->id, true); + if (closest.first != 0) + ENSURE_AI(SmartAI, creature->AI())->StartPath(false, closest.first, true, nullptr, closest.second); } } } @@ -2038,7 +2041,12 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (IsUnit(target)) { uint32 sound = Trinity::Containers::SelectRandomContainerElement(sounds); - target->PlayDirectSound(sound, onlySelf ? target->ToPlayer() : nullptr); + + if (e.action.randomSound.distance == 1) + target->PlayDistanceSound(sound, onlySelf ? target->ToPlayer() : nullptr); + else + target->PlayDirectSound(sound, onlySelf ? target->ToPlayer() : nullptr); + TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_RANDOM_SOUND: target: %s (%s), sound: %u, onlyself: %s", target->GetName().c_str(), target->GetGUID().ToString().c_str(), sound, onlySelf ? "true" : "false"); } @@ -2052,6 +2060,92 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (IsCreature(target)) target->ToCreature()->SetCorpseDelay(e.action.corpseDelay.timer); } + + break; + } + case SMART_ACTION_SPAWN_SPAWNGROUP: + { + if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0) + { + bool const ignoreRespawn = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN) != 0); + bool const force = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_FORCE_SPAWN) != 0); + + // Instant spawn + GetBaseObject()->GetMap()->SpawnGroupSpawn(e.action.groupSpawn.groupId, ignoreRespawn, force); + } + else + { + // Delayed spawn (use values from parameter to schedule event to call us back + SmartEvent ne = SmartEvent(); + ne.type = (SMART_EVENT)SMART_EVENT_UPDATE; + ne.event_chance = 100; + + ne.minMaxRepeat.min = e.action.groupSpawn.minDelay; + ne.minMaxRepeat.max = e.action.groupSpawn.maxDelay; + ne.minMaxRepeat.repeatMin = 0; + ne.minMaxRepeat.repeatMax = 0; + + ne.event_flags = 0; + ne.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE; + + SmartAction ac = SmartAction(); + ac.type = (SMART_ACTION)SMART_ACTION_SPAWN_SPAWNGROUP; + ac.groupSpawn.groupId = e.action.groupSpawn.groupId; + ac.groupSpawn.minDelay = 0; + ac.groupSpawn.maxDelay = 0; + ac.groupSpawn.spawnflags = e.action.groupSpawn.spawnflags; + ac.timeEvent.id = e.action.timeEvent.id; + + SmartScriptHolder ev = SmartScriptHolder(); + ev.event = ne; + ev.event_id = e.event_id; + ev.target = e.target; + ev.action = ac; + InitTimer(ev); + mStoredEvents.push_back(ev); + } + break; + } + case SMART_ACTION_DESPAWN_SPAWNGROUP: + { + if (e.action.groupSpawn.minDelay == 0 && e.action.groupSpawn.maxDelay == 0) + { + bool const deleteRespawnTimes = ((e.action.groupSpawn.spawnflags & SMARTAI_SPAWN_FLAGS::SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN) != 0); + + // Instant spawn + GetBaseObject()->GetMap()->SpawnGroupDespawn(e.action.groupSpawn.groupId, deleteRespawnTimes); + } + else + { + // Delayed spawn (use values from parameter to schedule event to call us back + SmartEvent ne = SmartEvent(); + ne.type = (SMART_EVENT)SMART_EVENT_UPDATE; + ne.event_chance = 100; + + ne.minMaxRepeat.min = e.action.groupSpawn.minDelay; + ne.minMaxRepeat.max = e.action.groupSpawn.maxDelay; + ne.minMaxRepeat.repeatMin = 0; + ne.minMaxRepeat.repeatMax = 0; + + ne.event_flags = 0; + ne.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE; + + SmartAction ac = SmartAction(); + ac.type = (SMART_ACTION)SMART_ACTION_DESPAWN_SPAWNGROUP; + ac.groupSpawn.groupId = e.action.groupSpawn.groupId; + ac.groupSpawn.minDelay = 0; + ac.groupSpawn.maxDelay = 0; + ac.groupSpawn.spawnflags = e.action.groupSpawn.spawnflags; + ac.timeEvent.id = e.action.timeEvent.id; + + SmartScriptHolder ev = SmartScriptHolder(); + ev.event = ne; + ev.event_id = e.event_id; + ev.target = e.target; + ev.action = ac; + InitTimer(ev); + mStoredEvents.push_back(ev); + } break; } case SMART_ACTION_DISABLE_EVADE: @@ -2947,7 +3041,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui case SMART_EVENT_WAYPOINT_STOPPED: case SMART_EVENT_WAYPOINT_ENDED: { - if (!me || (e.event.waypoint.pointID && var0 != e.event.waypoint.pointID) || (e.event.waypoint.pathID && GetPathId() != e.event.waypoint.pathID)) + if (!me || (e.event.waypoint.pointID && var0 != e.event.waypoint.pointID) || (e.event.waypoint.pathID && var1 != e.event.waypoint.pathID)) return; ProcessAction(e, unit); break; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 31282a934ac..b4713b7ed4d 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -29,6 +29,7 @@ #include "SpellMgr.h" #include "Timer.h" #include "UnitDefines.h" +#include "WaypointDefines.h" SmartWaypointMgr* SmartWaypointMgr::instance() { @@ -40,15 +41,7 @@ void SmartWaypointMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - for (std::unordered_map<uint32, WPPath*>::iterator itr = waypoint_map.begin(); itr != waypoint_map.end(); ++itr) - { - for (WPPath::iterator pathItr = itr->second->begin(); pathItr != itr->second->end(); ++pathItr) - delete pathItr->second; - - delete itr->second; - } - - waypoint_map.clear(); + _waypointStore.clear(); PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -62,50 +55,47 @@ void SmartWaypointMgr::LoadFromDB() uint32 count = 0; uint32 total = 0; - uint32 last_entry = 0; - uint32 last_id = 1; + uint32 lastEntry = 0; + uint32 lastId = 1; do { Field* fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); uint32 id = fields[1].GetUInt32(); - float x, y, z; - x = fields[2].GetFloat(); - y = fields[3].GetFloat(); - z = fields[4].GetFloat(); + float x = fields[2].GetFloat(); + float y = fields[3].GetFloat(); + float z = fields[4].GetFloat(); - if (last_entry != entry) + if (lastEntry != entry) { - waypoint_map[entry] = new WPPath(); - last_id = 1; - count++; + lastId = 1; + ++count; } - if (last_id != id) - TC_LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry %u, unexpected point id %u, expected %u.", entry, id, last_id); + if (lastId != id) + TC_LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry %u, unexpected point id %u, expected %u.", entry, id, lastId); - last_id++; - (*waypoint_map[entry])[id] = new WayPoint(id, x, y, z); + ++lastId; + WaypointPath& path = _waypointStore[entry]; + path.id = entry; + path.nodes.emplace_back(id, x, y, z); - last_entry = entry; - total++; + lastEntry = entry; + ++total; } while (result->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded %u SmartAI waypoint paths (total %u waypoints) in %u ms", count, total, GetMSTimeDiffToNow(oldMSTime)); } -SmartWaypointMgr::~SmartWaypointMgr() +WaypointPath const* SmartWaypointMgr::GetPath(uint32 id) { - for (std::unordered_map<uint32, WPPath*>::iterator itr = waypoint_map.begin(); itr != waypoint_map.end(); ++itr) - { - for (WPPath::iterator pathItr = itr->second->begin(); pathItr != itr->second->end(); ++pathItr) - delete pathItr->second; - - delete itr->second; - } + auto itr = _waypointStore.find(id); + if (itr != _waypointStore.end()) + return &itr->second; + return nullptr; } SmartAIMgr* SmartAIMgr::instance() @@ -228,7 +218,7 @@ void SmartAIMgr::LoadSmartAIFromDB() } case SMART_SCRIPT_TYPE_GAMEOBJECT: { - GameObjectData const* gameObject = sObjectMgr->GetGOData(uint32(std::abs(temp.entryOrGuid))); + GameObjectData const* gameObject = sObjectMgr->GetGameObjectData(uint32(std::abs(temp.entryOrGuid))); if (!gameObject) { TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: GameObject guid (%u) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid))); @@ -932,7 +922,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) return false; } - if (e.event.distance.guid != 0 && !sObjectMgr->GetGOData(e.event.distance.guid)) + if (e.event.distance.guid != 0 && !sObjectMgr->GetGameObjectData(e.event.distance.guid)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Event SMART_EVENT_DISTANCE_GAMEOBJECT using invalid gameobject guid %u, skipped.", e.event.distance.guid); return false; @@ -1314,7 +1304,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) break; case SMART_ACTION_WP_START: { - if (!sSmartWaypointMgr->GetPath(e.action.wpStart.pathID)) + WaypointPath const* path = sSmartWaypointMgr->GetPath(e.action.wpStart.pathID); + if (!path || path->nodes.empty()) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature %d Event %u Action %u uses non-existent WaypointPath id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.pathID); return false; @@ -1508,6 +1499,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) case SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT: case SMART_ACTION_SET_COUNTER: case SMART_ACTION_REMOVE_ALL_GAMEOBJECTS: + case SMART_ACTION_SPAWN_SPAWNGROUP: + case SMART_ACTION_DESPAWN_SPAWNGROUP: break; default: TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id); diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 7a1433bc7fb..499b1ad458e 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -20,6 +20,7 @@ #include "Define.h" #include "ObjectGuid.h" +#include "WaypointDefines.h" #include <map> #include <string> #include <unordered_map> @@ -27,22 +28,6 @@ class WorldObject; enum SpellEffIndex : uint8; -struct WayPoint -{ - WayPoint(uint32 _id, float _x, float _y, float _z) - { - id = _id; - x = _x; - y = _y; - z = _z; - } - - uint32 id; - float x; - float y; - float z; -}; - enum eSmartAI { SMART_EVENT_PARAM_COUNT = 4, @@ -589,8 +574,12 @@ enum SMART_ACTION SMART_ACTION_REMOVE_ALL_GAMEOBJECTS = 126, SMART_ACTION_STOP_MOTION = 127, // stopMoving, movementExpired SMART_ACTION_PLAY_ANIMKIT = 128, // don't use on 3.3.5a + SMART_ACTION_SCENE_PLAY = 129, // don't use on 3.3.5a + SMART_ACTION_SCENE_CANCEL = 130, // don't use on 3.3.5a + SMART_ACTION_SPAWN_SPAWNGROUP = 131, // Group ID, min secs, max secs, spawnflags + SMART_ACTION_DESPAWN_SPAWNGROUP = 132, // Group ID, min secs, max secs, spawnflags - SMART_ACTION_END = 129 + SMART_ACTION_END = 133 }; struct SmartAction @@ -621,6 +610,7 @@ struct SmartAction { uint32 sound; uint32 onlySelf; + uint32 distance; } sound; struct @@ -1081,8 +1071,9 @@ struct SmartAction struct { - uint32 sounds[SMART_ACTION_PARAM_COUNT - 1]; + uint32 sounds[SMART_ACTION_PARAM_COUNT - 2]; uint32 onlySelf; + uint32 distance; } randomSound; struct @@ -1094,6 +1085,13 @@ struct SmartAction { uint32 disable; } disableEvade; + struct + { + uint32 groupId; + uint32 minDelay; + uint32 maxDelay; + uint32 spawnflags; + } groupSpawn; struct { @@ -1138,6 +1136,14 @@ struct SmartAction }; }; +enum SMARTAI_SPAWN_FLAGS +{ + SMARTAI_SPAWN_FLAG_NONE = 0x00, + SMARTAI_SPAWN_FLAG_IGNORE_RESPAWN = 0x01, + SMARTAI_SPAWN_FLAG_FORCE_SPAWN = 0x02, + SMARTAI_SPAWN_FLAG_NOSAVE_RESPAWN = 0x04, +}; + enum SMARTAI_TEMPLATE { SMARTAI_TEMPLATE_BASIC = 0, //nothing is preset @@ -1495,8 +1501,6 @@ struct SmartScriptHolder operator bool() const { return entryOrGuid != 0; } }; -typedef std::unordered_map<uint32, WayPoint*> WPPath; - typedef std::vector<WorldObject*> ObjectVector; class ObjectGuidVector @@ -1523,26 +1527,22 @@ typedef std::unordered_map<uint32, ObjectGuidVector> ObjectVectorMap; class TC_GAME_API SmartWaypointMgr { - private: - SmartWaypointMgr() { } - ~SmartWaypointMgr(); - public: static SmartWaypointMgr* instance(); void LoadFromDB(); - WPPath* GetPath(uint32 id) - { - if (waypoint_map.find(id) != waypoint_map.end()) - return waypoint_map[id]; - else return nullptr; - } + WaypointPath const* GetPath(uint32 id); private: - std::unordered_map<uint32, WPPath*> waypoint_map; + SmartWaypointMgr() { } + ~SmartWaypointMgr() { } + + std::unordered_map<uint32, WaypointPath> _waypointStore; }; +#define sSmartWaypointMgr SmartWaypointMgr::instance() + // all events for a single entry typedef std::vector<SmartScriptHolder> SmartAIEventList; typedef std::vector<SmartScriptHolder> SmartAIEventStoredList; @@ -1608,5 +1608,5 @@ class TC_GAME_API SmartAIMgr }; #define sSmartScriptMgr SmartAIMgr::instance() -#define sSmartWaypointMgr SmartWaypointMgr::instance() + #endif diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 8fc5f220d43..0f8e031ba20 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -57,7 +57,7 @@ enum RBACPermissions RBAC_PERM_JOIN_RANDOM_BG = 4, RBAC_PERM_JOIN_ARENAS = 5, RBAC_PERM_JOIN_DUNGEON_FINDER = 6, - // 7 - reuse + RBAC_PERM_IGNORE_IDLE_CONNECTION = 7, // 8 - reuse // 9 - reuse RBAC_PERM_USE_CHARACTER_TEMPLATES = 10, // not on 3.3.5a @@ -748,7 +748,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_SERVER_RESTART_FORCE = 840, RBAC_PERM_COMMAND_NEARGRAVEYARD = 841, RBAC_PERM_COMMAND_RELOAD_CHARACTER_TEMPLATE = 842, // not on 3.3.5a - RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING = 843, // not on 3.3.5a + RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING = 843, RBAC_PERM_COMMAND_SCENE = 844, // not on 3.3.5a RBAC_PERM_COMMAND_SCENE_DEBUG = 845, // not on 3.3.5a RBAC_PERM_COMMAND_SCENE_PLAY = 846, // not on 3.3.5a @@ -761,16 +761,18 @@ enum RBACPermissions RBAC_PERM_COMMAND_RELOAD_CONVERSATION_TEMPLATE = 853, // not on 3.3.5a RBAC_PERM_COMMAND_DEBUG_CONVERSATION = 854, // not on 3.3.5a RBAC_PERM_COMMAND_DEBUG_PLAY_MUSIC = 855, - RBAC_PERM_COMMAND_NPC_SPAWNGROUP = 856, // reserved for dynamic_spawning - RBAC_PERM_COMMAND_NPC_DESPAWNGROUP = 857, // reserved for dynamic_spawning - RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP = 858, // reserved for dynamic_spawning - RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP = 859, // reserved for dynamic_spawning - RBAC_PERM_COMMAND_LIST_RESPAWNS = 860, // reserved for dynamic_spawning + RBAC_PERM_COMMAND_NPC_SPAWNGROUP = 856, + RBAC_PERM_COMMAND_NPC_DESPAWNGROUP = 857, + RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP = 858, + RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP = 859, + RBAC_PERM_COMMAND_LIST_RESPAWNS = 860, RBAC_PERM_COMMAND_GROUP_SET = 861, RBAC_PERM_COMMAND_GROUP_ASSISTANT = 862, RBAC_PERM_COMMAND_GROUP_MAINTANK = 863, RBAC_PERM_COMMAND_GROUP_MAINASSIST = 864, RBAC_PERM_COMMAND_NPC_SHOWLOOT = 865, + RBAC_PERM_COMMAND_LIST_SPAWNPOINTS = 866, + RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING_LOCALE = 867, // custom permissions 1000+ RBAC_PERM_MAX diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index a43ef238521..f904614960a 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -2287,7 +2287,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList() if (sAchievementCriteriaStore.GetNumRows() == 0) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 achievement criteria."); + TC_LOG_INFO("server.loading", ">> Loaded 0 achievement criteria."); return; } @@ -2574,7 +2574,7 @@ void AchievementGlobalMgr::LoadRewards() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty."); return; } diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 27e83185e27..92b78216cb0 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -776,18 +776,15 @@ Creature* Battlefield::SpawnCreature(uint32 entry, Position const& pos) return nullptr; } - float x, y, z, o; - pos.GetPosition(x, y, z, o); - Creature* creature = new Creature(); - if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, x, y, z, o)) + if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, pos)) { TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u", entry); delete creature; return nullptr; } - creature->SetHomePosition(x, y, z, o); + creature->SetHomePosition(pos); // Set creature in world map->AddToMap(creature); diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 41591c419e9..e184c22354e 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -1515,7 +1515,7 @@ Creature* Battleground::AddCreature(uint32 entry, uint32 type, float x, float y, Creature* creature = new Creature(); - if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, x, y, z, o)) + if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, PHASEMASK_NORMAL, entry, { x, y, z, o })) { TC_LOG_ERROR("bg.battleground", "Battleground::AddCreature: cannot create creature (entry: %u) for BG (map: %u, instance id: %u)!", entry, m_MapId, m_InstanceID); diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index 1b8fb8ee907..76e3d7ea08c 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -535,7 +535,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates() QueryResult result = WorldDatabase.Query("SELECT ID, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, AllianceStartLoc, AllianceStartO, HordeStartLoc, HordeStartO, StartMaxDist, Weight, ScriptName FROM battleground_template"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 battlegrounds. DB table `battleground_template` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 battlegrounds. DB table `battleground_template` is empty."); return; } @@ -973,7 +973,6 @@ BattlegroundTypeId BattlegroundMgr::GetRandomBG(BattlegroundTypeId bgTypeId) { if (BattlegroundTemplate const* bgTemplate = GetBattlegroundTemplateByTypeId(bgTypeId)) { - BattlegroundSelectionWeightMap selectionWeights; std::vector<BattlegroundTypeId> ids; ids.reserve(16); std::vector<double> weights; diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp index 6ef3bbcc3c3..3ebca71a326 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp @@ -176,7 +176,7 @@ void BattlegroundAB::PostUpdateImpl(uint32 diff) if (team == TEAM_ALLIANCE) UpdateWorldState(BG_AB_OP_RESOURCES_ALLY, m_TeamScores[team]); - else if (team == TEAM_HORDE) + else UpdateWorldState(BG_AB_OP_RESOURCES_HORDE, m_TeamScores[team]); // update achievement flags // we increased m_TeamScores[team] so we just need to check if it is 500 more than other teams resources @@ -418,12 +418,12 @@ void BattlegroundAB::_NodeOccupied(uint8 node, Team team) void BattlegroundAB::_NodeDeOccupied(uint8 node) { + //only dynamic nodes, no start points if (node >= BG_AB_DYNAMIC_NODES_COUNT) return; //remove bonus honor aura trigger creature when node is lost - if (node < BG_AB_DYNAMIC_NODES_COUNT)//only dynamic nodes, no start points - DelCreature(node+7);//NULL checks are in DelCreature! 0-6 spirit guides + DelCreature(node+7);//NULL checks are in DelCreature! 0-6 spirit guides RelocateDeadPlayers(BgCreatures[node]); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h index 7f68ef3eb0d..f7215989093 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h @@ -212,11 +212,11 @@ const uint32 BG_AB_GraveyardIds[BG_AB_ALL_NODES_COUNT] = {895, 894, 893, 897, 89 // x, y, z, o const float BG_AB_BuffPositions[BG_AB_DYNAMIC_NODES_COUNT][4] = { - {1185.71f, 1185.24f, -56.36f, 2.56f}, // stables - {990.75f, 1008.18f, -42.60f, 2.43f}, // blacksmith - {817.66f, 843.34f, -56.54f, 3.01f}, // farm - {807.46f, 1189.16f, 11.92f, 5.44f}, // lumber mill - {1146.62f, 816.94f, -98.49f, 6.14f} // gold mine + {1185.566f, 1184.629f, -56.36329f, 2.303831f}, // stables + {990.1131f, 1008.73f, -42.60328f, 0.8203033f}, // blacksmith + {818.0089f, 842.3543f, -56.54062f, 3.176533f}, // farm + {808.8463f, 1185.417f, 11.92161f, 5.619962f}, // lumber mill + {1147.091f, 816.8362f, -98.39896f, 6.056293f} // gold mine }; Position const BG_AB_SpiritGuidePos[BG_AB_ALL_NODES_COUNT] = diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp index fc2bee156ee..506e4d2dea3 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp @@ -314,6 +314,7 @@ Creature* BattlegroundAV::AddAVCreature(uint16 cinfoid, uint16 type) || (cinfoid >= AV_NPC_H_GRAVEDEFENSE0 && cinfoid <= AV_NPC_H_GRAVEDEFENSE3))) { CreatureData &data = sObjectMgr->NewOrExistCreatureData(creature->GetSpawnId()); + data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup(); data.spawndist = 5; } //else spawndist will be 15, so creatures move maximum=10 diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h index 9adaea025d3..7336caaf0d7 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h @@ -987,19 +987,19 @@ Position const BG_AV_CreaturePos[AV_CPLACE_MAX] = enum BG_AV_CreatureIds { - AV_NPC_A_TOWERDEFENSE = 0, // stormpike bowman - AV_NPC_A_GRAVEDEFENSE0 = 1, // stormpike Defender - AV_NPC_A_GRAVEDEFENSE1 = 2, // seasoned defender - AV_NPC_A_GRAVEDEFENSE2 = 3, // veteran defender - AV_NPC_A_GRAVEDEFENSE3 = 4, // champion defender + AV_NPC_A_GRAVEDEFENSE0 = 0, // stormpike Defender + AV_NPC_A_GRAVEDEFENSE1 = 1, // seasoned defender + AV_NPC_A_GRAVEDEFENSE2 = 2, // veteran defender + AV_NPC_A_GRAVEDEFENSE3 = 3, // champion defender + AV_NPC_A_TOWERDEFENSE = 4, // stormpike bowman AV_NPC_A_CAPTAIN = 5, // balinda AV_NPC_A_BOSS = 6, // vanndar - AV_NPC_H_TOWERDEFENSE = 7, // frostwolf bowman - AV_NPC_H_GRAVEDEFENSE0 = 8, // frostwolf guardian - AV_NPC_H_GRAVEDEFENSE1 = 9, // seasoned guardian - AV_NPC_H_GRAVEDEFENSE2 = 10, // veteran guardian - AV_NPC_H_GRAVEDEFENSE3 = 11, // champion guardian + AV_NPC_H_GRAVEDEFENSE0 = 7, // frostwolf guardian + AV_NPC_H_GRAVEDEFENSE1 = 8, // seasoned guardian + AV_NPC_H_GRAVEDEFENSE2 = 9, // veteran guardian + AV_NPC_H_GRAVEDEFENSE3 = 10, // champion guardian + AV_NPC_H_TOWERDEFENSE = 11, // frostwolf bowman AV_NPC_H_CAPTAIN = 12, // galvangar AV_NPC_H_BOSS = 13, // drek thar diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp index e1d1394cb05..a686d52cda2 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp @@ -529,8 +529,7 @@ bool BattlegroundEY::SetupBattleground() TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Could not spawn Speedbuff Fel Reaver."); } - WorldSafeLocsEntry const* sg = nullptr; - sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE); + WorldSafeLocsEntry const* sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE); if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, TEAM_ALLIANCE)) { TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide. The battleground was not created."); @@ -774,8 +773,7 @@ void BattlegroundEY::EventTeamCapturedPoint(Player* player, uint32 Point) if (BgCreatures[Point]) DelCreature(Point); - WorldSafeLocsEntry const* sg = nullptr; - sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveyardId); + WorldSafeLocsEntry const* sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveyardId); if (!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, GetTeamIndexByTeamId(Team))) TC_LOG_ERROR("bg.battleground", "BatteGroundEY: Failed to spawn spirit guide. point: %u, team: %u, graveyard_id: %u", Point, Team, m_CapturingPointTypes[Point].GraveyardId); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp index b28f31cd09c..90fd95b5780 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp @@ -198,8 +198,7 @@ bool BattlegroundSA::ResetObjs() //Graveyards for (uint8 i = 0; i < BG_SA_MAX_GY; i++) { - WorldSafeLocsEntry const* sg = nullptr; - sg = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]); + WorldSafeLocsEntry const* sg = sWorldSafeLocsStore.LookupEntry(BG_SA_GYEntries[i]); if (!sg) { diff --git a/src/server/game/Calendar/CalendarMgr.cpp b/src/server/game/Calendar/CalendarMgr.cpp index 08a406c4f15..d93786d66f1 100644 --- a/src/server/game/Calendar/CalendarMgr.cpp +++ b/src/server/game/Calendar/CalendarMgr.cpp @@ -57,6 +57,8 @@ CalendarMgr* CalendarMgr::instance() void CalendarMgr::LoadFromDB() { + uint32 oldMSTime = getMSTime(); + uint32 count = 0; _maxEventId = 0; _maxInviteId = 0; @@ -90,7 +92,7 @@ void CalendarMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u calendar events", count); + TC_LOG_INFO("server.loading", ">> Loaded %u calendar events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); count = 0; // 0 1 2 3 4 5 6 7 @@ -117,7 +119,7 @@ void CalendarMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u calendar invites", count); + TC_LOG_INFO("server.loading", ">> Loaded %u calendar invites in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); for (uint64 i = 1; i < _maxEventId; ++i) if (!GetEvent(i)) diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 3e15ecc9319..46c6a26760c 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -33,6 +33,7 @@ #include "Realm.h" #include "ScriptMgr.h" #include "World.h" +#include <boost/algorithm/string/replace.hpp> ChatCommand::ChatCommand(char const* name, uint32 permission, bool allowConsole, pHandler handler, std::string help, std::vector<ChatCommand> childCommands /*= std::vector<ChatCommand>()*/) : Name(ASSERT_NOTNULL(name)), Permission(permission), AllowConsole(allowConsole), Handler(handler), Help(std::move(help)), ChildCommands(std::move(childCommands)) @@ -347,6 +348,7 @@ bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, c SendSysMessage(table[i].Help.c_str()); else SendSysMessage(LANG_CMD_SYNTAX); + SetSentErrorMessage(true); } return true; @@ -410,41 +412,39 @@ bool ChatHandler::SetDataForCommandInTable(std::vector<ChatCommand>& table, char return false; } +bool ChatHandler::_ParseCommands(char const* text) +{ + if (ExecuteCommandInTable(getCommandTable(), text, text)) + return true; + + // Pretend commands don't exist for regular players + if (m_session && !m_session->HasPermission(rbac::RBAC_PERM_COMMANDS_NOTIFY_COMMAND_NOT_FOUND_ERROR)) + return false; + + // Send error message for GMs + SendSysMessage(LANG_NO_CMD); + SetSentErrorMessage(true); + return true; +} + bool ChatHandler::ParseCommands(char const* text) { ASSERT(text); ASSERT(*text); - std::string fullcmd = text; - /// chat case (.command or !command format) - if (m_session) - { - if (text[0] != '!' && text[0] != '.') - return false; - } + if (text[0] != '!' && text[0] != '.') + return false; /// ignore single . and ! in line - if (strlen(text) < 2) + if (!text[1]) return false; - // original `text` can't be used. It content destroyed in command code processing. /// ignore messages staring from many dots. - if ((text[0] == '.' && text[1] == '.') || (text[0] == '!' && text[1] == '!')) + if (text[1] == '!' || text[1] == '.') return false; - /// skip first . or ! (in console allowed use command with . and ! and without its) - if (text[0] == '!' || text[0] == '.') - ++text; - - if (!ExecuteCommandInTable(getCommandTable(), text, fullcmd)) - { - if (m_session && !m_session->HasPermission(rbac::RBAC_PERM_COMMANDS_NOTIFY_COMMAND_NOT_FOUND_ERROR)) - return false; - - SendSysMessage(LANG_NO_CMD); - } - return true; + return _ParseCommands(text+1); } bool ChatHandler::isValidChatMessage(char const* message) @@ -1232,6 +1232,16 @@ void CliHandler::SendSysMessage(const char *str, bool /*escapeCharacters*/) m_print(m_callbackArg, "\r\n"); } +bool CliHandler::ParseCommands(char const* str) +{ + if (!str[0]) + return false; + // Console allows using commands both with and without leading indicator + if (str[0] == '.' || str[0] == '!') + ++str; + return _ParseCommands(str); +} + std::string CliHandler::GetNameLink() const { return GetTrinityString(LANG_CONSOLE_COMMAND); @@ -1295,3 +1305,102 @@ int CliHandler::GetSessionDbLocaleIndex() const { return sObjectMgr->GetDBCLocaleIndex(); } + +bool AddonChannelCommandHandler::ParseCommands(char const* str) +{ + if (memcmp(str, "TrinityCore\t", 12)) + return false; + char opcode = str[12]; + if (!opcode) // str[12] is opcode + return false; + if (!str[13] || !str[14] || !str[15] || !str[16]) // str[13] through str[16] is 4-character command counter + return false; + echo = str+13; + + switch (opcode) + { + case 'p': // p Ping + SendAck(); + return true; + case 'h': // h Issue human-readable command + case 'i': // i Issue command + if (!str[17]) + return false; + humanReadable = (opcode == 'h'); + if (_ParseCommands(str + 17)) // actual command starts at str[17] + { + if (!hadAck) + SendAck(); + if (HasSentErrorMessage()) + SendFailed(); + else + SendOK(); + } + else + { + SendSysMessage(LANG_NO_CMD); + SendFailed(); + } + return true; + default: + return false; + } +} + +void AddonChannelCommandHandler::Send(std::string const& msg) +{ + WorldPacket data; + ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, LANG_ADDON, GetSession()->GetPlayer(), GetSession()->GetPlayer(), msg); + GetSession()->SendPacket(&data); +} + +void AddonChannelCommandHandler::SendAck() // a Command acknowledged, no body +{ + ASSERT(echo); + char ack[18] = "TrinityCore\ta"; + memcpy(ack+13, echo, 4); + ack[17] = '\0'; + Send(ack); + hadAck = true; +} + +void AddonChannelCommandHandler::SendOK() // o Command OK, no body +{ + ASSERT(echo); + char ok[18] = "TrinityCore\to"; + memcpy(ok+13, echo, 4); + ok[17] = '\0'; + Send(ok); +} + +void AddonChannelCommandHandler::SendFailed() // f Command failed, no body +{ + ASSERT(echo); + char fail[18] = "TrinityCore\tf"; + memcpy(fail + 13, echo, 4); + fail[17] = '\0'; + Send(fail); +} + +// m Command message, message in body +void AddonChannelCommandHandler::SendSysMessage(char const* str, bool escapeCharacters) +{ + ASSERT(echo); + if (!hadAck) + SendAck(); + + std::string msg = "TrinityCore\tm"; + msg.append(echo, 4); + std::string body(str); + if (escapeCharacters) + boost::replace_all(body, "|", "||"); + size_t pos, lastpos; + for (lastpos = 0, pos = body.find('\n', lastpos); pos != std::string::npos; lastpos = pos + 1, pos = body.find('\n', lastpos)) + { + std::string line(msg); + line.append(body, lastpos, pos - lastpos); + Send(line); + } + msg.append(body, lastpos, pos - lastpos); + Send(msg); +} diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h index f2ab3772a0d..8bbcf49c7a1 100644 --- a/src/server/game/Chat/Chat.h +++ b/src/server/game/Chat/Chat.h @@ -93,7 +93,8 @@ class TC_GAME_API ChatHandler return Trinity::StringFormat(GetTrinityString(entry), std::forward<Args>(args)...); } - bool ParseCommands(char const* text); + bool _ParseCommands(char const* text); + virtual bool ParseCommands(char const* text); static std::vector<ChatCommand> const& getCommandTable(); static void invalidateCommandTable(); @@ -105,6 +106,7 @@ class TC_GAME_API ChatHandler // function with different implementation for chat/console virtual bool isAvailable(ChatCommand const& cmd) const; + virtual bool IsHumanReadable() const { return true; } virtual bool HasPermission(uint32 permission) const; virtual std::string GetNameLink() const; virtual bool needReportToTarget(Player* chr) const; @@ -171,6 +173,7 @@ class TC_GAME_API CliHandler : public ChatHandler bool isAvailable(ChatCommand const& cmd) const override; bool HasPermission(uint32 /*permission*/) const override { return true; } void SendSysMessage(const char *str, bool escapeCharacters) override; + bool ParseCommands(char const* str) override; std::string GetNameLink() const override; bool needReportToTarget(Player* chr) const override; LocaleConstant GetSessionDbcLocale() const override; @@ -181,4 +184,24 @@ class TC_GAME_API CliHandler : public ChatHandler Print* m_print; }; +class TC_GAME_API AddonChannelCommandHandler : public ChatHandler +{ + public: + using ChatHandler::ChatHandler; + bool ParseCommands(char const* str) override; + void SendSysMessage(char const* str, bool escapeCharacters) override; + using ChatHandler::SendSysMessage; + bool IsHumanReadable() const override { return humanReadable; } + + private: + void Send(std::string const& msg); + void SendAck(); + void SendOK(); + void SendFailed(); + + char const* echo = nullptr; + bool hadAck = false; + bool humanReadable = false; +}; + #endif diff --git a/src/server/game/Chat/ChatLink.cpp b/src/server/game/Chat/ChatLink.cpp index db07e9e2efd..e7fd0b97703 100644 --- a/src/server/game/Chat/ChatLink.cpp +++ b/src/server/game/Chat/ChatLink.cpp @@ -153,7 +153,7 @@ bool ItemChatLink::Initialize(std::istringstream& iss) return false; } } - else if (id < 0) + else { _suffix = sItemRandomSuffixStore.LookupEntry(-id); if (!_suffix) diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h index a6432d62216..21d975ccea1 100644 --- a/src/server/game/Combat/ThreatManager.h +++ b/src/server/game/Combat/ThreatManager.h @@ -20,11 +20,11 @@ #define _THREATMANAGER #include "Common.h" +#include "IteratorPair.h" #include "SharedDefines.h" #include "LinkedReference/Reference.h" #include "UnitEvents.h" #include "ObjectGuid.h" -#include "Containers.h" #include <list> @@ -220,8 +220,8 @@ class TC_GAME_API ThreatManager { public: // -- compatibility layer for combat rewrite (PR #19930) - Trinity::Containers::IteratorPair<std::list<ThreatReference*>::const_iterator> GetSortedThreatList() const { auto& list = iThreatContainer.getThreatList(); return { list.cbegin(), list.cend() }; } - Trinity::Containers::IteratorPair<std::list<ThreatReference*>::const_iterator> GetUnsortedThreatList() const { return GetSortedThreatList(); } + Trinity::IteratorPair<std::list<ThreatReference*>::const_iterator> GetSortedThreatList() const { auto& list = iThreatContainer.getThreatList(); return { list.cbegin(), list.cend() }; } + Trinity::IteratorPair<std::list<ThreatReference*>::const_iterator> GetUnsortedThreatList() const { return GetSortedThreatList(); } std::list<ThreatReference*> GetModifiableThreatList() const { return iThreatContainer.getThreatList(); } Unit* SelectVictim() { return getHostilTarget(); } Unit* GetCurrentVictim() const { if (ThreatReference* ref = getCurrentVictim()) return ref->GetVictim(); else return nullptr; } diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 146d2cb3bde..5b7e85d6d3e 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1036,7 +1036,7 @@ void ConditionMgr::LoadConditions(bool isReload) if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 conditions. DB table `conditions` is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 conditions. DB table `conditions` is empty!"); return; } @@ -1677,7 +1677,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) const } break; } - case CONDITION_SOURCE_TYPE_QUEST_ACCEPT: + case CONDITION_SOURCE_TYPE_QUEST_AVAILABLE: if (!sObjectMgr->GetQuestTemplate(cond->SourceEntry)) { TC_LOG_ERROR("sql.sql", "%s SourceEntry specifies non-existing quest, skipped.", cond->ToString().c_str()); @@ -2011,7 +2011,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const } if (cond->ConditionValue3) { - if (GameObjectData const* goData = sObjectMgr->GetGOData(cond->ConditionValue3)) + if (GameObjectData const* goData = sObjectMgr->GetGameObjectData(cond->ConditionValue3)) { if (cond->ConditionValue2 && goData->id != cond->ConditionValue2) { diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index ff3f096abae..4f6c98e87d9 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -133,7 +133,7 @@ enum ConditionSourceType CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE = 16, CONDITION_SOURCE_TYPE_SPELL = 17, CONDITION_SOURCE_TYPE_SPELL_CLICK_EVENT = 18, - CONDITION_SOURCE_TYPE_QUEST_ACCEPT = 19, + CONDITION_SOURCE_TYPE_QUEST_AVAILABLE = 19, // Condition source type 20 unused CONDITION_SOURCE_TYPE_VEHICLE_SPELL = 21, CONDITION_SOURCE_TYPE_SMART_EVENT = 22, diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index f7d3fe325dc..923ed40b93a 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -248,7 +248,7 @@ enum AreaFlags AREA_FLAG_UNK0 = 0x00000001, // Unknown AREA_FLAG_UNK1 = 0x00000002, // Razorfen Downs, Naxxramas and Acherus: The Ebon Hold (3.3.5a) AREA_FLAG_UNK2 = 0x00000004, // Only used for areas on map 571 (development before) - AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // city and city subsones + AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // city and city subzones AREA_FLAG_UNK3 = 0x00000010, // can't find common meaning AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag? AREA_FLAG_ALLOW_DUELS = 0x00000040, // allow to duel here diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index e72408205c0..f41e9b4e205 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -239,7 +239,7 @@ inline void LoadDBC(uint32& availableDbcLocales, StoreProblemList& errors, DBCSt localizedName.push_back('/'); localizedName.append(filename); - if (!storage.LoadStringsFrom(localizedName.c_str())) + if (!storage.LoadStringsFrom(localizedName)) availableDbcLocales &= ~(1 << i); // mark as not available for speedup next checks } @@ -435,10 +435,10 @@ void LoadDBCStores(const std::string& dataPath) ASSERT(Utf8toWStr(namesProfanity->Name, wname)); if (namesProfanity->Language != -1) - NamesProfaneValidators[namesProfanity->Language].emplace_back(wname, Trinity::regex::icase | Trinity::regex::optimize); + NamesProfaneValidators[namesProfanity->Language].emplace_back(wname, Trinity::regex::perl | Trinity::regex::icase | Trinity::regex::optimize); else for (uint32 i = 0; i < TOTAL_LOCALES; ++i) - NamesProfaneValidators[i].emplace_back(wname, Trinity::regex::icase | Trinity::regex::optimize); + NamesProfaneValidators[i].emplace_back(wname, Trinity::regex::perl | Trinity::regex::icase | Trinity::regex::optimize); } for (NamesReservedEntry const* namesReserved : sNamesReservedStore) @@ -448,10 +448,10 @@ void LoadDBCStores(const std::string& dataPath) ASSERT(Utf8toWStr(namesReserved->Name, wname)); if (namesReserved->Language != -1) - NamesReservedValidators[namesReserved->Language].emplace_back(wname, Trinity::regex::icase | Trinity::regex::optimize); + NamesReservedValidators[namesReserved->Language].emplace_back(wname, Trinity::regex::perl | Trinity::regex::icase | Trinity::regex::optimize); else for (uint32 i = 0; i < TOTAL_LOCALES; ++i) - NamesReservedValidators[i].emplace_back(wname, Trinity::regex::icase | Trinity::regex::optimize); + NamesReservedValidators[i].emplace_back(wname, Trinity::regex::perl | Trinity::regex::icase | Trinity::regex::optimize); } for (PvPDifficultyEntry const* entry : sPvPDifficultyStore) diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 39d30a28c4f..344759a2022 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -129,7 +129,7 @@ void LFGMgr::LoadRewards() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 lfg dungeon rewards. DB table `lfg_dungeon_rewards` is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 lfg dungeon rewards. DB table `lfg_dungeon_rewards` is empty!"); return; } @@ -215,7 +215,7 @@ void LFGMgr::LoadLFGDungeons(bool reload /* = false */) if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 lfg entrance positions. DB table `lfg_dungeon_template` is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 lfg entrance positions. DB table `lfg_dungeon_template` is empty!"); return; } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index b11a5a8018c..27ea877ca08 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -233,14 +233,12 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) return true; } -Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), -m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), -m_lootRecipient(), m_lootRecipientGroup(0), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), -m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), -m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), -m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), -m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f), -_lastDamagedTime(0) +Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(), m_lootRecipientGroup(0), _pickpocketLootRestore(0), + m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), + m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0), + m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), + m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f), _lastDamagedTime(0), + _regenerateHealth(true), _regenerateHealthLock(false) { m_regenTimer = CREATURE_REGEN_INTERVAL; m_valuesCount = UNIT_END; @@ -254,7 +252,6 @@ _lastDamagedTime(0) m_CombatDistance = 0;//MELEE_RANGE; ResetLootMode(); // restore default loot mode - m_TriggerJustRespawned = false; m_isTempWorldObject = false; } @@ -314,6 +311,14 @@ void Creature::DisappearAndDie() ForcedDespawn(0); } +bool Creature::IsReturningHome() const +{ + if (GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == HOME_MOTION_TYPE) + return true; + + return false; +} + void Creature::SearchFormation() { if (IsSummon()) @@ -328,48 +333,96 @@ void Creature::SearchFormation() sFormationMgr->AddCreatureToGroup(frmdata->second->leaderGUID, this); } +bool Creature::IsFormationLeader() const +{ + if (!m_formation) + return false; + + return m_formation->IsLeader(this); +} + +void Creature::SignalFormationMovement(Position const& destination, uint32 id/* = 0*/, uint32 moveType/* = 0*/, bool orientation/* = false*/) +{ + if (!m_formation) + return; + + if (!m_formation->IsLeader(this)) + return; + + m_formation->LeaderMoveTo(destination, id, moveType, orientation); +} + +bool Creature::IsFormationLeaderMoveAllowed() const +{ + if (!m_formation) + return false; + + return m_formation->CanLeaderStartMoving(); +} + void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers) { if (getDeathState() != CORPSE) return; - m_corpseRemoveTime = time(nullptr); - setDeathState(DEAD); - RemoveAllAuras(); - loot.clear(); - uint32 respawnDelay = m_respawnDelay; - if (IsAIEnabled) - AI()->CorpseRemoved(respawnDelay); + if (m_respawnCompatibilityMode) + { + m_corpseRemoveTime = time(nullptr); + setDeathState(DEAD); + RemoveAllAuras(); + loot.clear(); + uint32 respawnDelay = m_respawnDelay; + if (IsAIEnabled) + AI()->CorpseRemoved(respawnDelay); - if (destroyForNearbyPlayers) - DestroyForNearbyPlayers(); + if (destroyForNearbyPlayers) + DestroyForNearbyPlayers(); - // Should get removed later, just keep "compatibility" with scripts - if (setSpawnTime) - m_respawnTime = std::max<time_t>(time(nullptr) + respawnDelay, m_respawnTime); + // Should get removed later, just keep "compatibility" with scripts + if (setSpawnTime) + m_respawnTime = std::max<time_t>(time(nullptr) + respawnDelay, m_respawnTime); - // if corpse was removed during falling, the falling will continue and override relocation to respawn position - if (IsFalling()) - StopMoving(); + // if corpse was removed during falling, the falling will continue and override relocation to respawn position + if (IsFalling()) + StopMoving(); - float x, y, z, o; - GetRespawnPosition(x, y, z, &o); + float x, y, z, o; + GetRespawnPosition(x, y, z, &o); - // We were spawned on transport, calculate real position - if (IsSpawnedOnTransport()) - { - Position& pos = m_movementInfo.transport.pos; - pos.m_positionX = x; - pos.m_positionY = y; - pos.m_positionZ = z; - pos.SetOrientation(o); + // We were spawned on transport, calculate real position + if (IsSpawnedOnTransport()) + { + Position& pos = m_movementInfo.transport.pos; + pos.m_positionX = x; + pos.m_positionY = y; + pos.m_positionZ = z; + pos.SetOrientation(o); + + if (TransportBase* transport = GetDirectTransport()) + transport->CalculatePassengerPosition(x, y, z, &o); + } - if (TransportBase* transport = GetDirectTransport()) - transport->CalculatePassengerPosition(x, y, z, &o); + SetHomePosition(x, y, z, o); + GetMap()->CreatureRelocation(this, x, y, z, o); } + else + { + // In case this is called directly and normal respawn timer not set + // Since this timer will be longer than the already present time it + // will be ignored if the correct place added a respawn timer + if (setSpawnTime) + { + uint32 respawnDelay = m_respawnDelay; + m_respawnTime = std::max<time_t>(time(NULL) + respawnDelay, m_respawnTime); + + SaveRespawnTime(0, false); + } - SetHomePosition(x, y, z, o); - GetMap()->CreatureRelocation(this, x, y, z, o); + if (TempSummon* summon = ToTempSummon()) + summon->UnSummon(); + else + AddObjectToRemoveList(); + } } /** @@ -478,7 +531,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, CreatureTemplate const* cInfo = GetCreatureTemplate(); - m_regenHealth = cInfo->RegenHealth; + _regenerateHealth = cInfo->RegenHealth; // creatures always have melee weapon ready if any unless specified otherwise if (!GetCreatureAddon()) @@ -561,12 +614,12 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, void Creature::Update(uint32 diff) { - if (IsAIEnabled && m_TriggerJustRespawned) + if (IsAIEnabled && m_triggerJustAppeared && m_deathState == ALIVE) { - m_TriggerJustRespawned = false; - AI()->JustRespawned(); - if (m_vehicleKit) + if (m_respawnCompatibilityMode && m_vehicleKit) m_vehicleKit->Reset(); + m_triggerJustAppeared = false; + AI()->JustAppeared(); } UpdateMovementFlags(); @@ -579,30 +632,34 @@ void Creature::Update(uint32 diff) break; case JUST_DIED: // Must not be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting. - TC_LOG_ERROR("entities.unit", "Creature (GUID: %u Entry: %u) in wrong state: JUST_DEAD (1)", GetGUID().GetCounter(), GetEntry()); + TC_LOG_ERROR("entities.unit", "Creature (GUID: %u Entry: %u) in wrong state: JUST_DIED (1)", GetGUID().GetCounter(), GetEntry()); break; case DEAD: { time_t now = time(nullptr); if (m_respawnTime <= now) { + // First check if there are any scripts that object to us respawning - if (!sScriptMgr->CanSpawn(GetSpawnId(), GetEntry(), GetCreatureTemplate(), GetCreatureData(), GetMap())) - break; // Will be rechecked on next Update call + if (!sScriptMgr->CanSpawn(GetSpawnId(), GetEntry(), GetCreatureData(), GetMap())) + { + m_respawnTime = now + urand(4,7); + break; // Will be rechecked on next Update call after delay expires + } ObjectGuid dbtableHighGuid(HighGuid::Unit, GetEntry(), m_spawnId); - time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid); - if (!linkedRespawntime) // Can respawn + time_t linkedRespawnTime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid); + if (!linkedRespawnTime) // Can respawn Respawn(); else // the master is dead { ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid); - if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day) - SetRespawnTime(DAY); + if (targetGuid == dbtableHighGuid) // if linking self, never respawn + SetRespawnTime(WEEK); else { // else copy time from master and add a little - time_t baseRespawnTime = std::max(linkedRespawntime, now); + time_t baseRespawnTime = std::max(linkedRespawnTime, now); time_t const offset = urand(5, MINUTE); // linked guid can be a boss, uses std::numeric_limits<time_t>::max to never respawn in that instance @@ -742,7 +799,7 @@ void Creature::Update(uint32 diff) if (m_regenTimer == 0) { - bool bInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim + bool bInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster()); // or the victim/owner/charmer is not a GameMaster @@ -830,7 +887,7 @@ void Creature::Regenerate(Powers power) void Creature::RegenerateHealth() { - if (!isRegeneratingHealth()) + if (!CanRegenerateHealth()) return; uint32 curValue = GetHealth(); @@ -947,12 +1004,16 @@ void Creature::Motion_Initialize() GetMotionMaster()->Initialize(); } -bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, float x, float y, float z, float ang, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/) +bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, Position const& pos, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/, bool dynamic) { ASSERT(map); SetMap(map); SetPhaseMask(phaseMask, false); + // Set if this creature can handle dynamic spawns + if (!dynamic) + SetRespawnCompatibilityMode(); + CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry); if (!cinfo) { @@ -962,15 +1023,16 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u //! Relocate before CreateFromProto, to initialize coords and allow //! returning correct zone id for selecting OutdoorPvP/Battlefield script - Relocate(x, y, z, ang); + Relocate(pos); // Check if the position is valid before calling CreateFromProto(), otherwise we might add Auras to Creatures at // invalid position, triggering a crash about Auras not removed in the destructor if (!IsPositionValid()) { - TC_LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow %d, entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, entry, x, y, z, ang); + TC_LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow %d, entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); return false; } + UpdatePositionData(); // Allow players to see those units while dead, do it here (mayby altered by addon auras) if (cinfo->type_flags & CREATURE_TYPE_FLAG_GHOST_VISIBLE) @@ -1004,10 +1066,8 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u //! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there if (HasUnitMovementFlag(MOVEMENTFLAG_HOVER)) { - z += GetFloatValue(UNIT_FIELD_HOVERHEIGHT); - //! Relocate again with updated Z coord - Relocate(x, y, z, ang); + m_positionZ += GetFloatValue(UNIT_FIELD_HOVERHEIGHT); } LastUsedScriptID = GetScriptId(); @@ -1187,27 +1247,17 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) dynamicflags = 0; } - // data->guid = guid must not be updated at save + if (!data.spawnId) + data.spawnId = m_spawnId; + ASSERT(data.spawnId == m_spawnId); data.id = GetEntry(); - data.mapid = mapid; data.phaseMask = phaseMask; data.displayid = displayId; data.equipmentId = GetCurrentEquipmentId(); if (!GetTransport()) - { - data.posX = GetPositionX(); - data.posY = GetPositionY(); - data.posZ = GetPositionZMinusOffset(); - data.orientation = GetOrientation(); - } + data.spawnPoint.WorldRelocate(this); else - { - data.posX = GetTransOffsetX(); - data.posY = GetTransOffsetY(); - data.posZ = GetTransOffsetZ(); - data.orientation = GetTransOffsetO(); - } - + data.spawnPoint.WorldRelocate(mapid, GetTransOffsetX(), GetTransOffsetY(), GetTransOffsetZ(), GetTransOffsetO()); data.spawntimesecs = m_respawnDelay; // prevent add data integrity problems data.spawndist = GetDefaultMovementType() == IDLE_MOTION_TYPE ? 0.0f : m_respawnradius; @@ -1221,6 +1271,8 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) data.npcflag = npcflag; data.unit_flags = unit_flags; data.dynamicflags = dynamicflags; + if (!data.spawnGroupData) + data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup(); // update in DB SQLTransaction trans = WorldDatabase.BeginTransaction(); @@ -1425,7 +1477,7 @@ bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, Creatu return true; } -bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate) +bool Creature::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate) { if (!allowDuplicate) { @@ -1466,31 +1518,41 @@ bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool ad } m_spawnId = spawnId; + + m_respawnCompatibilityMode = ((data->spawnGroupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) != 0); m_creatureData = data; m_respawnradius = data->spawndist; m_respawnDelay = data->spawntimesecs; - if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, data->id, data->posX, data->posY, data->posZ, data->orientation, data)) + + // Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update) + if (!m_respawnCompatibilityMode && !m_respawnTime && !sScriptMgr->CanSpawn(spawnId, data->id, data, map)) + { + SaveRespawnTime(urand(4,7)); + return false; + } + + if (!Create(map->GenerateLowGuid<HighGuid::Unit>(), map, data->phaseMask, data->id, data->spawnPoint, data, 0U , !m_respawnCompatibilityMode)) return false; //We should set first home position, because then AI calls home movement - SetHomePosition(data->posX, data->posY, data->posZ, data->orientation); + SetHomePosition(data->spawnPoint); m_deathState = ALIVE; m_respawnTime = GetMap()->GetCreatureRespawnTime(m_spawnId); - // Is the creature script objecting to us spawning? If yes, delay by one second (then re-check in ::Update) - if (!m_respawnTime && !sScriptMgr->CanSpawn(spawnId, GetEntry(), GetCreatureTemplate(), GetCreatureData(), map)) - m_respawnTime = time(nullptr)+1; + // Is the creature script objecting to us spawning? If yes, delay by a little bit (then re-check in ::Update) + if (m_respawnCompatibilityMode && !m_respawnTime && !sScriptMgr->CanSpawn(spawnId, GetEntry(), GetCreatureData(), map)) + m_respawnTime = time(nullptr)+urand(4,7); if (m_respawnTime) // respawn on Update { m_deathState = DEAD; if (CanFly()) { - float tz = map->GetHeight(GetPhaseMask(), data->posX, data->posY, data->posZ, true, MAX_FALL_DISTANCE); - if (data->posZ - tz > 0.1f && Trinity::IsValidMapCoord(tz)) - Relocate(data->posX, data->posY, tz); + float tz = map->GetHeight(GetPhaseMask(), data->spawnPoint, true, MAX_FALL_DISTANCE); + if (data->spawnPoint.GetPositionZ() - tz > 0.1f && Trinity::IsValidMapCoord(tz)) + Relocate(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY(), tz); } } @@ -1535,8 +1597,11 @@ void Creature::LoadEquipment(int8 id, bool force /*= true*/) void Creature::SetSpawnHealth() { + if (_regenerateHealthLock) + return; + uint32 curhealth; - if (m_creatureData && !m_regenHealth) + if (m_creatureData && !_regenerateHealth) { curhealth = m_creatureData->curhealth; if (curhealth) @@ -1586,15 +1651,24 @@ void Creature::DeleteFromDB() return; } - GetMap()->RemoveCreatureRespawnTime(m_spawnId); + // remove any scheduled respawns + GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId); + + // delete data from memory sObjectMgr->DeleteCreatureData(m_spawnId); + // delete data and all its associations from DB SQLTransaction trans = WorldDatabase.BeginTransaction(); PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); stmt->setUInt32(0, m_spawnId); trans->Append(stmt); + stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER); + stmt->setUInt8(0, uint8(SPAWN_TYPE_CREATURE)); + stmt->setUInt32(1, m_spawnId); + trans->Append(stmt); + stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE_ADDON); stmt->setUInt32(0, m_spawnId); trans->Append(stmt); @@ -1608,6 +1682,11 @@ void Creature::DeleteFromDB() trans->Append(stmt); WorldDatabase.CommitTransaction(trans); + + // then delete any active instances of the creature + auto const& spawnMap = GetMap()->GetCreatureBySpawnIdStore(); + for (auto it = spawnMap.find(m_spawnId); it != spawnMap.end(); it = spawnMap.find(m_spawnId)) + it->second->AddObjectToRemoveList(); } bool Creature::IsInvisibleDueToDespawn() const @@ -1734,14 +1813,31 @@ void Creature::setDeathState(DeathState s) if (s == JUST_DIED) { m_corpseRemoveTime = time(nullptr) + m_corpseDelay; - if (IsDungeonBoss() && !m_respawnDelay) - m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance + + uint32 respawnDelay = m_respawnDelay; + if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE)) + GetMap()->ApplyDynamicModeRespawnScaling(this, m_spawnId, respawnDelay, scalingMode); + // @todo remove the boss respawn time hack in a dynspawn follow-up once we have creature groups in instances + if (m_respawnCompatibilityMode) + { + if (IsDungeonBoss() && !m_respawnDelay) + m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance + else + m_respawnTime = time(nullptr) + respawnDelay + m_corpseDelay; + } else - m_respawnTime = time(nullptr) + m_respawnDelay + m_corpseDelay; + { + if (IsDungeonBoss() && !m_respawnDelay) + m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance + else + m_respawnTime = time(nullptr) + respawnDelay; + } // always save boss respawn time at death to prevent crash cheating if (sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY) || isWorldBoss()) SaveRespawnTime(); + else if (!m_respawnCompatibilityMode) + SaveRespawnTime(0, false); ReleaseFocus(nullptr, false); // remove spellcast focus DoNotReacquireTarget(); // cancel delayed re-target @@ -1809,8 +1905,6 @@ void Creature::setDeathState(DeathState s) void Creature::Respawn(bool force) { - DestroyForNearbyPlayers(); - if (force) { if (IsAlive()) @@ -1819,51 +1913,59 @@ void Creature::Respawn(bool force) setDeathState(CORPSE); } - RemoveCorpse(false, false); - - if (getDeathState() == DEAD) + if (m_respawnCompatibilityMode) { - if (m_spawnId) - GetMap()->RemoveCreatureRespawnTime(m_spawnId); + DestroyForNearbyPlayers(); + RemoveCorpse(false, false); - TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", - GetName().c_str(), GetGUID().ToString().c_str()); - m_respawnTime = 0; - ResetPickPocketRefillTimer(); - loot.clear(); + if (getDeathState() == DEAD) + { + if (m_spawnId) + GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId); - if (m_originalEntry != GetEntry()) - UpdateEntry(m_originalEntry); + TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", GetName().c_str(), GetGUID().ToString().c_str()); + m_respawnTime = 0; + ResetPickPocketRefillTimer(); + loot.clear(); - SelectLevel(); + if (m_originalEntry != GetEntry()) + UpdateEntry(m_originalEntry); - setDeathState(JUST_RESPAWNED); + SelectLevel(); - uint32 displayID = GetNativeDisplayId(); - if (sObjectMgr->GetCreatureModelRandomGender(&displayID)) - { - SetDisplayId(displayID); - SetNativeDisplayId(displayID); - } + setDeathState(JUST_RESPAWNED); - GetMotionMaster()->InitDefault(); - //Re-initialize reactstate that could be altered by movementgenerators - InitializeReactState(); + uint32 displayID = GetNativeDisplayId(); + if (sObjectMgr->GetCreatureModelRandomGender(&displayID)) + { + SetDisplayId(displayID); + SetNativeDisplayId(displayID); + } - //Call AI respawn virtual function - if (IsAIEnabled) - { - //reset the AI to be sure no dirty or uninitialized values will be used till next tick - AI()->Reset(); - m_TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing - } + GetMotionMaster()->InitDefault(); + //Re-initialize reactstate that could be altered by movementgenerators + InitializeReactState(); - uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0; - if (poolid) - sPoolMgr->UpdatePool<Creature>(poolid, GetSpawnId()); + if (IsAIEnabled) // reset the AI to be sure no dirty or uninitialized values will be used till next tick + AI()->Reset(); + + m_triggerJustAppeared = true; + + uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<Creature>(GetSpawnId()) : 0; + if (poolid) + sPoolMgr->UpdatePool<Creature>(poolid, GetSpawnId()); + } + UpdateObjectVisibility(); + } + else + { + if (m_spawnId) + GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, true); } - UpdateObjectVisibility(); + TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", + GetName().c_str(), GetGUID().ToString().c_str()); + } void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds const& forceRespawnTimer) @@ -1874,30 +1976,48 @@ void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds const& forceRespawn return; } - uint32 corpseDelay = GetCorpseDelay(); - uint32 respawnDelay = GetRespawnDelay(); + if (m_respawnCompatibilityMode) + { + uint32 corpseDelay = GetCorpseDelay(); + uint32 respawnDelay = GetRespawnDelay(); + + // do it before killing creature + DestroyForNearbyPlayers(); - // do it before killing creature - DestroyForNearbyPlayers(); + bool overrideRespawnTime = false; + if (IsAlive()) + { + if (forceRespawnTimer > Seconds::zero()) + { + SetCorpseDelay(0); + SetRespawnDelay(forceRespawnTimer.count()); + overrideRespawnTime = true; + } - bool overrideRespawnTime = true; - if (IsAlive()) + setDeathState(JUST_DIED); + } + + // Skip corpse decay time + RemoveCorpse(!overrideRespawnTime, false); + + SetCorpseDelay(corpseDelay); + SetRespawnDelay(respawnDelay); + } + else { if (forceRespawnTimer > Seconds::zero()) + SaveRespawnTime(forceRespawnTimer.count()); + else { - SetCorpseDelay(0); - SetRespawnDelay(forceRespawnTimer.count()); - overrideRespawnTime = false; + uint32 respawnDelay = m_respawnDelay; + if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE)) + GetMap()->ApplyDynamicModeRespawnScaling(this, m_spawnId, respawnDelay, scalingMode); + m_respawnTime = time(NULL) + respawnDelay; + SaveRespawnTime(); } - setDeathState(JUST_DIED); + AddObjectToRemoveList(); } - - // Skip corpse decay time - RemoveCorpse(overrideRespawnTime, false); - - SetCorpseDelay(corpseDelay); - SetRespawnDelay(respawnDelay); } void Creature::DespawnOrUnsummon(uint32 msTimeToDespawn /*= 0*/, Seconds const& forceRespawnTimer /*= 0*/) @@ -2156,9 +2276,6 @@ void Creature::CallForHelp(float radius) bool Creature::CanAssistTo(Unit const* u, Unit const* enemy, bool checkfaction /*= true*/) const { - if (IsInEvadeMode()) - return false; - // is it true? if (!HasReactState(REACT_AGGRESSIVE)) return false; @@ -2244,12 +2361,19 @@ bool Creature::_IsTargetAcceptable(Unit const* target) const return false; } -void Creature::SaveRespawnTime() +void Creature::SaveRespawnTime(uint32 forceDelay, bool savetodb) { if (IsSummon() || !m_spawnId || (m_creatureData && !m_creatureData->dbData)) return; - GetMap()->SaveCreatureRespawnTime(m_spawnId, m_respawnTime); + if (m_respawnCompatibilityMode) + { + GetMap()->SaveRespawnTimeDB(SPAWN_TYPE_CREATURE, m_spawnId, m_respawnTime); + return; + } + + time_t thisRespawnTime = forceDelay ? time(NULL) + forceDelay : m_respawnTime; + GetMap()->SaveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, GetEntry(), thisRespawnTime, GetMap()->GetZoneId(GetHomePosition()), Trinity::ComputeGridCoord(GetHomePosition().GetPositionX(), GetHomePosition().GetPositionY()).GetId(), savetodb && m_creatureData && m_creatureData->dbData); } // this should not be called by petAI or @@ -2365,9 +2489,9 @@ bool Creature::LoadCreaturesAddon() if (cainfo->emote != 0) SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote); - //Load Path + // Load Path if (cainfo->path_id != 0) - m_path_id = cainfo->path_id; + _waypointPathId = cainfo->path_id; if (!cainfo->auras.empty()) { @@ -2461,33 +2585,26 @@ time_t Creature::GetRespawnTimeEx() const void Creature::GetRespawnPosition(float &x, float &y, float &z, float* ori, float* dist) const { - if (m_spawnId) + if (m_creatureData) { - // for npcs on transport, this will return transport offset - if (CreatureData const* data = sObjectMgr->GetCreatureData(GetSpawnId())) - { - x = data->posX; - y = data->posY; - z = data->posZ; - if (ori) - *ori = data->orientation; - if (dist) - *dist = data->spawndist; + if (ori) + m_creatureData->spawnPoint.GetPosition(x, y, z, *ori); + else + m_creatureData->spawnPoint.GetPosition(x, y, z); - return; - } + if (dist) + *dist = m_creatureData->spawndist; + } + else + { + Position const& homePos = GetHomePosition(); + if (ori) + homePos.GetPosition(x, y, z, *ori); + else + homePos.GetPosition(x, y, z); + if (dist) + *dist = 0; } - - // changed this from current position to home position, fixes world summons with infinite duration (wg npcs for example) - Position homePos = GetHomePosition(); - x = homePos.GetPositionX(); - y = homePos.GetPositionY(); - z = homePos.GetPositionZ(); - if (ori) - *ori = homePos.GetOrientation(); - - if (dist) - *dist = 0; } void Creature::AllLootRemovedFromCorpse() @@ -2538,7 +2655,7 @@ std::string Creature::GetScriptName() const uint32 Creature::GetScriptId() const { if (CreatureData const* creatureData = GetCreatureData()) - if (uint32 scriptId = creatureData->ScriptId) + if (uint32 scriptId = creatureData->scriptId) return scriptId; return sObjectMgr->GetCreatureTemplate(GetEntry())->ScriptID; @@ -3081,3 +3198,11 @@ bool Creature::CanGiveExperience() const && !IsTotem() && !(GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL); } + +bool Creature::IsEscortNPC(bool onlyIfActive) +{ + if (!IsAIEnabled) + return false; + + return AI()->IsEscortNPC(onlyIfActive); +} diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index cc12efbd7eb..4a7c3362f30 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -35,6 +35,7 @@ class Quest; class Player; class SpellInfo; class WorldSession; + enum MovementGeneratorType : uint8; struct VendorItemCount @@ -72,7 +73,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma void DisappearAndDie(); - bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, float x, float y, float z, float ang, CreatureData const* data = nullptr, uint32 vehId = 0); + bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, Position const& pos, CreatureData const* data = nullptr, uint32 vehId = 0, bool dynamic = false); bool LoadCreaturesAddon(); void SelectLevel(); void UpdateLevelDependantStats(); @@ -83,7 +84,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma void Update(uint32 time) override; // overwrited Unit::Update void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr, float* dist = nullptr) const; - bool IsSpawnedOnTransport() const { return m_creatureData && m_creatureData->mapid != GetMapId(); } + bool IsSpawnedOnTransport() const { return m_creatureData && m_creatureData->spawnPoint.GetMapId() != GetMapId(); } void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; } uint32 GetCorpseDelay() const { return m_corpseDelay; } @@ -176,8 +177,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma void setDeathState(DeathState s) override; // override virtual Unit::setDeathState - bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map) { return LoadCreatureFromDB(spawnId, map, false); } - bool LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true, bool allowDuplicate = false); + bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate); void SaveToDB(); // overriden in Pet virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); @@ -239,7 +239,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma time_t GetRespawnTimeEx() const; void SetRespawnTime(uint32 respawn) { m_respawnTime = respawn ? time(nullptr) + respawn : 0; } void Respawn(bool force = false); - void SaveRespawnTime() override; + void SaveRespawnTime(uint32 forceDelay = 0, bool savetodb = true) override; uint32 GetRespawnDelay() const { return m_respawnDelay; } void SetRespawnDelay(uint32 delay) { m_respawnDelay = delay; } @@ -266,8 +266,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool hasQuest(uint32 quest_id) const override; bool hasInvolvedQuest(uint32 quest_id) const override; - bool isRegeneratingHealth() { return m_regenHealth; } - void setRegeneratingHealth(bool regenHealth) { m_regenHealth = regenHealth; } + bool CanRegenerateHealth() { return !_regenerateHealthLock && _regenerateHealth; } + void SetRegenerateHealth(bool value) { _regenerateHealthLock = !value; } virtual uint8 GetPetAutoSpellSize() const { return MAX_SPELL_CHARM; } virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const; float GetPetChaseDistance() const; @@ -291,15 +291,21 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma void GetTransportHomePosition(float& x, float& y, float& z, float& ori) const { m_transportHomePosition.GetPosition(x, y, z, ori); } Position const& GetTransportHomePosition() const { return m_transportHomePosition; } - uint32 GetWaypointPath() const { return m_path_id; } - void LoadPath(uint32 pathid) { m_path_id = pathid; } + uint32 GetWaypointPath() const { return _waypointPathId; } + void LoadPath(uint32 pathid) { _waypointPathId = pathid; } + + // nodeId, pathId + std::pair<uint32, uint32> GetCurrentWaypointInfo() const { return _currentWaypointNodeInfo; } + void UpdateCurrentWaypointInfo(uint32 nodeId, uint32 pathId) { _currentWaypointNodeInfo = { nodeId, pathId }; } - uint32 GetCurrentWaypointID() const { return m_waypointID; } - void UpdateWaypointID(uint32 wpID) { m_waypointID = wpID; } + bool IsReturningHome() const; void SearchFormation(); CreatureGroup* GetFormation() { return m_formation; } void SetFormation(CreatureGroup* formation) { m_formation = formation; } + bool IsFormationLeader() const; + void SignalFormationMovement(Position const& destination, uint32 id = 0, uint32 moveType = 0, bool orientation = false); + bool IsFormationLeaderMoveAllowed() const; Unit* SelectVictim(); @@ -313,6 +319,10 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma uint32 GetOriginalEntry() const { return m_originalEntry; } void SetOriginalEntry(uint32 entry) { m_originalEntry = entry; } + // There's many places not ready for dynamic spawns. This allows them to live on for now. + void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; } + bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; } + static float _GetDamageMod(int32 Rank); float m_SightDistance, m_CombatDistance; @@ -336,6 +346,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma CreatureTextRepeatIds GetTextRepeatGroup(uint8 textGroup); void SetTextRepeatId(uint8 textGroup, uint8 id); void ClearTextRepeatGroup(uint8 textGroup); + bool IsEscortNPC(bool onlyIfActive = true); bool CanGiveExperience() const; @@ -372,7 +383,6 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool m_AlreadyCallAssistance; bool m_AlreadySearchedAssistance; - bool m_regenHealth; bool m_cannotReachTarget; uint32 m_cannotReachTimer; bool m_AI_locked; @@ -397,13 +407,14 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma void ForcedDespawn(uint32 timeMSToDespawn = 0, Seconds const& forceRespawnTimer = Seconds(0)); bool CheckNoGrayAggroConfig(uint32 playerLevel, uint32 creatureLevel) const; // No aggro from gray creatures - //WaypointMovementGenerator vars - uint32 m_waypointID; - uint32 m_path_id; + // Waypoint path + uint32 _waypointPathId; + std::pair<uint32/*nodeId*/, uint32/*pathId*/> _currentWaypointNodeInfo; //Formation var CreatureGroup* m_formation; - bool m_TriggerJustRespawned; + bool m_triggerJustAppeared; + bool m_respawnCompatibilityMode; /* Spell focus system */ Spell const* m_focusSpell; // Locks the target during spell cast for proper facing @@ -414,6 +425,10 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma time_t _lastDamagedTime; // Part of Evade mechanics CreatureTextRepeatGroup m_textRepeat; + + // Regenerate health + bool _regenerateHealth; // Set on creation + bool _regenerateHealthLock; // Dynamically set }; class TC_GAME_API AssistDelayEvent : public BasicEvent diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 754963f9812..de169c97136 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -20,6 +20,7 @@ #include "DBCEnums.h" #include "SharedDefines.h" +#include "SpawnData.h" #include "UnitDefines.h" #include "WorldPacket.h" #include <string> @@ -235,33 +236,19 @@ struct EquipmentInfo }; // from `creature` table -struct CreatureData +struct CreatureData : public SpawnData { - CreatureData() : id(0), mapid(0), phaseMask(0), displayid(0), equipmentId(0), - posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), spawntimesecs(0), - spawndist(0.0f), currentwaypoint(0), curhealth(0), curmana(0), movementType(0), - spawnMask(0), npcflag(0), unit_flags(0), dynamicflags(0), ScriptId(0), dbData(true) { } - uint32 id; // entry in creature_template - uint16 mapid; - uint32 phaseMask; - uint32 displayid; - int8 equipmentId; - float posX; - float posY; - float posZ; - float orientation; - uint32 spawntimesecs; - float spawndist; - uint32 currentwaypoint; - uint32 curhealth; - uint32 curmana; - uint8 movementType; - uint8 spawnMask; - uint32 npcflag; - uint32 unit_flags; // enum UnitFlags mask values - uint32 dynamicflags; - uint32 ScriptId; - bool dbData; + CreatureData() : SpawnData(SPAWN_TYPE_CREATURE) { } + uint32 displayid = 0; + int8 equipmentId = 0; + float spawndist = 0.0f; + uint32 currentwaypoint = 0; + uint32 curhealth = 0; + uint32 curmana = 0; + uint8 movementType = 0; + uint32 npcflag = 0; + uint32 unit_flags = 0; + uint32 dynamicflags = 0; }; struct CreatureModelInfo diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp index 3984a81cf71..4297a745ccc 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.cpp +++ b/src/server/game/Entities/Creature/CreatureGroups.cpp @@ -93,7 +93,7 @@ void FormationMgr::LoadCreatureFormations() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 creatures in formations. DB table `creature_formations` is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in formations. DB table `creature_formations` is empty!"); return; } @@ -236,7 +236,7 @@ void CreatureGroup::LeaderMoveTo(Position const& destination, uint32 id /*= 0*/, continue; if (itr->second->point_1) - if (m_leader->GetCurrentWaypointID() == itr->second->point_1 - 1 || m_leader->GetCurrentWaypointID() == itr->second->point_2 - 1) + if (m_leader->GetCurrentWaypointInfo().first == itr->second->point_1 || m_leader->GetCurrentWaypointInfo().first == itr->second->point_2) itr->second->follow_angle = float(M_PI) * 2 - itr->second->follow_angle; float angle = itr->second->follow_angle; @@ -258,3 +258,17 @@ void CreatureGroup::LeaderMoveTo(Position const& destination, uint32 id /*= 0*/, member->SetHomePosition(dx, dy, dz, pathangle); } } + +bool CreatureGroup::CanLeaderStartMoving() const +{ + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) + { + if (itr->first != m_leader && itr->first->IsAlive()) + { + if (itr->first->IsEngaged() || itr->first->IsReturningHome()) + return false; + } + } + + return true; +} diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h index b36144db42f..52eb4eddf2a 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.h +++ b/src/server/game/Entities/Creature/CreatureGroups.h @@ -84,6 +84,7 @@ class TC_GAME_API CreatureGroup uint32 GetId() const { return m_groupID; } bool isEmpty() const { return m_members.empty(); } bool isFormed() const { return m_Formed; } + bool IsLeader(Creature const* creature) const { return m_leader == creature; } void AddMember(Creature* member); void RemoveMember(Creature* member); @@ -91,6 +92,7 @@ class TC_GAME_API CreatureGroup void LeaderMoveTo(Position const& destination, uint32 id = 0, uint32 moveType = 0, bool orientation = false); void MemberEngagingTarget(Creature* member, Unit* target); + bool CanLeaderStartMoving() const; }; #define sFormationMgr FormationMgr::instance() diff --git a/src/server/game/Entities/Creature/GossipDef.cpp b/src/server/game/Entities/Creature/GossipDef.cpp index caa700f6672..4a39c233fc2 100644 --- a/src/server/game/Entities/Creature/GossipDef.cpp +++ b/src/server/game/Entities/Creature/GossipDef.cpp @@ -323,16 +323,33 @@ void QuestMenu::ClearMenu() _questMenuItems.clear(); } -void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string& Title, ObjectGuid npcGUID) +void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string& Title, ObjectGuid guid) { WorldPacket data(SMSG_QUESTGIVER_QUEST_LIST, 100); // guess size - data << uint64(npcGUID); - data << Title; - data << uint32(eEmote._Delay); // player emote - data << uint32(eEmote._Emote); // NPC emote + data << uint64(guid); + + if (QuestGreeting const* questGreeting = sObjectMgr->GetQuestGreeting(guid)) + { + std::string strGreeting = questGreeting->greeting; + + LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex(); + if (localeConstant != LOCALE_enUS) + if (QuestGreetingLocale const* questGreetingLocale = sObjectMgr->GetQuestGreetingLocale(MAKE_PAIR32(guid.GetEntry(), guid.GetTypeId()))) + ObjectMgr::GetLocaleString(questGreetingLocale->greeting, localeConstant, strGreeting); + + data << strGreeting; + data << uint32(questGreeting->greetEmoteDelay); + data << uint32(questGreeting->greetEmoteType); + } + else + { + data << Title; + data << uint32(eEmote._Delay); // player emote + data << uint32(eEmote._Emote); // NPC emote + } size_t count_pos = data.wpos(); - data << uint8 (0); + data << uint8(0); uint32 count = 0; // Store this instead of checking the Singleton every loop iteration @@ -340,9 +357,9 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string for (uint32 i = 0; i < _questMenu.GetMenuItemCount(); ++i) { - QuestMenuItem const& qmi = _questMenu.GetItem(i); + QuestMenuItem const& questMenuItem = _questMenu.GetItem(i); - uint32 questID = qmi.QuestId; + uint32 questID = questMenuItem.QuestId; if (Quest const* quest = sObjectMgr->GetQuestTemplate(questID)) { @@ -351,14 +368,14 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex(); if (localeConstant != LOCALE_enUS) - if (QuestLocale const* localeData = sObjectMgr->GetQuestLocale(questID)) - ObjectMgr::GetLocaleString(localeData->Title, localeConstant, title); + if (QuestLocale const* questTemplateLocaleData = sObjectMgr->GetQuestLocale(questID)) + ObjectMgr::GetLocaleString(questTemplateLocaleData->Title, localeConstant, title); if (questLevelInTitle) Quest::AddQuestLevelToTitle(title, quest->GetQuestLevel()); data << uint32(questID); - data << uint32(qmi.QuestIcon); + data << uint32(questMenuItem.QuestIcon); data << int32(quest->GetQuestLevel()); data << uint32(quest->GetFlags()); // 3.3.3 quest flags data << uint8(0); // 3.3.3 changes icon: blue question or yellow exclamation @@ -368,7 +385,8 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string data.put<uint8>(count_pos, count); _session->SendPacket(&data); - TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST NPC=%s", npcGUID.ToString().c_str()); + + TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST (QuestGiver: %s)", guid.ToString().c_str()); } void PlayerMenu::SendQuestGiverStatus(uint8 questStatus, ObjectGuid npcGUID) const diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp index 7e9cf6bf7a3..70a7e3446d4 100644 --- a/src/server/game/Entities/Creature/TemporarySummon.cpp +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp @@ -57,6 +57,7 @@ void TempSummon::Update(uint32 diff) switch (m_type) { case TEMPSUMMON_MANUAL_DESPAWN: + case TEMPSUMMON_DEAD_DESPAWN: break; case TEMPSUMMON_TIMED_DESPAWN: { @@ -104,7 +105,7 @@ void TempSummon::Update(uint32 diff) case TEMPSUMMON_CORPSE_DESPAWN: { // if m_deathState is DEAD, CORPSE was skipped - if (m_deathState == CORPSE || m_deathState == DEAD) + if (m_deathState == CORPSE) { UnSummon(); return; @@ -112,19 +113,9 @@ void TempSummon::Update(uint32 diff) break; } - case TEMPSUMMON_DEAD_DESPAWN: - { - if (m_deathState == DEAD) - { - UnSummon(); - return; - } - break; - } case TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN: { - // if m_deathState is DEAD, CORPSE was skipped - if (m_deathState == CORPSE || m_deathState == DEAD) + if (m_deathState == CORPSE) { UnSummon(); return; @@ -146,13 +137,6 @@ void TempSummon::Update(uint32 diff) } case TEMPSUMMON_TIMED_OR_DEAD_DESPAWN: { - // if m_deathState is DEAD, CORPSE was skipped - if (m_deathState == DEAD) - { - UnSummon(); - return; - } - if (!IsInCombat() && IsAlive()) { if (m_timer <= diff) diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index be0406be5b0..ce9658ef37e 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -101,7 +101,7 @@ QuaternionData QuaternionData::fromEulerAnglesZYX(float Z, float Y, float X) } GameObject::GameObject() : WorldObject(false), MapObject(), - m_model(nullptr), m_goValue(), m_AI(nullptr) + m_model(nullptr), m_goValue(), m_AI(nullptr), m_respawnCompatibilityMode(false) { m_objectType |= TYPEMASK_GAMEOBJECT; m_objectTypeId = TYPEID_GAMEOBJECT; @@ -240,7 +240,7 @@ void GameObject::RemoveFromWorld() } } -bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit /*= 0*/) +bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit /*= 0*/, bool dynamic, ObjectGuid::LowType spawnid) { ASSERT(map); SetMap(map); @@ -253,7 +253,12 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u return false; } + // Set if this object can handle dynamic spawns + if (!dynamic) + SetRespawnCompatibilityMode(); + SetPhaseMask(phaseMask, false); + UpdatePositionData(); SetZoneScript(); if (m_zoneScript) @@ -375,6 +380,9 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u if (map->Is25ManRaid()) loot.maxDuplicates = 3; + if (spawnid) + m_spawnId = spawnid; + if (uint32 linkedEntry = GetGOInfo()->GetLinkedGameObjectEntry()) { GameObject* linkedGO = new GameObject(); @@ -486,83 +494,90 @@ void GameObject::Update(uint32 diff) } case GO_READY: { - if (m_respawnTime > 0) // timer on + if (m_respawnCompatibilityMode) { - time_t now = time(nullptr); - if (m_respawnTime <= now) // timer expired + if (m_respawnTime > 0) // timer on { - ObjectGuid dbtableHighGuid(HighGuid::GameObject, GetEntry(), m_spawnId); - time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid); - if (linkedRespawntime) // Can't respawn, the master is dead + time_t now = time(nullptr); + if (m_respawnTime <= now) // timer expired { - ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid); - if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day) - SetRespawnTime(DAY); - else - m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little - SaveRespawnTime(); // also save to DB immediately - return; - } + ObjectGuid dbtableHighGuid(HighGuid::GameObject, GetEntry(), m_spawnId); + time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid); + if (linkedRespawntime) // Can't respawn, the master is dead + { + ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid); + if (targetGuid == dbtableHighGuid) // if linking self, never respawn + SetRespawnTime(WEEK); + else + m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little + SaveRespawnTime(); // also save to DB immediately + return; + } - m_respawnTime = 0; - m_SkillupList.clear(); - m_usetimes = 0; + m_respawnTime = 0; + m_SkillupList.clear(); + m_usetimes = 0; - // If nearby linked trap exists, respawn it - if (GameObject* linkedTrap = GetLinkedTrap()) - linkedTrap->SetLootState(GO_READY); + // If nearby linked trap exists, respawn it + if (GameObject* linkedTrap = GetLinkedTrap()) + linkedTrap->SetLootState(GO_READY); - switch (GetGoType()) - { - case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now + switch (GetGoType()) { - Unit* caster = GetOwner(); - if (caster && caster->GetTypeId() == TYPEID_PLAYER) + case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now { - caster->ToPlayer()->RemoveGameObject(this, false); - - WorldPacket data(SMSG_FISH_ESCAPED, 0); - caster->ToPlayer()->SendDirectMessage(&data); + Unit* caster = GetOwner(); + if (caster && caster->GetTypeId() == TYPEID_PLAYER) + { + caster->ToPlayer()->RemoveGameObject(this, false); + + WorldPacket data(SMSG_FISH_ESCAPED, 0); + caster->ToPlayer()->SendDirectMessage(&data); + } + // can be delete + m_lootState = GO_JUST_DEACTIVATED; + return; } - // can be delete - m_lootState = GO_JUST_DEACTIVATED; - return; + case GAMEOBJECT_TYPE_DOOR: + case GAMEOBJECT_TYPE_BUTTON: + // We need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds) + if (GetGoState() != GO_STATE_READY) + ResetDoorOrButton(); + break; + case GAMEOBJECT_TYPE_FISHINGHOLE: + // Initialize a new max fish count on respawn + m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishinghole.minSuccessOpens, GetGOInfo()->fishinghole.maxSuccessOpens); + break; + default: + break; } - case GAMEOBJECT_TYPE_DOOR: - case GAMEOBJECT_TYPE_BUTTON: - // We need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds) - if (GetGoState() != GO_STATE_READY) - ResetDoorOrButton(); - break; - case GAMEOBJECT_TYPE_FISHINGHOLE: - // Initialize a new max fish count on respawn - m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishinghole.minSuccessOpens, GetGOInfo()->fishinghole.maxSuccessOpens); - break; - default: - break; - } - // Despawn timer - if (!m_spawnedByDefault) - { - // Can be despawned or destroyed - SetLootState(GO_JUST_DEACTIVATED); - return; - } + // Despawn timer + if (!m_spawnedByDefault) + { + // Can be despawned or destroyed + SetLootState(GO_JUST_DEACTIVATED); + return; + } - // Call AI Reset (required for example in SmartAI to clear one time events) - if (AI()) - AI()->Reset(); + // Call AI Reset (required for example in SmartAI to clear one time events) + if (AI()) + AI()->Reset(); - // Respawn timer - uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<GameObject>(GetSpawnId()) : 0; - if (poolid) - sPoolMgr->UpdatePool<GameObject>(poolid, GetSpawnId()); - else - GetMap()->AddToMap(this); + // Respawn timer + uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<GameObject>(GetSpawnId()) : 0; + if (poolid) + sPoolMgr->UpdatePool<GameObject>(poolid, GetSpawnId()); + else + GetMap()->AddToMap(this); + } } } + // Set respawn timer + if (!m_respawnCompatibilityMode && m_respawnTime > 0) + SaveRespawnTime(0, false); + if (isSpawned()) { GameObjectTemplate const* goInfo = GetGOInfo(); @@ -750,6 +765,7 @@ void GameObject::Update(uint32 diff) if (!m_respawnDelayTime) return; + // ToDo: Decide if we should properly despawn these. Maybe they expect to be able to manually respawn from script? if (!m_spawnedByDefault) { m_respawnTime = 0; @@ -757,12 +773,28 @@ void GameObject::Update(uint32 diff) return; } - m_respawnTime = time(nullptr) + m_respawnDelayTime; + uint32 respawnDelay = m_respawnDelayTime; + if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE)) + GetMap()->ApplyDynamicModeRespawnScaling(this, this->m_spawnId, respawnDelay, scalingMode); + m_respawnTime = time(nullptr) + respawnDelay; // if option not set then object will be saved at grid unload + // Otherwise just save respawn time to map object memory if (sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY)) SaveRespawnTime(); + if (!m_respawnCompatibilityMode) + { + // Respawn time was just saved if set to save to DB + // If not, we save only to map memory + if (!sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY)) + SaveRespawnTime(0, false); + + // Then despawn + AddObjectToRemoveList(); + return; + } + DestroyForNearbyPlayers(); // old UpdateObjectVisibility() break; } @@ -848,7 +880,7 @@ void GameObject::SaveToDB() { // this should only be used when the gameobject has already been loaded // preferably after adding to map, because mapid may not be valid otherwise - GameObjectData const* data = sObjectMgr->GetGOData(m_spawnId); + GameObjectData const* data = sObjectMgr->GetGameObjectData(m_spawnId); if (!data) { TC_LOG_ERROR("misc", "GameObject::SaveToDB failed, cannot get gameobject data!"); @@ -868,22 +900,22 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) m_spawnId = sObjectMgr->GenerateGameObjectSpawnId(); // update in loaded data (changing data only in this place) - GameObjectData& data = sObjectMgr->NewGOData(m_spawnId); + GameObjectData& data = sObjectMgr->NewOrExistGameObjectData(m_spawnId); - // data->guid = guid must not be updated at save + if (!data.spawnId) + data.spawnId = m_spawnId; + ASSERT(data.spawnId == m_spawnId); data.id = GetEntry(); - data.mapid = mapid; + data.spawnPoint.WorldRelocate(this); data.phaseMask = phaseMask; - data.posX = GetPositionX(); - data.posY = GetPositionY(); - data.posZ = GetPositionZ(); - data.orientation = GetOrientation(); data.rotation = m_worldRotation; data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime; data.animprogress = GetGoAnimProgress(); - data.go_state = GetGoState(); + data.goState = GetGoState(); data.spawnMask = spawnMask; data.artKit = GetGoArtKit(); + if (!data.spawnGroupData) + data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup(); // Update in DB SQLTransaction trans = WorldDatabase.BeginTransaction(); @@ -916,9 +948,9 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) WorldDatabase.CommitTransaction(trans); } -bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap) +bool GameObject::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool) { - GameObjectData const* data = sObjectMgr->GetGOData(spawnId); + GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId); if (!data) { @@ -929,14 +961,14 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo uint32 entry = data->id; //uint32 map_id = data->mapid; // already used before call uint32 phaseMask = data->phaseMask; - Position pos(data->posX, data->posY, data->posZ, data->orientation); uint32 animprogress = data->animprogress; - GOState go_state = data->go_state; + GOState go_state = data->goState; uint32 artKit = data->artKit; m_spawnId = spawnId; - if (!Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, pos, data->rotation, animprogress, go_state, artKit)) + m_respawnCompatibilityMode = ((data->spawnGroupData->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) != 0); + if (!Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, data->spawnPoint, data->rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode)) return false; if (data->spawntimesecs >= 0) @@ -958,7 +990,7 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo if (m_respawnTime && m_respawnTime <= time(nullptr)) { m_respawnTime = 0; - GetMap()->RemoveGORespawnTime(m_spawnId); + GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId); } } } @@ -979,20 +1011,25 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo void GameObject::DeleteFromDB() { - GetMap()->RemoveGORespawnTime(m_spawnId); - sObjectMgr->DeleteGOData(m_spawnId); + GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId); + sObjectMgr->DeleteGameObjectData(m_spawnId); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT); + SQLTransaction trans = WorldDatabase.BeginTransaction(); + PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT); stmt->setUInt32(0, m_spawnId); + trans->Append(stmt); - WorldDatabase.Execute(stmt); + stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER); + stmt->setUInt8(0, uint8(SPAWN_TYPE_GAMEOBJECT)); + stmt->setUInt32(1, m_spawnId); + trans->Append(stmt); stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_EVENT_GAMEOBJECT); - stmt->setUInt32(0, m_spawnId); + trans->Append(stmt); - WorldDatabase.Execute(stmt); + WorldDatabase.CommitTransaction(trans); } /*********************************************************/ @@ -1055,10 +1092,19 @@ Unit* GameObject::GetOwner() const return ObjectAccessor::GetUnit(*this, GetOwnerGUID()); } -void GameObject::SaveRespawnTime() +void GameObject::SaveRespawnTime(uint32 forceDelay, bool savetodb) { - if (m_goData && m_goData->dbData && m_respawnTime > time(nullptr) && m_spawnedByDefault) - GetMap()->SaveGORespawnTime(m_spawnId, m_respawnTime); + if (m_goData && m_respawnTime > time(nullptr) && m_spawnedByDefault) + { + if (m_respawnCompatibilityMode) + { + GetMap()->SaveRespawnTimeDB(SPAWN_TYPE_GAMEOBJECT, m_spawnId, m_respawnTime); + return; + } + + uint32 thisRespawnTime = forceDelay ? time(nullptr) + forceDelay : m_respawnTime; + GetMap()->SaveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId, GetEntry(), thisRespawnTime, GetZoneId(), Trinity::ComputeGridCoord(GetPositionX(), GetPositionY()).GetId(), m_goData->dbData ? savetodb : false); + } } bool GameObject::IsNeverVisible() const @@ -1123,7 +1169,7 @@ void GameObject::Respawn() if (m_spawnedByDefault && m_respawnTime > 0) { m_respawnTime = time(nullptr); - GetMap()->RemoveGORespawnTime(m_spawnId); + GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId, true); } } @@ -1227,7 +1273,7 @@ void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = f void GameObject::SetGoArtKit(uint8 kit) { SetByteValue(GAMEOBJECT_BYTES_1, 2, kit); - GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGOData(m_spawnId)); + GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGameObjectData(m_spawnId)); if (data) data->artKit = kit; } @@ -1238,10 +1284,10 @@ void GameObject::SetGoArtKit(uint8 artkit, GameObject* go, ObjectGuid::LowType l if (go) { go->SetGoArtKit(artkit); - data = go->GetGOData(); + data = go->GetGameObjectData(); } else if (lowguid) - data = sObjectMgr->GetGOData(lowguid); + data = sObjectMgr->GetGameObjectData(lowguid); if (data) const_cast<GameObjectData*>(data)->artKit = artkit; @@ -1971,8 +2017,8 @@ void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= nullptr*/) uint32 GameObject::GetScriptId() const { - if (GameObjectData const* gameObjectData = GetGOData()) - if (uint32 scriptId = gameObjectData->ScriptId) + if (GameObjectData const* gameObjectData = GetGameObjectData()) + if (uint32 scriptId = gameObjectData->scriptId) return scriptId; return GetGOInfo()->ScriptId; @@ -2410,24 +2456,20 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = nullptr*/) const { - if (m_spawnId) + if (m_goData) { - if (GameObjectData const* data = sObjectMgr->GetGOData(GetSpawnId())) - { - x = data->posX; - y = data->posY; - z = data->posZ; - if (ori) - *ori = data->orientation; - return; - } + if (ori) + m_goData->spawnPoint.GetPosition(x, y, z, *ori); + else + m_goData->spawnPoint.GetPosition(x, y, z); + } + else + { + if (ori) + GetPosition(x, y, z, *ori); + else + GetPosition(x, y, z); } - - x = GetPositionX(); - y = GetPositionY(); - z = GetPositionZ(); - if (ori) - *ori = GetOrientation(); } float GameObject::GetInteractionDistance() const diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 3080bd94860..784af8fda27 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -89,11 +89,11 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void RemoveFromWorld() override; void CleanupsBeforeDelete(bool finalCleanup = true) override; - bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0); + bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, QuaternionData const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0, bool dynamic = false, ObjectGuid::LowType spawnid = 0); void Update(uint32 p_time) override; GameObjectTemplate const* GetGOInfo() const { return m_goInfo; } GameObjectTemplateAddon const* GetTemplateAddon() const { return m_goTemplateAddon; } - GameObjectData const* GetGOData() const { return m_goData; } + GameObjectData const* GetGameObjectData() const { return m_goData; } GameObjectValue const* GetGOValue() const { return &m_goValue; } bool IsTransport() const; @@ -113,8 +113,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void SaveToDB(); void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); - bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map) { return LoadGameObjectFromDB(spawnId, map, false); } - bool LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true); + bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool = true); // arg4 is unused, only present to match the signature on Creature void DeleteFromDB(); void SetOwnerGUID(ObjectGuid owner) @@ -212,7 +211,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> uint32 GetUseCount() const { return m_usetimes; } uint32 GetUniqueUseCount() const { return m_unique_users.size(); } - void SaveRespawnTime() override; + void SaveRespawnTime(uint32 forceDelay = 0, bool savetodb = true) override; Loot loot; @@ -264,6 +263,10 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void EventInform(uint32 eventId, WorldObject* invoker = nullptr); + // There's many places not ready for dynamic spawns. This allows them to live on for now. + void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; } + bool GetRespawnCompatibilityMode() {return m_respawnCompatibilityMode; } + uint32 GetScriptId() const; GameObjectAI* AI() const { return m_AI; } @@ -344,5 +347,6 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> return IsInRange(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare); } GameObjectAI* m_AI; + bool m_respawnCompatibilityMode; }; #endif diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h index e2d2f27e5b5..6e1a07786f9 100644 --- a/src/server/game/Entities/GameObject/GameObjectData.h +++ b/src/server/game/Entities/GameObject/GameObjectData.h @@ -20,6 +20,7 @@ #include "Common.h" #include "SharedDefines.h" +#include "SpawnData.h" #include "WorldPacket.h" #include <string> #include <vector> @@ -593,26 +594,14 @@ struct GameObjectAddon uint32 InvisibilityValue; }; -// from `gameobject` -struct GameObjectData +// `gameobject` table +struct GameObjectData : public SpawnData { - explicit GameObjectData() : id(0), mapid(0), phaseMask(0), posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), spawntimesecs(0), - animprogress(0), go_state(GO_STATE_ACTIVE), spawnMask(0), artKit(0), ScriptId(0), dbData(true) { } - uint32 id; // entry in gamobject_template - uint16 mapid; - uint32 phaseMask; - float posX; - float posY; - float posZ; - float orientation; + GameObjectData() : SpawnData(SPAWN_TYPE_GAMEOBJECT) { } QuaternionData rotation; - int32 spawntimesecs; - uint32 animprogress; - GOState go_state; - uint8 spawnMask; - uint8 artKit; - uint32 ScriptId; - bool dbData; + uint32 animprogress = 0; + GOState goState = GO_STATE_ACTIVE; + uint8 artKit = 0; }; #endif // GameObjectData_h__ diff --git a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp index 50e0805797c..1468d168ffa 100644 --- a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp +++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp @@ -76,7 +76,7 @@ void LoadRandomEnchantmentsTable() TC_LOG_INFO("server.loading", ">> Loaded %u Item Enchantment definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } else - TC_LOG_ERROR("server.loading", ">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty."); } uint32 GetItemEnchantMod(int32 entry) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 508a89a86c8..9e2546f2df7 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1105,7 +1105,7 @@ void WorldObject::RemoveFromWorld() Object::RemoveFromWorld(); } -InstanceScript* WorldObject::GetInstanceScript() +InstanceScript* WorldObject::GetInstanceScript() const { Map* map = GetMap(); return map->IsDungeon() ? ((InstanceMap*)map)->GetInstanceScript() : nullptr; @@ -1908,7 +1908,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert break; } - if (!summon->Create(GenerateLowGuid<HighGuid::Unit>(), this, phase, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), nullptr, vehId)) + if (!summon->Create(GenerateLowGuid<HighGuid::Unit>(), this, phase, entry, pos, nullptr, vehId, true)) { delete summon; return nullptr; diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index ca1b547f68b..b6cee547126 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -291,7 +291,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation uint32 GetAreaId() const { return m_areaId; } void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const { zoneid = m_zoneId, areaid = m_areaId; } - InstanceScript* GetInstanceScript(); + InstanceScript* GetInstanceScript() const; std::string const& GetName() const { return m_name; } void SetName(std::string const& newname) { m_name = newname; } @@ -342,7 +342,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation void SendObjectDeSpawnAnim(ObjectGuid guid); - virtual void SaveRespawnTime() { } + virtual void SaveRespawnTime(uint32 /*forceDelay*/ = 0, bool /*saveToDB*/ = true) { } void AddObjectToRemoveList(); float GetGridActivationRange() const; diff --git a/src/server/game/Entities/Object/ObjectGuid.cpp b/src/server/game/Entities/Object/ObjectGuid.cpp index 3a26dfd0d7f..71837474ca9 100644 --- a/src/server/game/Entities/Object/ObjectGuid.cpp +++ b/src/server/game/Entities/Object/ObjectGuid.cpp @@ -98,6 +98,14 @@ void ObjectGuidGeneratorBase::HandleCounterOverflow(HighGuid high) World::StopNow(ERROR_EXIT_CODE); } +void ObjectGuidGeneratorBase::CheckGuidTrigger(ObjectGuid::LowType guidlow) +{ + if (!sWorld->IsGuidAlert() && guidlow > sWorld->getIntConfig(CONFIG_RESPAWN_GUIDALERTLEVEL)) + sWorld->TriggerGuidAlert(); + else if (!sWorld->IsGuidWarning() && guidlow > sWorld->getIntConfig(CONFIG_RESPAWN_GUIDWARNLEVEL)) + sWorld->TriggerGuidWarning(); +} + #define GUID_TRAIT_INSTANTIATE_GUID( HIGH_GUID ) \ template class TC_GAME_API ObjectGuidGenerator< HIGH_GUID >; diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h index 75410aa4f28..d1e34d3a38c 100644 --- a/src/server/game/Entities/Object/ObjectGuid.h +++ b/src/server/game/Entities/Object/ObjectGuid.h @@ -287,6 +287,7 @@ public: protected: static void HandleCounterOverflow(HighGuid high); + static void CheckGuidTrigger(ObjectGuid::LowType guid); ObjectGuid::LowType _nextGuid; }; @@ -300,6 +301,10 @@ public: { if (_nextGuid >= ObjectGuid::GetMaxCounter(high) - 1) HandleCounterOverflow(high); + + if (high == HighGuid::Unit || high == HighGuid::GameObject) + CheckGuidTrigger(_nextGuid); + return _nextGuid++; } }; diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h index 98cb82df2e1..11867c6fed6 100644 --- a/src/server/game/Entities/Object/Position.h +++ b/src/server/game/Entities/Object/Position.h @@ -207,15 +207,9 @@ public: return GetExactDist2dSq(pos) < dist * dist; } - bool IsInDist(float x, float y, float z, float dist) const - { - return GetExactDistSq(x, y, z) < dist * dist; - } - - bool IsInDist(Position const* pos, float dist) const - { - return GetExactDistSq(pos) < dist * dist; - } + bool IsInDist(float x, float y, float z, float dist) const { return GetExactDistSq(x, y, z) < dist * dist; } + bool IsInDist(Position const& pos, float dist) const { return GetExactDistSq(pos) < dist * dist; } + bool IsInDist(Position const* pos, float dist) const { return GetExactDistSq(pos) < dist * dist; } bool IsWithinBox(Position const& center, float xradius, float yradius, float zradius) const; @@ -245,15 +239,12 @@ class WorldLocation : public Position WorldLocation(WorldLocation const& loc) : Position(loc), m_mapId(loc.GetMapId()) { } - void WorldRelocate(WorldLocation const& loc) - { - m_mapId = loc.GetMapId(); - Relocate(loc); - } - - void WorldRelocate(uint32 _mapId = MAPID_INVALID, float x = 0.f, float y = 0.f, float z = 0.f, float o = 0.f) + void WorldRelocate(WorldLocation const& loc) { m_mapId = loc.GetMapId(); Relocate(loc); } + void WorldRelocate(WorldLocation const* loc) { m_mapId = loc->GetMapId(); Relocate(loc); } + void WorldRelocate(uint32 mapId, Position const& pos) { m_mapId = mapId; Relocate(pos); } + void WorldRelocate(uint32 mapId = MAPID_INVALID, float x = 0.f, float y = 0.f, float z = 0.f, float o = 0.f) { - m_mapId = _mapId; + m_mapId = mapId; Relocate(x, y, z, o); } diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 20f9e57b666..6890113178a 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1306,24 +1306,22 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel if (itr != m_spells.end()) { if (itr->second.state == PETSPELL_REMOVED) - { - m_spells.erase(itr); state = PETSPELL_CHANGED; - } - else if (state == PETSPELL_UNCHANGED && itr->second.state != PETSPELL_UNCHANGED) + else { - // can be in case spell loading but learned at some previous spell loading - itr->second.state = PETSPELL_UNCHANGED; + if (state == PETSPELL_UNCHANGED && itr->second.state != PETSPELL_UNCHANGED) + { + // can be in case spell loading but learned at some previous spell loading + itr->second.state = PETSPELL_UNCHANGED; - if (active == ACT_ENABLED) - ToggleAutocast(spellInfo, true); - else if (active == ACT_DISABLED) - ToggleAutocast(spellInfo, false); + if (active == ACT_ENABLED) + ToggleAutocast(spellInfo, true); + else if (active == ACT_DISABLED) + ToggleAutocast(spellInfo, false); + } return false; } - else - return false; } PetSpell newspell; @@ -1520,7 +1518,7 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab) } // if remove last rank or non-ranked then update action bar at server and client if need - if (m_charmInfo->RemoveSpellFromActionBar(spell_id) && !learn_prev && clear_ab) + if (clear_ab && !learn_prev && m_charmInfo->RemoveSpellFromActionBar(spell_id)) { if (!m_loading) GetOwner()->PetSpellInitialize(); // need update action bar for last removed rank diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 032c02f2288..7bf4e185efd 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -176,6 +176,7 @@ Player::Player(WorldSession* session): Unit(true) m_session = session; m_ingametime = 0; + m_sharedQuestId = 0; m_ExtraFlags = 0; @@ -490,6 +491,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo } SetMap(sMapMgr->CreateMap(info->mapId, this)); + UpdatePositionData(); uint8 powertype = cEntry->powerType; @@ -6444,6 +6446,12 @@ void Player::CheckAreaExploreAndOutdoor() XP = uint32(sObjectMgr->GetBaseXP(areaEntry->area_level)*sWorld->getRate(RATE_XP_EXPLORE)); } + if (sWorld->getIntConfig(CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO)) + { + uint32 minScaledXP = uint32(sObjectMgr->GetBaseXP(areaEntry->area_level)*sWorld->getRate(RATE_XP_EXPLORE)) * sWorld->getIntConfig(CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO) / 100; + XP = std::max(minScaledXP, XP); + } + GiveXP(XP, nullptr); SendExplorationExperience(areaId, XP); } @@ -6993,24 +7001,25 @@ void Player::UpdateArea(uint32 newArea) void Player::UpdateZone(uint32 newZone, uint32 newArea) { - if (m_zoneUpdateId != newZone) + if (!IsInWorld()) + return; + uint32 const oldZone = m_zoneUpdateId; + m_zoneUpdateId = newZone; + m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL; + + GetMap()->UpdatePlayerZoneStats(oldZone, newZone); + + // call leave script hooks immedately (before updating flags) + if (oldZone != newZone) { sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); - sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone); sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); - sBattlefieldMgr->HandlePlayerEnterZone(this, newZone); - SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange... - if (Guild* guild = GetGuild()) - guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone); } // group update if (GetGroup()) SetGroupUpdateFlag(GROUP_UPDATE_FULL); - m_zoneUpdateId = newZone; - m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL; - // zone changed, so area changed as well, update it UpdateArea(newArea); @@ -7027,8 +7036,6 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) WeatherMgr::SendFineWeatherUpdateToPlayer(this); } - sScriptMgr->OnPlayerUpdateZone(this, newZone, newArea); - // in PvP, any not controlled zone (except zone->team == 6, default case) // in PvE, only opposition team capital switch (zone->team) @@ -7075,6 +7082,17 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) UpdateLocalChannels(newZone); UpdateZoneDependentAuras(newZone); + + // call enter script hooks after everyting else has processed + sScriptMgr->OnPlayerUpdateZone(this, newZone, newArea); + if (oldZone != newZone) + { + sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone); + sBattlefieldMgr->HandlePlayerEnterZone(this, newZone); + SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange... + if (Guild* guild = GetGuild()) + guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone); + } } //If players are too far away from the duel flag... they lose the duel @@ -11194,6 +11212,9 @@ InventoryResult Player::CanEquipItem(uint8 slot, uint16 &dest, Item* pItem, bool if (HasUnitState(UNIT_STATE_STUNNED)) return EQUIP_ERR_YOU_ARE_STUNNED; + if (IsCharmed()) + return EQUIP_ERR_CANT_DO_RIGHT_NOW; // @todo is this the correct error? + // do not allow equipping gear except weapons, offhands, projectiles, relics in // - combat // - in-progress arenas @@ -11351,6 +11372,9 @@ InventoryResult Player::CanUnequipItem(uint16 pos, bool swap) const if (pItem->m_lootGenerated) return EQUIP_ERR_ALREADY_LOOTED; + if (IsCharmed()) + return EQUIP_ERR_CANT_DO_RIGHT_NOW; // @todo is this the correct error? + // do not allow unequipping gear except weapons, offhands, projectiles, relics in // - combat // - in-progress arenas @@ -11775,14 +11799,11 @@ void Player::SetAmmo(uint32 item) return; // check ammo - if (item) + InventoryResult msg = CanUseAmmo(item); + if (msg != EQUIP_ERR_OK) { - InventoryResult msg = CanUseAmmo(item); - if (msg != EQUIP_ERR_OK) - { - SendEquipError(msg, nullptr, nullptr, item); - return; - } + SendEquipError(msg, nullptr, nullptr, item); + return; } SetUInt32Value(PLAYER_AMMO_ID, item); @@ -13984,7 +14005,7 @@ void Player::SendItemDurations() } } -void Player::SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast) +void Player::SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast, bool sendChatMessage) { if (!item) // prevent crash return; @@ -13994,7 +14015,7 @@ void Player::SendNewItem(Item* item, uint32 count, bool received, bool created, data << uint64(GetGUID()); // player GUID data << uint32(received); // 0=looted, 1=from npc data << uint32(created); // 0=received, 1=created - data << uint32(1); // bool print error to chat + data << uint32(sendChatMessage); // bool print message to chat data << uint8(item->GetBagSlot()); // bagslot // item slot, but when added to stack: 0xFFFFFFFF data << uint32((item->GetCount() == count) ? item->GetSlot() : -1); @@ -15009,7 +15030,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, quest->RewardItemIdCount[i]) == EQUIP_ERR_OK) { Item* item = StoreNewItem(dest, itemId, true, GenerateItemRandomPropertyId(itemId)); - SendNewItem(item, quest->RewardItemIdCount[i], true, false); + SendNewItem(item, quest->RewardItemIdCount[i], true, false, false, false); } else if (quest->IsDFQuest()) SendItemRetrievalMail(itemId, quest->RewardItemIdCount[i]); @@ -15025,7 +15046,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, quest->RewardChoiceItemCount[reward]) == EQUIP_ERR_OK) { Item* item = StoreNewItem(dest, itemId, true, GenerateItemRandomPropertyId(itemId)); - SendNewItem(item, quest->RewardChoiceItemCount[reward], true, false); + SendNewItem(item, quest->RewardChoiceItemCount[reward], true, false, false, false); } } } @@ -15502,7 +15523,7 @@ bool Player::SatisfyQuestStatus(Quest const* qInfo, bool msg) const bool Player::SatisfyQuestConditions(Quest const* qInfo, bool msg) { - if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_ACCEPT, qInfo->GetQuestId(), this)) + if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_AVAILABLE, qInfo->GetQuestId(), this)) { if (msg) { @@ -15901,7 +15922,7 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver) if (!quest) continue; - if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_ACCEPT, quest->GetQuestId(), this)) + if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_QUEST_AVAILABLE, quest->GetQuestId(), this)) continue; QuestStatus status = GetQuestStatus(questId); @@ -17148,7 +17169,6 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) //join player to battleground group currentBg->EventPlayerLoggedIn(this); - currentBg->AddOrSetPlayerToCorrectBgGroup(this, m_bgData.bgTeam); SetInviteForBattlegroundQueueType(bgQueueTypeId, currentBg->GetInstanceID()); } @@ -17373,6 +17393,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) SetMap(map); StoreRaidMapDifficulty(); + UpdatePositionData(); // now that map position is determined, check instance validity if (!CheckInstanceValidity(true) && !IsInstanceLoginGameMasterException()) @@ -21217,7 +21238,14 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc uint32 money = GetMoney(); if (npc) - totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc)); + { + float discount = GetReputationPriceDiscount(npc); + totalcost = uint32(ceil(totalcost * discount)); + firstcost = uint32(ceil(firstcost * discount)); + m_taxi.SetFlightMasterFactionTemplateId(npc->GetFaction()); + } + else + m_taxi.SetFlightMasterFactionTemplateId(0); if (money < totalcost) { @@ -21325,6 +21353,27 @@ void Player::ContinueTaxiFlight() const GetSession()->SendDoFlight(mountDisplayId, path, startNode); } +void Player::SendTaxiNodeStatusMultiple() +{ + for (auto itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) + { + if (!itr->IsCreature()) + continue; + Creature* creature = ObjectAccessor::GetCreature(*this, *itr); + if (!creature || creature->IsHostileTo(this)) + continue; + if (!creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_FLIGHTMASTER)) + continue; + uint32 nearestNode = sObjectMgr->GetNearestTaxiNode(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetMapId(), GetTeam()); + if (!nearestNode) + continue; + WorldPacket data(SMSG_TAXINODE_STATUS, 9); + data << *itr; + data << uint8(m_taxi.IsTaximaskNodeKnown(nearestNode) ? 1 : 0); + SendDirectMessage(&data); + } +} + void Player::InitDataForForm(bool reapplyMods) { ShapeshiftForm form = GetShapeshiftForm(); @@ -22517,6 +22566,8 @@ void Player::SendInitialPacketsAfterAddToMap() SendAurasForTarget(this); SendEnchantmentDurations(); // must be after add to map SendItemDurations(); // must be after add to map + SendQuestGiverStatusMultiple(); + SendTaxiNodeStatusMultiple(); // raid downscaling - send difficulty to player if (GetMap()->IsRaid()) @@ -23168,11 +23219,15 @@ bool Player::GetBGAccessByLevel(BattlegroundTypeId bgTypeId) const float Player::GetReputationPriceDiscount(Creature const* creature) const { - FactionTemplateEntry const* vendor_faction = creature->GetFactionTemplateEntry(); - if (!vendor_faction || !vendor_faction->faction) + return GetReputationPriceDiscount(creature->GetFactionTemplateEntry()); +} + +float Player::GetReputationPriceDiscount(FactionTemplateEntry const* factionTemplate) const +{ + if (!factionTemplate || !factionTemplate->faction) return 1.0f; - ReputationRank rank = GetReputationRank(vendor_faction->faction); + ReputationRank rank = GetReputationRank(factionTemplate->faction); if (rank <= REP_NEUTRAL) return 1.0f; @@ -23557,7 +23612,7 @@ bool Player::isHonorOrXPTarget(Unit* victim) const uint8 k_grey = Trinity::XP::GetGrayLevel(getLevel()); // Victim level less gray level - if (v_level <= k_grey) + if (v_level <= k_grey && !sWorld->getIntConfig(CONFIG_MIN_CREATURE_SCALED_XP_RATIO)) return false; if (Creature const* creature = victim->ToCreature()) @@ -24030,7 +24085,7 @@ void Player::ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional<LiquidDat else m_MirrorTimerFlags &= ~UNDERWATER_INLAVA; } - + // Slime state (any contact) if (liquidData->type_flags & MAP_LIQUID_TYPE_SLIME) { @@ -26240,9 +26295,6 @@ bool Player::SetDisableGravity(bool disable, bool packetOnly /*= false*/) bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/) { - if (!packetOnly && !Unit::SetCanFly(apply)) - return false; - if (!apply) SetFallInformation(0, GetPositionZ()); @@ -26251,11 +26303,16 @@ bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/) data << uint32(0); //! movement counter SendDirectMessage(&data); - data.Initialize(MSG_MOVE_UPDATE_CAN_FLY, 64); - data << GetPackGUID(); - BuildMovementPacket(&data); - SendMessageToSet(&data, false); - return true; + if (packetOnly || Unit::SetCanFly(apply)) + { + data.Initialize(MSG_MOVE_UPDATE_CAN_FLY, 64); + data << GetPackGUID(); + BuildMovementPacket(&data); + SendMessageToSet(&data, false); + return true; + } + else + return false; } bool Player::SetHover(bool apply, bool packetOnly /*= false*/) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 4a2a82000b4..bc72066307f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -946,6 +946,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid = 0); void CleanupAfterTaxiFlight(); void ContinueTaxiFlight() const; + void SendTaxiNodeStatusMultiple(); // mount_id can be used in scripting calls bool isAcceptWhispers() const { return (m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS) != 0; } void SetAcceptWhispers(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_ACCEPT_WHISPERS; else m_ExtraFlags &= ~PLAYER_EXTRA_ACCEPT_WHISPERS; } @@ -1120,11 +1121,12 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool IsUseEquipedWeapon(bool mainhand) const; bool IsTwoHandUsed() const; bool IsUsingTwoHandedWeaponInOneHand() const; - void SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast = false); + void SendNewItem(Item* item, uint32 count, bool received, bool created, bool broadcast = false, bool sendChatMessage = true); bool BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot); bool _StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 count, uint8 bag, uint8 slot, int32 price, ItemTemplate const* pProto, Creature* pVendor, VendorItem const* crItem, bool bStore); float GetReputationPriceDiscount(Creature const* creature) const; + float GetReputationPriceDiscount(FactionTemplateEntry const* factionTemplate) const; Player* GetTrader() const; TradeData* GetTradeData() const { return m_trade; } diff --git a/src/server/game/Entities/Player/PlayerTaxi.cpp b/src/server/game/Entities/Player/PlayerTaxi.cpp index 9a53838e4a9..74a11230d4e 100644 --- a/src/server/game/Entities/Player/PlayerTaxi.cpp +++ b/src/server/game/Entities/Player/PlayerTaxi.cpp @@ -91,9 +91,13 @@ bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, uint3 { ClearTaxiDestinations(); - Tokenizer Tokenizer(values, ' '); + Tokenizer tokens(values, ' '); + auto iter = tokens.begin(); + if (iter != tokens.end()) + m_flightMasterFactionId = atoul(*iter); - for (Tokenizer::const_iterator iter = Tokenizer.begin(); iter != Tokenizer.end(); ++iter) + ++iter; + for (; iter != tokens.end(); ++iter) { uint32 node = atoul(*iter); AddTaxiDestination(node); @@ -128,6 +132,7 @@ std::string PlayerTaxi::SaveTaxiDestinationsToString() return ""; std::ostringstream ss; + ss << m_flightMasterFactionId << ' '; for (size_t i = 0; i < m_TaxiDestinations.size(); ++i) ss << m_TaxiDestinations[i] << ' '; @@ -154,3 +159,8 @@ std::ostringstream& operator<<(std::ostringstream& ss, PlayerTaxi const& taxi) ss << taxi.m_taximask[i] << ' '; return ss; } + +FactionTemplateEntry const* PlayerTaxi::GetFlightMasterFactionTemplate() const +{ + return sFactionTemplateStore.LookupEntry(m_flightMasterFactionId); +} diff --git a/src/server/game/Entities/Player/PlayerTaxi.h b/src/server/game/Entities/Player/PlayerTaxi.h index ae5052b3e7a..a4791c0ff4e 100644 --- a/src/server/game/Entities/Player/PlayerTaxi.h +++ b/src/server/game/Entities/Player/PlayerTaxi.h @@ -24,11 +24,12 @@ #include <iosfwd> class ByteBuffer; +struct FactionTemplateEntry; class TC_GAME_API PlayerTaxi { public: - PlayerTaxi() { m_taximask.fill(0); } + PlayerTaxi() : m_flightMasterFactionId(0) { m_taximask.fill(0); } ~PlayerTaxi() { } // Nodes void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level); @@ -71,11 +72,14 @@ class TC_GAME_API PlayerTaxi std::deque<uint32> const& GetPath() const { return m_TaxiDestinations; } bool empty() const { return m_TaxiDestinations.empty(); } + FactionTemplateEntry const* GetFlightMasterFactionTemplate() const; + void SetFlightMasterFactionTemplateId(uint32 factionTemplateId) { m_flightMasterFactionId = factionTemplateId; } friend std::ostringstream& operator<<(std::ostringstream& ss, PlayerTaxi const& taxi); private: TaxiMask m_taximask; std::deque<uint32> m_TaxiDestinations; + uint32 m_flightMasterFactionId; }; std::ostringstream& operator<<(std::ostringstream& ss, PlayerTaxi const& taxi); diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 6d36095ee7d..2b9b82b392b 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -302,16 +302,14 @@ Creature* Transport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData c Map* map = GetMap(); Creature* creature = new Creature(); - if (!creature->LoadCreatureFromDB(guid, map, false)) + if (!creature->LoadFromDB(guid, map, false, true)) { delete creature; return nullptr; } - float x = data->posX; - float y = data->posY; - float z = data->posZ; - float o = data->orientation; + float x, y, z, o; + data->spawnPoint.GetPosition(x, y, z, o); creature->SetTransport(this); creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); @@ -349,7 +347,7 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat Map* map = GetMap(); GameObject* go = new GameObject(); - if (!go->LoadGameObjectFromDB(guid, map, false)) + if (!go->LoadFromDB(guid, map, false)) { delete go; return nullptr; @@ -357,10 +355,8 @@ GameObject* Transport::CreateGOPassenger(ObjectGuid::LowType guid, GameObjectDat ASSERT(data); - float x = data->posX; - float y = data->posY; - float z = data->posZ; - float o = data->orientation; + float x, y, z, o; + data->spawnPoint.GetPosition(x, y, z, o); go->SetTransport(this); go->m_movementInfo.transport.guid = GetGUID(); @@ -468,7 +464,7 @@ TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSu pos.GetPosition(x, y, z, o); CalculatePassengerPosition(x, y, z, &o); - if (!summon->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, phase, entry, x, y, z, o, nullptr, vehId)) + if (!summon->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, phase, entry, { x, y, z, o }, nullptr, vehId)) { delete summon; return nullptr; @@ -545,7 +541,7 @@ void Transport::LoadStaticPassengers() // GameObjects on transport guidEnd = cellItr->second.gameobjects.end(); for (CellGuidSet::const_iterator guidItr = cellItr->second.gameobjects.begin(); guidItr != guidEnd; ++guidItr) - CreateGOPassenger(*guidItr, sObjectMgr->GetGOData(*guidItr)); + CreateGOPassenger(*guidItr, sObjectMgr->GetGameObjectData(*guidItr)); } } } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 733c3231915..bd4c71c06e6 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -40,6 +40,7 @@ #include "Log.h" #include "LootMgr.h" #include "MotionMaster.h" +#include "MovementGenerator.h" #include "MoveSpline.h" #include "MoveSplineInit.h" #include "ObjectAccessor.h" @@ -2607,7 +2608,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const { // Can`t miss on dead target (on skinning for example) - if ((!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER)) + if (!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) return SPELL_MISS_NONE; SpellSchoolMask schoolMask = spellInfo->GetSchoolMask(); @@ -3391,7 +3392,8 @@ void Unit::ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional<LiquidData> { if (_lastLiquid && _lastLiquid->SpellId) RemoveAurasDueToSpell(_lastLiquid->SpellId); - if (curLiquid && curLiquid->SpellId) + Player* player = GetCharmerOrOwnerPlayerOrPlayerItself(); + if (curLiquid && curLiquid->SpellId && (!player || !player->IsGameMaster())) CastSpell(this, curLiquid->SpellId, true); _lastLiquid = curLiquid; } @@ -5675,8 +5677,6 @@ ReputationRank Unit::GetFactionReactionTo(FactionTemplateEntry const* factionTem return REP_NEUTRAL; FactionTemplateEntry const* targetFactionTemplateEntry = target->GetFactionTemplateEntry(); - if (!targetFactionTemplateEntry) - return REP_NEUTRAL; if (Player const* targetPlayerOwner = target->GetAffectingPlayer()) { @@ -5728,7 +5728,7 @@ bool Unit::IsFriendlyTo(Unit const* unit) const bool Unit::IsHostileToPlayers() const { FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); - if (!my_faction || !my_faction->faction) + if (!my_faction->faction) return false; FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction); @@ -5741,7 +5741,7 @@ bool Unit::IsHostileToPlayers() const bool Unit::IsNeutralToAll() const { FactionTemplateEntry const* my_faction = GetFactionTemplateEntry(); - if (!my_faction || !my_faction->faction) + if (!my_faction->faction) return true; FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction); @@ -9683,11 +9683,10 @@ Unit* Creature::SelectVictim() // last case when creature must not go to evade mode: // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list - // for example at owner command to pet attack some far away creature // Note: creature does not have targeted movement generator but has attacker in this case for (AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr) { - if ((*itr) && !CanCreatureAttack(*itr) && (*itr)->GetTypeId() != TYPEID_PLAYER + if ((*itr) && CanCreatureAttack(*itr) && (*itr)->GetTypeId() != TYPEID_PLAYER && !(*itr)->ToCreature()->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) return nullptr; } @@ -11407,6 +11406,27 @@ void Unit::StopMoving() init.Stop(); } +void Unit::PauseMovement(uint32 timer/* = 0*/, uint8 slot/* = 0*/, bool forced/* = true*/) +{ + if (slot >= MAX_MOTION_SLOT) + return; + + if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(MovementSlot(slot))) + movementGenerator->Pause(timer); + + if (forced) + StopMoving(); +} + +void Unit::ResumeMovement(uint32 timer/* = 0*/, uint8 slot/* = 0*/) +{ + if (slot >= MAX_MOTION_SLOT) + return; + + if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(MovementSlot(slot))) + movementGenerator->Resume(timer); +} + void Unit::SendMovementFlagUpdate(bool self /* = false */) { WorldPacket data; @@ -12597,8 +12617,14 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au if (GetTypeId() == TYPEID_UNIT) { + if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE)) + movementGenerator->Pause(0); + + GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE); + + StopMoving(); + ToCreature()->AI()->OnCharmed(true); - GetMotionMaster()->MoveIdle(); } else if (Player* player = ToPlayer()) { @@ -12712,6 +12738,7 @@ void Unit::RemoveCharmedBy(Unit* charmer) else RestoreFaction(); + ///@todo Handle SLOT_IDLE motion resume GetMotionMaster()->InitDefault(); if (Creature* creature = ToCreature()) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 4fc9a43e464..cfe2f840915 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1279,7 +1279,9 @@ class TC_GAME_API Unit : public WorldObject void CastSpell(SpellCastTargets const& targets, SpellInfo const* spellInfo, CustomSpellValues const* value, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); void CastSpell(Unit* victim, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(std::nullptr_t, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty) { CastSpell((Unit*)nullptr, spellId, triggered, castItem, triggeredByAura, originalCaster); } void CastSpell(Unit* victim, uint32 spellId, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + void CastSpell(std::nullptr_t, uint32 spellId, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty) { CastSpell((Unit*)nullptr, spellId, triggerFlags, castItem, triggeredByAura, originalCaster); } void CastSpell(Unit* victim, SpellInfo const* spellInfo, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); void CastSpell(Unit* victim, SpellInfo const* spellInfo, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); @@ -1792,6 +1794,8 @@ class TC_GAME_API Unit : public WorldObject bool IsStopped() const { return !(HasUnitState(UNIT_STATE_MOVING)); } void StopMoving(); + void PauseMovement(uint32 timer = 0, uint8 slot = 0, bool forced = true); // timer in ms + void ResumeMovement(uint32 timer = 0, uint8 slot = 0); // timer in ms void AddUnitMovementFlag(uint32 f) { m_movementInfo.flags |= f; } void RemoveUnitMovementFlag(uint32 f) { m_movementInfo.flags &= ~f; } diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h index 47f0ed28923..04d3521e40c 100644 --- a/src/server/game/Entities/Unit/UnitDefines.h +++ b/src/server/game/Entities/Unit/UnitDefines.h @@ -131,7 +131,7 @@ enum UnitFlags : uint32 UNIT_FLAG_IMMUNE_TO_PC = 0x00000100, // disables combat/assistance with PlayerCharacters (PC) - see Unit::_IsValidAttackTarget, Unit::_IsValidAssistTarget UNIT_FLAG_IMMUNE_TO_NPC = 0x00000200, // disables combat/assistance with NonPlayerCharacters (NPC) - see Unit::_IsValidAttackTarget, Unit::_IsValidAssistTarget UNIT_FLAG_LOOTING = 0x00000400, // loot animation - UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8 + UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // on player pets: whether the pet is chasing a target to attack || on other units: whether any of the unit's minions is in combat UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3 UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1 UNIT_FLAG_CANNOT_SWIM = 0x00004000, // 2.0.8 diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 357cceb99b6..8b259438efc 100755 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -148,7 +148,8 @@ void Vehicle::Reset(bool evading /*= false*/) TC_LOG_DEBUG("entities.vehicle", "Vehicle::Reset (Entry: %u, GuidLow: %u, DBGuid: %u)", GetCreatureEntry(), _me->GetGUID().GetCounter(), _me->ToCreature()->GetSpawnId()); ApplyAllImmunities(); - InstallAllAccessories(evading); + if (GetBase()->IsAlive()) + InstallAllAccessories(evading); sScriptMgr->OnReset(this); } @@ -401,7 +402,7 @@ void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 typ * @author Machiavelli * @date 17-2-2013 * - * @param [in, out] The prospective passenger. + * @param unit The prospective passenger. * @param seatId Identifier for the seat. Value of -1 indicates the next available seat. * * @return true if it succeeds, false if it fails. @@ -772,6 +773,13 @@ bool VehicleJoinEvent::Execute(uint64, uint32) Target->RemovePendingEventsForSeat(Seat->first); Target->RemovePendingEventsForPassenger(Passenger); + // Passenger might've died in the meantime - abort if this is the case + if (!Passenger->IsAlive()) + { + Abort(0); + return true; + } + Passenger->SetVehicle(Target); Seat->second.Passenger.Guid = Passenger->GetGUID(); Seat->second.Passenger.IsUnselectable = Passenger->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index fc00ce5ee9e..14fdccce85e 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -218,12 +218,12 @@ void GameEventMgr::LoadFromDB() { { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 - QueryResult result = WorldDatabase.Query("SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, description, world_event, announce FROM game_event"); + // 0 1 2 3 4 5 6 7 8 9 + QueryResult result = WorldDatabase.Query("SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, holidayStage, description, world_event, announce FROM game_event"); if (!result) { mGameEvent.clear(); - TC_LOG_ERROR("server.loading", ">> Loaded 0 game events. DB table `game_event` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 game events. DB table `game_event` is empty."); return; } @@ -247,10 +247,13 @@ void GameEventMgr::LoadFromDB() pGameEvent.occurence = fields[3].GetUInt64(); pGameEvent.length = fields[4].GetUInt64(); pGameEvent.holiday_id = HolidayIds(fields[5].GetUInt32()); - - pGameEvent.state = (GameEventState)(fields[7].GetUInt8()); + pGameEvent.holidayStage = fields[6].GetUInt8(); + pGameEvent.description = fields[7].GetString(); + pGameEvent.state = (GameEventState)(fields[8].GetUInt8()); + pGameEvent.announce = fields[9].GetUInt8(); pGameEvent.nextstart = 0; - pGameEvent.announce = fields[8].GetUInt8(); + + ++count; if (pGameEvent.length == 0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check { @@ -264,12 +267,18 @@ void GameEventMgr::LoadFromDB() { TC_LOG_ERROR("sql.sql", "`game_event`: game event id (%i) contains nonexisting holiday id %u.", event_id, pGameEvent.holiday_id); pGameEvent.holiday_id = HOLIDAY_NONE; + continue; + } + if (pGameEvent.holidayStage > MAX_HOLIDAY_DURATIONS) + { + TC_LOG_ERROR("sql.sql", "`game_event` game event id (%i) has out of range holidayStage %u.", event_id, pGameEvent.holidayStage); + pGameEvent.holidayStage = 0; + continue; } - } - pGameEvent.description = fields[6].GetString(); + SetHolidayEventTime(pGameEvent); + } - ++count; } while (result->NextRow()); @@ -436,7 +445,7 @@ void GameEventMgr::LoadFromDB() int32 internal_event_id = mGameEvent.size() + event_id - 1; - GameObjectData const* data = sObjectMgr->GetGOData(guid); + GameObjectData const* data = sObjectMgr->GetGameObjectData(guid); if (!data) { TC_LOG_ERROR("sql.sql", "`game_event_gameobject` contains gameobject (GUID: %u) not found in `gameobject` table.", guid); @@ -929,6 +938,45 @@ void GameEventMgr::LoadFromDB() } } +void GameEventMgr::LoadHolidayDates() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT id, date_id, date_value FROM holiday_dates"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 holiday dates. DB table `holiday_dates` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + uint32 holidayId = fields[0].GetUInt32(); + HolidaysEntry* entry = const_cast<HolidaysEntry*>(sHolidaysStore.LookupEntry(holidayId)); + if (!entry) + { + TC_LOG_ERROR("sql.sql", "holiday_dates entry has invalid holiday id %u.", holidayId); + continue; + } + uint8 dateId = fields[1].GetUInt8(); + if (dateId >= MAX_HOLIDAY_DATES) + { + TC_LOG_ERROR("sql.sql", "holiday_dates entry has out of range date_id %u.", dateId); + continue; + } + entry->Date[dateId] = fields[2].GetUInt32(); + modifiedHolidays.insert(entry->Id); + ++count; + + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u holiday dates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + uint32 GameEventMgr::GetNPCFlag(Creature* cr) { uint32 mask = 0; @@ -1140,7 +1188,7 @@ void GameEventMgr::UpdateEventNPCFlags(uint16 event_id) for (NPCFlagList::iterator itr = mGameEventNPCFlags[event_id].begin(); itr != mGameEventNPCFlags[event_id].end(); ++itr) // get the creature data from the low guid to get the entry, to be able to find out the whole guid if (CreatureData const* data = sObjectMgr->GetCreatureData(itr->first)) - creaturesByMap[data->mapid].insert(itr->first); + creaturesByMap[data->spawnPoint.GetMapId()].insert(itr->first); for (auto const& p : creaturesByMap) { @@ -1203,13 +1251,13 @@ void GameEventMgr::GameEventSpawn(int16 event_id) sObjectMgr->AddCreatureToGrid(*itr, data); // Spawn if necessary (loaded grids only) - Map* map = sMapMgr->CreateBaseMap(data->mapid); + Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); // We use spawn coords to spawn - if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY)) + if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) { Creature* creature = new Creature(); //TC_LOG_DEBUG("misc", "Spawning creature %u", *itr); - if (!creature->LoadCreatureFromDB(*itr, map)) + if (!creature->LoadFromDB(*itr, map, true, false)) delete creature; } } @@ -1225,19 +1273,19 @@ void GameEventMgr::GameEventSpawn(int16 event_id) for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin(); itr != mGameEventGameobjectGuids[internal_event_id].end(); ++itr) { // Add to correct cell - if (GameObjectData const* data = sObjectMgr->GetGOData(*itr)) + if (GameObjectData const* data = sObjectMgr->GetGameObjectData(*itr)) { sObjectMgr->AddGameobjectToGrid(*itr, data); // Spawn if necessary (loaded grids only) // this base map checked as non-instanced and then only existed - Map* map = sMapMgr->CreateBaseMap(data->mapid); + Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); // We use current coords to unspawn, not spawn coords since creature can have changed grid - if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY)) + if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) { GameObject* pGameobject = new GameObject; //TC_LOG_DEBUG("misc", "Spawning gameobject %u", *itr); /// @todo find out when it is add to map - if (!pGameobject->LoadGameObjectFromDB(*itr, map, false)) + if (!pGameobject->LoadFromDB(*itr, map, false)) delete pGameobject; else { @@ -1280,7 +1328,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) { sObjectMgr->RemoveCreatureFromGrid(*itr, data); - sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr](Map* map) + sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr](Map* map) { auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(*itr); for (auto itr2 = creatureBounds.first; itr2 != creatureBounds.second;) @@ -1306,11 +1354,11 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) if (event_id >0 && hasGameObjectActiveEventExcept(*itr, event_id)) continue; // Remove the gameobject from grid - if (GameObjectData const* data = sObjectMgr->GetGOData(*itr)) + if (GameObjectData const* data = sObjectMgr->GetGameObjectData(*itr)) { sObjectMgr->RemoveGameobjectFromGrid(*itr, data); - sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr](Map* map) + sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr](Map* map) { auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(*itr); for (auto itr2 = gameobjectBounds.first; itr2 != gameobjectBounds.second;) @@ -1344,7 +1392,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate) continue; // Update if spawned - sMapMgr->DoForAllMapsWithMapId(data->mapid, [&itr, activate](Map* map) + sMapMgr->DoForAllMapsWithMapId(data->spawnPoint.GetMapId(), [&itr, activate](Map* map) { auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(itr->first); @@ -1667,6 +1715,84 @@ void GameEventMgr::RunSmartAIScripts(uint16 event_id, bool activate) }); } +void GameEventMgr::SetHolidayEventTime(GameEventData& event) +{ + if (!event.holidayStage) // Ignore holiday + return; + + const HolidaysEntry* holiday = sHolidaysStore.LookupEntry(event.holiday_id); + + if (!holiday->Date[0] || !holiday->Duration[0]) // Invalid definitions + { + TC_LOG_ERROR("sql.sql", "Missing date or duration for holiday %u.", event.holiday_id); + return; + } + + uint8 stageIndex = event.holidayStage - 1; + event.length = holiday->Duration[stageIndex] * HOUR / MINUTE; + + time_t stageOffset = 0; + for (int i = 0; i < stageIndex; ++i) + stageOffset += holiday->Duration[i] * HOUR; + + switch (holiday->CalendarFilterType) + { + case -1: // Yearly + event.occurence = YEAR / MINUTE; // Not all too useful + break; + case 0: // Weekly + event.occurence = WEEK / MINUTE; + break; + case 1: // Defined dates only (Darkmoon Faire) + break; + case 2: // Only used for looping events (Call to Arms) + break; + } + + if (holiday->Looping) + { + event.occurence = 0; + for (int i = 0; i < MAX_HOLIDAY_DURATIONS && holiday->Duration[i]; ++i) + event.occurence += holiday->Duration[i] * HOUR / MINUTE; + } + + bool singleDate = ((holiday->Date[0] >> 24) & 0x1F) == 31; // Events with fixed date within year have - 1 + + time_t curTime = time(NULL); + for (int i = 0; i < MAX_HOLIDAY_DATES && holiday->Date[i]; ++i) + { + uint32 date = holiday->Date[i]; + + tm timeInfo; + if (singleDate) + timeInfo.tm_year = localtime(&curTime)->tm_year - 1; // First try last year (event active through New Year) + else + timeInfo.tm_year = ((date >> 24) & 0x1F) + 100; + + timeInfo.tm_mon = (date >> 20) & 0xF; + timeInfo.tm_mday = ((date >> 14) & 0x3F) + 1; + timeInfo.tm_hour = (date >> 6) & 0x1F; + timeInfo.tm_min = date & 0x3F; + timeInfo.tm_sec = 0; + timeInfo.tm_isdst = -1; + tm tmCopy = timeInfo; + + time_t startTime = mktime(&timeInfo); + if (curTime < startTime + event.length * MINUTE) + { + event.start = startTime + stageOffset; + return; + } + else if (singleDate) + { + tmCopy.tm_year = localtime(&curTime)->tm_year; // This year + event.start = mktime(&tmCopy) + stageOffset; + return; + } + } + TC_LOG_ERROR("sql.sql", "No suitable start date found for holiday %u.", event.holiday_id); +} + bool IsHolidayActive(HolidayIds id) { if (id == HOLIDAY_NONE) diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h index bee6dfc8fb3..4d1ce718422 100644 --- a/src/server/game/Events/GameEventMgr.h +++ b/src/server/game/Events/GameEventMgr.h @@ -60,7 +60,7 @@ typedef std::map<uint32 /*condition id*/, GameEventFinishCondition> GameEventCon struct GameEventData { - GameEventData() : start(1), end(0), nextstart(0), occurence(0), length(0), holiday_id(HOLIDAY_NONE), state(GAMEEVENT_NORMAL), + GameEventData() : start(1), end(0), nextstart(0), occurence(0), length(0), holiday_id(HOLIDAY_NONE), holidayStage(0), state(GAMEEVENT_NORMAL), announce(0) { } time_t start; // occurs after this time time_t end; // occurs before this time @@ -68,6 +68,7 @@ struct GameEventData uint32 occurence; // time between end and start uint32 length; // length of the event (minutes) after finishing all conditions HolidayIds holiday_id; + uint8 holidayStage; GameEventState state; // state of the game event, these are saved into the game_event table on change! GameEventConditionMap conditions; // conditions to finish std::set<uint16 /*gameevent id*/> prerequisite_events; // events that must be completed before starting this event @@ -114,6 +115,7 @@ class TC_GAME_API GameEventMgr bool CheckOneGameEvent(uint16 entry) const; uint32 NextCheck(uint16 entry) const; void LoadFromDB(); + void LoadHolidayDates(); uint32 Update(); bool IsActiveEvent(uint16 event_id) { return (m_ActiveEvents.find(event_id) != m_ActiveEvents.end()); } uint32 StartSystem(); @@ -147,6 +149,7 @@ class TC_GAME_API GameEventMgr bool hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 event_id); bool hasCreatureActiveEventExcept(ObjectGuid::LowType creature_guid, uint16 event_id); bool hasGameObjectActiveEventExcept(ObjectGuid::LowType go_guid, uint16 event_id); + void SetHolidayEventTime(GameEventData& event); typedef std::list<ObjectGuid::LowType> GuidList; typedef std::list<uint32> IdList; @@ -181,6 +184,7 @@ class TC_GAME_API GameEventMgr public: GameEventGuidMap mGameEventCreatureGuids; GameEventGuidMap mGameEventGameobjectGuids; + std::set<uint32> modifiedHolidays; }; #define sGameEventMgr GameEventMgr::instance() diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 1b7713ab363..2f1e4fa48f3 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -30,6 +30,7 @@ #include "GroupMgr.h" #include "GuildMgr.h" #include "InstanceSaveMgr.h" +#include "InstanceScript.h" #include "Language.h" #include "LFGMgr.h" #include "Log.h" @@ -831,7 +832,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid1 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid1); const_cast<CreatureTemplate*>(cInfo)->Modelid1 = 0; } - else if (!displayScaleEntry) + else displayScaleEntry = displayEntry; CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1); @@ -1123,7 +1124,7 @@ void ObjectMgr::LoadGameObjectAddons() ObjectGuid::LowType guid = fields[0].GetUInt32(); - GameObjectData const* goData = GetGOData(guid); + GameObjectData const* goData = GetGameObjectData(guid); if (!goData) { TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) does not exist but has a record in `gameobject_addon`", guid); @@ -1427,7 +1428,7 @@ void ObjectMgr::LoadLinkedRespawn() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty."); return; } @@ -1461,8 +1462,8 @@ void ObjectMgr::LoadLinkedRespawn() break; } - MapEntry const* const map = sMapStore.LookupEntry(master->mapid); - if (!map || !map->Instanceable() || (master->mapid != slave->mapid)) + MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); + if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -1490,7 +1491,7 @@ void ObjectMgr::LoadLinkedRespawn() break; } - GameObjectData const* master = GetGOData(linkedGuidLow); + GameObjectData const* master = GetGameObjectData(linkedGuidLow); if (!master) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow); @@ -1498,8 +1499,8 @@ void ObjectMgr::LoadLinkedRespawn() break; } - MapEntry const* const map = sMapStore.LookupEntry(master->mapid); - if (!map || !map->Instanceable() || (master->mapid != slave->mapid)) + MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); + if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -1519,7 +1520,7 @@ void ObjectMgr::LoadLinkedRespawn() } case GO_TO_GO: { - GameObjectData const* slave = GetGOData(guidLow); + GameObjectData const* slave = GetGameObjectData(guidLow); if (!slave) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow); @@ -1527,7 +1528,7 @@ void ObjectMgr::LoadLinkedRespawn() break; } - GameObjectData const* master = GetGOData(linkedGuidLow); + GameObjectData const* master = GetGameObjectData(linkedGuidLow); if (!master) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '%u' not found in gameobject table", linkedGuidLow); @@ -1535,8 +1536,8 @@ void ObjectMgr::LoadLinkedRespawn() break; } - MapEntry const* const map = sMapStore.LookupEntry(master->mapid); - if (!map || !map->Instanceable() || (master->mapid != slave->mapid)) + MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); + if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Gameobject '%u' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -1556,7 +1557,7 @@ void ObjectMgr::LoadLinkedRespawn() } case GO_TO_CREATURE: { - GameObjectData const* slave = GetGOData(guidLow); + GameObjectData const* slave = GetGameObjectData(guidLow); if (!slave) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '%u' not found in gameobject table", guidLow); @@ -1572,8 +1573,8 @@ void ObjectMgr::LoadLinkedRespawn() break; } - MapEntry const* const map = sMapStore.LookupEntry(master->mapid); - if (!map || !map->Instanceable() || (master->mapid != slave->mapid)) + MapEntry const* const map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); + if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) { TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '%u' linking to Creature '%u' on an unpermitted map.", guidLow, linkedGuidLow); error = true; @@ -1626,8 +1627,8 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid return false; } - MapEntry const* map = sMapStore.LookupEntry(master->mapid); - if (!map || !map->Instanceable() || (master->mapid != slave->mapid)) + MapEntry const* map = sMapStore.LookupEntry(master->spawnPoint.GetMapId()); + if (!map || !map->Instanceable() || (master->spawnPoint.GetMapId() != slave->spawnPoint.GetMapId())) { TC_LOG_ERROR("sql.sql", "Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow); return false; @@ -1741,8 +1742,8 @@ void ObjectMgr::LoadCreatures() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 9 10 - QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, " + // 0 1 2 3 4 5 6 7 8 9 10 + QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, spawndist, " // 11 12 13 14 15 16 17 18 19 20 21 "currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, creature.npcflag, creature.unit_flags, creature.dynamicflags, " // 22 @@ -1753,7 +1754,7 @@ void ObjectMgr::LoadCreatures() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty."); return; } @@ -1782,14 +1783,11 @@ void ObjectMgr::LoadCreatures() } CreatureData& data = _creatureDataStore[guid]; + data.spawnId = guid; data.id = entry; - data.mapid = fields[2].GetUInt16(); - data.displayid = fields[3].GetUInt32(); - data.equipmentId = fields[4].GetInt8(); - data.posX = fields[5].GetFloat(); - data.posY = fields[6].GetFloat(); - data.posZ = fields[7].GetFloat(); - data.orientation = fields[8].GetFloat(); + data.spawnPoint.WorldRelocate(fields[2].GetUInt16(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat()); + data.displayid = fields[7].GetUInt32(); + data.equipmentId = fields[8].GetInt8(); data.spawntimesecs = fields[9].GetUInt32(); data.spawndist = fields[10].GetFloat(); data.currentwaypoint= fields[11].GetUInt32(); @@ -1803,18 +1801,19 @@ void ObjectMgr::LoadCreatures() data.npcflag = fields[19].GetUInt32(); data.unit_flags = fields[20].GetUInt32(); data.dynamicflags = fields[21].GetUInt32(); - data.ScriptId = GetScriptId(fields[22].GetString()); + data.scriptId = GetScriptId(fields[22].GetString()); + data.spawnGroupData = &_spawnGroupDataStore[0]; - MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid); + MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId()); if (!mapEntry) { - TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.mapid); + TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid, data.spawnPoint.GetMapId()); continue; } // Skip spawnMask check for transport maps - if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid]) - TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that have wrong spawn mask %u including unsupported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.mapid); + if (!IsTransportMap(data.spawnPoint.GetMapId()) && data.spawnMask & ~spawnMasks[data.spawnPoint.GetMapId()]) + TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u) that have wrong spawn mask %u including unsupported difficulty modes for map (Id: %u).", guid, data.spawnMask, data.spawnPoint.GetMapId()); bool ok = true; for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff) @@ -1879,17 +1878,11 @@ void ObjectMgr::LoadCreatures() data.phaseMask = 1; } - if (std::abs(data.orientation) > 2 * float(M_PI)) - { - TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: %u Entry: %u) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id); - data.orientation = Position::NormalizeOrientation(data.orientation); - } - if (sWorld->getBoolConfig(CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA)) { uint32 zoneId = 0; uint32 areaId = 0; - sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ); + sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.spawnPoint); PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA); @@ -1916,8 +1909,8 @@ void ObjectMgr::AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const* { if (mask & 1) { - CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY); - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()]; + CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()); + CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()]; cell_guids.creatures.insert(guid); } } @@ -1930,14 +1923,14 @@ void ObjectMgr::RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData co { if (mask & 1) { - CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY); - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()]; + CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()); + CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()]; cell_guids.creatures.erase(guid); } } } -ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay /*= 0*/) +ObjectGuid::LowType ObjectMgr::AddGameObjectData(uint32 entry, uint32 mapId, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay /*= 0*/) { GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry); if (!goinfo) @@ -1947,41 +1940,40 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, Position co if (!map) return 0; - ObjectGuid::LowType guid = GenerateGameObjectSpawnId(); + ObjectGuid::LowType spawnId = GenerateGameObjectSpawnId(); - GameObjectData& data = NewGOData(guid); + GameObjectData& data = NewOrExistGameObjectData(spawnId); + data.spawnId = spawnId; data.id = entry; - data.mapid = mapId; - - pos.GetPosition(data.posX, data.posY, data.posZ, data.orientation); - + data.spawnPoint.WorldRelocate(mapId,pos); data.rotation = rot; data.spawntimesecs = spawntimedelay; data.animprogress = 100; data.spawnMask = 1; - data.go_state = GO_STATE_READY; + data.goState = GO_STATE_READY; data.phaseMask = PHASEMASK_NORMAL; data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0; - data.dbData = false; + data.dbData = false; + data.spawnGroupData = GetLegacySpawnGroup(); - AddGameobjectToGrid(guid, &data); + AddGameobjectToGrid(spawnId, &data); // Spawn if necessary (loaded grids only) // We use spawn coords to spawn - if (!map->Instanceable() && map->IsGridLoaded(data.posX, data.posY)) + if (!map->Instanceable() && map->IsGridLoaded(data.spawnPoint)) { GameObject* go = new GameObject; - if (!go->LoadGameObjectFromDB(guid, map)) + if (!go->LoadFromDB(spawnId, map, true)) { - TC_LOG_ERROR("misc", "AddGOData: cannot add gameobject entry %u to map", entry); + TC_LOG_ERROR("misc", "AddGameObjectData: cannot add gameobject entry %u to map", entry); delete go; return 0; } } - TC_LOG_DEBUG("maps", "AddGOData: dbguid %u entry %u map %u x %f y %f z %f o %f", guid, entry, mapId, data.posX, data.posY, data.posZ, data.orientation); + TC_LOG_DEBUG("maps", "AddGameObjectData: dbguid %u entry %u map %u pos %s", spawnId, entry, mapId, data.spawnPoint.ToString().c_str()); - return guid; + return spawnId; } @@ -1997,15 +1989,13 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit if (!map) return 0; - ObjectGuid::LowType guid = GenerateCreatureSpawnId(); - CreatureData& data = NewOrExistCreatureData(guid); + ObjectGuid::LowType spawnId = GenerateCreatureSpawnId(); + CreatureData& data = NewOrExistCreatureData(spawnId); + data.spawnId = spawnId; data.id = entry; - data.mapid = mapId; + data.spawnPoint.WorldRelocate(mapId, pos); data.displayid = 0; data.equipmentId = 0; - - pos.GetPosition(data.posX, data.posY, data.posZ, data.orientation); - data.spawntimesecs = spawntimedelay; data.spawndist = 0; data.currentwaypoint = 0; @@ -2018,14 +2008,15 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit data.npcflag = cInfo->npcflag; data.unit_flags = cInfo->unit_flags; data.dynamicflags = cInfo->dynamicflags; + data.spawnGroupData = GetLegacySpawnGroup(); - AddCreatureToGrid(guid, &data); + AddCreatureToGrid(spawnId, &data); // We use spawn coords to spawn - if (!map->Instanceable() && !map->IsRemovalGrid(data.posX, data.posY)) + if (!map->Instanceable() && !map->IsRemovalGrid(data.spawnPoint)) { Creature* creature = new Creature(); - if (!creature->LoadCreatureFromDB(guid, map)) + if (!creature->LoadFromDB(spawnId, map, true, true)) { TC_LOG_ERROR("misc", "AddCreature: Cannot add creature entry %u to map", entry); delete creature; @@ -2033,10 +2024,10 @@ ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Posit } } - return guid; + return spawnId; } -void ObjectMgr::LoadGameobjects() +void ObjectMgr::LoadGameObjects() { uint32 oldMSTime = getMSTime(); @@ -2051,7 +2042,7 @@ void ObjectMgr::LoadGameobjects() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty."); return; } @@ -2100,22 +2091,20 @@ void ObjectMgr::LoadGameobjects() GameObjectData& data = _gameObjectDataStore[guid]; + data.spawnId = guid; data.id = entry; - data.mapid = fields[2].GetUInt16(); - data.posX = fields[3].GetFloat(); - data.posY = fields[4].GetFloat(); - data.posZ = fields[5].GetFloat(); - data.orientation = fields[6].GetFloat(); + data.spawnPoint.WorldRelocate(fields[2].GetUInt16(), fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat()); data.rotation.x = fields[7].GetFloat(); data.rotation.y = fields[8].GetFloat(); data.rotation.z = fields[9].GetFloat(); data.rotation.w = fields[10].GetFloat(); data.spawntimesecs = fields[11].GetInt32(); + data.spawnGroupData = &_spawnGroupDataStore[0]; - MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid); + MapEntry const* mapEntry = sMapStore.LookupEntry(data.spawnPoint.GetMapId()); if (!mapEntry) { - TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) spawned on a non-existed map (Id: %u), skip", guid, data.id, data.mapid); + TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) spawned on a non-existed map (Id: %u), skip", guid, data.id, data.spawnPoint.GetMapId()); continue; } @@ -2133,24 +2122,18 @@ void ObjectMgr::LoadGameobjects() TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state); continue; } - data.go_state = GOState(go_state); + data.goState = GOState(go_state); data.spawnMask = fields[14].GetUInt8(); - if (!IsTransportMap(data.mapid) && data.spawnMask & ~spawnMasks[data.mapid]) - TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) that has wrong spawn mask %u including unsupported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.mapid); + if (!IsTransportMap(data.spawnPoint.GetMapId()) && data.spawnMask & ~spawnMasks[data.spawnPoint.GetMapId()]) + TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) that has wrong spawn mask %u including unsupported difficulty modes for map (Id: %u), skip", guid, data.id, data.spawnMask, data.spawnPoint.GetMapId()); data.phaseMask = fields[15].GetUInt32(); int16 gameEvent = fields[16].GetInt8(); uint32 PoolId = fields[17].GetUInt32(); - data.ScriptId = GetScriptId(fields[18].GetString()); - - if (std::abs(data.orientation) > 2 * float(M_PI)) - { - TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with abs(`orientation`) > 2*PI (orientation is expressed in radians), normalized.", guid, data.id); - data.orientation = Position::NormalizeOrientation(data.orientation); - } + data.scriptId = GetScriptId(fields[18].GetString()); if (data.rotation.x < -1.0f || data.rotation.x > 1.0f) { @@ -2176,7 +2159,7 @@ void ObjectMgr::LoadGameobjects() continue; } - if (!MapManager::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation)) + if (!MapManager::IsValidMapCoord(data.spawnPoint)) { TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id); continue; @@ -2192,7 +2175,7 @@ void ObjectMgr::LoadGameobjects() { uint32 zoneId = 0; uint32 areaId = 0; - sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.mapid, data.posX, data.posY, data.posZ); + sMapMgr->GetZoneAndAreaId(zoneId, areaId, data.spawnPoint); PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA); @@ -2211,6 +2194,207 @@ void ObjectMgr::LoadGameobjects() TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " gameobjects in %u ms", _gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadSpawnGroupTemplates() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template"); + + if (result) + { + do + { + Field* fields = result->Fetch(); + uint32 groupId = fields[0].GetUInt32(); + SpawnGroupTemplateData& group = _spawnGroupDataStore[groupId]; + group.groupId = groupId; + group.name = fields[1].GetString(); + group.mapId = SPAWNGROUP_MAP_UNSET; + uint32 flags = fields[2].GetUInt32(); + if (flags & ~SPAWNGROUP_FLAGS_ALL) + { + flags &= SPAWNGROUP_FLAGS_ALL; + TC_LOG_ERROR("sql.sql", "Invalid spawn group flag %u on group ID %u (%s), reduced to valid flag %u.", flags, groupId, group.name.c_str(), uint32(group.flags)); + } + if (flags & SPAWNGROUP_FLAG_SYSTEM && flags & SPAWNGROUP_FLAG_MANUAL_SPAWN) + { + flags &= ~SPAWNGROUP_FLAG_MANUAL_SPAWN; + TC_LOG_ERROR("sql.sql", "System spawn group %u (%s) has invalid manual spawn flag. Ignored.", groupId, group.name.c_str()); + } + group.flags = SpawnGroupFlags(flags); + } while (result->NextRow()); + } + + if (_spawnGroupDataStore.find(0) == _spawnGroupDataStore.end()) + { + TC_LOG_ERROR("sql.sql", "Default spawn group (index 0) is missing from DB! Manually inserted."); + SpawnGroupTemplateData& data = _spawnGroupDataStore[0]; + data.groupId = 0; + data.name = "Default Group"; + data.mapId = 0; + data.flags = SPAWNGROUP_FLAG_SYSTEM; + } + if (_spawnGroupDataStore.find(1) == _spawnGroupDataStore.end()) + { + TC_LOG_ERROR("sql.sql", "Default legacy spawn group (index 1) is missing from DB! Manually inserted."); + SpawnGroupTemplateData&data = _spawnGroupDataStore[1]; + data.groupId = 1; + data.name = "Legacy Group"; + data.mapId = 0; + data.flags = SpawnGroupFlags(SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE); + } + + if (result) + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " spawn group templates in %u ms", _spawnGroupDataStore.size(), GetMSTimeDiffToNow(oldMSTime)); + else + TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group templates. DB table `spawn_group_template` is empty."); + + return; +} + +void ObjectMgr::LoadSpawnGroups() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group members. DB table `spawn_group` is empty."); + return; + } + + uint32 numMembers = 0; + do + { + Field* fields = result->Fetch(); + uint32 groupId = fields[0].GetUInt32(); + SpawnObjectType spawnType; + { + uint32 type = fields[1].GetUInt8(); + if (type >= SPAWN_TYPE_MAX) + { + TC_LOG_ERROR("sql.sql", "Spawn data with invalid type %u listed for spawn group %u. Skipped.", type, groupId); + continue; + } + spawnType = SpawnObjectType(type); + } + ObjectGuid::LowType spawnId = fields[2].GetUInt32(); + + SpawnData const* data = GetSpawnData(spawnType, spawnId); + if (!data) + { + TC_LOG_ERROR("sql.sql", "Spawn data with ID (%u,%u) not found, but is listed as a member of spawn group %u!", uint32(spawnType), spawnId, groupId); + continue; + } + else if (data->spawnGroupData->groupId) + { + TC_LOG_ERROR("sql.sql", "Spawn with ID (%u,%u) is listed as a member of spawn group %u, but is already a member of spawn group %u. Skipping.", uint32(spawnType), spawnId, groupId, data->spawnGroupData->groupId); + continue; + } + auto it = _spawnGroupDataStore.find(groupId); + if (it == _spawnGroupDataStore.end()) + { + TC_LOG_ERROR("sql.sql", "Spawn group %u assigned to spawn ID (%u,%u), but group is found!", groupId, uint32(spawnType), spawnId); + continue; + } + else + { + SpawnGroupTemplateData& groupTemplate = it->second; + if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET) + groupTemplate.mapId = data->spawnPoint.GetMapId(); + else if (groupTemplate.mapId != data->spawnPoint.GetMapId() && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM)) + { + TC_LOG_ERROR("sql.sql", "Spawn group %u has map ID %u, but spawn (%u,%u) has map id %u - spawn NOT added to group!", groupId, groupTemplate.mapId, uint32(spawnType), spawnId, data->spawnPoint.GetMapId()); + continue; + } + const_cast<SpawnData*>(data)->spawnGroupData = &groupTemplate; + if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM)) + _spawnGroupMapStore.emplace(groupId, data); + ++numMembers; + } + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u spawn group members in %u ms", numMembers, GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::LoadInstanceSpawnGroups() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty."); + return; + } + + uint32 n = 0; + do + { + Field* fields = result->Fetch(); + uint32 const spawnGroupId = fields[3].GetUInt32(); + auto it = _spawnGroupDataStore.find(spawnGroupId); + if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM)) + { + TC_LOG_ERROR("sql.sql", "Invalid spawn group %u specified for instance %u. Skipped.", spawnGroupId, fields[0].GetUInt16()); + continue; + } + + uint16 const instanceMapId = fields[0].GetUInt16(); + auto& vector = _instanceSpawnGroupStore[instanceMapId]; + vector.emplace_back(); + InstanceSpawnGroupInfo& info = vector.back(); + info.SpawnGroupId = spawnGroupId; + info.BossStateId = fields[1].GetUInt8(); + + uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1; + uint8 const states = fields[2].GetUInt8(); + if (states & ~ALL_STATES) + { + info.BossStates = states & ALL_STATES; + TC_LOG_ERROR("sql.sql", "Instance spawn group (%u,%u) had invalid boss state mask %u - truncated to %u.", instanceMapId, spawnGroupId, states, info.BossStates); + } + else + info.BossStates = states; + + uint8 const flags = fields[4].GetUInt8(); + if (flags & ~InstanceSpawnGroupInfo::FLAG_ALL) + { + info.Flags = flags & InstanceSpawnGroupInfo::FLAG_ALL; + TC_LOG_ERROR("sql.sql", "Instance spawn group (%u,%u) had invalid flags %u - truncated to %u.", instanceMapId, spawnGroupId, flags, info.Flags); + } + else + info.Flags = flags; + + ++n; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u instance spawn groups in %u ms", n, GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::OnDeleteSpawnData(SpawnData const* data) +{ + auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId); + ASSERT(templateIt != _spawnGroupDataStore.end(), "Creature data for (%u,%u) is being deleted and has invalid spawn group index %u!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId); + if (templateIt->second.flags & SPAWNGROUP_FLAG_SYSTEM) // system groups don't store their members in the map + return; + + auto pair = _spawnGroupMapStore.equal_range(data->spawnGroupData->groupId); + for (auto it = pair.first; it != pair.second; ++it) + { + if (it->second != data) + continue; + _spawnGroupMapStore.erase(it); + return; + } + ASSERT(false, "Spawn data (%u,%u) being removed is member of spawn group %u, but not actually listed in the lookup table for that group!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId); +} + void ObjectMgr::AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData const* data) { uint8 mask = data->spawnMask; @@ -2218,8 +2402,8 @@ void ObjectMgr::AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData con { if (mask & 1) { - CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY); - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()]; + CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()); + CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()]; cell_guids.gameobjects.insert(guid); } } @@ -2232,8 +2416,8 @@ void ObjectMgr::RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectDat { if (mask & 1) { - CellCoord cellCoord = Trinity::ComputeCellCoord(data->posX, data->posY); - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapid, i)][cellCoord.GetId()]; + CellCoord cellCoord = Trinity::ComputeCellCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()); + CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->spawnPoint.GetMapId(), i)][cellCoord.GetId()]; cell_guids.gameobjects.erase(guid); } } @@ -3011,7 +3195,7 @@ void ObjectMgr::LoadVehicleTemplateAccessories() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty."); return; } @@ -3105,7 +3289,7 @@ void ObjectMgr::LoadPetLevelInfo() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty."); return; } @@ -3399,7 +3583,7 @@ void ObjectMgr::LoadPlayerInfo() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty."); } else { @@ -3473,7 +3657,7 @@ void ObjectMgr::LoadPlayerInfo() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty."); } else { @@ -3534,7 +3718,7 @@ void ObjectMgr::LoadPlayerInfo() QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell FROM playercreateinfo_cast_spell"); if (!result) - TC_LOG_ERROR("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty."); else { uint32 count = 0; @@ -3591,7 +3775,7 @@ void ObjectMgr::LoadPlayerInfo() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty."); } else { @@ -4019,7 +4203,7 @@ void ObjectMgr::LoadQuests() " FROM quest_template"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty."); return; } @@ -4070,7 +4254,7 @@ void ObjectMgr::LoadQuests() { QueryResult result = WorldDatabase.PQuery("SELECT %s FROM %s", loader.QueryFields, loader.TableName); if (!result) - TC_LOG_ERROR("server.loading", ">> Loaded 0 quest %s. DB table `%s` is empty.", loader.TableDesc, loader.TableName); + TC_LOG_INFO("server.loading", ">> Loaded 0 quest %s. DB table `%s` is empty.", loader.TableDesc, loader.TableName); else { do @@ -4881,7 +5065,7 @@ void ObjectMgr::LoadScripts(ScriptsType type) case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: { - GameObjectData const* data = GetGOData(tmp.RespawnGameobject.GOGuid); + GameObjectData const* data = GetGameObjectData(tmp.RespawnGameobject.GOGuid); if (!data) { TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", @@ -4931,7 +5115,7 @@ void ObjectMgr::LoadScripts(ScriptsType type) case SCRIPT_COMMAND_OPEN_DOOR: case SCRIPT_COMMAND_CLOSE_DOOR: { - GameObjectData const* data = GetGOData(tmp.ToggleDoor.GOGuid); + GameObjectData const* data = GetGameObjectData(tmp.ToggleDoor.GOGuid); if (!data) { TC_LOG_ERROR("sql.sql", "Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u", @@ -5409,7 +5593,7 @@ void ObjectMgr::LoadInstanceEncounters() QueryResult result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon FROM instance_encounters"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 instance encounters, table is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 instance encounters, table is empty!"); return; } @@ -5695,6 +5879,10 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) stmt->setUInt32(0, itr2->item_guid); CharacterDatabase.Execute(stmt); } + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID); + stmt->setUInt32(0, m->messageID); + CharacterDatabase.Execute(stmt); } else { @@ -5794,6 +5982,134 @@ void ObjectMgr::LoadQuestAreaTriggers() TC_LOG_INFO("server.loading", ">> Loaded %u quest trigger points in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +QuestGreeting const* ObjectMgr::GetQuestGreeting(ObjectGuid guid) const +{ + auto itr = _questGreetingStore.find(guid.GetTypeId()); + if (itr == _questGreetingStore.end()) + return nullptr; + + auto questItr = itr->second.find(guid.GetEntry()); + if (questItr == itr->second.end()) + return nullptr; + + return questItr->second; +} + +void ObjectMgr::LoadQuestGreetings() +{ + uint32 oldMSTime = getMSTime(); + + _questGreetingStore.clear(); // need for reload case + + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT ID, Type, GreetEmoteType, GreetEmoteDelay, Greeting FROM quest_greeting"); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 quest greetings. DB table `quest_greeting` is empty."); + return; + } + + _questGreetingStore.rehash(result->GetRowCount()); + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + + uint32 id = fields[0].GetUInt32(); + uint8 type = fields[1].GetUInt8(); + // overwrite + switch (type) + { + case 0: // Creature + type = TYPEID_UNIT; + if (!sObjectMgr->GetCreatureTemplate(id)) + { + TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template (entry: %u) does not exist.", id); + continue; + } + break; + case 1: // GameObject + type = TYPEID_GAMEOBJECT; + if (!sObjectMgr->GetGameObjectTemplate(id)) + { + TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template (entry: %u) does not exist.", id); + continue; + } + break; + default: + TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: unknown type = %u for entry = %u. Skipped.", type, id); + continue; + } + + uint16 greetEmoteType = fields[2].GetUInt16(); + + if (greetEmoteType > 0 && !sEmotesStore.LookupEntry(greetEmoteType)) + { + TC_LOG_DEBUG("sql.sql", "Table `quest_greeting`: entry %u has greetEmoteType = %u but emote does not exist. Set to 0.", id, greetEmoteType); + greetEmoteType = 0; + } + + uint32 greetEmoteDelay = fields[3].GetUInt32(); + std::string greeting = fields[4].GetString(); + + _questGreetingStore[type][id] = new QuestGreeting(greetEmoteType, greetEmoteDelay, greeting); + + ++count; + } + while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u quest_greeting in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + +void ObjectMgr::LoadQuestGreetingsLocales() +{ + uint32 oldMSTime = getMSTime(); + + _questGreetingLocaleStore.clear(); // need for reload case + + // 0 1 2 3 + QueryResult result = WorldDatabase.Query("SELECT ID, Type, Locale, Greeting FROM quest_greeting_locale"); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 quest_greeting locales. DB table `quest_greeting_locale` is empty."); + return; + } + + do + { + Field* fields = result->Fetch(); + + uint32 id = fields[0].GetUInt32(); + uint8 type = fields[1].GetUInt8(); + // overwrite + switch (type) + { + case 0: // Creature + type = TYPEID_UNIT; + break; + case 1: // GameObject + type = TYPEID_GAMEOBJECT; + break; + default: + break; + } + + std::string localeName = fields[2].GetString(); + std::string greeting = fields[3].GetString(); + + QuestGreetingLocale& data = _questGreetingLocaleStore[MAKE_PAIR32(id, type)]; + LocaleConstant locale = GetLocaleByName(localeName); + if (locale == LOCALE_enUS) + continue; + + AddLocaleString(greeting, locale, data.greeting); + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u quest greeting locale strings in %u ms", uint32(_questGreetingLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime)); +} + void ObjectMgr::LoadTavernAreaTriggers() { uint32 oldMSTime = getMSTime(); @@ -6586,7 +6902,7 @@ uint32 ObjectMgr::GenerateGameObjectSpawnId() { if (_gameObjectSpawnId >= uint32(0xFFFFFF)) { - TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. "); + TC_LOG_ERROR("misc", "GameObject spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. "); World::StopNow(ERROR_EXIT_CODE); } return _gameObjectSpawnId++; @@ -6937,7 +7253,7 @@ void ObjectMgr::LoadExplorationBaseXP() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty."); return; } @@ -7044,7 +7360,7 @@ void ObjectMgr::LoadReputationRewardRate() QueryResult result = WorldDatabase.Query("SELECT faction, quest_rate, quest_daily_rate, quest_weekly_rate, quest_monthly_rate, quest_repeatable_rate, creature_rate, spell_rate FROM reputation_reward_rate"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!"); return; } @@ -7139,7 +7455,7 @@ void ObjectMgr::LoadReputationOnKill() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty."); return; } @@ -7299,7 +7615,7 @@ void ObjectMgr::LoadPointsOfInterest() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty."); return; } @@ -7344,7 +7660,7 @@ void ObjectMgr::LoadQuestPOI() QueryResult result = WorldDatabase.Query("SELECT QuestID, id, ObjectiveIndex, MapID, WorldMapAreaId, Floor, Priority, Flags FROM quest_poi order by QuestID"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty."); return; } @@ -7436,7 +7752,7 @@ void ObjectMgr::LoadNPCSpellClickSpells() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty."); return; } @@ -7497,17 +7813,23 @@ void ObjectMgr::DeleteCreatureData(ObjectGuid::LowType guid) // remove mapid*cellid -> guid_set map CreatureData const* data = GetCreatureData(guid); if (data) + { RemoveCreatureFromGrid(guid, data); + OnDeleteSpawnData(data); + } _creatureDataStore.erase(guid); } -void ObjectMgr::DeleteGOData(ObjectGuid::LowType guid) +void ObjectMgr::DeleteGameObjectData(ObjectGuid::LowType guid) { // remove mapid*cellid -> guid_set map - GameObjectData const* data = GetGOData(guid); + GameObjectData const* data = GetGameObjectData(guid); if (data) + { RemoveGameobjectFromGrid(guid, data); + OnDeleteSpawnData(data); + } _gameObjectDataStore.erase(guid); } @@ -7524,7 +7846,7 @@ void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReve if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 quest relations from `%s`, table is empty.", table.c_str()); + TC_LOG_INFO("server.loading", ">> Loaded 0 quest relations from `%s`, table is empty.", table.c_str()); return; } @@ -7550,7 +7872,7 @@ void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReve if (reverseMap) reverseMap->insert(QuestRelationsReverse::value_type(quest, id)); } - else if (starter) + else poolRelationMap->insert(PooledQuestRelation::value_type(quest, id)); ++count; @@ -7884,7 +8206,7 @@ bool ObjectMgr::LoadTrinityStrings() QueryResult result = WorldDatabase.Query("SELECT entry, content_default, content_loc1, content_loc2, content_loc3, content_loc4, content_loc5, content_loc6, content_loc7, content_loc8 FROM trinity_string"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty. You have imported an incorrect database for more info search for TCE00003 on forum."); + TC_LOG_INFO("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty. You have imported an incorrect database for more info search for TCE00003 on forum."); return false; } @@ -7930,7 +8252,7 @@ void ObjectMgr::LoadFishingBaseSkillLevel() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty."); return; } @@ -8049,7 +8371,7 @@ void ObjectMgr::LoadGameTele() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!"); return; } @@ -8207,7 +8529,7 @@ void ObjectMgr::LoadMailLevelRewards() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty."); return; } @@ -8474,7 +8796,7 @@ void ObjectMgr::LoadGossipMenu() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!"); return; } @@ -8512,7 +8834,7 @@ void ObjectMgr::LoadGossipMenuItems() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!"); return; } @@ -8733,7 +9055,7 @@ void ObjectMgr::LoadScriptNames() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded empty set of Script Names!"); + TC_LOG_INFO("server.loading", ">> Loaded empty set of Script Names!"); return; } @@ -9002,7 +9324,7 @@ void ObjectMgr::LoadFactionChangeAchievements() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty."); return; } @@ -9072,7 +9394,7 @@ void ObjectMgr::LoadFactionChangeQuests() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty."); return; } @@ -9142,7 +9464,7 @@ void ObjectMgr::LoadFactionChangeSpells() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty."); return; } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 95e7019dacf..8a07723a34e 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -23,8 +23,10 @@ #include "ConditionMgr.h" #include "CreatureData.h" #include "DatabaseEnvFwd.h" +#include "Errors.h" #include "GameObjectData.h" #include "ItemTemplate.h" +#include "IteratorPair.h" #include "NPCHandler.h" #include "ObjectDefines.h" #include "ObjectGuid.h" @@ -38,6 +40,7 @@ class Item; class Unit; class Vehicle; +class Map; struct AccessRequirement; struct DeclinedName; struct DungeonEncounterEntry; @@ -98,7 +101,7 @@ struct TempSummonData // DB scripting commands enum ScriptCommands { - SCRIPT_COMMAND_TALK = 0, // source/target = Creature, target = any, datalong = talk type (0=say, 1=whisper, 2=yell, 3=emote text, 4=boss emote text), datalong2 & 1 = player talk (instead of creature), dataint = string_id + SCRIPT_COMMAND_TALK = 0, // source/target = Creature, target = any, datalong = talk type (see ChatType enum), datalong2 & 1 = player talk (instead of creature), dataint = string_id SCRIPT_COMMAND_EMOTE = 1, // source/target = Creature, datalong = emote id, datalong2 = 0: set emote state; > 0: play emote state SCRIPT_COMMAND_FIELD_SET = 2, // source/target = Creature, datalong = field id, datalog2 = value SCRIPT_COMMAND_MOVE_TO = 3, // source/target = Creature, datalong2 = time to reach, x/y/z = destination @@ -419,6 +422,21 @@ std::string GetScriptsTableNameByType(ScriptsType type); ScriptMapMap* GetScriptsMapByType(ScriptsType type); std::string GetScriptCommandName(ScriptCommands command); +struct TC_GAME_API InstanceSpawnGroupInfo +{ + enum + { + FLAG_ACTIVATE_SPAWN = 0x01, + FLAG_BLOCK_SPAWN = 0x02, + + FLAG_ALL = (FLAG_ACTIVATE_SPAWN | FLAG_BLOCK_SPAWN) + }; + uint8 BossStateId; + uint8 BossStates; + uint32 SpawnGroupId; + uint8 Flags; +}; + struct TC_GAME_API SpellClickInfo { uint32 spellId; @@ -514,6 +532,11 @@ struct TrinityString std::vector<std::string> Content; }; +struct QuestGreetingLocale +{ + std::vector<std::string> greeting; +}; + typedef std::map<ObjectGuid, ObjectGuid> LinkedRespawnContainer; typedef std::unordered_map<uint32, CreatureTemplate> CreatureTemplateContainer; typedef std::unordered_map<uint32, CreatureAddon> CreatureTemplateAddonContainer; @@ -529,6 +552,9 @@ typedef std::unordered_map<uint32, GameObjectTemplateAddon> GameObjectTemplateAd typedef std::unordered_map<ObjectGuid::LowType, GameObjectData> GameObjectDataContainer; typedef std::unordered_map<ObjectGuid::LowType, GameObjectAddon> GameObjectAddonContainer; typedef std::unordered_map<uint32, std::vector<uint32>> GameObjectQuestItemMap; +typedef std::unordered_map<uint32, SpawnGroupTemplateData> SpawnGroupDataContainer; +typedef std::multimap<uint32, SpawnData const*> SpawnGroupLinkContainer; +typedef std::unordered_map<uint16, std::vector<InstanceSpawnGroupInfo>> InstanceSpawnGroupContainer; typedef std::map<TempSummonGroupKey, std::vector<TempSummonData>> TempSummonDataContainer; typedef std::unordered_map<uint32, CreatureLocale> CreatureLocaleContainer; typedef std::unordered_map<uint32, GameObjectLocale> GameObjectLocaleContainer; @@ -553,6 +579,7 @@ struct PointOfInterestLocale }; typedef std::unordered_map<uint32, PointOfInterestLocale> PointOfInterestLocaleContainer; +typedef std::unordered_map<uint32, QuestGreetingLocale> QuestGreetingLocaleContainer; typedef std::unordered_map<uint32, TrinityString> TrinityStringContainer; @@ -773,6 +800,19 @@ struct QuestPOIWrapper typedef std::unordered_map<uint32, QuestPOIWrapper> QuestPOIContainer; +struct QuestGreeting +{ + uint16 greetEmoteType; + uint32 greetEmoteDelay; + std::string greeting; + + QuestGreeting() : greetEmoteType(0), greetEmoteDelay(0) { } + QuestGreeting(uint16 _greetEmoteType, uint32 _greetEmoteDelay, std::string _greeting) + : greetEmoteType(_greetEmoteType), greetEmoteDelay(_greetEmoteDelay), greeting(_greeting) { } +}; + +typedef std::unordered_map<uint8, std::unordered_map<uint32, QuestGreeting const*>> QuestGreetingContainer; + struct GraveyardData { uint32 safeLocId; @@ -803,6 +843,7 @@ SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry); #define MAX_CHARTER_NAME 24 // max allowed by client name length TC_GAME_API bool normalizePlayerName(std::string& name); +#define SPAWNGROUP_MAP_UNSET 0xFFFFFFFF struct LanguageDesc { @@ -976,6 +1017,7 @@ class TC_GAME_API ObjectMgr } GossipText const* GetGossipText(uint32 Text_ID) const; + QuestGreeting const* GetQuestGreeting(ObjectGuid guid) const; WorldSafeLocsEntry const* GetDefaultGraveyard(uint32 team) const; WorldSafeLocsEntry const* GetClosestGraveyard(float x, float y, float z, uint32 MapId, uint32 team) const; @@ -1120,7 +1162,10 @@ class TC_GAME_API ObjectMgr void LoadCreatureModelInfo(); void LoadEquipmentTemplates(); void LoadGameObjectLocales(); - void LoadGameobjects(); + void LoadGameObjects(); + void LoadSpawnGroupTemplates(); + void LoadSpawnGroups(); + void LoadInstanceSpawnGroups(); void LoadItemTemplates(); void LoadItemLocales(); void LoadItemSetNames(); @@ -1130,6 +1175,7 @@ class TC_GAME_API ObjectMgr void LoadPageTextLocales(); void LoadGossipMenuItemsLocales(); void LoadPointOfInterestLocales(); + void LoadQuestGreetingsLocales(); void LoadInstanceTemplate(); void LoadInstanceEncounters(); void LoadMailLevelRewards(); @@ -1141,6 +1187,7 @@ class TC_GAME_API ObjectMgr void LoadAreaTriggerTeleports(); void LoadAccessRequirements(); void LoadQuestAreaTriggers(); + void LoadQuestGreetings(); void LoadAreaTriggerScripts(); void LoadTavernAreaTriggers(); void LoadGameObjectForQuests(); @@ -1202,8 +1249,14 @@ class TC_GAME_API ObjectMgr uint64 GenerateEquipmentSetGuid(); uint32 GenerateMailID(); uint32 GeneratePetNumber(); - uint32 GenerateCreatureSpawnId(); - uint32 GenerateGameObjectSpawnId(); + ObjectGuid::LowType GenerateCreatureSpawnId(); + ObjectGuid::LowType GenerateGameObjectSpawnId(); + + SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const { auto it = _spawnGroupDataStore.find(groupId); return it != _spawnGroupDataStore.end() ? &it->second : nullptr; } + SpawnGroupTemplateData const* GetDefaultSpawnGroup() const { return &_spawnGroupDataStore.at(0); } + SpawnGroupTemplateData const* GetLegacySpawnGroup() const { return &_spawnGroupDataStore.at(1); } + Trinity::IteratorPair<SpawnGroupLinkContainer::const_iterator> GetSpawnDataForGroup(uint32 groupId) const { return Trinity::Containers::MapEqualRange(_spawnGroupMapStore, groupId); } + std::vector<InstanceSpawnGroupInfo> const* GetSpawnGroupsForInstance(uint32 instanceId) const { auto it = _instanceSpawnGroupStore.find(instanceId); return it != _instanceSpawnGroupStore.end() ? &it->second : nullptr; } MailLevelReward const* GetMailLevelReward(uint32 level, uint32 raceMask) const { @@ -1254,6 +1307,18 @@ class TC_GAME_API ObjectMgr return nullptr; } + SpawnData const* GetSpawnData(SpawnObjectType type, ObjectGuid::LowType guid) + { + if (type == SPAWN_TYPE_CREATURE) + return GetCreatureData(guid); + else if (type == SPAWN_TYPE_GAMEOBJECT) + return GetGameObjectData(guid); + else + ASSERT(false, "Invalid spawn object type %u", uint32(type)); + return nullptr; + } + void OnDeleteSpawnData(SpawnData const* data); + CreatureDataContainer const& GetAllCreatureData() const { return _creatureDataStore; } CreatureData const* GetCreatureData(ObjectGuid::LowType guid) const { CreatureDataContainer::const_iterator itr = _creatureDataStore.find(guid); @@ -1274,6 +1339,15 @@ class TC_GAME_API ObjectMgr if (itr == _creatureLocaleStore.end()) return nullptr; return &itr->second; } + GameObjectDataContainer const& GetAllGameObjectData() const { return _gameObjectDataStore; } + GameObjectData const* GetGameObjectData(ObjectGuid::LowType guid) const + { + GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(guid); + if (itr == _gameObjectDataStore.end()) return nullptr; + return &itr->second; + } + GameObjectData& NewOrExistGameObjectData(ObjectGuid::LowType guid) { return _gameObjectDataStore[guid]; } + void DeleteGameObjectData(ObjectGuid::LowType guid); GameObjectLocale const* GetGameObjectLocale(uint32 entry) const { GameObjectLocaleContainer::const_iterator itr = _gameObjectLocaleStore.find(entry); @@ -1322,15 +1396,12 @@ class TC_GAME_API ObjectMgr if (itr == _pointOfInterestLocaleStore.end()) return nullptr; return &itr->second; } - - GameObjectData const* GetGOData(ObjectGuid::LowType guid) const + QuestGreetingLocale const* GetQuestGreetingLocale(uint32 id) const { - GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(guid); - if (itr == _gameObjectDataStore.end()) return nullptr; + QuestGreetingLocaleContainer::const_iterator itr = _questGreetingLocaleStore.find(id); + if (itr == _questGreetingLocaleStore.end()) return nullptr; return &itr->second; } - GameObjectData& NewGOData(ObjectGuid::LowType guid) { return _gameObjectDataStore[guid]; } - void DeleteGOData(ObjectGuid::LowType guid); TrinityString const* GetTrinityString(uint32 entry) const { @@ -1349,7 +1420,7 @@ class TC_GAME_API ObjectMgr void RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData const* data); void AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData const* data); void RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectData const* data); - ObjectGuid::LowType AddGOData(uint32 entry, uint32 map, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay = 0); + ObjectGuid::LowType AddGameObjectData(uint32 entry, uint32 map, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay = 0); ObjectGuid::LowType AddCreatureData(uint32 entry, uint32 map, Position const& pos, uint32 spawntimedelay = 0); // reserved names @@ -1458,8 +1529,8 @@ class TC_GAME_API ObjectMgr std::atomic<uint32> _mailId; std::atomic<uint32> _hiPetNumber; - uint32 _creatureSpawnId; - uint32 _gameObjectSpawnId; + ObjectGuid::LowType _creatureSpawnId; + ObjectGuid::LowType _gameObjectSpawnId; // first free low guid for selected guid type template<HighGuid high> @@ -1484,6 +1555,7 @@ class TC_GAME_API ObjectMgr TavernAreaTriggerContainer _tavernAreaTriggerStore; GameObjectForQuestContainer _gameObjectForQuestStore; GossipTextContainer _gossipTextStore; + QuestGreetingContainer _questGreetingStore; AreaTriggerContainer _areaTriggerStore; AreaTriggerScriptContainer _areaTriggerScriptStore; AccessRequirementContainer _accessRequirementStore; @@ -1579,6 +1651,9 @@ class TC_GAME_API ObjectMgr GameObjectLocaleContainer _gameObjectLocaleStore; GameObjectTemplateContainer _gameObjectTemplateStore; GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore; + SpawnGroupDataContainer _spawnGroupDataStore; + SpawnGroupLinkContainer _spawnGroupMapStore; + InstanceSpawnGroupContainer _instanceSpawnGroupStore; /// Stores temp summon data grouped by summoner's entry, summoner's type and group id TempSummonDataContainer _tempSummonDataStore; @@ -1591,6 +1666,7 @@ class TC_GAME_API ObjectMgr PageTextLocaleContainer _pageTextLocaleStore; GossipMenuItemsLocaleContainer _gossipMenuItemsLocaleStore; PointOfInterestLocaleContainer _pointOfInterestLocaleStore; + QuestGreetingLocaleContainer _questGreetingLocaleStore; TrinityStringContainer _trinityStringStore; diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 897e43d5e03..16583516243 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -552,6 +552,11 @@ namespace Trinity : ContainerInserter<Player*>(container), i_phaseMask(searcher->GetPhaseMask()), i_check(check) { } + template<typename Container> + PlayerListSearcher(uint32 phaseMask, Container& container, Check & check) + : ContainerInserter<Player*>(container), + i_phaseMask(phaseMask), i_check(check) { } + void Visit(PlayerMapType &m); template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) { } @@ -928,9 +933,9 @@ namespace Trinity return false; float searchRadius = i_range; - if (i_incOwnRadius) + if (i_incOwnRadius) searchRadius += i_obj->GetCombatReach(); - if (i_incTargetRadius) + if (i_incTargetRadius) searchRadius += u->GetCombatReach(); if (!u->IsInMap(i_obj) || !u->InSamePhase(i_obj) || !u->IsWithinDoubleVerticalCylinder(i_obj, searchRadius, searchRadius)) @@ -954,7 +959,7 @@ namespace Trinity class AnyGroupedUnitInObjectRangeCheck { public: - AnyGroupedUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool raid, bool playerOnly = false, bool incOwnRadius = true, bool incTargetRadius = true) + AnyGroupedUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool raid, bool playerOnly = false, bool incOwnRadius = true, bool incTargetRadius = true) : _source(obj), _refUnit(funit), _range(range), _raid(raid), _playerOnly(playerOnly), i_incOwnRadius(incOwnRadius), i_incTargetRadius(incTargetRadius) { } bool operator()(Unit* u) const @@ -977,9 +982,9 @@ namespace Trinity return false; float searchRadius = _range; - if (i_incOwnRadius) + if (i_incOwnRadius) searchRadius += _source->GetCombatReach(); - if (i_incTargetRadius) + if (i_incTargetRadius) searchRadius += u->GetCombatReach(); return u->IsInMap(_source) && u->InSamePhase(_source) && u->IsWithinDoubleVerticalCylinder(_source, searchRadius, searchRadius); @@ -1022,7 +1027,7 @@ namespace Trinity bool operator()(Unit* u) { if (u->isTargetableForAttack() && i_obj->IsWithinDistInMap(u, i_range) && - !i_funit->IsFriendlyTo(u) && i_funit->CanSeeOrDetect(u)) + (i_funit->IsInCombatWith(u) || i_funit->IsHostileTo(u)) && i_obj->CanSeeOrDetect(u)) { i_range = i_obj->GetDistance(u); // use found unit range as new range limit for next check return true; @@ -1064,9 +1069,9 @@ namespace Trinity return false; float searchRadius = i_range; - if (i_incOwnRadius) + if (i_incOwnRadius) searchRadius += i_obj->GetCombatReach(); - if (i_incTargetRadius) + if (i_incTargetRadius) searchRadius += u->GetCombatReach(); return u->IsInMap(i_obj) && u->InSamePhase(i_obj) && u->IsWithinDoubleVerticalCylinder(i_obj, searchRadius, searchRadius); @@ -1321,6 +1326,27 @@ namespace Trinity bool _reqAlive; }; + class AnyPlayerInPositionRangeCheck + { + public: + AnyPlayerInPositionRangeCheck(Position const* pos, float range, bool reqAlive = true) : _pos(pos), _range(range), _reqAlive(reqAlive) { } + bool operator()(Player* u) + { + if (_reqAlive && !u->IsAlive()) + return false; + + if (!u->IsWithinDist3d(_pos, _range)) + return false; + + return true; + } + + private: + Position const* _pos; + float _range; + bool _reqAlive; + }; + class NearestPlayerInObjectRangeCheck { public: diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp index 653c9d51d11..4dfca6f98c0 100644 --- a/src/server/game/Grids/ObjectGridLoader.cpp +++ b/src/server/game/Grids/ObjectGridLoader.cpp @@ -27,6 +27,7 @@ #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "World.h" +#include "ScriptMgr.h" void ObjectGridEvacuator::Visit(CreatureMapType &m) { @@ -119,15 +120,56 @@ void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager<T> for (CellGuidSet::const_iterator i_guid = guid_set.begin(); i_guid != guid_set.end(); ++i_guid) { T* obj = new T; - ObjectGuid::LowType guid = *i_guid; - //TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid); - if (!obj->LoadFromDB(guid, map)) + + // Don't spawn at all if there's a respawn time + if ((obj->GetTypeId() == TYPEID_UNIT && !map->GetCreatureRespawnTime(*i_guid)) || (obj->GetTypeId() == TYPEID_GAMEOBJECT && !map->GetGORespawnTime(*i_guid))) { - delete obj; - continue; - } + ObjectGuid::LowType guid = *i_guid; + //TC_LOG_INFO("misc", "DEBUG: LoadHelper from table: %s for (guid: %u) Loading", table, guid); - AddObjectHelper(cell, m, count, map, obj); + if (obj->GetTypeId() == TYPEID_UNIT) + { + CreatureData const* cdata = sObjectMgr->GetCreatureData(guid); + ASSERT(cdata, "Tried to load creature with spawnId %u, but no such creature exists.", guid); + SpawnGroupTemplateData const* const group = cdata->spawnGroupData; + // If creature in manual spawn group, don't spawn here, unless group is already active. + if (!(group->flags & SPAWNGROUP_FLAG_SYSTEM)) + if (!map->IsSpawnGroupActive(group->groupId)) + { + delete obj; + continue; + } + + // If script is blocking spawn, don't spawn but queue for a re-check in a little bit + if (!(group->flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE) && !sScriptMgr->CanSpawn(guid, cdata->id, cdata, map)) + { + map->SaveRespawnTime(SPAWN_TYPE_CREATURE, guid, cdata->id, time(NULL) + urand(4,7), map->GetZoneId(cdata->spawnPoint), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false); + delete obj; + continue; + } + } + else if (obj->GetTypeId() == TYPEID_GAMEOBJECT) + { + // If gameobject in manual spawn group, don't spawn here, unless group is already active. + GameObjectData const* godata = sObjectMgr->GetGameObjectData(guid); + ASSERT(godata, "Tried to load gameobject with spawnId %u, but no such object exists.", guid); + if (!(godata->spawnGroupData->flags & SPAWNGROUP_FLAG_SYSTEM)) + if (!map->IsSpawnGroupActive(godata->spawnGroupData->groupId)) + { + delete obj; + continue; + } + } + + if (!obj->LoadFromDB(guid, map, false, false)) + { + delete obj; + continue; + } + AddObjectHelper(cell, m, count, map, obj); + } + else + delete obj; } } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 76b7de68ea9..79df88b61a5 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1534,8 +1534,7 @@ void Group::CountTheRoll(Rolls::iterator rollI, Map* allowedMap) // remove is_blocked so that the item is lootable by all players LootItem* item = &(roll->itemSlot >= roll->getLoot()->items.size() ? roll->getLoot()->quest_items[roll->itemSlot - roll->getLoot()->items.size()] : roll->getLoot()->items[roll->itemSlot]); - if (item) - item->is_blocked = false; + item->is_blocked = false; } RollId.erase(rollI); diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index 8b03ff91240..bb8f52b90fd 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -51,7 +51,8 @@ void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket& recvData) return; // Stop the npc if moving - unit->StopMoving(); + unit->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); + unit->SetHomePosition(unit->GetPosition()); BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(unit->GetEntry()); diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp index 6d884ca2ee9..4384ff82474 100644 --- a/src/server/game/Handlers/CalendarHandler.cpp +++ b/src/server/game/Handlers/CalendarHandler.cpp @@ -41,6 +41,7 @@ Copied events should probably have a new owner #include "CharacterCache.h" #include "DatabaseEnv.h" #include "DBCStores.h" +#include "GameEventMgr.h" #include "Guild.h" #include "GuildMgr.h" #include "InstanceSaveMgr.h" @@ -151,12 +152,10 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recvData*/) data << uint32(boundCounter); data.append(dataBuffer); - /// @todo Fix this, how we do know how many and what holidays to send? - uint32 holidayCount = 0; - data << uint32(holidayCount); - for (uint32 i = 0; i < holidayCount; ++i) + data << uint32(sGameEventMgr->modifiedHolidays.size()); + for (uint32 entry : sGameEventMgr->modifiedHolidays) { - HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(666); + HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(entry); data << uint32(holiday->Id); // m_ID data << uint32(holiday->Region); // m_region, might be looping diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 9bdf294c9b3..df8f418c245 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1560,6 +1560,9 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData) InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sDest, uItem, false); if (msg == EQUIP_ERR_OK) { + if (_player->CanEquipItem(NULL_SLOT, dstpos, uItem, false) != EQUIP_ERR_OK) + continue; + _player->RemoveItem(INVENTORY_SLOT_BAG_0, i, true); _player->StoreItem(sDest, uItem, true); } @@ -1572,6 +1575,9 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData) if (item->GetPos() == dstpos) continue; + if (_player->CanUnequipItem(dstpos, true) != EQUIP_ERR_OK) + continue; + _player->SwapItem(item->GetPos(), dstpos); } @@ -2084,7 +2090,7 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr<Charact ss << knownTitles[index] << ' '; stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_TITLES_FACTION_CHANGE); - stmt->setString(0, ss.str().c_str()); + stmt->setString(0, ss.str()); stmt->setUInt32(1, lowGuid); trans->Append(stmt); diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index eb3200ea87d..259bc08838e 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -126,7 +126,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) // LANG_ADDON should not be changed nor be affected by flood control else { - // send in universal language if player in .gmon mode (ignore spell effects) + // send in universal language if player in .gm on mode (ignore spell effects) if (sender->IsGameMaster()) lang = LANG_UNIVERSAL; else @@ -216,11 +216,15 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) if (msg.empty()) return; - if (ChatHandler(this).ParseCommands(msg.c_str())) - return; - + if (lang == LANG_ADDON) + { + if (AddonChannelCommandHandler(this).ParseCommands(msg.c_str())) + return; + } if (lang != LANG_ADDON) { + if (ChatHandler(this).ParseCommands(msg.c_str())) + return; // Strip invisible characters for non-addon messages if (sWorld->getBoolConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) stripLineInvisibleChars(msg); @@ -466,7 +470,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) if (Channel* chn = ChannelMgr::GetChannelForPlayerByNamePart(channel, sender)) { sScriptMgr->OnPlayerChat(sender, type, lang, msg, chn); - chn->Say(sender->GetGUID(), msg.c_str(), lang); + chn->Say(sender->GetGUID(), msg, lang); } break; } diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index 9f3e2a52efe..ee2666c9f96 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -615,8 +615,8 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); // Stop the npc if moving - if (vendor->HasUnitState(UNIT_STATE_MOVING)) - vendor->StopMoving(); + vendor->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); + vendor->SetHomePosition(vendor->GetPosition()); VendorItemData const* items = vendor->GetVendorItems(); if (!items) diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index a632bf1979c..9a130fab45f 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -51,6 +51,7 @@ #include "WhoListStorage.h" #include "World.h" #include "WorldPacket.h" +#include <cstdarg> #include <zlib.h> void WorldSession::HandleRepopRequestOpcode(WorldPacket& recvData) diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 8b64e0e08bb..a8b365e5db2 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -24,9 +24,10 @@ #include "Corpse.h" #include "Player.h" #include "MapManager.h" +#include "MotionMaster.h" +#include "MovementGenerator.h" #include "Transport.h" #include "Battleground.h" -#include "WaypointMovementGenerator.h" #include "InstanceSaveMgr.h" #include "ObjectMgr.h" #include "Vehicle.h" @@ -133,8 +134,8 @@ void WorldSession::HandleMoveWorldportAck() if (!_player->InBattleground()) { // short preparations to continue flight - FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top()); - flight->Initialize(GetPlayer()); + MovementGenerator* movementGenerator = GetPlayer()->GetMotionMaster()->top(); + movementGenerator->Initialize(GetPlayer()); return; } diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index ddde13ae860..59344509283 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -38,6 +38,7 @@ #include "ScriptMgr.h" #include "SpellInfo.h" #include "SpellMgr.h" +#include "World.h" #include "WorldPacket.h" enum StableResultCode @@ -322,11 +323,9 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData) //if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) // GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - // and if he has pure gossip or is banker and moves or is tabard designer? - //if (unit->IsArmorer() || unit->IsCivilian() || unit->IsQuestGiver() || unit->IsServiceProvider() || unit->IsGuard()) - { - unit->StopMoving(); - } + // Stop the npc if moving + unit->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); + unit->SetHomePosition(unit->GetPosition()); // If spiritguide, no need for gossip menu, just put player into resurrect queue if (unit->IsSpiritGuide()) diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp index 4e47f260fff..03087ce5e7e 100644 --- a/src/server/game/Handlers/PetitionsHandler.cpp +++ b/src/server/game/Handlers/PetitionsHandler.cpp @@ -420,7 +420,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData) { if (_player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName().c_str(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName(), ERR_ARENA_TEAM_TARGET_TOO_LOW_S); return; } @@ -430,13 +430,13 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData) if (_player->GetArenaTeamId(slot)) { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName().c_str(), ERR_ALREADY_IN_ARENA_TEAM_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_IN_ARENA_TEAM_S); return; } if (_player->GetArenaTeamIdInvited()) { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName().c_str(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); return; } } @@ -549,7 +549,7 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData) if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) { // player is too low level to join an arena team - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName().c_str(), "", ERR_ARENA_TEAM_TARGET_TOO_LOW_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ARENA_TEAM_TARGET_TOO_LOW_S); return; } @@ -560,13 +560,13 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData) if (player->GetArenaTeamId(slot)) { // player is already in an arena team - SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName().c_str(), "", ERR_ALREADY_IN_ARENA_TEAM_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S); return; } if (player->GetArenaTeamIdInvited()) { - SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName().c_str(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", _player->GetName(), ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); return; } } diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index f95fb01ed36..23c0ac25524 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -91,8 +91,10 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recvData) // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + // Stop the npc if moving - creature->StopMoving(); + creature->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); + creature->SetHomePosition(creature->GetPosition()); _player->PlayerTalkClass->ClearMenus(); if (creature->AI()->GossipHello(_player)) diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index 3912707ea25..6adc2d04aed 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -18,9 +18,12 @@ #include "WorldSession.h" #include "Common.h" +#include "Creature.h" #include "DatabaseEnv.h" #include "DBCStores.h" #include "Log.h" +#include "MotionMaster.h" +#include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" @@ -39,25 +42,22 @@ void WorldSession::HandleTaxiNodeStatusQueryOpcode(WorldPacket& recvData) void WorldSession::SendTaxiStatus(ObjectGuid guid) { - // cheating checks - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER); - if (!unit) + Player* const player = GetPlayer(); + Creature* unit = ObjectAccessor::GetCreature(*player, guid); + if (!unit || unit->IsHostileTo(player) || !unit->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_FLIGHTMASTER)) { TC_LOG_DEBUG("network", "WorldSession::SendTaxiStatus - %s not found or you can't interact with him.", guid.ToString().c_str()); return; } - uint32 curloc = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), GetPlayer()->GetTeam()); - - // not found nearest - if (curloc == 0) + // find taxi node + uint32 nearest = sObjectMgr->GetNearestTaxiNode(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), unit->GetMapId(), player->GetTeam()); + if (!nearest) return; - TC_LOG_DEBUG("network", "WORLD: current location %u ", curloc); - WorldPacket data(SMSG_TAXINODE_STATUS, 9); data << guid; - data << uint8(GetPlayer()->m_taxi.IsTaximaskNodeKnown(curloc) ? 1 : 0); + data << uint8(player->m_taxi.IsTaximaskNodeKnown(nearest) ? 1 : 0); SendPacket(&data); } @@ -119,8 +119,7 @@ void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathN if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - while (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) - GetPlayer()->GetMotionMaster()->MovementExpired(false); + GetPlayer()->GetMotionMaster()->Clear(MOTION_SLOT_CONTROLLED); if (mountDisplayId) GetPlayer()->Mount(mountDisplayId); diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index 3fa7eb52d6e..154254e25b2 100644 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -46,7 +46,7 @@ BossBoundaryData::~BossBoundaryData() delete it->Boundary; } -InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0) +InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0), _instanceSpawnGroups(sObjectMgr->GetSpawnGroupsForInstance(map->GetId())) { #ifdef TRINITY_API_USE_DYNAMIC_LINKING uint32 scriptId = sObjectMgr->GetInstanceTemplate(map->GetId())->ScriptId; @@ -186,27 +186,6 @@ void InstanceScript::LoadObjectData(ObjectData const* data, ObjectInfoMap& objec } } -void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state) -{ - switch (state) - { - case NOT_STARTED: - if (!minion->IsAlive()) - minion->Respawn(); - else if (minion->IsInCombat()) - minion->AI()->EnterEvadeMode(); - break; - case IN_PROGRESS: - if (!minion->IsAlive()) - minion->Respawn(); - else if (!minion->GetVictim()) - minion->AI()->DoZoneInCombat(); - break; - default: - break; - } -} - void InstanceScript::UpdateDoorState(GameObject* door) { DoorInfoMapBounds range = doors.equal_range(door->GetEntry()); @@ -236,6 +215,60 @@ void InstanceScript::UpdateDoorState(GameObject* door) door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY); } +void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state) +{ + switch (state) + { + case NOT_STARTED: + if (!minion->IsAlive()) + minion->Respawn(); + else if (minion->IsInCombat()) + minion->AI()->EnterEvadeMode(); + break; + case IN_PROGRESS: + if (!minion->IsAlive()) + minion->Respawn(); + else if (!minion->GetVictim()) + minion->AI()->DoZoneInCombat(); + break; + default: + break; + } +} + +void InstanceScript::UpdateSpawnGroups() +{ + if (!_instanceSpawnGroups) + return; + enum states { BLOCK, SPAWN, FORCEBLOCK }; + std::unordered_map<uint32, states> newStates; + for (auto it = _instanceSpawnGroups->begin(), end = _instanceSpawnGroups->end(); it != end; ++it) + { + InstanceSpawnGroupInfo const& info = *it; + states& curValue = newStates[info.SpawnGroupId]; // makes sure there's a BLOCK value in the map + if (curValue == FORCEBLOCK) // nothing will change this + continue; + if (!((1 << GetBossState(info.BossStateId)) & info.BossStates)) + continue; + if (info.Flags & InstanceSpawnGroupInfo::FLAG_BLOCK_SPAWN) + curValue = FORCEBLOCK; + else if (info.Flags & InstanceSpawnGroupInfo::FLAG_ACTIVATE_SPAWN) + curValue = SPAWN; + } + for (auto const& pair : newStates) + { + uint32 const groupId = pair.first; + bool const doSpawn = (pair.second == SPAWN); + if (instance->IsSpawnGroupActive(groupId) == doSpawn) + continue; // nothing to do here + // if we should spawn group, then spawn it... + if (doSpawn) + instance->SpawnGroupSpawn(groupId); + else // otherwise, set it as inactive so it no longer respawns (but don't despawn it) + instance->SetSpawnGroupActive(groupId, false); + } +} + BossInfo* InstanceScript::GetBossInfo(uint32 id) { ASSERT(id < bosses.size()); @@ -310,7 +343,7 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state) if (bossInfo->state == TO_BE_DECIDED) // loading { bossInfo->state = state; - //TC_LOG_ERROR("misc", "Inialize boss %u state as %u.", id, (uint32)state); + TC_LOG_DEBUG("scripts", "InstanceScript: Initialize boss %u state as %s (map %u, %u).", id, GetBossStateName(state), instance->GetId(), instance->GetInstanceId()); return false; } else @@ -318,6 +351,12 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state) if (bossInfo->state == state) return false; + if (bossInfo->state == DONE) + { + TC_LOG_ERROR("map", "InstanceScript: Tried to set instance state from %s back to %s for map %u, instance id %u. Blocked!", GetBossStateName(bossInfo->state), GetBossStateName(state), instance->GetId(), instance->GetInstanceId()); + return false; + } + if (state == DONE) for (GuidSet::iterator i = bossInfo->minion.begin(); i != bossInfo->minion.end(); ++i) if (Creature* minion = instance->GetCreature(*i)) @@ -337,6 +376,7 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state) if (Creature* minion = instance->GetCreature(*i)) UpdateMinionState(minion, state); + UpdateSpawnGroups(); return true; } return false; @@ -347,6 +387,13 @@ bool InstanceScript::_SkipCheckRequiredBosses(Player const* player /*= nullptr*/ return player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES); } +void InstanceScript::Create() +{ + for (size_t i = 0; i < bosses.size(); ++i) + SetBossState(i, NOT_STARTED); + UpdateSpawnGroups(); +} + void InstanceScript::Load(char const* data) { if (!data) @@ -397,6 +444,7 @@ void InstanceScript::ReadSaveDataBossStates(std::istringstream& data) if (buff < TO_BE_DECIDED) SetBossState(bossId, EncounterState(buff)); } + UpdateSpawnGroups(); } std::string InstanceScript::GetSaveData() @@ -691,7 +739,7 @@ void InstanceScript::UpdateEncounterStateForSpellCast(uint32 spellId, Unit* sour UpdateEncounterState(ENCOUNTER_CREDIT_CAST_SPELL, spellId, source); } -std::string InstanceScript::GetBossStateName(uint8 state) +/*static*/ char const* InstanceScript::GetBossStateName(uint8 state) { // See enum EncounterState in InstanceScript.h switch (state) diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index 55452ebf3c1..d9802aa548b 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -34,6 +34,7 @@ class AreaBoundary; class Creature; class GameObject; +struct InstanceSpawnGroupInfo; class Map; class ModuleReference; class Player; @@ -156,7 +157,10 @@ class TC_GAME_API InstanceScript : public ZoneScript // KEEPING THIS METHOD ONLY FOR BACKWARD COMPATIBILITY !!! virtual void Initialize() { } - // On load + // On instance load, exactly ONE of these methods will ALWAYS be called: + // if we're starting without any saved instance data + virtual void Create(); + // if we're loading existing instance save data virtual void Load(char const* data); // When save is needed, this function generates the data @@ -223,7 +227,7 @@ class TC_GAME_API InstanceScript : public ZoneScript virtual bool SetBossState(uint32 id, EncounterState state); EncounterState GetBossState(uint32 id) const { return id < bosses.size() ? bosses[id].state : TO_BE_DECIDED; } - static std::string GetBossStateName(uint8 state); + static char const* GetBossStateName(uint8 state); CreatureBoundary const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : nullptr; } // Achievement criteria additional requirements check @@ -249,6 +253,11 @@ class TC_GAME_API InstanceScript : public ZoneScript uint32 GetEncounterCount() const { return bosses.size(); } + // Only used by areatriggers that inherit from OnlyOnceAreaTriggerScript + void MarkAreaTriggerDone(uint32 id) { _activatedAreaTriggers.insert(id); } + void ResetAreaTriggerDone(uint32 id) { _activatedAreaTriggers.erase(id); } + bool IsAreaTriggerDone(uint32 id) const { return _activatedAreaTriggers.find(id) != _activatedAreaTriggers.end(); } + protected: void SetHeaders(std::string const& dataHeaders); void SetBossNumber(uint32 number) { bosses.resize(number); } @@ -267,6 +276,8 @@ class TC_GAME_API InstanceScript : public ZoneScript virtual void UpdateDoorState(GameObject* door); void UpdateMinionState(Creature* minion, EncounterState state); + void UpdateSpawnGroups(); + // Exposes private data that should never be modified unless exceptional cases. // Pay very much attention at how the returned BossInfo data is modified to avoid issues. BossInfo* GetBossInfo(uint32 id); @@ -293,6 +304,8 @@ class TC_GAME_API InstanceScript : public ZoneScript ObjectInfoMap _gameObjectInfo; ObjectGuidMap _objectGuids; uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets + std::vector<InstanceSpawnGroupInfo> const* const _instanceSpawnGroups; + std::unordered_set<uint32> _activatedAreaTriggers; #ifdef TRINITY_API_USE_DYNAMIC_LINKING // Strong reference to the associated script module diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 7c6cc28fb48..f93ee0a91bc 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -812,7 +812,7 @@ void LoadLootTemplates_Creature() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u creature loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 creature loot templates. DB table `creature_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 creature loot templates. DB table `creature_loot_template` is empty"); } void LoadLootTemplates_Disenchant() @@ -845,7 +845,7 @@ void LoadLootTemplates_Disenchant() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u disenchanting loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 disenchanting loot templates. DB table `disenchant_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 disenchanting loot templates. DB table `disenchant_loot_template` is empty"); } void LoadLootTemplates_Fishing() @@ -868,7 +868,7 @@ void LoadLootTemplates_Fishing() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u fishing loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 fishing loot templates. DB table `fishing_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 fishing loot templates. DB table `fishing_loot_template` is empty"); } void LoadLootTemplates_Gameobject() @@ -902,7 +902,7 @@ void LoadLootTemplates_Gameobject() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u gameobject loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 gameobject loot templates. DB table `gameobject_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject loot templates. DB table `gameobject_loot_template` is empty"); } void LoadLootTemplates_Item() @@ -926,7 +926,7 @@ void LoadLootTemplates_Item() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u item loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 item loot templates. DB table `item_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 item loot templates. DB table `item_loot_template` is empty"); } void LoadLootTemplates_Milling() @@ -955,7 +955,7 @@ void LoadLootTemplates_Milling() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u milling loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 milling loot templates. DB table `milling_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 milling loot templates. DB table `milling_loot_template` is empty"); } void LoadLootTemplates_Pickpocketing() @@ -989,7 +989,7 @@ void LoadLootTemplates_Pickpocketing() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u pickpocketing loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 pickpocketing loot templates. DB table `pickpocketing_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 pickpocketing loot templates. DB table `pickpocketing_loot_template` is empty"); } void LoadLootTemplates_Prospecting() @@ -1018,7 +1018,7 @@ void LoadLootTemplates_Prospecting() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u prospecting loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 prospecting loot templates. DB table `prospecting_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 prospecting loot templates. DB table `prospecting_loot_template` is empty"); } void LoadLootTemplates_Mail() @@ -1042,7 +1042,7 @@ void LoadLootTemplates_Mail() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u mail loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 mail loot templates. DB table `mail_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 mail loot templates. DB table `mail_loot_template` is empty"); } void LoadLootTemplates_Skinning() @@ -1076,7 +1076,7 @@ void LoadLootTemplates_Skinning() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u skinning loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 skinning loot templates. DB table `skinning_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 skinning loot templates. DB table `skinning_loot_template` is empty"); } void LoadLootTemplates_Spell() @@ -1116,7 +1116,7 @@ void LoadLootTemplates_Spell() if (count) TC_LOG_INFO("server.loading", ">> Loaded %u spell loot templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); else - TC_LOG_ERROR("server.loading", ">> Loaded 0 spell loot templates. DB table `spell_loot_template` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 spell loot templates. DB table `spell_loot_template` is empty"); } void LoadLootTemplates_Reference() diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 0793422f8ad..61f24f2b6e5 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -23,6 +23,7 @@ #include "DisableMgr.h" #include "DynamicTree.h" #include "GameObjectModel.h" +#include "GameTime.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "GridStates.h" @@ -37,6 +38,7 @@ #include "ObjectGridLoader.h" #include "ObjectMgr.h" #include "Pet.h" +#include "PoolMgr.h" #include "ScriptMgr.h" #include "Transport.h" #include "Vehicle.h" @@ -61,6 +63,10 @@ Map::~Map() sScriptMgr->OnDestroyMap(this); + // Delete all waiting spawns, else there will be a memory leak + // This doesn't delete from database. + DeleteRespawnInfo(); + while (!i_worldObjects.empty()) { WorldObject* obj = *i_worldObjects.begin(); @@ -253,7 +259,7 @@ m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD), m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()), i_gridExpiry(expiry), -i_scriptLock(false), _defaultLight(GetDefaultMapLight(id)) +i_scriptLock(false), _respawnCheckTimer(0), _defaultLight(GetDefaultMapLight(id)) { m_parentMap = (_parent ? _parent : this); for (unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx) @@ -266,6 +272,8 @@ i_scriptLock(false), _defaultLight(GetDefaultMapLight(id)) } } + _zonePlayerCountMap.clear(); + //lets initialize visibility distance for map Map::InitVisibilityDistance(); @@ -522,6 +530,29 @@ bool Map::EnsureGridLoaded(Cell const& cell) return false; } +void Map::GridMarkNoUnload(uint32 x, uint32 y) +{ + // First make sure this grid is loaded + float gX = ((float(x) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2); + float gY = ((float(y) - 0.5f - CENTER_GRID_ID) * SIZE_OF_GRIDS) + (CENTER_GRID_OFFSET * 2); + Cell cell = Cell(gX, gY); + EnsureGridLoaded(cell); + + // Mark as don't unload + NGridType* grid = getNGrid(x, y); + grid->setUnloadExplicitLock(true); +} + +void Map::GridUnmarkNoUnload(uint32 x, uint32 y) +{ + // If grid is loaded, clear unload lock + if (IsGridLoaded(GridCoord(x, y))) + { + NGridType* grid = getNGrid(x, y); + grid->setUnloadExplicitLock(false); + } +} + void Map::LoadGrid(float x, float y) { EnsureGridLoaded(Cell(x, y)); @@ -689,6 +720,21 @@ void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Trinity::Obj } } +void Map::UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone) +{ + // Nothing to do if no change + if (oldZone == newZone) + return; + + if (oldZone != MAP_INVALID_ZONE) + { + uint32& oldZoneCount = _zonePlayerCountMap[oldZone]; + ASSERT(oldZoneCount, "A player left zone %u (went to %u) - but there were no players in the zone!", oldZone, newZone); + --oldZoneCount; + } + ++_zonePlayerCountMap[newZone]; +} + void Map::Update(uint32 t_diff) { _dynamicTree.update(t_diff); @@ -704,6 +750,16 @@ void Map::Update(uint32 t_diff) session->Update(t_diff, updater); } } + + /// process any due respawns + if (_respawnCheckTimer <= t_diff) + { + ProcessRespawns(); + _respawnCheckTimer = sWorld->getIntConfig(CONFIG_RESPAWN_MINCHECKINTERVALMS); + } + else + _respawnCheckTimer -= t_diff; + /// update active cells around players and active objects resetMarkedCells(); @@ -885,6 +941,8 @@ void Map::ProcessRelocationNotifies(const uint32 diff) void Map::RemovePlayerFromMap(Player* player, bool remove) { + // Before leaving map, update zone/area for stats + player->UpdateZone(MAP_INVALID_ZONE, 0); sScriptMgr->OnPlayerLeaveMap(this, player); player->getHostileRefManager().deleteReferences(); // multithreading crashfix @@ -2639,10 +2697,7 @@ void Map::GetFullTerrainStatusForPosition(float x, float y, float z, PositionFul else { data.floorZ = mapHeight; - if (gmap) - data.areaId = gmap->getArea(x, y); - else - data.areaId = 0; + data.areaId = gmap->getArea(x, y); if (!data.areaId) data.areaId = i_mapEntry->linked_zone; @@ -2744,11 +2799,6 @@ bool Map::getObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float return result; } -float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=true*/, float maxSearchDist/*=DEFAULT_HEIGHT_SEARCH*/) const -{ - return std::max<float>(GetHeight(x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phasemask, x, y, z, maxSearchDist)); -} - bool Map::IsInWater(float x, float y, float pZ, LiquidData* data) const { LiquidData liquid_status; @@ -2867,6 +2917,474 @@ void Map::SendObjectUpdates() } } +bool Map::CheckRespawn(RespawnInfo* info) +{ + uint32 poolId = info->spawnId ? sPoolMgr->IsPartOfAPool(info->type, info->spawnId) : 0; + // First, check if there's already an instance of this object that would block the respawn + // Only do this for unpooled spawns + if (!poolId) + { + bool doDelete = false; + switch (info->type) + { + case SPAWN_TYPE_CREATURE: + { + // escort check for creatures only (if the world config boolean is set) + bool isEscort = false; + if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && info->type == SPAWN_TYPE_CREATURE) + if (CreatureData const* cdata = sObjectMgr->GetCreatureData(info->spawnId)) + if (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC) + isEscort = true; + + auto range = _creatureBySpawnIdStore.equal_range(info->spawnId); + for (auto it = range.first; it != range.second; ++it) + { + Creature* creature = it->second; + if (!creature->IsAlive()) + continue; + // escort NPCs are allowed to respawn as long as all other instances are already escorting + if (isEscort && creature->IsEscortNPC(true)) + continue; + doDelete = true; + break; + } + break; + } + case SPAWN_TYPE_GAMEOBJECT: + // gameobject check is simpler - they cannot be dead or escorting + if (_gameobjectBySpawnIdStore.find(info->spawnId) != _gameobjectBySpawnIdStore.end()) + doDelete = true; + break; + default: + ASSERT(false, "Invalid spawn type %u with spawnId %u on map %u", uint32(info->type), info->spawnId, GetId()); + return true; + } + if (doDelete) + { + info->respawnTime = 0; + return false; + } + } + + // next, check linked respawn time + ObjectGuid thisGUID = ObjectGuid((info->type == SPAWN_TYPE_GAMEOBJECT) ? HighGuid::GameObject : HighGuid::Unit, info->entry, info->spawnId); + if (time_t linkedTime = GetLinkedRespawnTime(thisGUID)) + { + time_t now = time(NULL); + time_t respawnTime; + if (linkedTime == std::numeric_limits<time_t>::max()) + respawnTime = linkedTime; + else if (sObjectMgr->GetLinkedRespawnGuid(thisGUID) == thisGUID) // never respawn, save "something" in DB + respawnTime = now + WEEK; + else // set us to check again shortly after linked unit + respawnTime = std::max<time_t>(now, linkedTime) + urand(5, 15); + info->respawnTime = respawnTime; + return false; + } + + // now, check if we're part of a pool + if (poolId) + { + // ok, part of a pool - hand off to pool logic to handle this, we're just going to remove the respawn and call it a day + if (info->type == SPAWN_TYPE_GAMEOBJECT) + sPoolMgr->UpdatePool<GameObject>(poolId, info->spawnId); + else if (info->type == SPAWN_TYPE_CREATURE) + sPoolMgr->UpdatePool<Creature>(poolId, info->spawnId); + else + ASSERT(false, "Invalid spawn type %u (spawnid %u) on map %u", uint32(info->type), info->spawnId, GetId()); + info->respawnTime = 0; + return false; + } + + // if we're a creature, see if the script objects to us spawning + if (info->type == SPAWN_TYPE_CREATURE) + { + if (!sScriptMgr->CanSpawn(info->spawnId, info->entry, sObjectMgr->GetCreatureData(info->spawnId), this)) + { // if a script blocks our respawn, schedule next check in a little bit + info->respawnTime = time(NULL) + urand(4, 7); + return false; + } + } + return true; +} + +void Map::DoRespawn(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 gridId) +{ + if (!IsGridLoaded(gridId)) // if grid isn't loaded, this will be processed in grid load handler + return; + + switch (type) + { + case SPAWN_TYPE_CREATURE: + { + Creature* obj = new Creature(); + if (!obj->LoadFromDB(spawnId, this, true, true)) + delete obj; + break; + } + case SPAWN_TYPE_GAMEOBJECT: + { + GameObject* obj = new GameObject(); + if (!obj->LoadFromDB(spawnId, this, true)) + delete obj; + break; + } + default: + ASSERT(false, "Invalid spawn type %u (spawnid %u) on map %u", uint32(type), spawnId, GetId()); + } +} + +void Map::Respawn(RespawnInfo* info, bool force, SQLTransaction dbTrans) +{ + if (!force && !CheckRespawn(info)) + { + if (info->respawnTime) + SaveRespawnTime(info->type, info->spawnId, info->entry, info->respawnTime, info->zoneId, info->gridId, true, true, dbTrans); + else + RemoveRespawnTime(info); + return; + } + + // remove the actual respawn record first - since this deletes it, we save what we need + SpawnObjectType const type = info->type; + uint32 const gridId = info->gridId; + ObjectGuid::LowType const spawnId = info->spawnId; + RemoveRespawnTime(info); + DoRespawn(type, spawnId, gridId); +} + +void Map::Respawn(RespawnVector& respawnData, bool force, SQLTransaction dbTrans) +{ + SQLTransaction trans = dbTrans ? dbTrans : CharacterDatabase.BeginTransaction(); + for (RespawnInfo* info : respawnData) + Respawn(info, force, trans); + if (!dbTrans) + CharacterDatabase.CommitTransaction(trans); +} + +void Map::AddRespawnInfo(RespawnInfo& info, bool replace) +{ + if (!info.spawnId) + return; + + RespawnInfoMap& bySpawnIdMap = GetRespawnMapForType(info.type); + + auto it = bySpawnIdMap.find(info.spawnId); + if (it != bySpawnIdMap.end()) // spawnid already has a respawn scheduled + { + RespawnInfo* const existing = it->second; + if (replace || info.respawnTime < existing->respawnTime) // delete existing in this case + DeleteRespawnInfo(existing); + else // don't delete existing, instead replace respawn time so caller saves the correct time + { + info.respawnTime = existing->respawnTime; + return; + } + } + + // if we get to this point, we should insert the respawninfo (there either was no prior entry, or it was deleted already) + RespawnInfo * ri = new RespawnInfo(info); + ri->handle = _respawnTimes.push(ri); + bool success = bySpawnIdMap.emplace(ri->spawnId, ri).second; + ASSERT(success, "Insertion of respawn info with id (%u,%u) into spawn id map failed - state desync.", uint32(ri->type), ri->spawnId); +} + +static void PushRespawnInfoFrom(RespawnVector& data, RespawnInfoMap const& map, uint32 zoneId) +{ + for (auto const& pair : map) + if (!zoneId || pair.second->zoneId == zoneId) + data.push_back(pair.second); +} +void Map::GetRespawnInfo(RespawnVector& respawnData, SpawnObjectTypeMask types, uint32 zoneId) const +{ + if (types & SPAWN_TYPEMASK_CREATURE) + PushRespawnInfoFrom(respawnData, _creatureRespawnTimesBySpawnId, zoneId); + if (types & SPAWN_TYPEMASK_GAMEOBJECT) + PushRespawnInfoFrom(respawnData, _gameObjectRespawnTimesBySpawnId, zoneId); +} + +RespawnInfo* Map::GetRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) const +{ + RespawnInfoMap const& map = GetRespawnMapForType(type); + auto it = map.find(spawnId); + if (it == map.end()) + return nullptr; + return it->second; +} + +void Map::DeleteRespawnInfo() // delete everything +{ + for (RespawnInfo* info : _respawnTimes) + delete info; + _respawnTimes.clear(); + _creatureRespawnTimesBySpawnId.clear(); + _gameObjectRespawnTimesBySpawnId.clear(); +} + +void Map::DeleteRespawnInfo(RespawnInfo* info) +{ + // Delete from all relevant containers to ensure consistency + ASSERT(info); + + // spawnid store + size_t const n = GetRespawnMapForType(info->type).erase(info->spawnId); + ASSERT(n == 1, "Respawn stores inconsistent for map %u, spawnid %u (type %u)", GetId(), info->spawnId, uint32(info->type)); + + //respawn heap + _respawnTimes.erase(info->handle); + + // then cleanup the object + delete info; +} + +void Map::RemoveRespawnTime(RespawnInfo* info, bool doRespawn, SQLTransaction dbTrans) +{ + PreparedStatement* stmt; + switch (info->type) + { + case SPAWN_TYPE_CREATURE: + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN); + break; + case SPAWN_TYPE_GAMEOBJECT: + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN); + break; + default: + ASSERT(false, "Invalid respawninfo type %u for spawnid %u map %u", uint32(info->type), info->spawnId, GetId()); + return; + } + stmt->setUInt32(0, info->spawnId); + stmt->setUInt16(1, GetId()); + stmt->setUInt32(2, GetInstanceId()); + CharacterDatabase.ExecuteOrAppend(dbTrans, stmt); + + if (doRespawn) + Respawn(info); + else + DeleteRespawnInfo(info); +} + +void Map::RemoveRespawnTime(RespawnVector& respawnData, bool doRespawn, SQLTransaction dbTrans) +{ + SQLTransaction trans = dbTrans ? dbTrans : CharacterDatabase.BeginTransaction(); + for (RespawnInfo* info : respawnData) + RemoveRespawnTime(info, doRespawn, trans); + if (!dbTrans) + CharacterDatabase.CommitTransaction(trans); +} + +void Map::ProcessRespawns() +{ + time_t now = time(NULL); + while (!_respawnTimes.empty()) + { + RespawnInfo* next = _respawnTimes.top(); + if (now < next->respawnTime) // done for this tick + break; + if (CheckRespawn(next)) // see if we're allowed to respawn + { + // ok, respawn + _respawnTimes.pop(); + GetRespawnMapForType(next->type).erase(next->spawnId); + DoRespawn(next->type, next->spawnId, next->gridId); + delete next; + } + else if (!next->respawnTime) // just remove respawn entry without rescheduling + { + _respawnTimes.pop(); + GetRespawnMapForType(next->type).erase(next->spawnId); + delete next; + } + else // value changed, update heap position + { + ASSERT(now < next->respawnTime); // infinite loop guard + _respawnTimes.decrease(next->handle); + } + } +} + +void Map::ApplyDynamicModeRespawnScaling(WorldObject const* obj, ObjectGuid::LowType spawnId, uint32& respawnDelay, uint32 mode) const +{ + ASSERT(mode == 1); + ASSERT(obj->GetMap() == this); + + if (IsBattlegroundOrArena()) + return; + + SpawnObjectType type; + switch (obj->GetTypeId()) + { + case TYPEID_UNIT: + type = SPAWN_TYPE_CREATURE; + break; + case TYPEID_GAMEOBJECT: + type = SPAWN_TYPE_GAMEOBJECT; + break; + default: + return; + } + + SpawnData const* data = sObjectMgr->GetSpawnData(type, spawnId); + if (!data || !data->spawnGroupData || !(data->spawnGroupData->flags & SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE)) + return; + + auto it = _zonePlayerCountMap.find(obj->GetZoneId()); + if (it == _zonePlayerCountMap.end()) + return; + uint32 const playerCount = it->second; + if (!playerCount) + return; + double const adjustFactor = sWorld->getFloatConfig(type == SPAWN_TYPE_GAMEOBJECT ? CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICRATE_CREATURE) / playerCount; + if (adjustFactor >= 1.0) // nothing to do here + return; + uint32 const timeMinimum = sWorld->getIntConfig(type == SPAWN_TYPE_GAMEOBJECT ? CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT : CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE); + if (respawnDelay <= timeMinimum) + return; + + respawnDelay = std::max<uint32>(ceil(respawnDelay * adjustFactor), timeMinimum); +} + +SpawnGroupTemplateData const* Map::GetSpawnGroupData(uint32 groupId) const +{ + SpawnGroupTemplateData const* data = sObjectMgr->GetSpawnGroupData(groupId); + if (data && data->mapId == GetId()) + return data; + return nullptr; +} + +bool Map::SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn, bool force, std::vector<WorldObject*>* spawnedObjects) +{ + SpawnGroupTemplateData const* groupData = GetSpawnGroupData(groupId); + if (!groupData || groupData->flags & SPAWNGROUP_FLAG_SYSTEM) + { + TC_LOG_ERROR("maps", "Tried to spawn non-existing (or system) spawn group %u on map %u. Blocked.", groupId, GetId()); + return false; + } + + for (auto& pair : sObjectMgr->GetSpawnDataForGroup(groupId)) + { + SpawnData const* data = pair.second; + ASSERT(groupData->mapId == data->spawnPoint.GetMapId()); + // Check if there's already an instance spawned + if (!force) + if (WorldObject* obj = GetWorldObjectBySpawnId(data->type, data->spawnId)) + if ((data->type != SPAWN_TYPE_CREATURE) || obj->ToCreature()->IsAlive()) + continue; + + time_t respawnTime = GetRespawnTime(data->type, data->spawnId); + if (respawnTime && respawnTime > time(NULL)) + { + if (!force && !ignoreRespawn) + continue; + + // we need to remove the respawn time, otherwise we'd end up double spawning + RemoveRespawnTime(data->type, data->spawnId, false); + } + + // don't spawn if the grid isn't loaded (will be handled in grid loader) + if (!IsGridLoaded(data->spawnPoint)) + continue; + + // Everything OK, now do the actual (re)spawn + switch (data->type) + { + case SPAWN_TYPE_CREATURE: + { + Creature* creature = new Creature(); + if (!creature->LoadFromDB(data->spawnId, this, true, force)) + delete creature; + else if (spawnedObjects) + spawnedObjects->push_back(creature); + break; + } + case SPAWN_TYPE_GAMEOBJECT: + { + GameObject* gameobject = new GameObject(); + if (!gameobject->LoadFromDB(data->spawnId, this, true)) + delete gameobject; + else if (spawnedObjects) + spawnedObjects->push_back(gameobject); + break; + } + default: + ASSERT(false, "Invalid spawn type %u with spawnId %u", uint32(data->type), data->spawnId); + return false; + } + } + SetSpawnGroupActive(groupId, true); // start processing respawns for the group + return true; +} + +bool Map::SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes) +{ + SpawnGroupTemplateData const* groupData = GetSpawnGroupData(groupId); + if (!groupData || groupData->flags & SPAWNGROUP_FLAG_SYSTEM) + { + TC_LOG_ERROR("maps", "Tried to despawn non-existing (or system) spawn group %u on map %u. Blocked.", groupId, GetId()); + return false; + } + + std::vector<WorldObject*> toUnload; // unload after iterating, otherwise iterator invalidation + for (auto const& pair : sObjectMgr->GetSpawnDataForGroup(groupId)) + { + SpawnData const* data = pair.second; + ASSERT(groupData->mapId == data->spawnPoint.GetMapId()); + if (deleteRespawnTimes) + RemoveRespawnTime(data->type, data->spawnId); + switch (data->type) + { + case SPAWN_TYPE_CREATURE: + { + auto bounds = GetCreatureBySpawnIdStore().equal_range(data->spawnId); + for (auto it = bounds.first; it != bounds.second; ++it) + toUnload.emplace_back(it->second); + break; + } + case SPAWN_TYPE_GAMEOBJECT: + { + auto bounds = GetGameObjectBySpawnIdStore().equal_range(data->spawnId); + for (auto it = bounds.first; it != bounds.second; ++it) + toUnload.emplace_back(it->second); + break; + } + default: + ASSERT(false, "Invalid spawn type %u in spawn data with spawnId %u.", uint32(data->type), data->spawnId); + return false; + } + } + // now do the actual despawning + for (WorldObject* obj : toUnload) + obj->AddObjectToRemoveList(); + SetSpawnGroupActive(groupId, false); // stop processing respawns for the group, too + return true; +} + +void Map::SetSpawnGroupActive(uint32 groupId, bool state) +{ + SpawnGroupTemplateData const* const data = GetSpawnGroupData(groupId); + if (!data || data->flags & SPAWNGROUP_FLAG_SYSTEM) + { + TC_LOG_ERROR("maps", "Tried to set non-existing (or system) spawn group %u to %s on map %u. Blocked.", groupId, state ? "active" : "inactive", GetId()); + return; + } + if (state != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN)) // toggled + _toggledSpawnGroupIds.insert(groupId); + else + _toggledSpawnGroupIds.erase(groupId); +} + +bool Map::IsSpawnGroupActive(uint32 groupId) const +{ + SpawnGroupTemplateData const* const data = GetSpawnGroupData(groupId); + if (!data) + { + TC_LOG_WARN("maps", "Tried to query state of non-existing spawn group %u on map %u.", groupId, GetId()); + return false; + } + if (data->flags & SPAWNGROUP_FLAG_SYSTEM) + return true; + return (_toggledSpawnGroupIds.find(groupId) != _toggledSpawnGroupIds.end()) != !(data->flags & SPAWNGROUP_FLAG_MANUAL_SPAWN); +} + void Map::DelayedUpdate(uint32 t_diff) { for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) @@ -3364,6 +3882,8 @@ void InstanceMap::CreateInstanceData(bool load) } } } + else + i_data->Create(); } /* @@ -3693,6 +4213,34 @@ Creature* Map::GetCreature(ObjectGuid const& guid) return _objectsStore.Find<Creature>(guid); } +Creature* Map::GetCreatureBySpawnId(ObjectGuid::LowType spawnId) const +{ + auto const bounds = GetCreatureBySpawnIdStore().equal_range(spawnId); + if (bounds.first == bounds.second) + return nullptr; + + std::unordered_multimap<uint32, Creature*>::const_iterator creatureItr = std::find_if(bounds.first, bounds.second, [](Map::CreatureBySpawnIdContainer::value_type const& pair) + { + return pair.second->IsAlive(); + }); + + return creatureItr != bounds.second ? creatureItr->second : bounds.first->second; +} + +GameObject* Map::GetGameObjectBySpawnId(ObjectGuid::LowType spawnId) const +{ + auto const bounds = GetGameObjectBySpawnIdStore().equal_range(spawnId); + if (bounds.first == bounds.second) + return nullptr; + + std::unordered_multimap<uint32, GameObject*>::const_iterator creatureItr = std::find_if(bounds.first, bounds.second, [](Map::GameObjectBySpawnIdContainer::value_type const& pair) + { + return pair.second->isSpawned(); + }); + + return creatureItr != bounds.second ? creatureItr->second : bounds.first->second; +} + GameObject* Map::GetGameObject(ObjectGuid const& guid) { return _objectsStore.Find<GameObject>(guid); @@ -3723,64 +4271,37 @@ void Map::UpdateIteratorBack(Player* player) m_mapRefIter = m_mapRefIter->nocheck_prev(); } -void Map::SaveCreatureRespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime) +void Map::SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 zoneId, uint32 gridId, bool writeDB, bool replace, SQLTransaction dbTrans) { if (!respawnTime) { // Delete only - RemoveCreatureRespawnTime(dbGuid); + RemoveRespawnTime(type, spawnId, false, dbTrans); return; } - _creatureRespawnTimes[dbGuid] = respawnTime; + RespawnInfo ri; + ri.type = type; + ri.spawnId = spawnId; + ri.entry = entry; + ri.respawnTime = respawnTime; + ri.gridId = gridId; + ri.zoneId = zoneId; + AddRespawnInfo(ri, replace); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN); - stmt->setUInt32(0, dbGuid); - stmt->setUInt64(1, uint64(respawnTime)); - stmt->setUInt16(2, GetId()); - stmt->setUInt32(3, GetInstanceId()); - CharacterDatabase.Execute(stmt); + if (writeDB) + SaveRespawnTimeDB(type, spawnId, ri.respawnTime, dbTrans); // might be different from original respawn time if we didn't replace } -void Map::RemoveCreatureRespawnTime(ObjectGuid::LowType dbGuid) +void Map::SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, SQLTransaction dbTrans) { - _creatureRespawnTimes.erase(dbGuid); - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN); - stmt->setUInt32(0, dbGuid); - stmt->setUInt16(1, GetId()); - stmt->setUInt32(2, GetInstanceId()); - CharacterDatabase.Execute(stmt); -} - -void Map::SaveGORespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime) -{ - if (!respawnTime) - { - // Delete only - RemoveGORespawnTime(dbGuid); - return; - } - - _goRespawnTimes[dbGuid] = respawnTime; - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN); - stmt->setUInt32(0, dbGuid); + // Just here for support of compatibility mode + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement((type == SPAWN_TYPE_GAMEOBJECT) ? CHAR_REP_GO_RESPAWN : CHAR_REP_CREATURE_RESPAWN); + stmt->setUInt32(0, spawnId); stmt->setUInt64(1, uint64(respawnTime)); stmt->setUInt16(2, GetId()); stmt->setUInt32(3, GetInstanceId()); - CharacterDatabase.Execute(stmt); -} - -void Map::RemoveGORespawnTime(ObjectGuid::LowType dbGuid) -{ - _goRespawnTimes.erase(dbGuid); - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN); - stmt->setUInt32(0, dbGuid); - stmt->setUInt16(1, GetId()); - stmt->setUInt32(2, GetInstanceId()); - CharacterDatabase.Execute(stmt); + CharacterDatabase.ExecuteOrAppend(dbTrans, stmt); } void Map::LoadRespawnTimes() @@ -3796,7 +4317,9 @@ void Map::LoadRespawnTimes() ObjectGuid::LowType loguid = fields[0].GetUInt32(); uint64 respawnTime = fields[1].GetUInt64(); - _creatureRespawnTimes[loguid] = time_t(respawnTime); + if (CreatureData const* cdata = sObjectMgr->GetCreatureData(loguid)) + SaveRespawnTime(SPAWN_TYPE_CREATURE, loguid, cdata->id, time_t(respawnTime), GetZoneId(cdata->spawnPoint), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false); + } while (result->NextRow()); } @@ -3811,19 +4334,13 @@ void Map::LoadRespawnTimes() ObjectGuid::LowType loguid = fields[0].GetUInt32(); uint64 respawnTime = fields[1].GetUInt64(); - _goRespawnTimes[loguid] = time_t(respawnTime); + if (GameObjectData const* godata = sObjectMgr->GetGameObjectData(loguid)) + SaveRespawnTime(SPAWN_TYPE_GAMEOBJECT, loguid, godata->id, time_t(respawnTime), GetZoneId(godata->spawnPoint), Trinity::ComputeGridCoord(godata->spawnPoint.GetPositionX(), godata->spawnPoint.GetPositionY()).GetId(), false); + } while (result->NextRow()); } } -void Map::DeleteRespawnTimes() -{ - _creatureRespawnTimes.clear(); - _goRespawnTimes.clear(); - - DeleteRespawnTimesInDB(GetId(), GetInstanceId()); -} - void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index dd0e43158c1..3d3fcfb528d 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -21,16 +21,18 @@ #include "Define.h" -#include "GridDefines.h" #include "Cell.h" -#include "Timer.h" -#include "SharedDefines.h" +#include "DynamicTree.h" +#include "GridDefines.h" #include "GridRefManager.h" #include "MapRefManager.h" -#include "DynamicTree.h" #include "ObjectGuid.h" #include "Optional.h" - +#include "SharedDefines.h" +#include "SpawnData.h" +#include "Timer.h" +#include "Transaction.h" +#include <boost/heap/fibonacci_heap.hpp> #include <bitset> #include <list> #include <memory> @@ -268,10 +270,41 @@ struct ZoneDynamicInfo #define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT #define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations #define MIN_UNLOAD_DELAY 1 // immediate unload +#define MAP_INVALID_ZONE 0xFFFFFFFF typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType; +struct RespawnInfo; // forward declaration +struct CompareRespawnInfo +{ + bool operator()(RespawnInfo const* a, RespawnInfo const* b) const; +}; typedef std::unordered_map<uint32 /*zoneId*/, ZoneDynamicInfo> ZoneDynamicInfoMap; +typedef boost::heap::fibonacci_heap<RespawnInfo*, boost::heap::compare<CompareRespawnInfo>> RespawnListContainer; +typedef RespawnListContainer::handle_type RespawnListHandle; +typedef std::unordered_map<uint32, RespawnInfo*> RespawnInfoMap; +typedef std::vector<RespawnInfo*> RespawnVector; +struct RespawnInfo +{ + SpawnObjectType type; + ObjectGuid::LowType spawnId; + uint32 entry; + time_t respawnTime; + uint32 gridId; + uint32 zoneId; + RespawnListHandle handle; +}; +inline bool CompareRespawnInfo::operator()(RespawnInfo const* a, RespawnInfo const* b) const +{ + if (a == b) + return false; + if (a->respawnTime != b->respawnTime) + return (a->respawnTime > b->respawnTime); + if (a->spawnId != b->spawnId) + return a->spawnId < b->spawnId; + ASSERT(a->type != b->type, "Duplicate respawn entry for spawnId (%u,%u) found!", a->type, a->spawnId); + return a->type < b->type; +} class TC_GAME_API Map : public GridRefManager<NGridType> { @@ -321,17 +354,19 @@ class TC_GAME_API Map : public GridRefManager<NGridType> GridCoord p = Trinity::ComputeGridCoord(x, y); return !getNGrid(p.x_coord, p.y_coord) || getNGrid(p.x_coord, p.y_coord)->GetGridState() == GRID_STATE_REMOVAL; } + bool IsRemovalGrid(Position const& pos) const { return IsRemovalGrid(pos.GetPositionX(), pos.GetPositionY()); } - bool IsGridLoaded(float x, float y) const - { - return IsGridLoaded(Trinity::ComputeGridCoord(x, y)); - } + bool IsGridLoaded(uint32 gridId) const { return IsGridLoaded(GridCoord(gridId % MAX_NUMBER_OF_GRIDS, gridId / MAX_NUMBER_OF_GRIDS)); } + bool IsGridLoaded(float x, float y) const { return IsGridLoaded(Trinity::ComputeGridCoord(x, y)); } + bool IsGridLoaded(Position const& pos) const { return IsGridLoaded(pos.GetPositionX(), pos.GetPositionY()); } bool GetUnloadLock(GridCoord const& p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadLock(); } void SetUnloadLock(GridCoord const& p, bool on) { getNGrid(p.x_coord, p.y_coord)->setUnloadExplicitLock(on); } void LoadGrid(float x, float y); void LoadAllCells(); bool UnloadGrid(NGridType& ngrid, bool pForce); + void GridMarkNoUnload(uint32 x, uint32 y); + void GridUnmarkNoUnload(uint32 x, uint32 y); virtual void UnloadAll(); void ResetGridExpiry(NGridType &grid, float factor = 1) const @@ -350,18 +385,16 @@ class TC_GAME_API Map : public GridRefManager<NGridType> Map const* GetParent() const { return m_parentMap; } - // some calls like isInWater should not use vmaps due to processor power - // can return INVALID_HEIGHT if under z+2 z coord not found height - float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; - float GetMinHeight(float x, float y) const; - void GetFullTerrainStatusForPosition(float x, float y, float z, PositionFullTerrainStatus& data, uint8 reqLiquidType = MAP_ALL_LIQUIDS) const; ZLiquidStatus GetLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr) const; bool GetAreaInfo(float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const; uint32 GetAreaId(float x, float y, float z, bool *isOutdoors = nullptr) const; + uint32 GetAreaId(Position const& pos) const { return GetAreaId(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } uint32 GetZoneId(float x, float y, float z) const; + uint32 GetZoneId(Position const& pos) const { return GetZoneId(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const; + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, Position const& pos) const { GetZoneAndAreaId(zoneid, areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } bool IsOutdoors(float x, float y, float z) const; @@ -464,6 +497,9 @@ class TC_GAME_API Map : public GridRefManager<NGridType> Corpse* GetCorpse(ObjectGuid const& guid); Creature* GetCreature(ObjectGuid const& guid); GameObject* GetGameObject(ObjectGuid const& guid); + Creature* GetCreatureBySpawnId(ObjectGuid::LowType spawnId) const; + GameObject* GetGameObjectBySpawnId(ObjectGuid::LowType spawnId) const; + WorldObject* GetWorldObjectBySpawnId(SpawnObjectType type, ObjectGuid::LowType spawnId) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? reinterpret_cast<WorldObject*>(GetGameObjectBySpawnId(spawnId)) : reinterpret_cast<WorldObject*>(GetCreatureBySpawnId(spawnId)); } Transport* GetTransport(ObjectGuid const& guid); DynamicObject* GetDynamicObject(ObjectGuid const& guid); Pet* GetPet(ObjectGuid const& guid); @@ -472,9 +508,11 @@ class TC_GAME_API Map : public GridRefManager<NGridType> typedef std::unordered_multimap<ObjectGuid::LowType, Creature*> CreatureBySpawnIdContainer; CreatureBySpawnIdContainer& GetCreatureBySpawnIdStore() { return _creatureBySpawnIdStore; } + CreatureBySpawnIdContainer const& GetCreatureBySpawnIdStore() const { return _creatureBySpawnIdStore; } typedef std::unordered_multimap<ObjectGuid::LowType, GameObject*> GameObjectBySpawnIdContainer; GameObjectBySpawnIdContainer& GetGameObjectBySpawnIdStore() { return _gameobjectBySpawnIdStore; } + GameObjectBySpawnIdContainer const& GetGameObjectBySpawnIdStore() const { return _gameobjectBySpawnIdStore; } std::unordered_set<Corpse*> const* GetCorpsesInCell(uint32 cellId) const { @@ -504,7 +542,11 @@ class TC_GAME_API Map : public GridRefManager<NGridType> BattlegroundMap const* ToBattlegroundMap() const { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap const*>(this); return nullptr; } float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false) const; - float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; + float GetMinHeight(float x, float y) const; + float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; + float GetHeight(Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); } + float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return std::max<float>(GetHeight(x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phasemask, x, y, z, maxSearchDist)); } + float GetHeight(uint32 phasemask, Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(phasemask, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); } bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const; void Balance() { _dynamicTree.balance(); } void RemoveGameObjectModel(GameObjectModel const& model) { _dynamicTree.remove(model); } @@ -522,28 +564,24 @@ class TC_GAME_API Map : public GridRefManager<NGridType> time_t GetLinkedRespawnTime(ObjectGuid guid) const; time_t GetCreatureRespawnTime(ObjectGuid::LowType dbGuid) const { - std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t>::const_iterator itr = _creatureRespawnTimes.find(dbGuid); - if (itr != _creatureRespawnTimes.end()) - return itr->second; - - return time_t(0); + RespawnInfoMap::const_iterator itr = _creatureRespawnTimesBySpawnId.find(dbGuid); + return itr != _creatureRespawnTimesBySpawnId.end() ? itr->second->respawnTime : 0; } time_t GetGORespawnTime(ObjectGuid::LowType dbGuid) const { - std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t>::const_iterator itr = _goRespawnTimes.find(dbGuid); - if (itr != _goRespawnTimes.end()) - return itr->second; - - return time_t(0); + RespawnInfoMap::const_iterator itr = _gameObjectRespawnTimesBySpawnId.find(dbGuid); + return itr != _gameObjectRespawnTimesBySpawnId.end() ? itr->second->respawnTime : 0; } - void SaveCreatureRespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime); - void RemoveCreatureRespawnTime(ObjectGuid::LowType dbGuid); - void SaveGORespawnTime(ObjectGuid::LowType dbGuid, time_t respawnTime); - void RemoveGORespawnTime(ObjectGuid::LowType dbGuid); + time_t GetRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? GetGORespawnTime(spawnId) : GetCreatureRespawnTime(spawnId); } + + void UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone); + + void SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 zoneId, uint32 gridId = 0, bool writeDB = true, bool replace = false, SQLTransaction dbTrans = nullptr); + void SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, SQLTransaction dbTrans = nullptr); void LoadRespawnTimes(); - void DeleteRespawnTimes(); + void DeleteRespawnTimes() { DeleteRespawnInfo(); DeleteRespawnTimesInDB(GetId(), GetInstanceId()); } void LoadCorpseData(); void DeleteCorpseData(); @@ -702,6 +740,65 @@ class TC_GAME_API Map : public GridRefManager<NGridType> typedef std::multimap<time_t, ScriptAction> ScriptScheduleMap; ScriptScheduleMap m_scriptSchedule; + public: + void ProcessRespawns(); + void ApplyDynamicModeRespawnScaling(WorldObject const* obj, ObjectGuid::LowType spawnId, uint32& respawnDelay, uint32 mode) const; + + private: + // if return value is true, we can respawn + // if return value is false, reschedule the respawn to new value of info->respawnTime iff nonzero, delete otherwise + // if return value is false and info->respawnTime is nonzero, it is guaranteed to be greater than time(NULL) + bool CheckRespawn(RespawnInfo* info); + void DoRespawn(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 gridId); + void Respawn(RespawnInfo* info, bool force = false, SQLTransaction dbTrans = nullptr); + void Respawn(RespawnVector& respawnData, bool force = false, SQLTransaction dbTrans = nullptr); + void AddRespawnInfo(RespawnInfo& info, bool replace = false); + void DeleteRespawnInfo(); + void DeleteRespawnInfo(RespawnInfo* info); + void DeleteRespawnInfo(RespawnVector& toDelete) + { + for (RespawnInfo* info : toDelete) + DeleteRespawnInfo(info); + toDelete.clear(); + } + void DeleteRespawnInfo(SpawnObjectTypeMask types, uint32 zoneId = 0) + { + RespawnVector v; + GetRespawnInfo(v, types, zoneId); + if (!v.empty()) + DeleteRespawnInfo(v); + } + void DeleteRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) + { + if (RespawnInfo* info = GetRespawnInfo(type, spawnId)) + DeleteRespawnInfo(info); + } + + public: + void GetRespawnInfo(RespawnVector& respawnData, SpawnObjectTypeMask types, uint32 zoneId = 0) const; + RespawnInfo* GetRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) const; + void RemoveRespawnTime(RespawnInfo* info, bool doRespawn = false, SQLTransaction dbTrans = nullptr); + void RemoveRespawnTime(RespawnVector& respawnData, bool doRespawn = false, SQLTransaction dbTrans = nullptr); + void RemoveRespawnTime(SpawnObjectTypeMask types = SPAWN_TYPEMASK_ALL, uint32 zoneId = 0, bool doRespawn = false, SQLTransaction dbTrans = nullptr) + { + RespawnVector v; + GetRespawnInfo(v, types, zoneId); + if (!v.empty()) + RemoveRespawnTime(v, doRespawn, dbTrans); + } + void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, bool doRespawn = false, SQLTransaction dbTrans = nullptr) + { + if (RespawnInfo* info = GetRespawnInfo(type, spawnId)) + RemoveRespawnTime(info, doRespawn, dbTrans); + } + + SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const; + bool SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn = false, bool force = false, std::vector<WorldObject*>* spawnedObjects = nullptr); + bool SpawnGroupDespawn(uint32 groupId, bool deleteRespawnTimes = false); + void SetSpawnGroupActive(uint32 groupId, bool state); + bool IsSpawnGroupActive(uint32 groupId) const; + + private: // Type specific code for add/remove to/from grid template<class T> void AddToGrid(T* object, Cell const& cell); @@ -730,8 +827,15 @@ class TC_GAME_API Map : public GridRefManager<NGridType> m_activeNonPlayers.erase(obj); } - std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _creatureRespawnTimes; - std::unordered_map<ObjectGuid::LowType /*dbGUID*/, time_t> _goRespawnTimes; + RespawnListContainer _respawnTimes; + RespawnInfoMap _creatureRespawnTimesBySpawnId; + RespawnInfoMap _gameObjectRespawnTimesBySpawnId; + RespawnInfoMap& GetRespawnMapForType(SpawnObjectType type) { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; } + RespawnInfoMap const& GetRespawnMapForType(SpawnObjectType type) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; } + std::unordered_set<uint32> _toggledSpawnGroupIds; + + uint32 _respawnCheckTimer; + std::unordered_map<uint32, uint32> _zonePlayerCountMap; ZoneDynamicInfoMap _zoneDynamicInfo; uint32 _defaultLight; diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index e628a7030ae..96147c7ded7 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -44,16 +44,22 @@ class TC_GAME_API MapManager Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); return m->GetAreaId(x, y, z); } + uint32 GetAreaId(uint32 mapid, Position const& pos) const { return GetAreaId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } + uint32 GetAreaId(WorldLocation const& loc) const { return GetAreaId(loc.GetMapId(), loc); } uint32 GetZoneId(uint32 mapid, float x, float y, float z) const { Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); return m->GetZoneId(x, y, z); } - void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) + uint32 GetZoneId(uint32 mapid, Position const& pos) const { return GetZoneId(mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } + uint32 GetZoneId(WorldLocation const& loc) const { return GetZoneId(loc.GetMapId(), loc); } + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) const { Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); m->GetZoneAndAreaId(zoneid, areaid, x, y, z); } + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, Position const& pos) const { GetZoneAndAreaId(zoneid, areaid, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, WorldLocation const& loc) const { GetZoneAndAreaId(zoneid, areaid, loc.GetMapId(), loc); } void Initialize(void); void Update(uint32); diff --git a/src/server/game/Maps/MapScripts.cpp b/src/server/game/Maps/MapScripts.cpp index e365a54c594..20ebc1db460 100644 --- a/src/server/game/Maps/MapScripts.cpp +++ b/src/server/game/Maps/MapScripts.cpp @@ -671,7 +671,7 @@ void Map::ScriptsProcess() Unit* uSource = nullptr; Unit* uTarget = nullptr; - // source/target cast spell at target/source (script->datalong2: 0: s->t 1: s->s 2: t->t 3: t->s + // source/target cast spell at target/source (script->datalong2: 0: s->t 1: s->s 2: t->t 3: t->s) switch (step.script->CastSpell.Flags) { case SF_CASTSPELL_SOURCE_TO_TARGET: // source -> target diff --git a/src/server/game/Maps/SpawnData.h b/src/server/game/Maps/SpawnData.h new file mode 100644 index 00000000000..768d1fd26bd --- /dev/null +++ b/src/server/game/Maps/SpawnData.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_SPAWNDATA_H +#define TRINITY_SPAWNDATA_H + +#include "Position.h" + +enum SpawnObjectType +{ + SPAWN_TYPE_CREATURE = 0, + SPAWN_TYPE_GAMEOBJECT = 1, + + SPAWN_TYPE_MAX +}; + +enum SpawnObjectTypeMask +{ + SPAWN_TYPEMASK_CREATURE = (1 << SPAWN_TYPE_CREATURE), + SPAWN_TYPEMASK_GAMEOBJECT = (1 << SPAWN_TYPE_GAMEOBJECT), + + SPAWN_TYPEMASK_ALL = (1 << SPAWN_TYPE_MAX)-1 +}; + +enum SpawnGroupFlags +{ + SPAWNGROUP_FLAG_NONE = 0x00, + SPAWNGROUP_FLAG_SYSTEM = 0x01, + SPAWNGROUP_FLAG_COMPATIBILITY_MODE = 0x02, + SPAWNGROUP_FLAG_MANUAL_SPAWN = 0x04, + SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE = 0x08, + SPAWNGROUP_FLAG_ESCORTQUESTNPC = 0x10, + + SPAWNGROUP_FLAGS_ALL = (SPAWNGROUP_FLAG_SYSTEM | SPAWNGROUP_FLAG_COMPATIBILITY_MODE | SPAWNGROUP_FLAG_MANUAL_SPAWN | SPAWNGROUP_FLAG_DYNAMIC_SPAWN_RATE | SPAWNGROUP_FLAG_ESCORTQUESTNPC) +}; + +struct SpawnGroupTemplateData +{ + uint32 groupId; + std::string name; + uint32 mapId; + SpawnGroupFlags flags; +}; + +struct SpawnData +{ + SpawnObjectType const type; + uint32 spawnId = 0; + uint32 id = 0; // entry in respective _template table + WorldLocation spawnPoint; + uint32 phaseMask = 0; + int32 spawntimesecs = 0; + uint8 spawnMask = 0; + SpawnGroupTemplateData const* spawnGroupData = nullptr; + uint32 scriptId = 0; + bool dbData = true; + + protected: + SpawnData(SpawnObjectType t) : type(t) {} +}; + +#endif diff --git a/src/server/game/Miscellaneous/Formulas.h b/src/server/game/Miscellaneous/Formulas.h index 81e906ed47d..7931860868e 100644 --- a/src/server/game/Miscellaneous/Formulas.h +++ b/src/server/game/Miscellaneous/Formulas.h @@ -158,6 +158,13 @@ namespace Trinity baseGain = 0; } + if (sWorld->getIntConfig(CONFIG_MIN_CREATURE_SCALED_XP_RATIO)) + { + // Use mob level instead of player level to avoid overscaling on gain in a min is enforced + uint32 baseGainMin = (mob_level * 5 + nBaseExp) * sWorld->getIntConfig(CONFIG_MIN_CREATURE_SCALED_XP_RATIO) / 100; + baseGain = std::max(baseGainMin, baseGain); + } + sScriptMgr->OnBaseGainCalculation(baseGain, pl_level, mob_level, content); return baseGain; } diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index d96b33fd968..400685c1c1b 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1147,7 +1147,32 @@ enum TrinityStrings LANG_COMMAND_MUTEHISTORY_EMPTY = 5060, LANG_COMMAND_MUTEHISTORY_OUTPUT = 5061, - // Room for more Trinity strings 5062-9999 + // Scene debugs commands [Master only, not used in 3.3.5] + /*LANG_COMMAND_SCENE_DEBUG_ON = 5062, + LANG_COMMAND_SCENE_DEBUG_OFF = 5063, + LANG_COMMAND_SCENE_DEBUG_PLAY = 5064, + LANG_COMMAND_SCENE_DEBUG_TRIGGER = 5065, + LANG_COMMAND_SCENE_DEBUG_CANCEL = 5066, + LANG_COMMAND_SCENE_DEBUG_COMPLETE = 5067, + LANG_DEBUG_SCENE_OBJECT_LIST = 5068, + LANG_DEBUG_SCENE_OBJECT_DETAIL = 5069, */ + + // Strings added for dynamic_spawning + LANG_SPAWNINFO_GROUP_ID = 5070, + LANG_SPAWNINFO_COMPATIBILITY_MODE = 5071, + LANG_SPAWNINFO_GUIDINFO = 5072, + LANG_SPAWNINFO_SPAWNID_LOCATION = 5073, + LANG_SPAWNINFO_DISTANCEFROMPLAYER = 5074, + LANG_SPAWNGROUP_BADGROUP = 5075, + LANG_SPAWNGROUP_SPAWNCOUNT = 5076, + LANG_LIST_RESPAWNS_RANGE = 5077, + LANG_LIST_RESPAWNS_ZONE = 5078, + LANG_LIST_RESPAWNS_LISTHEADER = 5079, + LANG_LIST_RESPAWNS_OVERDUE = 5080, + LANG_LIST_RESPAWNS_CREATURES = 5081, + LANG_LIST_RESPAWNS_GAMEOBJECTS = 5082, + + // Room for more Trinity strings 5084-6603 // Level requirement notifications LANG_SAY_REQ = 6604, diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 7ea78f14ca4..d6bea21f2ad 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -1443,7 +1443,7 @@ enum Targets TARGET_DEST_CHANNEL_CASTER = 106, TARGET_UNK_DEST_AREA_UNK_107 = 107, // not enough info - only generic spells avalible TARGET_GAMEOBJECT_CONE = 108, - TARGET_DEST_UNK_110 = 110, // 1 spell + TARGET_UNIT_CONE_ENTRY_110 = 110, // 1 spell TOTAL_SPELL_TARGETS }; diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 6e771a29979..3f77e472189 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -17,25 +17,28 @@ */ #include "MotionMaster.h" +#include "ConfusedMovementGenerator.h" #include "Creature.h" #include "CreatureAISelector.h" #include "DBCStores.h" -#include "Log.h" -#include "Map.h" -#include "PathGenerator.h" -#include "ScriptSystem.h" -#include "ConfusedMovementGenerator.h" #include "FleeingMovementGenerator.h" +#include "FormationMovementGenerator.h" #include "HomeMovementGenerator.h" #include "IdleMovementGenerator.h" +#include "Log.h" +#include "Map.h" +#include "MoveSpline.h" +#include "MoveSplineInit.h" +#include "PathGenerator.h" +#include "Player.h" #include "PointMovementGenerator.h" -#include "TargetedMovementGenerator.h" -#include "WaypointMovementGenerator.h" #include "RandomMovementGenerator.h" +#include "ScriptSystem.h" #include "SplineChainMovementGenerator.h" -#include "FormationMovementGenerator.h" -#include "MoveSpline.h" -#include "MoveSplineInit.h" +#include "TargetedMovementGenerator.h" +#include "Unit.h" +#include "WaypointDefines.h" +#include "WaypointMovementGenerator.h" inline MovementGenerator* GetIdleMovementGenerator() { @@ -70,7 +73,7 @@ void MotionMaster::Initialize() // clear ALL movement generators (including default) while (!empty()) { - MovementGenerator *curr = top(); + MovementGenerator* curr = top(); pop(); if (curr) DirectDelete(curr); @@ -119,21 +122,15 @@ void MotionMaster::Clear(bool reset /*= true*/) DirectClean(reset); } -void MotionMaster::ClearExpireList() +void MotionMaster::Clear(MovementSlot slot) { - for (auto itr : _expireList) - DirectDelete(itr); - - _expireList.clear(); - - if (empty()) - Initialize(); - else if (NeedInitTop()) - InitTop(); - else if (_cleanFlag & MMCF_RESET) - top()->Reset(_owner); + if (empty() || slot >= MAX_MOTION_SLOT) + return; - _cleanFlag &= ~MMCF_RESET; + if (_cleanFlag & MMCF_UPDATE) + DelayedClean(slot); + else + DirectClean(slot); } void MotionMaster::MovementExpired(bool reset /*= true*/) @@ -158,27 +155,32 @@ MovementGeneratorType MotionMaster::GetCurrentMovementGeneratorType() const return top()->GetMovementGeneratorType(); } -MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const +MovementGeneratorType MotionMaster::GetMotionSlotType(MovementSlot slot) const { - if (!_slot[slot]) + if (empty() || slot >= MAX_MOTION_SLOT || !_slot[slot]) return MAX_MOTION_TYPE; - else - return _slot[slot]->GetMovementGeneratorType(); + + return _slot[slot]->GetMovementGeneratorType(); } -MovementGenerator* MotionMaster::GetMotionSlot(int slot) const +MovementGenerator* MotionMaster::GetMotionSlot(MovementSlot slot) const { - ASSERT(slot >= 0); + if (empty() || slot >= MAX_MOTION_SLOT || !_slot[slot]) + return nullptr; + return _slot[slot]; } void MotionMaster::PropagateSpeedChange() { - for (int i = 0; i <= _top; ++i) - { - if (_slot[i]) - _slot[i]->UnitSpeedChanged(); - } + if (empty()) + return; + + MovementGenerator* movement = top(); + if (!movement) + return; + + movement->UnitSpeedChanged(); } bool MotionMaster::GetDestination(float &x, float &y, float &z) @@ -675,16 +677,21 @@ void MotionMaster::MoveDistract(uint32 timer) Mutate(mgen, MOTION_SLOT_CONTROLLED); } -void MotionMaster::MovePath(uint32 path_id, bool repeatable) +void MotionMaster::MovePath(uint32 pathId, bool repeatable) { - if (!path_id) + if (!pathId) return; - Mutate(new WaypointMovementGenerator<Creature>(path_id, repeatable), MOTION_SLOT_IDLE); + Mutate(new WaypointMovementGenerator<Creature>(pathId, repeatable), MOTION_SLOT_IDLE); + + TC_LOG_DEBUG("misc", "%s (GUID: %u) starts moving over path(Id:%u, repeatable: %s).", _owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature", _owner->GetGUID().GetCounter(), pathId, repeatable ? "YES" : "NO"); +} + +void MotionMaster::MovePath(WaypointPath& path, bool repeatable) +{ + Mutate(new WaypointMovementGenerator<Creature>(path, repeatable), MOTION_SLOT_IDLE); - TC_LOG_DEBUG("misc", "%s (GUID: %u) starts moving over path(Id:%u, repeatable: %s).", - _owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature", - _owner->GetGUID().GetCounter(), path_id, repeatable ? "YES" : "NO"); + TC_LOG_DEBUG("misc", "%s (GUID: %u) start moving over path(repeatable: %s)", _owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature", _owner->GetGUID().GetCounter(), repeatable ? "YES" : "NO"); } void MotionMaster::MoveRotate(uint32 time, RotateDirection direction) @@ -731,7 +738,7 @@ void MotionMaster::InitTop() void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot) { - if (MovementGenerator *curr = _slot[slot]) + if (MovementGenerator* curr = _slot[slot]) { _slot[slot] = nullptr; // in case a new one is generated in this slot during directdelete if (_top == slot && (_cleanFlag & MMCF_UPDATE)) @@ -758,9 +765,10 @@ void MotionMaster::DirectClean(bool reset) { while (size() > 1) { - MovementGenerator *curr = top(); + MovementGenerator* curr = top(); pop(); - if (curr) DirectDelete(curr); + if (curr) + DirectDelete(curr); } if (empty()) @@ -776,18 +784,47 @@ void MotionMaster::DelayedClean() { while (size() > 1) { - MovementGenerator *curr = top(); + MovementGenerator* curr = top(); pop(); if (curr) DelayedDelete(curr); } } +void MotionMaster::DirectClean(MovementSlot slot) +{ + if (MovementGenerator* motion = GetMotionSlot(slot)) + { + _slot[slot] = nullptr; + DirectDelete(motion); + } + + while (!empty() && !top()) + --_top; + + if (empty()) + Initialize(); + else if (NeedInitTop()) + InitTop(); +} + +void MotionMaster::DelayedClean(MovementSlot slot) +{ + if (MovementGenerator* motion = GetMotionSlot(slot)) + { + _slot[slot] = nullptr; + DelayedDelete(motion); + } + + while (!empty() && !top()) + --_top; +} + void MotionMaster::DirectExpire(bool reset) { if (size() > 1) { - MovementGenerator *curr = top(); + MovementGenerator* curr = top(); pop(); DirectDelete(curr); } @@ -807,7 +844,7 @@ void MotionMaster::DelayedExpire() { if (size() > 1) { - MovementGenerator *curr = top(); + MovementGenerator* curr = top(); pop(); DelayedDelete(curr); } @@ -826,9 +863,26 @@ void MotionMaster::DirectDelete(MovementGenerator* curr) void MotionMaster::DelayedDelete(MovementGenerator* curr) { - TC_LOG_FATAL("misc", "Unit (Entry %u) is trying to delete its updating Movement Generator (Type %u)!", _owner->GetEntry(), curr->GetMovementGeneratorType()); + TC_LOG_DEBUG("misc", "MotionMaster::DelayedDelete: unit (%u) delayed deleting movement generator (type %u)", _owner->GetEntry(), curr->GetMovementGeneratorType()); if (IsStatic(curr)) return; _expireList.push_back(curr); } + +void MotionMaster::ClearExpireList() +{ + for (auto itr : _expireList) + DirectDelete(itr); + + _expireList.clear(); + + if (empty()) + Initialize(); + else if (NeedInitTop()) + InitTop(); + else if (_cleanFlag & MMCF_RESET) + top()->Reset(_owner); + + _cleanFlag &= ~MMCF_RESET; +} diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 4e20f9ab814..d132d8b30d0 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -31,6 +31,7 @@ class Unit; class PathGenerator; struct SplineChainLink; struct SplineChainResumeInfo; +struct WaypointPath; // Creature Entry ID used for waypoints show, visible only for GMs #define VISUAL_WAYPOINT 1 @@ -61,9 +62,9 @@ enum MovementGeneratorType : uint8 MAX_MOTION_TYPE // limit }; -enum MovementSlot +enum MovementSlot : uint8 { - MOTION_SLOT_IDLE, + MOTION_SLOT_IDLE = 0, MOTION_SLOT_ACTIVE, MOTION_SLOT_CONTROLLED, MAX_MOTION_SLOT @@ -105,11 +106,12 @@ class TC_GAME_API MotionMaster void UpdateMotion(uint32 diff); void Clear(bool reset = true); + void Clear(MovementSlot slot); void MovementExpired(bool reset = true); MovementGeneratorType GetCurrentMovementGeneratorType() const; - MovementGeneratorType GetMotionSlotType(int slot) const; - MovementGenerator* GetMotionSlot(int slot) const; + MovementGeneratorType GetMotionSlotType(MovementSlot slot) const; + MovementGenerator* GetMotionSlot(MovementSlot slot) const; void PropagateSpeedChange(); @@ -159,7 +161,8 @@ class TC_GAME_API MotionMaster void MoveSeekAssistanceDistract(uint32 timer); void MoveTaxiFlight(uint32 path, uint32 pathnode); void MoveDistract(uint32 time); - void MovePath(uint32 path_id, bool repeatable); + void MovePath(uint32 pathId, bool repeatable); + void MovePath(WaypointPath& path, bool repeatable); void MoveRotate(uint32 time, RotateDirection direction); void MoveFormation(uint32 id, Position destination, uint32 moveType, bool forceRun = false, bool forceOrientation = false); @@ -172,10 +175,12 @@ class TC_GAME_API MotionMaster bool NeedInitTop() const; void InitTop(); - void Mutate(MovementGenerator *m, MovementSlot slot); + void Mutate(MovementGenerator* m, MovementSlot slot); void DirectClean(bool reset); void DelayedClean(); + void DirectClean(MovementSlot slot); + void DelayedClean(MovementSlot slot); void DirectExpire(bool reset); void DelayedExpire(); void DirectDelete(MovementGenerator* curr); diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h index 8f75e3a2361..39b2a16cc8b 100755 --- a/src/server/game/Movement/MovementGenerator.h +++ b/src/server/game/Movement/MovementGenerator.h @@ -36,10 +36,11 @@ class TC_GAME_API MovementGenerator virtual void Finalize(Unit*) = 0; virtual void Reset(Unit*) = 0; virtual bool Update(Unit*, uint32 diff) = 0; - virtual MovementGeneratorType GetMovementGeneratorType() const = 0; virtual void UnitSpeedChanged() { } + virtual void Pause(uint32/* timer = 0*/) { } // timer in ms + virtual void Resume(uint32/* overrideTimer = 0*/) { } // timer in ms // used by Evade code for select point to evade with expected restart default movement virtual bool GetResetPosition(Unit*, float& /*x*/, float& /*y*/, float& /*z*/) { return false; } diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp index 498cdf04876..133de699e84 100644 --- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp @@ -83,11 +83,7 @@ void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner) owner->SetWalk(true); owner->LoadCreaturesAddon(); owner->AI()->JustReachedHome(); - if (owner->isRegeneratingHealth()) - { - owner->SetFullHealth(); - owner->SetPower(POWER_MANA, owner->GetMaxPower(POWER_MANA)); - } + owner->SetSpawnHealth(); } } diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp index a952421833d..7da90e2d515 100755 --- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp @@ -18,7 +18,6 @@ #include "CreatureAI.h" #include "Creature.h" -#include "CreatureGroups.h" #include "Player.h" #include "MoveSplineInit.h" #include "MoveSpline.h" @@ -55,8 +54,7 @@ void PointMovementGenerator<T>::DoInitialize(T* owner) // Call for creature group update if (Creature* creature = owner->ToCreature()) - if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(Position(_x, _y, _z), _movementId); + creature->SignalFormationMovement(Position(_x, _y, _z), _movementId); } template<class T> @@ -90,8 +88,7 @@ bool PointMovementGenerator<T>::DoUpdate(T* owner, uint32 /*diff*/) // Call for creature group update if (Creature* creature = owner->ToCreature()) - if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(Position(_x, _y, _z), _movementId); + creature->SignalFormationMovement(Position(_x, _y, _z), _movementId); } return !owner->movespline->Finalized(); diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index 0a1972c0972..b2a9e5b83b9 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp @@ -18,7 +18,6 @@ #include "RandomMovementGenerator.h" #include "Creature.h" -#include "CreatureGroups.h" #include "Map.h" #include "MoveSplineInit.h" #include "MoveSpline.h" @@ -116,8 +115,7 @@ void RandomMovementGenerator<Creature>::SetRandomLocation(Creature* owner) _timer.Reset(traveltime + resetTimer); // Call for creature group update - if (owner->GetFormation() && owner->GetFormation()->getLeader() == owner) - owner->GetFormation()->LeaderMoveTo(position); + owner->SignalFormationMovement(position); } template<class T> diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index f292c0a8091..74a8588163c 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -17,99 +17,135 @@ */ #include "WaypointMovementGenerator.h" +#include "Creature.h" #include "CreatureAI.h" -#include "CreatureGroups.h" #include "Log.h" #include "MapManager.h" #include "MoveSpline.h" #include "MoveSplineInit.h" #include "ObjectMgr.h" +#include "Player.h" #include "Transport.h" +#include "WaypointDefines.h" +#include "WaypointManager.h" #include "World.h" +WaypointMovementGenerator<Creature>::WaypointMovementGenerator(uint32 pathId, bool repeating) : _nextMoveTime(0), _recalculateSpeed(false), _isArrivalDone(false), _pathId(pathId), + _repeating(repeating), _loadedFromDB(true), _stalled(false), _done(false) +{ +} + +WaypointMovementGenerator<Creature>::WaypointMovementGenerator(WaypointPath& path, bool repeating) : _nextMoveTime(0), _recalculateSpeed(false), _isArrivalDone(false), _pathId(0), + _repeating(repeating), _loadedFromDB(false), _stalled(false), _done(false) +{ + _path = &path; +} + void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature) { - if (!path_id) - path_id = creature->GetWaypointPath(); + if (_loadedFromDB) + { + if (!_pathId) + _pathId = creature->GetWaypointPath(); - i_path = sWaypointMgr->GetPath(path_id); + _path = sWaypointMgr->GetPath(_pathId); + } - if (!i_path) + if (!_path) { // No path id found for entry - TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u DB GUID: %u) doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUID().GetCounter(), creature->GetSpawnId(), path_id); + TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u DB GUID: %u) doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUID().GetCounter(), creature->GetSpawnId(), _pathId); return; } - StartMoveNow(creature); + _nextMoveTime.Reset(1000); + + // inform AI + if (creature->AI()) + creature->AI()->WaypointPathStarted(1, _path->id); } void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature) { + _done = false; LoadPath(creature); - creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); } void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature) { - creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); + creature->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); creature->SetWalk(false); } void WaypointMovementGenerator<Creature>::DoReset(Creature* creature) { - creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); - StartMoveNow(creature); + if (!_done && CanMove(creature)) + StartMoveNow(creature); + else if (_done) + { + // mimic IdleMovementGenerator + if (!creature->IsStopped()) + creature->StopMoving(); + } } void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature) { - if (!i_path || i_path->empty()) + if (!_path || _path->nodes.empty()) return; - if (m_isArrivalDone) - return; - - m_isArrivalDone = true; - if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance) + ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::OnArrived: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id); + WaypointNode const &waypoint = _path->nodes.at(_currentNode); + if (waypoint.delay) { - TC_LOG_DEBUG("maps.script", "Creature movement start script %u at point %u for %s.", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID().ToString().c_str()); creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, nullptr); + _nextMoveTime.Reset(waypoint.delay); } - // Inform script - MovementInform(creature); - creature->UpdateWaypointID(i_currentNode); - - if (i_path->at(i_currentNode)->delay) + if (waypoint.eventId && urand(0, 99) < waypoint.eventChance) { + TC_LOG_DEBUG("maps.script", "Creature movement start script %u at point %u for %s.", waypoint.eventId, _currentNode, creature->GetGUID().ToString().c_str()); creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - Stop(i_path->at(i_currentNode)->delay); + creature->GetMap()->ScriptsStart(sWaypointScripts, waypoint.eventId, creature, nullptr); + } + + // inform AI + if (creature->AI()) + { + creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, _currentNode); + creature->AI()->WaypointReached(waypoint.id, _path->id); } + + creature->UpdateCurrentWaypointInfo(waypoint.id, _path->id); } bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) { - if (!i_path || i_path->empty()) - return false; + if (!creature || !creature->IsAlive()) + return true; - // Dont allow dead creatures to move - if (!creature->IsAlive()) - return false; + if (_done || !_path || _path->nodes.empty()) + return true; - if (Stopped()) + // if the owner is the leader of its formation, check members status + if (creature->IsFormationLeader() && !creature->IsFormationLeaderMoveAllowed()) + { + _nextMoveTime.Reset(1000); return true; + } - bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && creature->GetTransGUID(); + bool transportPath = creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && !creature->GetTransGUID().IsEmpty(); - if (m_isArrivalDone) + if (_isArrivalDone) { - if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint + ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id); + WaypointNode const &waypoint = _path->nodes.at(_currentNode); + + if ((_currentNode == _path->nodes.size() - 1) && !_repeating) // If that's our last waypoint { - float x = i_path->at(i_currentNode)->x; - float y = i_path->at(i_currentNode)->y; - float z = i_path->at(i_currentNode)->z; + float x = waypoint.x; + float y = waypoint.y; + float z = waypoint.z; float o = creature->GetOrientation(); if (!transportPath) @@ -127,21 +163,31 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) transportPath = false; // else if (vehicle) - this should never happen, vehicle offsets are const } + _done = true; + creature->UpdateCurrentWaypointInfo(0, 0); - creature->GetMotionMaster()->Initialize(); - return false; + // inform AI + if (creature->AI()) + creature->AI()->WaypointPathEnded(waypoint.id, _path->id); + return true; } - i_currentNode = (i_currentNode+1) % i_path->size(); + _currentNode = (_currentNode + 1) % _path->nodes.size(); + + // inform AI + if (creature->AI()) + creature->AI()->WaypointStarted(waypoint.id, _path->id); } - WaypointData const* node = i_path->at(i_currentNode); + ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id); + WaypointNode const &waypoint = _path->nodes.at(_currentNode); + Position formationDest(waypoint.x, waypoint.y, waypoint.z, (waypoint.orientation && waypoint.delay) ? waypoint.orientation : 0.0f); - m_isArrivalDone = false; + _isArrivalDone = false; + _recalculateSpeed = false; creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); - Position formationDest(node->x, node->y, node->z, (node->orientation && node->delay) ? node->orientation : 0.0f); Movement::MoveSplineInit init(creature); //! If creature is on transport, we assume waypoints set in DB are already transport offsets @@ -158,13 +204,13 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call //! but formationDest contains global coordinates - init.MoveTo(node->x, node->y, node->z); + init.MoveTo(waypoint.x, waypoint.y, waypoint.z); //! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table - if (node->orientation && node->delay) - init.SetFacing(node->orientation); + if (waypoint.orientation && waypoint.delay) + init.SetFacing(waypoint.orientation); - switch (node->move_type) + switch (waypoint.moveType) { case WAYPOINT_MOVE_TYPE_LAND: init.SetAnimation(Movement::ToGround); @@ -178,87 +224,135 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) case WAYPOINT_MOVE_TYPE_WALK: init.SetWalk(true); break; + default: + break; } init.Launch(); - // Call for creature group update - if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(formationDest, node->id, node->move_type, (node->orientation && node->delay) ? true : false); + // inform formation + creature->SignalFormationMovement(formationDest, waypoint.id, waypoint.moveType, (waypoint.orientation && waypoint.delay) ? true : false); return true; } bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 diff) { - // Waypoint movement can be switched on/off - // This is quite handy for escort quests and other stuff - if (creature->HasUnitState(UNIT_STATE_NOT_MOVE)) + if (!creature || !creature->IsAlive()) + return true; + + if (_done || !_path || _path->nodes.empty()) + return true; + + if (_stalled || creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting()) { - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + creature->StopMoving(); return true; } - // prevent a crash at empty waypoint path. - if (!i_path || i_path->empty()) - return false; - if (Stopped()) + if (!_nextMoveTime.Passed()) { - if (CanMove(diff)) - return StartMove(creature); + if (creature->movespline->Finalized()) + { + _nextMoveTime.Update(diff); + if (_nextMoveTime.Passed()) + return StartMoveNow(creature); + } } else { - // Set home position at place on waypoint movement. - if (!creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) || !creature->GetTransGUID()) - creature->SetHomePosition(creature->GetPosition()); - - if (creature->IsStopped()) - Stop(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); - else if (creature->movespline->Finalized()) + if (creature->movespline->Finalized()) { OnArrived(creature); - return StartMove(creature); + _isArrivalDone = true; + + if (_nextMoveTime.Passed()) + return StartMove(creature); + } + else + { + // Set home position at place on waypoint movement. + if (!creature->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) || creature->GetTransGUID().IsEmpty()) + creature->SetHomePosition(creature->GetPosition()); + + if (_recalculateSpeed) + { + if (_nextMoveTime.Passed()) + StartMove(creature); + } } } - return true; - } + return true; +} void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature) { if (creature->AI()) - creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode); + creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, _currentNode); } bool WaypointMovementGenerator<Creature>::GetResetPos(Creature*, float& x, float& y, float& z) { // prevent a crash at empty waypoint path. - if (!i_path || i_path->empty()) + if (!_path || _path->nodes.empty()) return false; - WaypointData const* node = i_path->at(i_currentNode); - x = node->x; y = node->y; z = node->z; + ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::GetResetPos: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id); + WaypointNode const &waypoint = _path->nodes.at(_currentNode); + + x = waypoint.x; + y = waypoint.y; + z = waypoint.z; return true; } +void WaypointMovementGenerator<Creature>::Pause(uint32 timer/* = 0*/) +{ + _stalled = timer ? false : true; + _nextMoveTime.Reset(timer ? timer : 1); +} + +void WaypointMovementGenerator<Creature>::Resume(uint32 overrideTimer/* = 0*/) +{ + _stalled = false; + if (overrideTimer) + _nextMoveTime.Reset(overrideTimer); +} + +bool WaypointMovementGenerator<Creature>::CanMove(Creature* creature) +{ + return _nextMoveTime.Passed() && !creature->HasUnitState(UNIT_STATE_NOT_MOVE) && !creature->IsMovementPreventedByCasting(); +} //----------------------------------------------------// +#define FLIGHT_TRAVEL_UPDATE 100 +#define TIMEDIFF_NEXT_WP 250 +#define SKIP_SPLINE_POINT_DISTANCE_SQ (40.f * 40.f) +#define PLAYER_FLIGHT_SPEED 32.0f + +FlightPathMovementGenerator::FlightPathMovementGenerator(uint32 startNode) +{ + _currentNode = startNode; + _endGridX = 0.0f; + _endGridY = 0.0f; + _endMapId = 0; + _preloadTargetNode = 0; +} + uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const { - if (i_currentNode >= i_path.size()) - return i_path.size(); + if (_currentNode >= _path.size()) + return _path.size(); - uint32 curMapId = i_path[i_currentNode]->MapID; - for (uint32 i = i_currentNode; i < i_path.size(); ++i) - if (i_path[i]->MapID != curMapId) - return i; + uint32 curMapId = _path[_currentNode]->MapID; + for (uint32 itr = _currentNode; itr < _path.size(); ++itr) + if (_path[itr]->MapID != curMapId) + return itr; - return i_path.size(); + return _path.size(); } -#define SKIP_SPLINE_POINT_DISTANCE_SQ (40.0f * 40.0f) - bool IsNodeIncludedInShortenedPath(TaxiPathNodeEntry const* p1, TaxiPathNodeEntry const* p2) { return p1->MapID != p2->MapID || std::pow(p1->LocX - p2->LocX, 2) + std::pow(p1->LocY - p2->LocY, 2) > SKIP_SPLINE_POINT_DISTANCE_SQ; @@ -268,6 +362,7 @@ void FlightPathMovementGenerator::LoadPath(Player* player) { _pointsForPathSwitch.clear(); std::deque<uint32> const& taxi = player->m_taxi.GetPath(); + float discount = player->GetReputationPriceDiscount(player->m_taxi.GetFlightMasterFactionTemplate()); for (uint32 src = 0, dst = 1; dst < taxi.size(); src = dst++) { uint32 path, cost; @@ -283,24 +378,24 @@ void FlightPathMovementGenerator::LoadPath(Player* player) bool passedPreviousSegmentProximityCheck = false; for (uint32 i = 0; i < nodes.size(); ++i) { - if (passedPreviousSegmentProximityCheck || !src || i_path.empty() || IsNodeIncludedInShortenedPath(i_path[i_path.size() - 1], nodes[i])) + if (passedPreviousSegmentProximityCheck || !src || _path.empty() || IsNodeIncludedInShortenedPath(_path[_path.size() - 1], nodes[i])) { if ((!src || (IsNodeIncludedInShortenedPath(start, nodes[i]) && i >= 2)) && (dst == taxi.size() - 1 || (IsNodeIncludedInShortenedPath(end, nodes[i]) && i < nodes.size() - 1))) { passedPreviousSegmentProximityCheck = true; - i_path.push_back(nodes[i]); + _path.push_back(nodes[i]); } } else { - i_path.pop_back(); + _path.pop_back(); --_pointsForPathSwitch.back().PathIndex; } } } - _pointsForPathSwitch.push_back({ uint32(i_path.size() - 1), int32(cost) }); + _pointsForPathSwitch.push_back({ uint32(_path.size() - 1), int32(ceil(cost * discount)) }); } } @@ -331,8 +426,6 @@ void FlightPathMovementGenerator::DoFinalize(Player* player) player->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_TAXI_BENCHMARK); } -#define PLAYER_FLIGHT_SPEED 32.0f - void FlightPathMovementGenerator::DoReset(Player* player) { player->getHostileRefManager().setOnlineOfflineState(false); @@ -343,7 +436,7 @@ void FlightPathMovementGenerator::DoReset(Player* player) uint32 end = GetPathAtMapEnd(); for (uint32 i = GetCurrentNode(); i != end; ++i) { - G3D::Vector3 vertice(i_path[i]->LocX, i_path[i]->LocY, i_path[i]->LocZ); + G3D::Vector3 vertice(_path[i]->LocX, _path[i]->LocY, _path[i]->LocZ); init.Path().push_back(vertice); } init.SetFirstPointId(GetCurrentNode()); @@ -355,13 +448,13 @@ void FlightPathMovementGenerator::DoReset(Player* player) bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/) { uint32 pointId = (uint32)player->movespline->currentPathIdx(); - if (pointId > i_currentNode) + if (pointId > _currentNode) { bool departureEvent = true; do { - DoEventIfAny(player, i_path[i_currentNode], departureEvent); - while (!_pointsForPathSwitch.empty() && _pointsForPathSwitch.front().PathIndex <= i_currentNode) + DoEventIfAny(player, _path[_currentNode], departureEvent); + while (!_pointsForPathSwitch.empty() && _pointsForPathSwitch.front().PathIndex <= _currentNode) { _pointsForPathSwitch.pop_front(); player->m_taxi.NextTaxiDestination(); @@ -372,31 +465,31 @@ bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/) } } - if (pointId == i_currentNode) + if (pointId == _currentNode) break; - if (i_currentNode == _preloadTargetNode) + if (_currentNode == _preloadTargetNode) PreloadEndGrid(); - i_currentNode += (uint32)departureEvent; + _currentNode += departureEvent ? 1 : 0; departureEvent = !departureEvent; } while (true); } - return i_currentNode < (i_path.size() - 1); + return _currentNode < (_path.size() - 1); } void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport() { - if (i_path.empty() || i_currentNode >= i_path.size()) + if (_path.empty() || _currentNode >= _path.size()) return; - uint32 map0 = i_path[i_currentNode]->MapID; - for (size_t i = i_currentNode + 1; i < i_path.size(); ++i) + uint32 map0 = _path[_currentNode]->MapID; + for (size_t i = _currentNode + 1; i < _path.size(); ++i) { - if (i_path[i]->MapID != map0) + if (_path[i]->MapID != map0) { - i_currentNode = i; + _currentNode = i; return; } } @@ -413,7 +506,7 @@ void FlightPathMovementGenerator::DoEventIfAny(Player* player, TaxiPathNodeEntry bool FlightPathMovementGenerator::GetResetPos(Player*, float& x, float& y, float& z) { - TaxiPathNodeEntry const* node = i_path[i_currentNode]; + TaxiPathNodeEntry const* node = _path[_currentNode]; x = node->LocX; y = node->LocY; z = node->LocZ; @@ -424,11 +517,11 @@ void FlightPathMovementGenerator::InitEndGridInfo() { /*! Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will be reinitialized for each flightmaster at the end of each spline (or stop) in the flight. */ - uint32 nodeCount = i_path.size(); //! Number of nodes in path. - _endMapId = i_path[nodeCount - 1]->MapID; //! MapId of last node + uint32 nodeCount = _path.size(); //! Number of nodes in path. + _endMapId = _path[nodeCount - 1]->MapID; //! MapId of last node _preloadTargetNode = nodeCount - 3; - _endGridX = i_path[nodeCount - 1]->LocX; - _endGridY = i_path[nodeCount - 1]->LocY; + _endGridX = _path[nodeCount - 1]->LocX; + _endGridY = _path[nodeCount - 1]->LocY; } void FlightPathMovementGenerator::PreloadEndGrid() @@ -439,7 +532,7 @@ void FlightPathMovementGenerator::PreloadEndGrid() // Load the grid if (endMap) { - TC_LOG_DEBUG("misc", "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(i_path.size() - 1)); + TC_LOG_DEBUG("misc", "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(_path.size() - 1)); endMap->LoadGrid(_endGridX, _endGridY); } else diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h index 7426a166ea1..e447bad7bb8 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h @@ -19,103 +19,91 @@ #ifndef TRINITY_WAYPOINTMOVEMENTGENERATOR_H #define TRINITY_WAYPOINTMOVEMENTGENERATOR_H -/** @page PathMovementGenerator is used to generate movements +/** + * @page PathMovementGenerator is used to generate movements * of waypoints and flight paths. Each serves the purpose * of generate activities so that it generates updated * packets for the players. */ -#include "MovementGenerator.h" -#include "Creature.h" #include "DBCStructure.h" -#include "Player.h" +#include "MovementGenerator.h" #include "Timer.h" -#include "WaypointManager.h" -#define FLIGHT_TRAVEL_UPDATE 100 -#define TIMEDIFF_NEXT_WP 250 +class Creature; +class Player; +struct WaypointPath; -template<class T, class P> +template<class Entity, class BasePath> class PathMovementBase { public: - PathMovementBase() : i_path(), i_currentNode(0) { } + PathMovementBase() : _path(), _currentNode(0) { } virtual ~PathMovementBase() { }; - uint32 GetCurrentNode() const { return i_currentNode; } + uint32 GetCurrentNode() const { return _currentNode; } protected: - P i_path; - uint32 i_currentNode; + BasePath _path; + uint32 _currentNode; }; template<class T> class WaypointMovementGenerator; template<> -class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Creature, WaypointMovementGenerator<Creature> >, - public PathMovementBase<Creature, WaypointPath const*> +class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creature, WaypointMovementGenerator<Creature>>, public PathMovementBase<Creature, WaypointPath const*> { public: - WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true) - : i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) { } - ~WaypointMovementGenerator() { i_path = nullptr; } + explicit WaypointMovementGenerator(uint32 pathId = 0, bool repeating = true); + explicit WaypointMovementGenerator(WaypointPath& path, bool repeating = true); + + ~WaypointMovementGenerator() { _path = nullptr; } + void DoInitialize(Creature*); void DoFinalize(Creature*); void DoReset(Creature*); bool DoUpdate(Creature*, uint32 diff); - void MovementInform(Creature*); - MovementGeneratorType GetMovementGeneratorType() const override { return WAYPOINT_MOTION_TYPE; } + void UnitSpeedChanged() override { _recalculateSpeed = true; } + void Pause(uint32 timer = 0) override; + void Resume(uint32 overrideTimer = 0) override; - // now path movement implmementation - void LoadPath(Creature*); + void MovementInform(Creature*); bool GetResetPos(Creature*, float& x, float& y, float& z); private: - - void Stop(int32 time) { i_nextMoveTime.Reset(time);} - - bool Stopped() { return !i_nextMoveTime.Passed();} - - bool CanMove(int32 diff) - { - i_nextMoveTime.Update(diff); - return i_nextMoveTime.Passed(); - } - + void LoadPath(Creature*); void OnArrived(Creature*); bool StartMove(Creature*); - - void StartMoveNow(Creature* creature) + bool CanMove(Creature*); + bool StartMoveNow(Creature* creature) { - i_nextMoveTime.Reset(0); - StartMove(creature); + _nextMoveTime.Reset(0); + return StartMove(creature); } - TimeTrackerSmall i_nextMoveTime; - bool m_isArrivalDone; - uint32 path_id; - bool repeating; + TimeTrackerSmall _nextMoveTime; + bool _recalculateSpeed; + bool _isArrivalDone; + uint32 _pathId; + bool _repeating; + bool _loadedFromDB; + bool _stalled; + bool _done; }; -/** FlightPathMovementGenerator generates movement of the player for the paths +/** + * FlightPathMovementGenerator generates movement of the player for the paths * and hence generates ground and activities for the player. */ -class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, FlightPathMovementGenerator >, - public PathMovementBase<Player, TaxiPathNodeList> +class FlightPathMovementGenerator : public MovementGeneratorMedium<Player, FlightPathMovementGenerator>, public PathMovementBase<Player, TaxiPathNodeList> { public: - explicit FlightPathMovementGenerator(uint32 startNode = 0) - { - i_currentNode = startNode; - _endGridX = 0.0f; - _endGridY = 0.0f; - _endMapId = 0; - _preloadTargetNode = 0; - } + explicit FlightPathMovementGenerator(uint32 startNode = 0); + void LoadPath(Player* player); void DoInitialize(Player*); void DoReset(Player*); @@ -123,15 +111,14 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig bool DoUpdate(Player*, uint32); MovementGeneratorType GetMovementGeneratorType() const override { return FLIGHT_MOTION_TYPE; } - TaxiPathNodeList const& GetPath() { return i_path; } + TaxiPathNodeList const& GetPath() { return _path; } uint32 GetPathAtMapEnd() const; - bool HasArrived() const { return (i_currentNode >= i_path.size()); } + bool HasArrived() const { return (_currentNode >= _path.size()); } void SetCurrentNodeAfterTeleport(); - void SkipCurrentNode() { ++i_currentNode; } + void SkipCurrentNode() { ++_currentNode; } void DoEventIfAny(Player* player, TaxiPathNodeEntry const* node, bool departure); bool GetResetPos(Player*, float& x, float& y, float& z); - void InitEndGridInfo(); void PreloadEndGrid(); diff --git a/src/server/game/Movement/Waypoints/WaypointDefines.h b/src/server/game/Movement/Waypoints/WaypointDefines.h new file mode 100644 index 00000000000..dbb7a15fa5c --- /dev/null +++ b/src/server/game/Movement/Waypoints/WaypointDefines.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_WAYPOINTDEFINES_H +#define TRINITY_WAYPOINTDEFINES_H + +#include "Define.h" +#include <vector> + +enum WaypointMoveType +{ + WAYPOINT_MOVE_TYPE_WALK, + WAYPOINT_MOVE_TYPE_RUN, + WAYPOINT_MOVE_TYPE_LAND, + WAYPOINT_MOVE_TYPE_TAKEOFF, + + WAYPOINT_MOVE_TYPE_MAX +}; + +struct WaypointNode +{ + WaypointNode() : id(0), x(0.f), y(0.f), z(0.f), orientation(0.f), delay(0), eventId(0), moveType(WAYPOINT_MOVE_TYPE_RUN), eventChance(0) { } + WaypointNode(uint32 _id, float _x, float _y, float _z, float _orientation = 0.f, uint32 _delay = 0) + { + id = _id; + x = _x; + y = _y; + z = _z; + orientation = _orientation; + delay = _delay; + eventId = 0; + moveType = WAYPOINT_MOVE_TYPE_WALK; + eventChance = 100; + } + + uint32 id; + float x, y, z, orientation; + uint32 delay; + uint32 eventId; + uint32 moveType; + uint8 eventChance; +}; + +struct WaypointPath +{ + WaypointPath() : id(0) { } + WaypointPath(uint32 _id, std::vector<WaypointNode>&& _nodes) + { + id = _id; + nodes = _nodes; + } + + std::vector<WaypointNode> nodes; + uint32 id; +}; + +#endif diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index 76c6228d302..71b2c29621b 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.cpp +++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp @@ -16,27 +16,12 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "WaypointManager.h" #include "DatabaseEnv.h" #include "GridDefines.h" -#include "WaypointManager.h" #include "MapManager.h" #include "Log.h" -WaypointMgr::WaypointMgr() { } - -WaypointMgr::~WaypointMgr() -{ - for (WaypointPathContainer::iterator itr = _waypointStore.begin(); itr != _waypointStore.end(); ++itr) - { - for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it) - delete *it; - - itr->second.clear(); - } - - _waypointStore.clear(); -} - void WaypointMgr::Load() { uint32 oldMSTime = getMSTime(); @@ -46,7 +31,7 @@ void WaypointMgr::Load() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 waypoints. DB table `waypoint_data` is empty!"); + TC_LOG_INFO("server.loading", ">> Loaded 0 waypoints. DB table `waypoint_data` is empty!"); return; } @@ -55,11 +40,7 @@ void WaypointMgr::Load() do { Field* fields = result->Fetch(); - WaypointData* wp = new WaypointData(); - uint32 pathId = fields[0].GetUInt32(); - WaypointPath& path = _waypointStore[pathId]; - float x = fields[2].GetFloat(); float y = fields[3].GetFloat(); float z = fields[4].GetFloat(); @@ -68,25 +49,27 @@ void WaypointMgr::Load() Trinity::NormalizeMapCoord(x); Trinity::NormalizeMapCoord(y); - wp->id = fields[1].GetUInt32(); - wp->x = x; - wp->y = y; - wp->z = z; - wp->orientation = o; - wp->move_type = fields[6].GetUInt32(); + WaypointNode waypoint; + waypoint.id = fields[1].GetUInt32(); + waypoint.x = x; + waypoint.y = y; + waypoint.z = z; + waypoint.orientation = o; + waypoint.moveType = fields[6].GetUInt32(); - if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (waypoint.moveType >= WAYPOINT_MOVE_TYPE_MAX) { - TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id); - delete wp; + TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", waypoint.id); continue; } - wp->delay = fields[7].GetUInt32(); - wp->event_id = fields[8].GetUInt32(); - wp->event_chance = fields[9].GetInt16(); + waypoint.delay = fields[7].GetUInt32(); + waypoint.eventId = fields[8].GetUInt32(); + waypoint.eventChance = fields[9].GetInt16(); - path.push_back(wp); + WaypointPath& path = _waypointStore[pathId]; + path.id = pathId; + path.nodes.push_back(std::move(waypoint)); ++count; } while (result->NextRow()); @@ -102,14 +85,9 @@ WaypointMgr* WaypointMgr::instance() void WaypointMgr::ReloadPath(uint32 id) { - WaypointPathContainer::iterator itr = _waypointStore.find(id); + auto itr = _waypointStore.find(id); if (itr != _waypointStore.end()) - { - for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it) - delete *it; - _waypointStore.erase(itr); - } PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID); @@ -120,13 +98,10 @@ void WaypointMgr::ReloadPath(uint32 id) if (!result) return; - WaypointPath& path = _waypointStore[id]; - + std::vector<WaypointNode> values; do { Field* fields = result->Fetch(); - WaypointData* wp = new WaypointData(); - float x = fields[1].GetFloat(); float y = fields[2].GetFloat(); float z = fields[3].GetFloat(); @@ -135,26 +110,36 @@ void WaypointMgr::ReloadPath(uint32 id) Trinity::NormalizeMapCoord(x); Trinity::NormalizeMapCoord(y); - wp->id = fields[0].GetUInt32(); - wp->x = x; - wp->y = y; - wp->z = z; - wp->orientation = o; - wp->move_type = fields[5].GetUInt32(); + WaypointNode waypoint; + waypoint.id = fields[0].GetUInt32(); + waypoint.x = x; + waypoint.y = y; + waypoint.z = z; + waypoint.orientation = o; + waypoint.moveType = fields[5].GetUInt32(); - if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (waypoint.moveType >= WAYPOINT_MOVE_TYPE_MAX) { - TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id); - delete wp; + TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", waypoint.id); continue; } - wp->delay = fields[6].GetUInt32(); - wp->event_id = fields[7].GetUInt32(); - wp->event_chance = fields[8].GetUInt8(); - - path.push_back(wp); + waypoint.delay = fields[6].GetUInt32(); + waypoint.eventId = fields[7].GetUInt32(); + waypoint.eventChance = fields[8].GetUInt8(); + values.push_back(std::move(waypoint)); } while (result->NextRow()); + + _waypointStore[id] = WaypointPath(id, std::move(values)); +} + +WaypointPath const* WaypointMgr::GetPath(uint32 id) const +{ + auto itr = _waypointStore.find(id); + if (itr != _waypointStore.end()) + return &itr->second; + + return nullptr; } diff --git a/src/server/game/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h index f62805594ef..769e45432e7 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.h +++ b/src/server/game/Movement/Waypoints/WaypointManager.h @@ -20,32 +20,10 @@ #define TRINITY_WAYPOINTMANAGER_H #include "Define.h" +#include "WaypointDefines.h" #include <vector> #include <unordered_map> -enum WaypointMoveType -{ - WAYPOINT_MOVE_TYPE_WALK, - WAYPOINT_MOVE_TYPE_RUN, - WAYPOINT_MOVE_TYPE_LAND, - WAYPOINT_MOVE_TYPE_TAKEOFF, - - WAYPOINT_MOVE_TYPE_MAX -}; - -struct WaypointData -{ - uint32 id; - float x, y, z, orientation; - uint32 delay; - uint32 event_id; - uint32 move_type; - uint8 event_chance; -}; - -typedef std::vector<WaypointData*> WaypointPath; -typedef std::unordered_map<uint32, WaypointPath> WaypointPathContainer; - class TC_GAME_API WaypointMgr { public: @@ -58,20 +36,12 @@ class TC_GAME_API WaypointMgr void Load(); // Returns the path from a given id - WaypointPath const* GetPath(uint32 id) const - { - WaypointPathContainer::const_iterator itr = _waypointStore.find(id); - if (itr != _waypointStore.end()) - return &itr->second; - - return nullptr; - } + WaypointPath const* GetPath(uint32 id) const; private: - WaypointMgr(); - ~WaypointMgr(); + WaypointMgr() { } - WaypointPathContainer _waypointStore; + std::unordered_map<uint32, WaypointPath> _waypointStore; }; #define sWaypointMgr WaypointMgr::instance() diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp index a2d195bea20..b2e962abc0f 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp @@ -91,7 +91,7 @@ void OPvPCapturePoint::AddGO(uint32 type, ObjectGuid::LowType guid, uint32 entry { if (!entry) { - GameObjectData const* data = sObjectMgr->GetGOData(guid); + GameObjectData const* data = sObjectMgr->GetGameObjectData(guid); if (!data) return; entry = data->id; @@ -117,7 +117,7 @@ void OPvPCapturePoint::AddCre(uint32 type, ObjectGuid::LowType guid, uint32 entr bool OPvPCapturePoint::AddObject(uint32 type, uint32 entry, uint32 map, Position const& pos, QuaternionData const& rot) { - if (ObjectGuid::LowType guid = sObjectMgr->AddGOData(entry, map, pos, rot, 0)) + if (ObjectGuid::LowType guid = sObjectMgr->AddGameObjectData(entry, map, pos, rot, 0)) { AddGO(type, guid, entry); return true; @@ -149,7 +149,7 @@ bool OPvPCapturePoint::SetCapturePointData(uint32 entry, uint32 map, Position co return false; } - m_capturePointSpawnId = sObjectMgr->AddGOData(entry, map, pos, rot, 0); + m_capturePointSpawnId = sObjectMgr->AddGameObjectData(entry, map, pos, rot, 0); if (m_capturePointSpawnId == 0) return false; @@ -217,7 +217,7 @@ bool OPvPCapturePoint::DelObject(uint32 type) go->SetRespawnTime(0); go->Delete(); } - sObjectMgr->DeleteGOData(spawnId); + sObjectMgr->DeleteGameObjectData(spawnId); m_ObjectTypes[m_Objects[type]] = 0; m_Objects[type] = 0; return true; @@ -225,7 +225,7 @@ bool OPvPCapturePoint::DelObject(uint32 type) bool OPvPCapturePoint::DelCapturePoint() { - sObjectMgr->DeleteGOData(m_capturePointSpawnId); + sObjectMgr->DeleteGameObjectData(m_capturePointSpawnId); m_capturePointSpawnId = 0; if (m_capturePoint) diff --git a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp index ebe3d568beb..48456efb424 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp @@ -54,7 +54,7 @@ void OutdoorPvPMgr::InitOutdoorPvP() QueryResult result = WorldDatabase.Query("SELECT TypeId, ScriptName FROM outdoorpvp_template"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 outdoor PvP definitions. DB table `outdoorpvp_template` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 outdoor PvP definitions. DB table `outdoorpvp_template` is empty."); return; } diff --git a/src/server/game/Pools/PoolMgr.cpp b/src/server/game/Pools/PoolMgr.cpp index 9305e226421..25b9bd5f363 100644 --- a/src/server/game/Pools/PoolMgr.cpp +++ b/src/server/game/Pools/PoolMgr.cpp @@ -153,34 +153,6 @@ bool PoolGroup<T>::CheckPool() const return true; } -template <class T> -PoolObject* PoolGroup<T>::RollOne(ActivePoolData& spawns, uint32 triggerFrom) -{ - if (!ExplicitlyChanced.empty()) - { - float roll = (float)rand_chance(); - - for (uint32 i = 0; i < ExplicitlyChanced.size(); ++i) - { - roll -= ExplicitlyChanced[i].chance; - // Triggering object is marked as spawned at this time and can be also rolled (respawn case) - // so this need explicit check for this case - if (roll < 0 && (ExplicitlyChanced[i].guid == triggerFrom || !spawns.IsActiveObject<T>(ExplicitlyChanced[i].guid))) - return &ExplicitlyChanced[i]; - } - } - if (!EqualChanced.empty()) - { - uint32 index = urand(0, EqualChanced.size()-1); - // Triggering object is marked as spawned at this time and can be also rolled (respawn case) - // so this need explicit check for this case - if (EqualChanced[index].guid == triggerFrom || !spawns.IsActiveObject<T>(EqualChanced[index].guid)) - return &EqualChanced[index]; - } - - return nullptr; -} - // Main method to despawn a creature or gameobject in a pool // If no guid is passed, the pool is just removed (event end case) // If guid is filled, cache will be used and no removal will occur, it just fill the cache @@ -222,7 +194,7 @@ void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid) { sObjectMgr->RemoveCreatureFromGrid(guid, data); - Map* map = sMapMgr->CreateBaseMap(data->mapid); + Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); if (!map->Instanceable()) { auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(guid); @@ -230,6 +202,9 @@ void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid) { Creature* creature = itr->second; ++itr; + // For dynamic spawns, save respawn time here + if (!creature->GetRespawnCompatibilityMode()) + creature->SaveRespawnTime(0, false); creature->AddObjectToRemoveList(); } } @@ -240,11 +215,11 @@ void PoolGroup<Creature>::Despawn1Object(ObjectGuid::LowType guid) template<> void PoolGroup<GameObject>::Despawn1Object(ObjectGuid::LowType guid) { - if (GameObjectData const* data = sObjectMgr->GetGOData(guid)) + if (GameObjectData const* data = sObjectMgr->GetGameObjectData(guid)) { sObjectMgr->RemoveGameobjectFromGrid(guid, data); - Map* map = sMapMgr->CreateBaseMap(data->mapid); + Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); if (!map->Instanceable()) { auto gameobjectBounds = map->GetGameObjectBySpawnIdStore().equal_range(guid); @@ -252,6 +227,10 @@ void PoolGroup<GameObject>::Despawn1Object(ObjectGuid::LowType guid) { GameObject* go = itr->second; ++itr; + + // For dynamic spawns, save respawn time here + if (!go->GetRespawnCompatibilityMode()) + go->SaveRespawnTime(0, false); go->AddObjectToRemoveList(); } } @@ -333,7 +312,6 @@ void PoolGroup<Pool>::RemoveOneRelation(uint32 child_pool_id) template <class T> void PoolGroup<T>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom) { - uint32 lastDespawned = 0; int count = limit - spawns.GetActiveObjectCount(poolId); // If triggered from some object respawn this object is still marked as spawned @@ -342,32 +320,70 @@ void PoolGroup<T>::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 trig if (triggerFrom) ++count; - // This will try to spawn the rest of pool, not guaranteed - for (int i = 0; i < count; ++i) + if (count > 0) { - PoolObject* obj = RollOne(spawns, triggerFrom); - if (!obj) - continue; - if (obj->guid == lastDespawned) - continue; + PoolObjectList rolledObjects; + rolledObjects.reserve(count); - if (obj->guid == triggerFrom) + // roll objects to be spawned + if (!ExplicitlyChanced.empty()) { - ReSpawn1Object(obj); - triggerFrom = 0; - continue; + while (count && ExplicitlyChanced.size() > rolledObjects.size()) + { + --count; + float roll = (float)rand_chance(); + + for (PoolObject& obj : ExplicitlyChanced) + { + roll -= obj.chance; + // Triggering object is marked as spawned at this time and can be also rolled (respawn case) + // so this need explicit check for this case + if (roll < 0 && (obj.guid == triggerFrom || !spawns.IsActiveObject<T>(obj.guid))) + { + rolledObjects.push_back(obj); + break; + } + } + } } - spawns.ActivateObject<T>(obj->guid, poolId); - Spawn1Object(obj); + else if (!EqualChanced.empty()) + { + rolledObjects = EqualChanced; + + for (auto itr = rolledObjects.begin(); itr != rolledObjects.end();) + { + // remove most of the active objects so there is higher chance inactive ones are spawned + if (spawns.IsActiveObject<T>(itr->guid) && urand(1, 4) != 1) + itr = rolledObjects.erase(itr); + else + ++itr; + } - if (triggerFrom) + Trinity::Containers::RandomResize(rolledObjects, count); + } + + // try to spawn rolled objects + for (PoolObject& obj : rolledObjects) { - // One spawn one despawn no count increase - DespawnObject(spawns, triggerFrom); - lastDespawned = triggerFrom; - triggerFrom = 0; + if (spawns.IsActiveObject<T>(obj.guid)) + continue; + + if (obj.guid == triggerFrom) + { + ReSpawn1Object(&obj); + triggerFrom = 0; + } + else + { + spawns.ActivateObject<T>(obj.guid, poolId); + Spawn1Object(&obj); + } } } + + // One spawn one despawn no count increase + if (triggerFrom) + DespawnObject(spawns, triggerFrom); } // Method that is actualy doing the spawn job on 1 creature @@ -379,13 +395,13 @@ void PoolGroup<Creature>::Spawn1Object(PoolObject* obj) sObjectMgr->AddCreatureToGrid(obj->guid, data); // Spawn if necessary (loaded grids only) - Map* map = sMapMgr->CreateBaseMap(data->mapid); + Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); // We use spawn coords to spawn - if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY)) + if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) { Creature* creature = new Creature(); //TC_LOG_DEBUG("pool", "Spawning creature %u", guid); - if (!creature->LoadCreatureFromDB(obj->guid, map)) + if (!creature->LoadFromDB(obj->guid, map, true, false)) { delete creature; return; @@ -398,18 +414,18 @@ void PoolGroup<Creature>::Spawn1Object(PoolObject* obj) template <> void PoolGroup<GameObject>::Spawn1Object(PoolObject* obj) { - if (GameObjectData const* data = sObjectMgr->GetGOData(obj->guid)) + if (GameObjectData const* data = sObjectMgr->GetGameObjectData(obj->guid)) { sObjectMgr->AddGameobjectToGrid(obj->guid, data); // Spawn if necessary (loaded grids only) // this base map checked as non-instanced and then only existed - Map* map = sMapMgr->CreateBaseMap(data->mapid); + Map* map = sMapMgr->CreateBaseMap(data->spawnPoint.GetMapId()); // We use current coords to unspawn, not spawn coords since creature can have changed grid - if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY)) + if (!map->Instanceable() && map->IsGridLoaded(data->spawnPoint)) { GameObject* pGameobject = new GameObject; //TC_LOG_DEBUG("pool", "Spawning gameobject %u", guid); - if (!pGameobject->LoadGameObjectFromDB(obj->guid, map, false)) + if (!pGameobject->LoadFromDB(obj->guid, map, false)) { delete pGameobject; return; @@ -688,7 +704,7 @@ void PoolMgr::LoadFromDB() uint32 pool_id = fields[1].GetUInt32(); float chance = fields[2].GetFloat(); - GameObjectData const* data = sObjectMgr->GetGOData(guid); + GameObjectData const* data = sObjectMgr->GetGameObjectData(guid); if (!data) { TC_LOG_ERROR("sql.sql", "`pool_gameobject` has a non existing gameobject spawn (GUID: %u) defined for pool id (%u), skipped.", guid, pool_id); @@ -1074,6 +1090,21 @@ void PoolMgr::DespawnPool(uint32 pool_id) mPoolQuestGroups[pool_id].DespawnObject(mSpawnedData); } +// Selects proper template overload to call based on passed type +uint32 PoolMgr::IsPartOfAPool(SpawnObjectType type, ObjectGuid::LowType spawnId) const +{ + switch (type) + { + case SPAWN_TYPE_CREATURE: + return IsPartOfAPool<Creature>(spawnId); + case SPAWN_TYPE_GAMEOBJECT: + return IsPartOfAPool<GameObject>(spawnId); + default: + ASSERT(false, "Invalid spawn type %u passed to PoolMgr::IsPartOfPool (with spawnId %u)", uint32(type), spawnId); + return 0; + } +} + // Method that check chance integrity of the creatures and gameobjects in this pool bool PoolMgr::CheckPool(uint32 pool_id) const { diff --git a/src/server/game/Pools/PoolMgr.h b/src/server/game/Pools/PoolMgr.h index 6c270e28851..6de34c05fb4 100644 --- a/src/server/game/Pools/PoolMgr.h +++ b/src/server/game/Pools/PoolMgr.h @@ -22,6 +22,7 @@ #include "Define.h" #include "Creature.h" #include "GameObject.h" +#include "SpawnData.h" #include "QuestDef.h" struct PoolTemplateData @@ -76,7 +77,6 @@ class TC_GAME_API PoolGroup bool isEmpty() const { return ExplicitlyChanced.empty() && EqualChanced.empty(); } void AddEntry(PoolObject& poolitem, uint32 maxentries); bool CheckPool() const; - PoolObject* RollOne(ActivePoolData& spawns, uint32 triggerFrom); void DespawnObject(ActivePoolData& spawns, ObjectGuid::LowType guid=0); void Despawn1Object(ObjectGuid::LowType guid); void SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 triggerFrom); @@ -118,6 +118,7 @@ class TC_GAME_API PoolMgr template<typename T> uint32 IsPartOfAPool(uint32 db_guid_or_pool_id) const; + uint32 IsPartOfAPool(SpawnObjectType type, ObjectGuid::LowType spawnId) const; template<typename T> bool IsSpawnedObject(uint32 db_guid_or_pool_id) const { return mSpawnedData.IsActiveObject<T>(db_guid_or_pool_id); } diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp index 55100ca7396..0c106f69b4a 100644 --- a/src/server/game/Quests/QuestDef.cpp +++ b/src/server/game/Quests/QuestDef.cpp @@ -213,15 +213,12 @@ uint32 Quest::XPValue(Player* player) const else if (diffFactor > 10) diffFactor = 10; - uint32 xp = diffFactor * xpentry->Exp[_rewardXPDifficulty] / 10; - if (xp <= 100) - xp = 5 * ((xp + 2) / 5); - else if (xp <= 500) - xp = 10 * ((xp + 5) / 10); - else if (xp <= 1000) - xp = 25 * ((xp + 12) / 25); - else - xp = 50 * ((xp + 25) / 50); + uint32 xp = RoundXPValue(diffFactor * xpentry->Exp[_rewardXPDifficulty] / 10); + if (sWorld->getIntConfig(CONFIG_MIN_QUEST_SCALED_XP_RATIO)) + { + uint32 minScaledXP = RoundXPValue(xpentry->Exp[_rewardXPDifficulty]) * sWorld->getIntConfig(CONFIG_MIN_QUEST_SCALED_XP_RATIO) / 100; + xp = std::max(minScaledXP, xp); + } return xp; } @@ -447,3 +444,15 @@ void Quest::AddQuestLevelToTitle(std::string &title, int32 level) questTitlePretty << "[" << level << "] " << title; title = questTitlePretty.str(); } + +uint32 Quest::RoundXPValue(uint32 xp) +{ + if (xp <= 100) + return 5 * ((xp + 2) / 5); + else if (xp <= 500) + return 10 * ((xp + 5) / 10); + else if (xp <= 1000) + return 25 * ((xp + 12) / 25); + else + return 50 * ((xp + 25) / 50); +} diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index d2a5e0d5d84..75c5abd7911 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -384,6 +384,9 @@ class TC_GAME_API Quest uint32 _startItemCount = 0; uint32 _rewardMailSenderEntry = 0; uint32 _specialFlags = 0; // custom flags, not sniffed/WDB + + // Helpers + static uint32 RoundXPValue(uint32 xp); }; struct QuestStatusData diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 9f42ded8cc6..2a26261d9f1 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -955,11 +955,9 @@ private: // Utility macros for looping over scripts. #define FOR_SCRIPTS(T, C, E) \ - if (SCR_REG_LST(T).empty()) \ - return; \ - \ - for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \ - C != SCR_REG_LST(T).end(); ++C) + if (!SCR_REG_LST(T).empty()) \ + for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \ + C != SCR_REG_LST(T).end(); ++C) #define FOR_SCRIPTS_RET(T, C, E, R) \ if (SCR_REG_LST(T).empty()) \ @@ -1566,16 +1564,32 @@ bool ScriptMgr::OnCastItemCombatSpell(Player* player, Unit* victim, SpellInfo co return tmpscript->OnCastItemCombatSpell(player, victim, spellInfo, item); } -bool ScriptMgr::CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureTemplate const* actTemplate, CreatureData const* cData, Map const* map) +bool ScriptMgr::CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureData const* cData, Map const* map) { - ASSERT(actTemplate); - + ASSERT(map); CreatureTemplate const* baseTemplate = sObjectMgr->GetCreatureTemplate(entry); - if (!baseTemplate) - baseTemplate = actTemplate; + ASSERT(baseTemplate); + + // find out which template we'd be using + CreatureTemplate const* actTemplate = baseTemplate; + for (uint8 diff = uint8(map->GetSpawnMode()); diff > 0;) + { + if (uint32 diffEntry = baseTemplate->DifficultyEntry[diff - 1]) + if (CreatureTemplate const* diffTemplate = sObjectMgr->GetCreatureTemplate(diffEntry)) + { + actTemplate = diffTemplate; + break; + } + if (diff >= RAID_DIFFICULTY_10MAN_HEROIC && map->IsRaid()) + diff -= 2; + else + diff -= 1; + } + uint32 scriptId = baseTemplate->ScriptID; - if (cData && cData->ScriptId) - scriptId = cData->ScriptId; + if (cData && cData->scriptId) + scriptId = cData->scriptId; + GET_SCRIPT_RET(CreatureScript, scriptId, tmpscript, true); return tmpscript->CanSpawn(spawnId, entry, baseTemplate, actTemplate, cData, map); } @@ -2215,6 +2229,21 @@ AreaTriggerScript::AreaTriggerScript(char const* name) ScriptRegistry<AreaTriggerScript>::Instance()->AddScript(this); } +bool OnlyOnceAreaTriggerScript::OnTrigger(Player* player, AreaTriggerEntry const* trigger) +{ + uint32 const triggerId = trigger->id; + if (InstanceScript* instance = player->GetInstanceScript()) + { + if (instance->IsAreaTriggerDone(triggerId)) + return true; + else + instance->MarkAreaTriggerDone(triggerId); + } + return _OnTrigger(player, trigger); +} +void OnlyOnceAreaTriggerScript::ResetAreaTriggerDone(InstanceScript* script, uint32 triggerId) { script->ResetAreaTriggerDone(triggerId); } +void OnlyOnceAreaTriggerScript::ResetAreaTriggerDone(Player const* player, AreaTriggerEntry const* trigger) { if (InstanceScript* instance = player->GetInstanceScript()) ResetAreaTriggerDone(instance, trigger->id); } + BattlegroundScript::BattlegroundScript(char const* name) : ScriptObject(name) { diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 311a7faf911..d801aba496d 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -448,6 +448,19 @@ class TC_GAME_API AreaTriggerScript : public ScriptObject virtual bool OnTrigger(Player* /*player*/, AreaTriggerEntry const* /*trigger*/) { return false; } }; +class TC_GAME_API OnlyOnceAreaTriggerScript : public AreaTriggerScript +{ + using AreaTriggerScript::AreaTriggerScript; + + public: + bool OnTrigger(Player* /*player*/, AreaTriggerEntry const* /*trigger*/) override; + + protected: + virtual bool _OnTrigger(Player* /*player*/, AreaTriggerEntry const* /*trigger*/) = 0; + void ResetAreaTriggerDone(InstanceScript* /*instance*/, uint32 /*triggerId*/); + void ResetAreaTriggerDone(Player const* /*player*/, AreaTriggerEntry const* /*trigger*/); +}; + class TC_GAME_API BattlegroundScript : public ScriptObject { protected: @@ -907,7 +920,7 @@ class TC_GAME_API ScriptMgr public: /* CreatureScript */ - bool CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureTemplate const* actTemplate, CreatureData const* cData, Map const* map); + bool CanSpawn(ObjectGuid::LowType spawnId, uint32 entry, CreatureData const* cData, Map const* map); CreatureAI* GetCreatureAI(Creature* creature); public: /* GameObjectScript */ @@ -1053,6 +1066,61 @@ class TC_GAME_API ScriptMgr std::string _currentContext; }; +template <class S> +class GenericSpellScriptLoader : public SpellScriptLoader +{ + public: + GenericSpellScriptLoader(char const* name) : SpellScriptLoader(name) { } + SpellScript* GetSpellScript() const override { return new S(); } +}; +#define RegisterSpellScript(spell_script) new GenericSpellScriptLoader<spell_script>(#spell_script) + +template <class A> +class GenericAuraScriptLoader : public SpellScriptLoader +{ + public: + GenericAuraScriptLoader(char const* name) : SpellScriptLoader(name) { } + AuraScript* GetAuraScript() const override { return new A(); } +}; +#define RegisterAuraScript(aura_script) new GenericAuraScriptLoader<aura_script>(#aura_script) + +template <class S, class A> +class GenericSpellAndAuraScriptLoader : public SpellScriptLoader +{ + public: + GenericSpellAndAuraScriptLoader(char const* name) : SpellScriptLoader(name) { } + SpellScript* GetSpellScript() const override { return new S(); } + AuraScript* GetAuraScript() const override { return new A(); } +}; +#define RegisterSpellAndAuraScriptPair(spell_script, aura_script) new GenericSpellAndAuraScriptLoader<spell_script, aura_script>(#spell_script) + +template <class AI> +class GenericCreatureScript : public CreatureScript +{ + public: + GenericCreatureScript(char const* name) : CreatureScript(name) { } + CreatureAI* GetAI(Creature* me) const override { return new AI(me); } +}; +#define RegisterCreatureAI(ai_name) new GenericCreatureScript<ai_name>(#ai_name) + +template <class AI, AI*(*AIFactory)(Creature*)> +class FactoryCreatureScript : public CreatureScript +{ + public: + FactoryCreatureScript(char const* name) : CreatureScript(name) { } + CreatureAI* GetAI(Creature* me) const override { return AIFactory(me); } +}; +#define RegisterCreatureAIWithFactory(ai_name, factory_fn) new FactoryCreatureScript<ai_name, &factory_fn>(#ai_name) + +template <class AI> +class GenericGameObjectScript : public GameObjectScript +{ + public: + GenericGameObjectScript(char const* name) : GameObjectScript(name) { } + GameObjectAI* GetAI(GameObject* go) const override { return new AI(go); } +}; +#define RegisterGameObjectAI(ai_name) new GenericGameObjectScript<ai_name>(#ai_name) + #define sScriptMgr ScriptMgr::instance() #endif diff --git a/src/server/game/Scripting/ScriptSystem.cpp b/src/server/game/Scripting/ScriptSystem.cpp index 1b4ad6b98d5..b814229151a 100644 --- a/src/server/game/Scripting/ScriptSystem.cpp +++ b/src/server/game/Scripting/ScriptSystem.cpp @@ -37,17 +37,17 @@ void SystemMgr::LoadScriptWaypoints() { uint32 oldMSTime = getMSTime(); - // Drop Existing Waypoint list - m_mPointMoveMap.clear(); + // drop Existing Waypoint list + _waypointStore.clear(); - uint64 uiCreatureCount = 0; + uint64 entryCount = 0; - // Load Waypoints + // load Waypoints QueryResult result = WorldDatabase.Query("SELECT COUNT(entry) FROM script_waypoint GROUP BY entry"); if (result) - uiCreatureCount = result->GetRowCount(); + entryCount = result->GetRowCount(); - TC_LOG_INFO("server.loading", "Loading Script Waypoints for " UI64FMTD " creature(s)...", uiCreatureCount); + TC_LOG_INFO("server.loading", "Loading Script Waypoints for " UI64FMTD " creature(s)...", entryCount); // 0 1 2 3 4 5 result = WorldDatabase.Query("SELECT entry, pointid, location_x, location_y, location_z, waittime FROM script_waypoint ORDER BY pointid"); @@ -61,29 +61,28 @@ void SystemMgr::LoadScriptWaypoints() do { - Field* pFields = result->Fetch(); - ScriptPointMove temp; - - temp.uiCreatureEntry = pFields[0].GetUInt32(); - uint32 uiEntry = temp.uiCreatureEntry; - temp.uiPointId = pFields[1].GetUInt32(); - temp.fX = pFields[2].GetFloat(); - temp.fY = pFields[3].GetFloat(); - temp.fZ = pFields[4].GetFloat(); - temp.uiWaitTime = pFields[5].GetUInt32(); - - CreatureTemplate const* pCInfo = sObjectMgr->GetCreatureTemplate(temp.uiCreatureEntry); - - if (!pCInfo) + Field* fields = result->Fetch(); + uint32 entry = fields[0].GetUInt32(); + uint32 id = fields[1].GetUInt32(); + float x = fields[2].GetFloat(); + float y = fields[3].GetFloat(); + float z = fields[4].GetFloat(); + uint32 waitTime = fields[5].GetUInt32(); + + CreatureTemplate const* info = sObjectMgr->GetCreatureTemplate(entry); + if (!info) { - TC_LOG_ERROR("sql.sql", "TSCR: DB table script_waypoint has waypoint for non-existant creature entry %u", temp.uiCreatureEntry); + TC_LOG_ERROR("sql.sql", "SystemMgr: DB table script_waypoint has waypoint for non-existant creature entry %u", entry); continue; } - if (!pCInfo->ScriptID) - TC_LOG_ERROR("sql.sql", "TSCR: DB table script_waypoint has waypoint for creature entry %u, but creature does not have ScriptName defined and then useless.", temp.uiCreatureEntry); + if (!info->ScriptID) + TC_LOG_ERROR("sql.sql", "SystemMgr: DB table script_waypoint has waypoint for creature entry %u, but creature does not have ScriptName defined and then useless.", entry); + + WaypointPath& path = _waypointStore[entry]; + path.id = entry; + path.nodes.emplace_back(id, x, y, z, 0.f, waitTime); - m_mPointMoveMap[uiEntry].push_back(temp); ++count; } while (result->NextRow()); @@ -163,6 +162,15 @@ void SystemMgr::LoadScriptSplineChains() } } +WaypointPath const* SystemMgr::GetPath(uint32 creatureEntry) const +{ + auto itr = _waypointStore.find(creatureEntry); + if (itr == _waypointStore.end()) + return nullptr; + + return &itr->second; +} + std::vector<SplineChainLink> const* SystemMgr::GetSplineChain(uint32 entry, uint16 chainId) const { auto it = m_mSplineChainsMap.find({ entry, chainId }); diff --git a/src/server/game/Scripting/ScriptSystem.h b/src/server/game/Scripting/ScriptSystem.h index 7828c24d680..5b42c5b5a25 100644 --- a/src/server/game/Scripting/ScriptSystem.h +++ b/src/server/game/Scripting/ScriptSystem.h @@ -21,59 +21,37 @@ #include "Define.h" #include "Hash.h" +#include "WaypointDefines.h" #include <unordered_map> #include <vector> class Creature; struct SplineChainLink; -#define TEXT_SOURCE_RANGE -1000000 //the amount of entries each text source has available - -struct ScriptPointMove -{ - uint32 uiCreatureEntry; - uint32 uiPointId; - float fX; - float fY; - float fZ; - uint32 uiWaitTime; -}; - -typedef std::vector<ScriptPointMove> ScriptPointVector; - class TC_GAME_API SystemMgr { - private: - SystemMgr(); - ~SystemMgr(); - SystemMgr(SystemMgr const&) = delete; - SystemMgr& operator=(SystemMgr const&) = delete; - public: static SystemMgr* instance(); - typedef std::unordered_map<uint32, ScriptPointVector> PointMoveMap; - - //Database + // database void LoadScriptWaypoints(); void LoadScriptSplineChains(); - ScriptPointVector const* GetPointMoveList(uint32 creatureEntry) const - { - PointMoveMap::const_iterator itr = m_mPointMoveMap.find(creatureEntry); - - if (itr == m_mPointMoveMap.end()) - return nullptr; - - return &itr->second; - } + WaypointPath const* GetPath(uint32 creatureEntry) const; std::vector<SplineChainLink> const* GetSplineChain(uint32 entry, uint16 chainId) const; std::vector<SplineChainLink> const* GetSplineChain(Creature const* who, uint16 id) const; - protected: - PointMoveMap m_mPointMoveMap; //coordinates for waypoints + private: typedef std::pair<uint32, uint16> ChainKeyType; // creature entry + chain ID + + SystemMgr(); + ~SystemMgr(); + + SystemMgr(SystemMgr const&) = delete; + SystemMgr& operator=(SystemMgr const&) = delete; + + std::unordered_map<uint32, WaypointPath> _waypointStore; std::unordered_map<ChainKeyType, std::vector<SplineChainLink>> m_mSplineChainsMap; // spline chains }; diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 05f69568507..a5a4f9139b2 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -273,7 +273,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) ///- Before we process anything: /// If necessary, kick the player because the client didn't send anything for too long /// (or they've been idling in character select) - if (IsConnectionIdle()) + if (IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION)) m_Socket->CloseSocket(); ///- Retrieve packets from the receive queue and call the appropriate handlers diff --git a/src/server/game/Skills/SkillDiscovery.cpp b/src/server/game/Skills/SkillDiscovery.cpp index 44d9a6a153f..722b5427c72 100644 --- a/src/server/game/Skills/SkillDiscovery.cpp +++ b/src/server/game/Skills/SkillDiscovery.cpp @@ -56,7 +56,7 @@ void LoadSkillDiscoveryTable() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty."); return; } diff --git a/src/server/game/Skills/SkillExtraItems.cpp b/src/server/game/Skills/SkillExtraItems.cpp index 3b066e71b8b..91e4ea0d244 100644 --- a/src/server/game/Skills/SkillExtraItems.cpp +++ b/src/server/game/Skills/SkillExtraItems.cpp @@ -61,7 +61,7 @@ void LoadSkillPerfectItemTable() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 spell perfection definitions. DB table `skill_perfect_item_template` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 spell perfection definitions. DB table `skill_perfect_item_template` is empty."); return; } @@ -148,7 +148,7 @@ void LoadSkillExtraItemTable() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 spell specialization definitions. DB table `skill_extra_item_template` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 spell specialization definitions. DB table `skill_extra_item_template` is empty."); return; } @@ -208,9 +208,6 @@ bool CanCreatePerfectItem(Player* player, uint32 spellId, float &perfectCreateCh return false; SkillPerfectItemEntry const* thisEntry = &ret->second; - // lack of entry means no perfection proc possible - if (!thisEntry) - return false; // if you don't have the spell needed, then no procs for you if (!player->HasSpell(thisEntry->requiredSpecialization)) @@ -233,10 +230,6 @@ bool CanCreateExtraItems(Player* player, uint32 spellId, float &additionalChance SkillExtraItemEntry const* specEntry = &ret->second; - // if no entry, then no extra items can be created - if (!specEntry) - return false; - // the player doesn't have the required specialization, return false if (!player->HasSpell(specEntry->requiredSpecialization)) return false; diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 2d57d1472b4..80565e1b464 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -4563,7 +4563,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool break; case 52172: // Coyote Spirit Despawn Aura case 60244: // Blood Parrot Despawn Aura - target->CastSpell((Unit*)nullptr, GetAmount(), true, nullptr, this); + target->CastSpell(nullptr, GetAmount(), true, nullptr, this); break; case 58600: // Restricted Flight Area case 58730: // Restricted Flight Area @@ -5121,7 +5121,7 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const } case 62292: // Blaze (Pool of Tar) // should we use custom damage? - target->CastSpell((Unit*)nullptr, m_spellInfo->Effects[m_effIndex].TriggerSpell, true); + target->CastSpell(nullptr, m_spellInfo->Effects[m_effIndex].TriggerSpell, true); break; case 62399: // Overload Circuit if (target->GetMap()->IsDungeon() && int(target->GetAppliedAuras().count(62399)) >= (target->GetMap()->IsHeroic() ? 4 : 2)) @@ -5145,7 +5145,7 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const // Mirror Image if (GetId() == 55342) // Set name of summons to name of caster - target->CastSpell((Unit*)nullptr, m_spellInfo->Effects[m_effIndex].TriggerSpell, true); + target->CastSpell(nullptr, m_spellInfo->Effects[m_effIndex].TriggerSpell, true); break; } case SPELLFAMILY_DRUID: diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index cff25bc8e2c..7c1cdc56de0 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -947,6 +947,9 @@ bool Aura::CanBeSaved() const if (IsPassive()) return false; + if (GetSpellInfo()->IsChanneled()) + return false; + // Check if aura is single target, not only spell info if (GetCasterGUID() != GetOwner()->GetGUID()) if (GetSpellInfo()->IsSingleTarget() || IsSingleTarget()) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index e56c573a43c..334e51eeb56 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -549,6 +549,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO m_referencedFromCurrentSpell = false; m_executedCurrently = false; m_needComboPoints = m_spellInfo->NeedsComboPoints(); + m_comboTarget = nullptr; m_comboPointGain = 0; m_delayStart = 0; m_delayAtDamageCount = 0; @@ -1399,12 +1400,9 @@ void Spell::SelectImplicitTargetDestTargets(SpellEffIndex effIndex, SpellImplici default: { float angle = targetType.CalcDirectionAngle(); - float objSize = target->GetCombatReach(); - float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); - if (dist < objSize) - dist = objSize; - else if (targetType.GetTarget() == TARGET_DEST_TARGET_RANDOM) - dist = objSize + (dist - objSize) * float(rand_norm()); + float dist = m_spellInfo->Effects[effIndex].CalcRadius(nullptr); + if (targetType.GetTarget() == TARGET_DEST_TARGET_RANDOM) + dist *= float(rand_norm()); Position pos = dest._position; target->MovePositionToFirstCollision(pos, dist, angle); @@ -2551,7 +2549,8 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA // for delayed spells ignore negative spells (after duel end) for friendly targets /// @todo this cause soul transfer bugged // 63881 - Malady of the Mind jump spell (Yogg-Saron) - if (m_spellInfo->Speed > 0.0f && unit->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->IsPositive() && m_spellInfo->Id != 63881) + // 45034 - Curse of Boundless Agony jump spell (Kalecgos) + if (m_spellInfo->Speed > 0.0f && unit->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->IsPositive() && m_spellInfo->Id != 63881 && m_spellInfo->Id != 45034) return SPELL_MISS_EVADE; // assisting case, healing and resurrection @@ -7881,7 +7880,7 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) if (!isInsideCylinder) return false; } - + return WorldObjectSpellTargetCheck::operator ()(target); } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 852e34a059f..75c97b005af 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -821,27 +821,6 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex) m_caster->CastSpell(unitTarget, spell->Id, true); return; } - // Cloak of Shadows - case 35729: - { - uint32 dispelMask = SpellInfo::GetDispelMask(DISPEL_ALL); - Unit::AuraApplicationMap& Auras = unitTarget->GetAppliedAuras(); - for (Unit::AuraApplicationMap::iterator iter = Auras.begin(); iter != Auras.end();) - { - // remove all harmful spells on you... - SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo(); - if (((spell->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && spell->GetSchoolMask() != SPELL_SCHOOL_MASK_NORMAL) // only affect magic spells - || (spell->GetDispelMask() & dispelMask)) && - // ignore positive and passive auras - !iter->second->IsPositive() && !iter->second->GetBase()->IsPassive()) - { - m_caster->RemoveAura(iter); - } - else - ++iter; - } - return; - } } } @@ -1008,7 +987,7 @@ void Spell::EffectTriggerRitualOfSummoning(SpellEffIndex effIndex) finish(); - m_caster->CastSpell((Unit*)nullptr, spellInfo, false); + m_caster->CastSpell(nullptr, spellInfo, false); } void Spell::EffectJump(SpellEffIndex effIndex) @@ -2818,8 +2797,6 @@ void Spell::EffectEnchantItemTmp(SpellEffIndex effIndex) } return; } - if (!itemTarget) - return; uint32 enchant_id = m_spellInfo->Effects[effIndex].MiscValue; @@ -3209,7 +3186,7 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) // Mangle (Cat): CP if (m_spellInfo->SpellFamilyFlags[1] & 0x400) AddComboPointGain(unitTarget, 1); - + // Shred, Maul - Rend and Tear else if (m_spellInfo->SpellFamilyFlags[0] & 0x00008800 && unitTarget->HasAuraState(AURA_STATE_BLEEDING)) { @@ -3946,6 +3923,9 @@ void Spell::EffectSanctuary(SpellEffIndex /*effIndex*/) if (!unitTarget) return; + if (unitTarget->GetTypeId() == TYPEID_PLAYER) + unitTarget->ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel + unitTarget->getHostileRefManager().UpdateVisibility(); Unit::AttackerSet const& attackers = unitTarget->getAttackers(); @@ -4655,9 +4635,13 @@ void Spell::EffectChargeDest(SpellEffIndex /*effIndex*/) if (m_targets.HasDst()) { Position pos = destTarget->GetPosition(); - float angle = m_caster->GetRelativeAngle(pos.GetPositionX(), pos.GetPositionY()); - float dist = m_caster->GetDistance(pos); - pos = m_caster->GetFirstCollisionPosition(dist, angle); + + if (!m_caster->IsWithinLOS(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ())) + { + float angle = m_caster->GetRelativeAngle(pos.GetPositionX(), pos.GetPositionY()); + float dist = m_caster->GetDistance(pos); + pos = m_caster->GetFirstCollisionPosition(dist, angle); + } m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ); } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 9e393f2fcad..e2946dbb45e 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -323,7 +323,7 @@ SpellImplicitTargetInfo::StaticData SpellImplicitTargetInfo::_data[TOTAL_SPELL_ {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_DEST, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 107 TARGET_UNK_DEST_AREA_UNK_107 {TARGET_OBJECT_TYPE_GOBJ, TARGET_REFERENCE_TYPE_CASTER, TARGET_SELECT_CATEGORY_CONE, TARGET_CHECK_DEFAULT, TARGET_DIR_FRONT}, // 108 TARGET_GAMEOBJECT_CONE {TARGET_OBJECT_TYPE_NONE, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 109 - {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_DEFAULT, TARGET_DIR_NONE}, // 110 TARGET_DEST_UNK_110 + {TARGET_OBJECT_TYPE_DEST, TARGET_REFERENCE_TYPE_NONE, TARGET_SELECT_CATEGORY_NYI, TARGET_CHECK_ENTRY, TARGET_DIR_NONE}, // 110 TARGET_UNIT_CONE_ENTRY_110 }; SpellEffectInfo::SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const* spellInfo, uint8 effIndex) @@ -1345,10 +1345,9 @@ bool SpellInfo::CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const if (HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) || HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) { // ...but not these (Divine shield, Ice block, Cyclone and Banish for example) - if (!auraSpellInfo || - (auraSpellInfo->Mechanic != MECHANIC_IMMUNE_SHIELD && - auraSpellInfo->Mechanic != MECHANIC_INVULNERABILITY && - (auraSpellInfo->Mechanic != MECHANIC_BANISH || (IsRankOf(auraSpellInfo) && auraSpellInfo->Dispel != DISPEL_NONE)))) // Banish shouldn't be immune to itself, but Cyclone should + if (auraSpellInfo->Mechanic != MECHANIC_IMMUNE_SHIELD && + auraSpellInfo->Mechanic != MECHANIC_INVULNERABILITY && + (auraSpellInfo->Mechanic != MECHANIC_BANISH || (IsRankOf(auraSpellInfo) && auraSpellInfo->Dispel != DISPEL_NONE))) // Banish shouldn't be immune to itself, but Cyclone should return true; } @@ -1370,7 +1369,8 @@ bool SpellInfo::CanDispelAura(SpellInfo const* auraSpellInfo) const return true; // These auras (Cyclone for example) are not dispelable - if (auraSpellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) || auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + if ((auraSpellInfo->HasAttribute(SPELL_ATTR1_UNAFFECTED_BY_SCHOOL_IMMUNE) && auraSpellInfo->Mechanic != MECHANIC_NONE) + || auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) return false; return true; @@ -2877,13 +2877,7 @@ void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, uint8 effIndex, bool app target->ApplySpellImmune(Id, IMMUNITY_MECHANIC, i, apply); if (apply && HasAttribute(SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY)) - { - // exception for purely snare mechanic (eg. hands of freedom)! - if (mechanicImmunity == (1 << MECHANIC_SNARE)) - target->RemoveMovementImpairingAuras(false); - else - target->RemoveAurasWithMechanic(mechanicImmunity, AURA_REMOVE_BY_DEFAULT, Id); - } + target->RemoveAurasWithMechanic(mechanicImmunity, AURA_REMOVE_BY_DEFAULT, Id); } if (uint32 dispelImmunity = immuneInfo->DispelImmune) @@ -3372,6 +3366,8 @@ bool SpellInfo::_IsPositiveEffect(uint8 effIndex, bool deep) const { case 29214: // Wrath of the Plaguebringer case 34700: // Allergic Reaction + case 41914: // Parasitic Shadowfiend (Illidan) + case 41917: // Parasitic Shadowfiend (Illidan) case 54836: // Wrath of the Plaguebringer case 61987: // Avenging Wrath Marker case 61988: // Divine Shield exclude aura diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index fc7b219d54d..22b16a29dde 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3898,6 +3898,12 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_100_YARDS); // 100yd }); + // Coldflame (Lord Marrowgar) + ApplySpellFix({ 69146, 70823, 70824, 70825 }, [](SpellInfo* spellInfo) + { + spellInfo->AttributesEx4 &= ~SPELL_ATTR4_IGNORE_RESISTANCES; + }); + // Shadow's Fate ApplySpellFix({ 71169 }, [](SpellInfo* spellInfo) { @@ -4127,13 +4133,13 @@ void SpellMgr::LoadSpellInfoCorrections() // Summon Shadow Trap ApplySpellFix({ 73540 }, [](SpellInfo* spellInfo) { - spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(23); // 90 seconds + spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(3); // 60 seconds }); // Shadow Trap (visual) ApplySpellFix({ 73530 }, [](SpellInfo* spellInfo) { - spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(28); // 5 seconds + spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(27); // 3 seconds }); // Shadow Trap @@ -4459,6 +4465,18 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->ProcFlags = 0; }); + // Shadowstep + ApplySpellFix({ 36563 }, [](SpellInfo* spellInfo) + { + spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_4_YARDS); // 4yd + }); + + // Feral Charge - Cat + ApplySpellFix({ 49376 }, [](SpellInfo* spellInfo) + { + spellInfo->Effects[EFFECT_1].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_3_YARDS); // 3yd + }); + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { SpellInfo* spellInfo = mSpellInfoMap[i]; diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 03f06961a4b..7e63770c3e1 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -786,13 +786,13 @@ class TC_GAME_API AuraScript : public _SpellScript HookList<EffectAbsorbHandler> AfterEffectAbsorb; // executed when mana shield aura effect is going to reduce damage - // example: OnEffectManaShield += AuraEffectAbsorbFn(class::function, EffectIndexSpecifier); + // example: OnEffectManaShield += AuraEffectManaShieldFn(class::function, EffectIndexSpecifier); // where function is: void function (AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount); HookList<EffectManaShieldHandler> OnEffectManaShield; #define AuraEffectManaShieldFn(F, I) EffectManaShieldFunction(&F, I) // executed after mana shield aura effect reduced damage to target - absorbAmount is real amount absorbed by aura - // example: AfterEffectManaShield += AuraEffectAbsorbFn(class::function, EffectIndexSpecifier); + // example: AfterEffectManaShield += AuraEffectManaShieldFn(class::function, EffectIndexSpecifier); // where function is: void function (AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount); HookList<EffectManaShieldHandler> AfterEffectManaShield; diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp index 0357fcc4e15..6bcf3128427 100644 --- a/src/server/game/Tools/PlayerDump.cpp +++ b/src/server/game/Tools/PlayerDump.cpp @@ -936,7 +936,7 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s if (!ChangeColumn(ts, line, "at_login", "1")) return DUMP_FILE_BROKEN; } - else if (!ChangeColumn(ts, line, "name", name.c_str())) // characters.name + else if (!ChangeColumn(ts, line, "name", name)) // characters.name return DUMP_FILE_BROKEN; break; } diff --git a/src/server/game/Warden/WardenCheckMgr.cpp b/src/server/game/Warden/WardenCheckMgr.cpp index 3b8387e3381..5a7a6bf49dd 100644 --- a/src/server/game/Warden/WardenCheckMgr.cpp +++ b/src/server/game/Warden/WardenCheckMgr.cpp @@ -38,6 +38,8 @@ WardenCheckMgr::~WardenCheckMgr() void WardenCheckMgr::LoadWardenChecks() { + uint32 oldMSTime = getMSTime(); + // Check if Warden is enabled by config before loading anything if (!sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED)) { @@ -141,11 +143,13 @@ void WardenCheckMgr::LoadWardenChecks() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u warden checks.", count); + TC_LOG_INFO("server.loading", ">> Loaded %u warden checks in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } void WardenCheckMgr::LoadWardenOverrides() { + uint32 oldMSTime = getMSTime(); + // Check if Warden is enabled by config before loading anything if (!sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED)) { @@ -187,7 +191,7 @@ void WardenCheckMgr::LoadWardenOverrides() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u warden action overrides.", count); + TC_LOG_INFO("server.loading", ">> Loaded %u warden action overrides in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } WardenCheckMgr* WardenCheckMgr::instance() diff --git a/src/server/game/Weather/WeatherMgr.cpp b/src/server/game/Weather/WeatherMgr.cpp index 95728b7e8ae..98fbf897e27 100644 --- a/src/server/game/Weather/WeatherMgr.cpp +++ b/src/server/game/Weather/WeatherMgr.cpp @@ -95,7 +95,7 @@ void LoadWeatherData() if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 weather definitions. DB table `game_weather` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 weather definitions. DB table `game_weather` is empty."); return; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index ae5cc87d097..03081a113de 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -81,7 +81,7 @@ #include "VMapFactory.h" #include "VMapManager2.h" #include "WardenCheckMgr.h" -#include "WaypointMovementGenerator.h" +#include "WaypointManager.h" #include "WeatherMgr.h" #include "WhoListStorage.h" #include "WorldSession.h" @@ -134,6 +134,11 @@ World::World() memset(m_int_configs, 0, sizeof(m_int_configs)); memset(m_bool_configs, 0, sizeof(m_bool_configs)); memset(m_float_configs, 0, sizeof(m_float_configs)); + + _guidWarn = false; + _guidAlert = false; + _warnDiff = 0; + _warnShutdownTime = time(NULL); } /// World destructor @@ -196,6 +201,59 @@ void World::SetClosed(bool val) sScriptMgr->OnOpenStateChange(!val); } +void World::TriggerGuidWarning() +{ + // Lock this only to prevent multiple maps triggering at the same time + std::lock_guard<std::mutex> lock(_guidAlertLock); + + time_t gameTime = GameTime::GetGameTime(); + time_t today = (gameTime / DAY) * DAY; + + // Check if our window to restart today has passed. 5 mins until quiet time + while (gameTime >= (today + (getIntConfig(CONFIG_RESPAWN_RESTARTQUIETTIME) * HOUR) - 1810)) + today += DAY; + + // Schedule restart for 30 minutes before quiet time, or as long as we have + _warnShutdownTime = today + (getIntConfig(CONFIG_RESPAWN_RESTARTQUIETTIME) * HOUR) - 1800; + + _guidWarn = true; + SendGuidWarning(); +} + +void World::TriggerGuidAlert() +{ + // Lock this only to prevent multiple maps triggering at the same time + std::lock_guard<std::mutex> lock(_guidAlertLock); + + DoGuidAlertRestart(); + _guidAlert = true; + _guidWarn = false; +} + +void World::DoGuidWarningRestart() +{ + if (m_ShutdownTimer) + return; + + ShutdownServ(1800, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE); + _warnShutdownTime += HOUR; +} + +void World::DoGuidAlertRestart() +{ + if (m_ShutdownTimer) + return; + + ShutdownServ(300, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE, _alertRestartReason); +} + +void World::SendGuidWarning() +{ + if (!m_ShutdownTimer && _guidWarn && getIntConfig(CONFIG_RESPAWN_GUIDWARNING_FREQUENCY) > 0) + SendServerMessage(SERVER_MSG_STRING, _guidWarningMsg.c_str()); + _warnDiff = 0; +} + /// Find a session by its id WorldSession* World::FindSession(uint32 id) const { @@ -670,6 +728,27 @@ void World::LoadConfigSettings(bool reload) m_float_configs[CONFIG_GROUP_XP_DISTANCE] = sConfigMgr->GetFloatDefault("MaxGroupXPDistance", 74.0f); m_float_configs[CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE] = sConfigMgr->GetFloatDefault("MaxRecruitAFriendBonusDistance", 100.0f); + m_int_configs[CONFIG_MIN_QUEST_SCALED_XP_RATIO] = sConfigMgr->GetIntDefault("MinQuestScaledXPRatio", 0); + if (m_int_configs[CONFIG_MIN_QUEST_SCALED_XP_RATIO] > 100) + { + TC_LOG_ERROR("server.loading", "MinQuestScaledXPRatio (%i) must be in range 0..100. Set to 0.", m_int_configs[CONFIG_MIN_QUEST_SCALED_XP_RATIO]); + m_int_configs[CONFIG_MIN_QUEST_SCALED_XP_RATIO] = 0; + } + + m_int_configs[CONFIG_MIN_CREATURE_SCALED_XP_RATIO] = sConfigMgr->GetIntDefault("MinCreatureScaledXPRatio", 0); + if (m_int_configs[CONFIG_MIN_CREATURE_SCALED_XP_RATIO] > 100) + { + TC_LOG_ERROR("server.loading", "MinCreatureScaledXPRatio (%i) must be in range 0..100. Set to 0.", m_int_configs[CONFIG_MIN_CREATURE_SCALED_XP_RATIO]); + m_int_configs[CONFIG_MIN_CREATURE_SCALED_XP_RATIO] = 0; + } + + m_int_configs[CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO] = sConfigMgr->GetIntDefault("MinDiscoveredScaledXPRatio", 0); + if (m_int_configs[CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO] > 100) + { + TC_LOG_ERROR("server.loading", "MinDiscoveredScaledXPRatio (%i) must be in range 0..100. Set to 0.", m_int_configs[CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO]); + m_int_configs[CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO] = 0; + } + /// @todo Add MonsterSight (with meaning) in worldserver.conf or put them as define m_float_configs[CONFIG_SIGHT_MONSTER] = sConfigMgr->GetFloatDefault("MonsterSight", 50.0f); @@ -916,6 +995,12 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_GROUP_VISIBILITY] = sConfigMgr->GetIntDefault("Visibility.GroupMode", 1); m_int_configs[CONFIG_MAIL_DELIVERY_DELAY] = sConfigMgr->GetIntDefault("MailDeliveryDelay", HOUR); + m_int_configs[CONFIG_CLEAN_OLD_MAIL_TIME] = sConfigMgr->GetIntDefault("CleanOldMailTime", 4); + if (m_int_configs[CONFIG_CLEAN_OLD_MAIL_TIME] > 23) + { + TC_LOG_ERROR("server.loading", "CleanOldMailTime (%u) must be an hour, between 0 and 23. Set to 4.", m_int_configs[CONFIG_CLEAN_OLD_MAIL_TIME]); + m_int_configs[CONFIG_CLEAN_OLD_MAIL_TIME] = 4; + } m_int_configs[CONFIG_UPTIME_UPDATE] = sConfigMgr->GetIntDefault("UpdateUptimeInterval", 10); if (int32(m_int_configs[CONFIG_UPTIME_UPDATE]) <= 0) @@ -1192,6 +1277,50 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_NO_GRAY_AGGRO_BELOW] = m_int_configs[CONFIG_NO_GRAY_AGGRO_ABOVE]; } + // Respawn Settings + m_int_configs[CONFIG_RESPAWN_MINCHECKINTERVALMS] = sConfigMgr->GetIntDefault("Respawn.MinCheckIntervalMS", 5000); + m_int_configs[CONFIG_RESPAWN_DYNAMICMODE] = sConfigMgr->GetIntDefault("Respawn.DynamicMode", 0); + if (m_int_configs[CONFIG_RESPAWN_DYNAMICMODE] > 1) + { + TC_LOG_ERROR("server.loading", "Invalid value for Respawn.DynamicMode (%u). Set to 0.", m_int_configs[CONFIG_RESPAWN_DYNAMICMODE]); + m_int_configs[CONFIG_RESPAWN_DYNAMICMODE] = 0; + } + m_bool_configs[CONFIG_RESPAWN_DYNAMIC_ESCORTNPC] = sConfigMgr->GetBoolDefault("Respawn.DynamicEscortNPC", false); + m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL] = sConfigMgr->GetIntDefault("Respawn.GuidWarnLevel", 12000000); + if (m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL] > 16777215) + { + TC_LOG_ERROR("server.loading", "Respawn.GuidWarnLevel (%u) cannot be greater than maximum GUID (16777215). Set to 12000000.", m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL]); + m_int_configs[CONFIG_RESPAWN_GUIDWARNLEVEL] = 12000000; + } + m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL] = sConfigMgr->GetIntDefault("Respawn.GuidAlertLevel", 16000000); + if (m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL] > 16777215) + { + TC_LOG_ERROR("server.loading", "Respawn.GuidWarnLevel (%u) cannot be greater than maximum GUID (16777215). Set to 16000000.", m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL]); + m_int_configs[CONFIG_RESPAWN_GUIDALERTLEVEL] = 16000000; + } + m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME] = sConfigMgr->GetIntDefault("Respawn.RestartQuietTime", 3); + if (m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME] > 23) + { + TC_LOG_ERROR("server.loading", "Respawn.RestartQuietTime (%u) must be an hour, between 0 and 23. Set to 3.", m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME]); + m_int_configs[CONFIG_RESPAWN_RESTARTQUIETTIME] = 3; + } + m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] = sConfigMgr->GetFloatDefault("Respawn.DynamicRateCreature", 10.0f); + if (m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] < 0.0f) + { + TC_LOG_ERROR("server.loading", "Respawn.DynamicRateCreature (%f) must be positive. Set to 10.", m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE]); + m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] = 10.0f; + } + m_int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE] = sConfigMgr->GetIntDefault("Respawn.DynamicMinimumCreature", 10); + m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] = sConfigMgr->GetFloatDefault("Respawn.DynamicRateGameObject", 10.0f); + if (m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] < 0.0f) + { + TC_LOG_ERROR("server.loading", "Respawn.DynamicRateGameObject (%f) must be positive. Set to 10.", m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT]); + m_float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] = 10.0f; + } + m_int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT] = sConfigMgr->GetIntDefault("Respawn.DynamicMinimumGameObject", 10); + _guidWarningMsg = sConfigMgr->GetStringDefault("Respawn.WarningMessage", "There will be an unscheduled server restart at 03:00. The server will be available again shortly after."); + _alertRestartReason = sConfigMgr->GetStringDefault("Respawn.AlertRestartReason", "Urgent Maintenance"); + m_int_configs[CONFIG_RESPAWN_GUIDWARNING_FREQUENCY] = sConfigMgr->GetIntDefault("Respawn.WarningFrequency", 1800); ///- Read the "Data" directory from the config file std::string dataPath = sConfigMgr->GetStringDefault("DataDir", "./"); if (dataPath.empty() || (dataPath.at(dataPath.length()-1) != '/' && dataPath.at(dataPath.length()-1) != '\\')) @@ -1504,6 +1633,7 @@ void World::SetInitialWorldSettings() sObjectMgr->LoadPageTextLocales(); sObjectMgr->LoadGossipMenuItemsLocales(); sObjectMgr->LoadPointOfInterestLocales(); + sObjectMgr->LoadQuestGreetingsLocales(); sObjectMgr->SetDBCLocaleIndex(GetDefaultDbcLocale()); // Get once for all the locale index of DBC language (console/broadcasts) TC_LOG_INFO("server.loading", ">> Localization strings loaded in %u ms", GetMSTimeDiffToNow(oldMSTime)); @@ -1601,6 +1731,12 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Creature Base Stats..."); sObjectMgr->LoadCreatureClassLevelStats(); + TC_LOG_INFO("server.loading", "Loading Spawn Group Templates..."); + sObjectMgr->LoadSpawnGroupTemplates(); + + TC_LOG_INFO("server.loading", "Loading instance spawn groups..."); + sObjectMgr->LoadInstanceSpawnGroups(); + TC_LOG_INFO("server.loading", "Loading Creature Data..."); sObjectMgr->LoadCreatures(); @@ -1617,10 +1753,13 @@ void World::SetInitialWorldSettings() sObjectMgr->LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures() TC_LOG_INFO("server.loading", "Loading Gameobject Data..."); - sObjectMgr->LoadGameobjects(); + sObjectMgr->LoadGameObjects(); + + TC_LOG_INFO("server.loading", "Loading Spawn Group Data..."); + sObjectMgr->LoadSpawnGroups(); TC_LOG_INFO("server.loading", "Loading GameObject Addon Data..."); - sObjectMgr->LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameobjects() + sObjectMgr->LoadGameObjectAddons(); // must be after LoadGameObjectTemplate() and LoadGameObjects() TC_LOG_INFO("server.loading", "Loading GameObject Quest Items..."); sObjectMgr->LoadGameObjectQuestItems(); @@ -1646,11 +1785,15 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Quests Starters and Enders..."); sObjectMgr->LoadQuestStartersAndEnders(); // must be after quest load + TC_LOG_INFO("server.loading", "Loading Quests Greetings..."); + sObjectMgr->LoadQuestGreetings(); // must be loaded after creature_template, gameobject_template tables + TC_LOG_INFO("server.loading", "Loading Objects Pooling Data..."); sPoolMgr->LoadFromDB(); TC_LOG_INFO("server.loading", "Loading Game Event Data..."); // must be after loading pools fully - sGameEventMgr->LoadFromDB(); + sGameEventMgr->LoadHolidayDates(); // Must be after loading DBC + sGameEventMgr->LoadFromDB(); // Must be after loading holiday dates TC_LOG_INFO("server.loading", "Loading UNIT_NPC_FLAG_SPELLCLICK Data..."); // must be after LoadQuests sObjectMgr->LoadNPCSpellClickSpells(); @@ -1914,7 +2057,8 @@ void World::SetInitialWorldSettings() tm localTm; time_t gameTime = GameTime::GetGameTime(); localtime_r(&gameTime, &localTm); - mail_timer = ((((localTm.tm_hour + 20) % 24)* HOUR * IN_MILLISECONDS) / m_timers[WUPDATE_AUCTIONS].GetInterval()); + uint8 CleanOldMailsTime = getIntConfig(CONFIG_CLEAN_OLD_MAIL_TIME); + mail_timer = ((((localTm.tm_hour + (24 - CleanOldMailsTime)) % 24)* HOUR * IN_MILLISECONDS) / m_timers[WUPDATE_AUCTIONS].GetInterval()); //1440 mail_timer_expires = ((DAY * IN_MILLISECONDS) / (m_timers[WUPDATE_AUCTIONS].GetInterval())); TC_LOG_INFO("server.loading", "Mail timer set to: " UI64FMTD ", mail return is called every " UI64FMTD " minutes", uint64(mail_timer), uint64(mail_timer_expires)); @@ -2289,6 +2433,16 @@ void World::Update(uint32 diff) // update the instance reset times sInstanceSaveMgr->Update(); + // Check for shutdown warning + if (_guidWarn && !_guidAlert) + { + _warnDiff += diff; + if (GameTime::GetGameTime() >= _warnShutdownTime) + DoGuidWarningRestart(); + else if (_warnDiff > getIntConfig(CONFIG_RESPAWN_GUIDWARNING_FREQUENCY) * IN_MILLISECONDS) + SendGuidWarning(); + } + // And last, but not least handle the issued cli commands ProcessCliCommands(); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 5254b32bbdf..8d06620641b 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -202,7 +202,6 @@ enum WorldFloatConfigs CONFIG_ARENA_WIN_RATING_MODIFIER_2, CONFIG_ARENA_LOSE_RATING_MODIFIER, CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER, - CONFIG_RESPAWN_DYNAMICRADIUS, CONFIG_RESPAWN_DYNAMICRATE_CREATURE, CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT, FLOAT_CONFIG_VALUE_COUNT @@ -251,6 +250,9 @@ enum WorldIntConfigs CONFIG_DAILY_QUEST_RESET_TIME_HOUR, CONFIG_MAX_PRIMARY_TRADE_SKILL, CONFIG_MIN_PETITION_SIGNS, + CONFIG_MIN_QUEST_SCALED_XP_RATIO, + CONFIG_MIN_CREATURE_SCALED_XP_RATIO, + CONFIG_MIN_DISCOVERED_SCALED_XP_RATIO, CONFIG_GM_LOGIN_STATE, CONFIG_GM_VISIBLE_STATE, CONFIG_GM_ACCEPT_TICKETS, @@ -263,6 +265,7 @@ enum WorldIntConfigs CONFIG_FORCE_SHUTDOWN_THRESHOLD, CONFIG_GROUP_VISIBILITY, CONFIG_MAIL_DELIVERY_DELAY, + CONFIG_CLEAN_OLD_MAIL_TIME, CONFIG_UPTIME_UPDATE, CONFIG_SKILL_CHANCE_ORANGE, CONFIG_SKILL_CHANCE_YELLOW, @@ -379,13 +382,11 @@ enum WorldIntConfigs CONFIG_AUCTION_GETALL_DELAY, CONFIG_AUCTION_SEARCH_DELAY, CONFIG_TALENTS_INSPECTING, - CONFIG_RESPAWN_MINCELLCHECKMS, + CONFIG_RESPAWN_MINCHECKINTERVALMS, CONFIG_RESPAWN_DYNAMICMODE, CONFIG_RESPAWN_GUIDWARNLEVEL, CONFIG_RESPAWN_GUIDALERTLEVEL, CONFIG_RESPAWN_RESTARTQUIETTIME, - CONFIG_RESPAWN_ACTIVITYSCOPECREATURE, - CONFIG_RESPAWN_ACTIVITYSCOPEGAMEOBJECT, CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE, CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT, CONFIG_RESPAWN_GUIDWARNING_FREQUENCY, @@ -765,6 +766,10 @@ class TC_GAME_API World void ReloadRBAC(); void RemoveOldCorpses(); + void TriggerGuidWarning(); + void TriggerGuidAlert(); + bool IsGuidWarning() { return _guidWarn; } + bool IsGuidAlert() { return _guidAlert; } protected: void _UpdateGameTime(); @@ -859,7 +864,21 @@ class TC_GAME_API World AutobroadcastsWeightMap m_AutobroadcastsWeights; void ProcessQueryCallbacks(); + + void SendGuidWarning(); + void DoGuidWarningRestart(); + void DoGuidAlertRestart(); QueryCallbackProcessor _queryProcessor; + + std::string _guidWarningMsg; + std::string _alertRestartReason; + + std::mutex _guidAlertLock; + + bool _guidWarn; + bool _guidAlert; + uint32 _warnDiff; + time_t _warnShutdownTime; }; TC_GAME_API extern Realm realm; diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt index 8beb5faacde..3a241fcd95e 100644 --- a/src/server/scripts/CMakeLists.txt +++ b/src/server/scripts/CMakeLists.txt @@ -94,7 +94,7 @@ if (USE_SCRIPTPCH) set(PRIVATE_PCH_SOURCE ScriptPCH.cpp) if (MSVC) list(INSERT PRIVATE_SOURCES 0 ScriptPCH.cpp) - endif (MSVC) + endif (MSVC) endif () GroupSources(${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/server/scripts/Commands/cs_ban.cpp b/src/server/scripts/Commands/cs_ban.cpp index 5c390a34e11..137620123a6 100644 --- a/src/server/scripts/Commands/cs_ban.cpp +++ b/src/server/scripts/Commands/cs_ban.cpp @@ -587,8 +587,7 @@ public: static bool HandleBanListIPCommand(ChatHandler* handler, char const* args) { - PreparedStatement* stmt = nullptr; - stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS); LoginDatabase.Execute(stmt); char* filterStr = strtok((char*)args, " "); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 69f7ecb3f9a..a7ac9028bc8 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -972,7 +972,7 @@ public: Map* map = handler->GetSession()->GetPlayer()->GetMap(); - if (!v->Create(map->GenerateLowGuid<HighGuid::Vehicle>(), map, handler->GetSession()->GetPlayer()->GetPhaseMask(), entry, x, y, z, o, nullptr, id)) + if (!v->Create(map->GenerateLowGuid<HighGuid::Vehicle>(), map, handler->GetSession()->GetPlayer()->GetPhaseMask(), entry, { x, y, z, o }, nullptr, id)) { delete v; return false; diff --git a/src/server/scripts/Commands/cs_disable.cpp b/src/server/scripts/Commands/cs_disable.cpp index bc80d3d6fd0..b8d1116574d 100644 --- a/src/server/scripts/Commands/cs_disable.cpp +++ b/src/server/scripts/Commands/cs_disable.cpp @@ -187,8 +187,7 @@ public: break; } - PreparedStatement* stmt = nullptr; - stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_DISABLES); + PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_DISABLES); stmt->setUInt32(0, entry); stmt->setUInt8(1, disableType); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -313,8 +312,7 @@ public: break; } - PreparedStatement* stmt = nullptr; - stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_DISABLES); + PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_DISABLES); stmt->setUInt32(0, entry); stmt->setUInt8(1, disableType); PreparedQueryResult result = WorldDatabase.Query(stmt); diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 6a27060c2ed..7d0563b2acc 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -138,8 +138,6 @@ public: float o = fields[3].GetFloat(); uint32 mapId = fields[4].GetUInt16(); - Transport* transport = nullptr; - if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId)) { handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId); @@ -157,11 +155,7 @@ public: else player->SaveRecallPosition(); - if (player->TeleportTo(mapId, x, y, z, o)) - { - if (transport) - transport->AddPassenger(player); - } + player->TeleportTo(mapId, x, y, z, o); return true; } @@ -272,28 +266,18 @@ public: if (!guidLow) return false; - float x, y, z, o; - uint32 mapId; - // by DB guid - if (GameObjectData const* goData = sObjectMgr->GetGOData(guidLow)) - { - x = goData->posX; - y = goData->posY; - z = goData->posZ; - o = goData->orientation; - mapId = goData->mapid; - } - else + GameObjectData const* goData = sObjectMgr->GetGameObjectData(guidLow); + if (!goData) { handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND); handler->SetSentErrorMessage(true); return false; } - if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId)) + if (!MapManager::IsValidMapCoord(goData->spawnPoint) || sObjectMgr->IsTransportMap(goData->spawnPoint.GetMapId())) { - handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId); + handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, goData->spawnPoint.GetPositionX(), goData->spawnPoint.GetPositionY(), goData->spawnPoint.GetMapId()); handler->SetSentErrorMessage(true); return false; } @@ -308,7 +292,7 @@ public: else player->SaveRecallPosition(); - player->TeleportTo(mapId, x, y, z, o); + player->TeleportTo(goData->spawnPoint); return true; } diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index a315355ff44..fd4baf69037 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -38,6 +38,10 @@ EndScriptData */ #include "RBAC.h" #include "WorldSession.h" +// definitions are over in cs_npc.cpp +bool HandleNpcSpawnGroup(ChatHandler* handler, char const* args); +bool HandleNpcDespawnGroup(ChatHandler* handler, char const* args); + class gobject_commandscript : public CommandScript { public: @@ -57,15 +61,17 @@ public: }; static std::vector<ChatCommand> gobjectCommandTable = { - { "activate", rbac::RBAC_PERM_COMMAND_GOBJECT_ACTIVATE, false, &HandleGameObjectActivateCommand, "" }, - { "delete", rbac::RBAC_PERM_COMMAND_GOBJECT_DELETE, false, &HandleGameObjectDeleteCommand, "" }, - { "info", rbac::RBAC_PERM_COMMAND_GOBJECT_INFO, false, &HandleGameObjectInfoCommand, "" }, - { "move", rbac::RBAC_PERM_COMMAND_GOBJECT_MOVE, false, &HandleGameObjectMoveCommand, "" }, - { "near", rbac::RBAC_PERM_COMMAND_GOBJECT_NEAR, false, &HandleGameObjectNearCommand, "" }, - { "target", rbac::RBAC_PERM_COMMAND_GOBJECT_TARGET, false, &HandleGameObjectTargetCommand, "" }, - { "turn", rbac::RBAC_PERM_COMMAND_GOBJECT_TURN, false, &HandleGameObjectTurnCommand, "" }, - { "add", rbac::RBAC_PERM_COMMAND_GOBJECT_ADD, false, nullptr, "", gobjectAddCommandTable }, - { "set", rbac::RBAC_PERM_COMMAND_GOBJECT_SET, false, nullptr, "", gobjectSetCommandTable }, + { "activate", rbac::RBAC_PERM_COMMAND_GOBJECT_ACTIVATE, false, &HandleGameObjectActivateCommand, "" }, + { "delete", rbac::RBAC_PERM_COMMAND_GOBJECT_DELETE, false, &HandleGameObjectDeleteCommand, "" }, + { "info", rbac::RBAC_PERM_COMMAND_GOBJECT_INFO, false, &HandleGameObjectInfoCommand, "" }, + { "move", rbac::RBAC_PERM_COMMAND_GOBJECT_MOVE, false, &HandleGameObjectMoveCommand, "" }, + { "near", rbac::RBAC_PERM_COMMAND_GOBJECT_NEAR, false, &HandleGameObjectNearCommand, "" }, + { "target", rbac::RBAC_PERM_COMMAND_GOBJECT_TARGET, false, &HandleGameObjectTargetCommand, "" }, + { "turn", rbac::RBAC_PERM_COMMAND_GOBJECT_TURN, false, &HandleGameObjectTurnCommand, "" }, + { "spawngroup", rbac::RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP, false, &HandleNpcSpawnGroup, "" }, + { "despawngroup", rbac::RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP, false, &HandleNpcDespawnGroup,""}, + { "add", rbac::RBAC_PERM_COMMAND_GOBJECT_ADD, false, nullptr, "", gobjectAddCommandTable }, + { "set", rbac::RBAC_PERM_COMMAND_GOBJECT_SET, false, nullptr, "", gobjectSetCommandTable }, }; static std::vector<ChatCommand> commandTable = { @@ -169,14 +175,14 @@ public: object = new GameObject(); // this will generate a new guid if the object is in an instance - if (!object->LoadGameObjectFromDB(guidLow, map)) + if (!object->LoadFromDB(guidLow, map, true)) { delete object; return false; } /// @todo is it really necessary to add both the real and DB table guid here ? - sObjectMgr->AddGameobjectToGrid(guidLow, sObjectMgr->GetGOData(guidLow)); + sObjectMgr->AddGameobjectToGrid(guidLow, sObjectMgr->GetGameObjectData(guidLow)); handler->PSendSysMessage(LANG_GAMEOBJECT_ADD, objectId, objectInfo->name.c_str(), guidLow, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); return true; @@ -345,6 +351,9 @@ public: if (!guidLow) return false; + Player const* const player = handler->GetSession()->GetPlayer(); + // force respawn to make sure we find something + player->GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, guidLow, true); GameObject* object = handler->GetObjectFromPlayerMapByDbGuid(guidLow); if (!object) { @@ -356,7 +365,7 @@ public: ObjectGuid ownerGuid = object->GetOwnerGUID(); if (ownerGuid) { - Unit* owner = ObjectAccessor::GetUnit(*handler->GetSession()->GetPlayer(), ownerGuid); + Unit* owner = ObjectAccessor::GetUnit(*player, ownerGuid); if (!owner || !ownerGuid.IsPlayer()) { handler->PSendSysMessage(LANG_COMMAND_DELOBJREFERCREATURE, ownerGuid.GetCounter(), guidLow); @@ -430,7 +439,7 @@ public: object->Delete(); object = new GameObject(); - if (!object->LoadGameObjectFromDB(guidLow, map)) + if (!object->LoadFromDB(guidLow, map, true)) { delete object; return false; @@ -499,7 +508,7 @@ public: object->Delete(); object = new GameObject(); - if (!object->LoadGameObjectFromDB(guidLow, map)) + if (!object->LoadFromDB(guidLow, map, true)) { delete object; return false; @@ -578,7 +587,7 @@ public: if (!gameObjectInfo) continue; - handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gameObjectInfo->name.c_str(), x, y, z, mapId); + handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gameObjectInfo->name.c_str(), x, y, z, mapId, "", ""); ++count; } while (result->NextRow()); @@ -611,7 +620,7 @@ public: if (!cValue) return false; ObjectGuid::LowType guidLow = atoul(cValue); - GameObjectData const* data = sObjectMgr->GetGOData(guidLow); + GameObjectData const* data = sObjectMgr->GetGameObjectData(guidLow); if (!data) return false; entry = data->id; @@ -623,9 +632,16 @@ public: GameObjectTemplate const* gameObjectInfo = sObjectMgr->GetGameObjectTemplate(entry); + GameObject* thisGO = nullptr; + if (!gameObjectInfo) return false; + if (*args && handler->GetSession()->GetPlayer()) + thisGO = handler->GetSession()->GetPlayer()->FindNearestGameObject(entry, 30); + else if (handler->getSelectedObject() && handler->getSelectedObject()->GetTypeId() == TYPEID_GAMEOBJECT) + thisGO = handler->getSelectedObject()->ToGameObject(); + type = gameObjectInfo->type; displayId = gameObjectInfo->displayId; name = gameObjectInfo->name; @@ -634,10 +650,32 @@ public: else if (type == GAMEOBJECT_TYPE_FISHINGHOLE) lootId = gameObjectInfo->fishinghole.lootId; + // If we have a real object, send some info about it + if (thisGO) + { + handler->PSendSysMessage(LANG_SPAWNINFO_GUIDINFO, thisGO->GetGUID().ToString().c_str()); + handler->PSendSysMessage(LANG_SPAWNINFO_SPAWNID_LOCATION, thisGO->GetSpawnId(), thisGO->GetPositionX(), thisGO->GetPositionY(), thisGO->GetPositionZ()); + if (Player* player = handler->GetSession()->GetPlayer()) + { + Position playerPos = player->GetPosition(); + float dist = thisGO->GetExactDist(&playerPos); + handler->PSendSysMessage(LANG_SPAWNINFO_DISTANCEFROMPLAYER, dist); + } + } handler->PSendSysMessage(LANG_GOINFO_ENTRY, entry); handler->PSendSysMessage(LANG_GOINFO_TYPE, type); handler->PSendSysMessage(LANG_GOINFO_LOOTID, lootId); handler->PSendSysMessage(LANG_GOINFO_DISPLAYID, displayId); + if (WorldObject* object = handler->getSelectedObject()) + { + if (object->ToGameObject() && object->ToGameObject()->GetGameObjectData() && object->ToGameObject()->GetGameObjectData()->spawnGroupData->groupId) + { + SpawnGroupTemplateData const* groupData = object->ToGameObject()->GetGameObjectData()->spawnGroupData; + handler->PSendSysMessage(LANG_SPAWNINFO_GROUP_ID, groupData->name.c_str(), groupData->groupId, groupData->flags, object->GetMap()->IsSpawnGroupActive(groupData->groupId)); + } + if (object->ToGameObject()) + handler->PSendSysMessage(LANG_SPAWNINFO_COMPATIBILITY_MODE, object->ToGameObject()->GetRespawnCompatibilityMode()); + } handler->PSendSysMessage(LANG_GOINFO_NAME, name.c_str()); handler->PSendSysMessage(LANG_GOINFO_SIZE, gameObjectInfo->size); diff --git a/src/server/scripts/Commands/cs_instance.cpp b/src/server/scripts/Commands/cs_instance.cpp index 682976ba897..6855fe21efc 100644 --- a/src/server/scripts/Commands/cs_instance.cpp +++ b/src/server/scripts/Commands/cs_instance.cpp @@ -256,8 +256,7 @@ public: } map->GetInstanceScript()->SetBossState(encounterId, EncounterState(state)); - std::string stateName = InstanceScript::GetBossStateName(state); - handler->PSendSysMessage(LANG_COMMAND_INST_SET_BOSS_STATE, encounterId, state, stateName); + handler->PSendSysMessage(LANG_COMMAND_INST_SET_BOSS_STATE, encounterId, state, InstanceScript::GetBossStateName(state)); return true; } @@ -321,8 +320,7 @@ public: } uint32 state = map->GetInstanceScript()->GetBossState(encounterId); - std::string stateName = InstanceScript::GetBossStateName(state); - handler->PSendSysMessage(LANG_COMMAND_INST_GET_BOSS_STATE, encounterId, state, stateName); + handler->PSendSysMessage(LANG_COMMAND_INST_GET_BOSS_STATE, encounterId, state, InstanceScript::GetBossStateName(state)); return true; } }; diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp index 8b502f60e11..304b7578177 100644 --- a/src/server/scripts/Commands/cs_list.cpp +++ b/src/server/scripts/Commands/cs_list.cpp @@ -25,9 +25,12 @@ EndScriptData */ #include "ScriptMgr.h" #include "CharacterCache.h" #include "Chat.h" +#include "Creature.h" #include "DatabaseEnv.h" #include "DBCStores.h" +#include "GameObject.h" #include "Language.h" +#include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" @@ -45,11 +48,13 @@ public: { static std::vector<ChatCommand> listCommandTable = { - { "creature", rbac::RBAC_PERM_COMMAND_LIST_CREATURE, true, &HandleListCreatureCommand, "" }, - { "item", rbac::RBAC_PERM_COMMAND_LIST_ITEM, true, &HandleListItemCommand, "" }, - { "object", rbac::RBAC_PERM_COMMAND_LIST_OBJECT, true, &HandleListObjectCommand, "" }, - { "auras", rbac::RBAC_PERM_COMMAND_LIST_AURAS, false, &HandleListAurasCommand, "" }, - { "mail", rbac::RBAC_PERM_COMMAND_LIST_MAIL, true, &HandleListMailCommand, "" }, + { "creature", rbac::RBAC_PERM_COMMAND_LIST_CREATURE, true, &HandleListCreatureCommand, "" }, + { "item", rbac::RBAC_PERM_COMMAND_LIST_ITEM, true, &HandleListItemCommand, "" }, + { "object", rbac::RBAC_PERM_COMMAND_LIST_OBJECT, true, &HandleListObjectCommand, "" }, + { "auras", rbac::RBAC_PERM_COMMAND_LIST_AURAS, false, &HandleListAurasCommand, "" }, + { "mail", rbac::RBAC_PERM_COMMAND_LIST_MAIL, true, &HandleListMailCommand, "" }, + { "spawnpoints", rbac::RBAC_PERM_COMMAND_LIST_SPAWNPOINTS, false, &HandleListSpawnPointsCommand, "" }, + { "respawns", rbac::RBAC_PERM_COMMAND_LIST_RESPAWNS, false, &HandleListRespawnsCommand, "" }, }; static std::vector<ChatCommand> commandTable = { @@ -117,11 +122,40 @@ public: float y = fields[2].GetFloat(); float z = fields[3].GetFloat(); uint16 mapId = fields[4].GetUInt16(); + bool liveFound = false; + // Get map (only support base map from console) + Map* thisMap; if (handler->GetSession()) - handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId); + thisMap = handler->GetSession()->GetPlayer()->GetMap(); else - handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId); + thisMap = sMapMgr->FindBaseNonInstanceMap(mapId); + + // If map found, try to find active version of this creature + if (thisMap) + { + auto const creBounds = thisMap->GetCreatureBySpawnIdStore().equal_range(guid); + if (creBounds.first != creBounds.second) + { + for (std::unordered_multimap<uint32, Creature*>::const_iterator itr = creBounds.first; itr != creBounds.second;) + { + if (handler->GetSession()) + handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " "); + else + handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " "); + ++itr; + } + liveFound = true; + } + } + + if (!liveFound) + { + if (handler->GetSession()) + handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId, "", ""); + else + handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId, "", ""); + } } while (result->NextRow()); } @@ -407,11 +441,40 @@ public: float z = fields[3].GetFloat(); uint16 mapId = fields[4].GetUInt16(); uint32 entry = fields[5].GetUInt32(); + bool liveFound = false; + // Get map (only support base map from console) + Map* thisMap; if (handler->GetSession()) - handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId); + thisMap = handler->GetSession()->GetPlayer()->GetMap(); else - handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId); + thisMap = sMapMgr->FindBaseNonInstanceMap(mapId); + + // If map found, try to find active version of this object + if (thisMap) + { + auto const goBounds = thisMap->GetGameObjectBySpawnIdStore().equal_range(guid); + if (goBounds.first != goBounds.second) + { + for (std::unordered_multimap<uint32, GameObject*>::const_iterator itr = goBounds.first; itr != goBounds.second;) + { + if (handler->GetSession()) + handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->isSpawned() ? "*" : " "); + else + handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->isSpawned() ? "*" : " "); + ++itr; + } + liveFound = true; + } + } + + if (!liveFound) + { + if (handler->GetSession()) + handler->PSendSysMessage(LANG_GO_LIST_CHAT, guid, entry, guid, gInfo->name.c_str(), x, y, z, mapId, "", ""); + else + handler->PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name.c_str(), x, y, z, mapId, "", ""); + } } while (result->NextRow()); } @@ -581,8 +644,104 @@ public: handler->PSendSysMessage(LANG_LIST_MAIL_NOT_FOUND); return true; } + + static bool HandleListSpawnPointsCommand(ChatHandler* handler, char const* /*args*/) + { + Player const* player = handler->GetSession()->GetPlayer(); + Map const* map = player->GetMap(); + uint32 const mapId = map->GetId(); + bool const showAll = map->IsBattlegroundOrArena() || map->IsDungeon(); + handler->PSendSysMessage("Listing all spawn points in map %u (%s)%s:", mapId, map->GetMapName(), showAll ? "" : " within 5000yd"); + for (auto const& pair : sObjectMgr->GetAllCreatureData()) + { + SpawnData const& data = pair.second; + if (data.spawnPoint.GetMapId() != mapId) + continue; + CreatureTemplate const* cTemp = sObjectMgr->GetCreatureTemplate(data.id); + if (!cTemp) + continue; + if (showAll || data.spawnPoint.IsInDist2d(player, 5000.0)) + handler->PSendSysMessage("Type: %u | SpawnId: %u | Entry: %u (%s) | X: %.3f | Y: %.3f | Z: %.3f", uint32(data.type), data.spawnId, data.id, cTemp->Name.c_str(), data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY(), data.spawnPoint.GetPositionZ()); + } + for (auto const& pair : sObjectMgr->GetAllGameObjectData()) + { + SpawnData const& data = pair.second; + if (data.spawnPoint.GetMapId() != mapId) + continue; + GameObjectTemplate const* goTemp = sObjectMgr->GetGameObjectTemplate(data.id); + if (!goTemp) + continue; + if (showAll || data.spawnPoint.IsInDist2d(player, 5000.0)) + handler->PSendSysMessage("Type: %u | SpawnId: %u | Entry: %u (%s) | X: %.3f | Y: %.3f | Z: %.3f", uint32(data.type), data.spawnId, data.id, goTemp->name.c_str(), data.spawnPoint.GetPositionX(), data.spawnPoint.GetPositionY(), data.spawnPoint.GetPositionZ()); + } + return true; + } + + static char const* GetZoneName(uint32 zoneId, LocaleConstant locale) + { + AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(zoneId); + return zoneEntry ? zoneEntry->area_name[locale] : "<unknown zone>"; + } + static bool HandleListRespawnsCommand(ChatHandler* handler, char const* args) + { + Player const* player = handler->GetSession()->GetPlayer(); + Map const* map = player->GetMap(); + uint32 range = 0; + if (*args) + range = atoi((char*)args); + + RespawnVector respawns; + LocaleConstant locale = handler->GetSession()->GetSessionDbcLocale(); + char const* stringOverdue = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_OVERDUE, locale); + char const* stringCreature = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_CREATURES, locale); + char const* stringGameobject = sObjectMgr->GetTrinityString(LANG_LIST_RESPAWNS_GAMEOBJECTS, locale); + + uint32 zoneId = player->GetZoneId(); + if (range) + handler->PSendSysMessage(LANG_LIST_RESPAWNS_RANGE, stringCreature, range); + else + handler->PSendSysMessage(LANG_LIST_RESPAWNS_ZONE, stringCreature, GetZoneName(zoneId, handler->GetSessionDbcLocale()), zoneId); + handler->PSendSysMessage(LANG_LIST_RESPAWNS_LISTHEADER); + map->GetRespawnInfo(respawns, SPAWN_TYPEMASK_CREATURE, range ? 0 : zoneId); + for (RespawnInfo* ri : respawns) + { + CreatureData const* data = sObjectMgr->GetCreatureData(ri->spawnId); + if (!data) + continue; + if (range && !player->IsInDist(data->spawnPoint, range)) + continue; + uint32 gridY = ri->gridId / MAX_NUMBER_OF_GRIDS; + uint32 gridX = ri->gridId % MAX_NUMBER_OF_GRIDS; + + std::string respawnTime = ri->respawnTime > time(NULL) ? secsToTimeString(uint64(ri->respawnTime - time(NULL)), true) : stringOverdue; + handler->PSendSysMessage("%u | %u | [%02u,%02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(ri->zoneId, handler->GetSessionDbcLocale()), ri->zoneId, map->IsSpawnGroupActive(data->spawnGroupData->groupId) ? respawnTime.c_str() : "inactive"); + } + + respawns.clear(); + if (range) + handler->PSendSysMessage(LANG_LIST_RESPAWNS_RANGE, stringGameobject, range); + else + handler->PSendSysMessage(LANG_LIST_RESPAWNS_ZONE, stringGameobject, GetZoneName(zoneId, handler->GetSessionDbcLocale()), zoneId); + handler->PSendSysMessage(LANG_LIST_RESPAWNS_LISTHEADER); + map->GetRespawnInfo(respawns, SPAWN_TYPEMASK_GAMEOBJECT, range ? 0 : zoneId); + for (RespawnInfo* ri : respawns) + { + GameObjectData const* data = sObjectMgr->GetGameObjectData(ri->spawnId); + if (!data) + continue; + if (range && !player->IsInDist(data->spawnPoint, range)) + continue; + uint32 gridY = ri->gridId / MAX_NUMBER_OF_GRIDS; + uint32 gridX = ri->gridId % MAX_NUMBER_OF_GRIDS; + + std::string respawnTime = ri->respawnTime > time(NULL) ? secsToTimeString(uint64(ri->respawnTime - time(NULL)), true) : stringOverdue; + handler->PSendSysMessage("%u | %u | [% 02u, % 02u] | %s (%u) | %s", ri->spawnId, ri->entry, gridX, gridY, GetZoneName(ri->zoneId, handler->GetSessionDbcLocale()), ri->zoneId, map->IsSpawnGroupActive(data->spawnGroupData->groupId) ? respawnTime.c_str() : "inactive"); + } + return true; + } }; + void AddSC_list_commandscript() { new list_commandscript(); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 29f1a0763fb..0860585dc65 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -544,10 +544,11 @@ public: } else if (map->Instanceable()) { - Group* targetGroup = target->GetGroup(); Map* targetMap = target->GetMap(); - Player* targetGroupLeader = ObjectAccessor::GetPlayer(map, targetGroup->GetLeaderGUID()); - + Player* targetGroupLeader = nullptr; + if (Group* targetGroup = target->GetGroup()) + targetGroupLeader = ObjectAccessor::GetPlayer(map, targetGroup->GetLeaderGUID()); + // check if far teleport is allowed if (!targetGroupLeader || (targetGroupLeader->GetMapId() != map->GetId()) || (targetGroupLeader->GetInstanceId() != map->GetInstanceId())) if ((targetMap->GetId() != map->GetId()) || (targetMap->GetInstanceId() != map->GetInstanceId())) @@ -1910,10 +1911,22 @@ public: return true; } + // First handle any creatures that still have a corpse around Trinity::RespawnDo u_do; Trinity::WorldObjectWorker<Trinity::RespawnDo> worker(player, u_do); Cell::VisitGridObjects(player, worker, player->GetGridActivationRange()); + // Now handle any that had despawned, but had respawn time logged. + RespawnVector data; + player->GetMap()->GetRespawnInfo(data, SPAWN_TYPEMASK_ALL, 0); + if (!data.empty()) + { + uint32 const gridId = Trinity::ComputeGridCoord(player->GetPositionX(), player->GetPositionY()).GetId(); + for (RespawnInfo* info : data) + if (info->gridId == gridId) + player->GetMap()->RemoveRespawnTime(info, true); + } + return true; } @@ -1977,15 +1990,15 @@ public: stmt->setInt64(0, muteTime); } - stmt->setString(1, muteReasonStr.c_str()); - stmt->setString(2, muteBy.c_str()); + stmt->setString(1, muteReasonStr); + stmt->setString(2, muteBy); stmt->setUInt32(3, accountId); LoginDatabase.Execute(stmt); stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_MUTE); stmt->setUInt32(0, accountId); stmt->setUInt32(1, notSpeakTime); - stmt->setString(2, muteBy.c_str()); - stmt->setString(3, muteReasonStr.c_str()); + stmt->setString(2, muteBy); + stmt->setString(3, muteReasonStr); LoginDatabase.Execute(stmt); std::string nameLink = handler->playerLink(targetName); @@ -2122,9 +2135,9 @@ public: float x, y, z; motionMaster->GetDestination(x, y, z); - for (uint8 i = 0; i < MAX_MOTION_SLOT; ++i) + for (uint8 itr = 0; itr < MAX_MOTION_SLOT; ++itr) { - MovementGenerator* movementGenerator = motionMaster->GetMotionSlot(i); + MovementGenerator* movementGenerator = motionMaster->GetMotionSlot(MovementSlot(itr)); if (!movementGenerator) { handler->SendSysMessage("Empty"); diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index dfb9ddb66a6..54c48cd1205 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -176,6 +176,86 @@ EnumName<CreatureFlagsExtra> const flagsExtra[FLAGS_EXTRA_COUNT] = CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK) }; +bool HandleNpcSpawnGroup(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + bool ignoreRespawn = false; + bool force = false; + uint32 groupId = 0; + + // Decode arguments + char* arg = strtok((char*)args, " "); + while (arg) + { + std::string thisArg = arg; + std::transform(thisArg.begin(), thisArg.end(), thisArg.begin(), ::tolower); + if (thisArg == "ignorerespawn") + ignoreRespawn = true; + else if (thisArg == "force") + force = true; + else if (thisArg.empty() || !(std::count_if(thisArg.begin(), thisArg.end(), ::isdigit) == (int)thisArg.size())) + return false; + else + groupId = atoi(thisArg.c_str()); + + arg = strtok(NULL, " "); + } + + Player* player = handler->GetSession()->GetPlayer(); + + std::vector <WorldObject*> creatureList; + if (!player->GetMap()->SpawnGroupSpawn(groupId, ignoreRespawn, force, &creatureList)) + { + handler->PSendSysMessage(LANG_SPAWNGROUP_BADGROUP, groupId); + handler->SetSentErrorMessage(true); + return false; + } + + handler->PSendSysMessage(LANG_SPAWNGROUP_SPAWNCOUNT, creatureList.size()); + for (WorldObject* obj : creatureList) + handler->PSendSysMessage("%s (%s)", obj->GetName(), obj->GetGUID().ToString().c_str()); + + return true; +} + +bool HandleNpcDespawnGroup(ChatHandler* handler, char const* args) +{ + if (!*args) + return false; + + bool deleteRespawnTimes = false; + uint32 groupId = 0; + + // Decode arguments + char* arg = strtok((char*)args, " "); + while (arg) + { + std::string thisArg = arg; + std::transform(thisArg.begin(), thisArg.end(), thisArg.begin(), ::tolower); + if (thisArg == "removerespawntime") + deleteRespawnTimes = true; + else if (thisArg.empty() || !(std::count_if(thisArg.begin(), thisArg.end(), ::isdigit) == (int)thisArg.size())) + return false; + else + groupId = atoi(thisArg.c_str()); + + arg = strtok(nullptr, " "); + } + + Player* player = handler->GetSession()->GetPlayer(); + + if (!player->GetMap()->SpawnGroupDespawn(groupId, deleteRespawnTimes)) + { + handler->PSendSysMessage(LANG_SPAWNGROUP_BADGROUP, groupId); + handler->SetSentErrorMessage(true); + return false; + } + + return true; +} + class npc_commandscript : public CommandScript { public: @@ -219,21 +299,23 @@ public: }; static std::vector<ChatCommand> npcCommandTable = { - { "info", rbac::RBAC_PERM_COMMAND_NPC_INFO, false, &HandleNpcInfoCommand, "" }, - { "near", rbac::RBAC_PERM_COMMAND_NPC_NEAR, false, &HandleNpcNearCommand, "" }, - { "move", rbac::RBAC_PERM_COMMAND_NPC_MOVE, false, &HandleNpcMoveCommand, "" }, - { "playemote", rbac::RBAC_PERM_COMMAND_NPC_PLAYEMOTE, false, &HandleNpcPlayEmoteCommand, "" }, - { "say", rbac::RBAC_PERM_COMMAND_NPC_SAY, false, &HandleNpcSayCommand, "" }, - { "textemote", rbac::RBAC_PERM_COMMAND_NPC_TEXTEMOTE, false, &HandleNpcTextEmoteCommand, "" }, - { "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" }, - { "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" }, - { "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" }, - { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable }, - { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable }, - { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable }, - { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable }, - { "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" }, - { "showloot", rbac::RBAC_PERM_COMMAND_NPC_SHOWLOOT, false, &HandleNpcShowLootCommand, "" }, + { "info", rbac::RBAC_PERM_COMMAND_NPC_INFO, false, &HandleNpcInfoCommand, "" }, + { "near", rbac::RBAC_PERM_COMMAND_NPC_NEAR, false, &HandleNpcNearCommand, "" }, + { "move", rbac::RBAC_PERM_COMMAND_NPC_MOVE, false, &HandleNpcMoveCommand, "" }, + { "playemote", rbac::RBAC_PERM_COMMAND_NPC_PLAYEMOTE, false, &HandleNpcPlayEmoteCommand, "" }, + { "say", rbac::RBAC_PERM_COMMAND_NPC_SAY, false, &HandleNpcSayCommand, "" }, + { "textemote", rbac::RBAC_PERM_COMMAND_NPC_TEXTEMOTE, false, &HandleNpcTextEmoteCommand, "" }, + { "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" }, + { "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" }, + { "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" }, + { "spawngroup", rbac::RBAC_PERM_COMMAND_NPC_SPAWNGROUP, false, &HandleNpcSpawnGroup, "" }, + { "despawngroup", rbac::RBAC_PERM_COMMAND_NPC_DESPAWNGROUP, false, &HandleNpcDespawnGroup, "" }, + { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable }, + { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable }, + { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable }, + { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable }, + { "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" }, + { "showloot", rbac::RBAC_PERM_COMMAND_NPC_SHOWLOOT, false, &HandleNpcShowLootCommand, "" }, }; static std::vector<ChatCommand> commandTable = { @@ -257,22 +339,16 @@ public: return false; Player* chr = handler->GetSession()->GetPlayer(); - float x = chr->GetPositionX(); - float y = chr->GetPositionY(); - float z = chr->GetPositionZ(); - float o = chr->GetOrientation(); Map* map = chr->GetMap(); if (Transport* trans = chr->GetTransport()) { ObjectGuid::LowType guid = map->GenerateLowGuid<HighGuid::Unit>(); CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid); + data.spawnId = guid; data.id = id; data.phaseMask = chr->GetPhaseMaskForSpawn(); - data.posX = chr->GetTransOffsetX(); - data.posY = chr->GetTransOffsetY(); - data.posZ = chr->GetTransOffsetZ(); - data.orientation = chr->GetTransOffsetO(); + data.spawnPoint.Relocate(chr->GetTransOffsetX(), chr->GetTransOffsetY(), chr->GetTransOffsetZ(), chr->GetTransOffsetO()); Creature* creature = trans->CreateNPCPassenger(guid, &data); @@ -283,7 +359,7 @@ public: } Creature* creature = new Creature(); - if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o)) + if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, *chr)) { delete creature; return false; @@ -298,7 +374,7 @@ public: creature->CleanupsBeforeDelete(); delete creature; creature = new Creature(); - if (!creature->LoadCreatureFromDB(db_guid, map)) + if (!creature->LoadFromDB(db_guid, map, true, true)) { delete creature; return false; @@ -473,7 +549,7 @@ public: static bool HandleNpcDeleteCommand(ChatHandler* handler, char const* args) { - Creature* unit = nullptr; + Creature* creature = nullptr; if (*args) { @@ -485,22 +561,30 @@ public: ObjectGuid::LowType lowguid = atoul(cId); if (!lowguid) return false; - unit = handler->GetCreatureFromPlayerMapByDbGuid(lowguid); + // force respawn to make sure we find something + handler->GetSession()->GetPlayer()->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, lowguid, true); + // then try to find it + creature = handler->GetCreatureFromPlayerMapByDbGuid(lowguid); } else - unit = handler->getSelectedCreature(); + creature = handler->getSelectedCreature(); - if (!unit || unit->IsPet() || unit->IsTotem()) + if (!creature || creature->IsPet() || creature->IsTotem()) { handler->SendSysMessage(LANG_SELECT_CREATURE); handler->SetSentErrorMessage(true); return false; } - // Delete the creature - unit->CombatStop(); - unit->DeleteFromDB(); - unit->AddObjectToRemoveList(); + if (TempSummon* summon = creature->ToTempSummon()) + summon->UnSummon(); + else + { + // Delete the creature + creature->CombatStop(); + creature->DeleteFromDB(); + creature->AddObjectToRemoveList(); + } handler->SendSysMessage(LANG_COMMAND_DELCREATMESSAGE); @@ -690,13 +774,20 @@ public: uint32 nativeid = target->GetNativeDisplayId(); uint32 Entry = target->GetEntry(); - int64 curRespawnDelay = target->GetRespawnTimeEx()-time(nullptr); + int64 curRespawnDelay = target->GetRespawnCompatibilityMode() ? target->GetRespawnTimeEx() - time(nullptr) : target->GetMap()->GetCreatureRespawnTime(target->GetSpawnId()) - time(nullptr); + if (curRespawnDelay < 0) curRespawnDelay = 0; std::string curRespawnDelayStr = secsToTimeString(uint64(curRespawnDelay), true); std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(), true); handler->PSendSysMessage(LANG_NPCINFO_CHAR, target->GetSpawnId(), target->GetGUID().GetCounter(), faction, npcflags, Entry, displayid, nativeid); + if (target->GetCreatureData() && target->GetCreatureData()->spawnGroupData->groupId) + { + SpawnGroupTemplateData const* const groupData = target->GetCreatureData()->spawnGroupData; + handler->PSendSysMessage(LANG_SPAWNINFO_GROUP_ID, groupData->name.c_str(), groupData->groupId, groupData->flags, target->GetMap()->IsSpawnGroupActive(groupData->groupId)); + } + handler->PSendSysMessage(LANG_SPAWNINFO_COMPATIBILITY_MODE, target->GetRespawnCompatibilityMode()); handler->PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel()); handler->PSendSysMessage(LANG_NPCINFO_EQUIPMENT, target->GetCurrentEquipmentId(), target->GetOriginalEquipmentId()); handler->PSendSysMessage(LANG_NPCINFO_HEALTH, target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth()); @@ -766,7 +857,7 @@ public: if (!creatureTemplate) continue; - handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, creatureTemplate->Name.c_str(), x, y, z, mapId); + handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, creatureTemplate->Name.c_str(), x, y, z, mapId, "", ""); ++count; } @@ -784,8 +875,13 @@ public: ObjectGuid::LowType lowguid = 0; Creature* creature = handler->getSelectedCreature(); + Player const* player = handler->GetSession()->GetPlayer(); + if (!player) + return false; - if (!creature) + if (creature) + lowguid = creature->GetSpawnId(); + else { // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r char* cId = handler->extractKeyFromLink((char*)args, "Hcreature"); @@ -793,63 +889,45 @@ public: return false; lowguid = atoul(cId); - - // Attempting creature load from DB data - CreatureData const* data = sObjectMgr->GetCreatureData(lowguid); - if (!data) - { - handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); - handler->SetSentErrorMessage(true); - return false; - } - - uint32 map_id = data->mapid; - - if (handler->GetSession()->GetPlayer()->GetMapId() != map_id) - { - handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); - handler->SetSentErrorMessage(true); - return false; - } } - else + // Attempting creature load from DB data + CreatureData const* data = sObjectMgr->GetCreatureData(lowguid); + if (!data) { - lowguid = creature->GetSpawnId(); + handler->PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid); + handler->SetSentErrorMessage(true); + return false; } - float x = handler->GetSession()->GetPlayer()->GetPositionX(); - float y = handler->GetSession()->GetPlayer()->GetPositionY(); - float z = handler->GetSession()->GetPlayer()->GetPositionZ(); - float o = handler->GetSession()->GetPlayer()->GetOrientation(); - - if (creature) + if (player->GetMapId() != data->spawnPoint.GetMapId()) { - if (CreatureData const* data = sObjectMgr->GetCreatureData(creature->GetSpawnId())) - { - const_cast<CreatureData*>(data)->posX = x; - const_cast<CreatureData*>(data)->posY = y; - const_cast<CreatureData*>(data)->posZ = z; - const_cast<CreatureData*>(data)->orientation = o; - } - creature->UpdatePosition(x, y, z, o); - creature->GetMotionMaster()->Initialize(); - if (creature->IsAlive()) // dead creature will reset movement generator at respawn - { - creature->setDeathState(JUST_DIED); - creature->Respawn(); - } + handler->PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid); + handler->SetSentErrorMessage(true); + return false; } - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_POSITION); + // update position in memory + const_cast<CreatureData*>(data)->spawnPoint.Relocate(*player); - stmt->setFloat(0, x); - stmt->setFloat(1, y); - stmt->setFloat(2, z); - stmt->setFloat(3, o); + // update position in DB + PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_POSITION); + stmt->setFloat(0, player->GetPositionX()); + stmt->setFloat(1, player->GetPositionY()); + stmt->setFloat(2, player->GetPositionZ()); + stmt->setFloat(3, player->GetOrientation()); stmt->setUInt32(4, lowguid); - WorldDatabase.Execute(stmt); + // respawn selected creature at the new location + if (creature) + { + if (creature->IsAlive()) + creature->setDeathState(JUST_DIED); + creature->Respawn(true); + if (!creature->GetRespawnCompatibilityMode()) + creature->AddObjectToRemoveList(); + } + handler->PSendSysMessage(LANG_COMMAND_CREATUREMOVED); return true; } @@ -1529,13 +1607,13 @@ public: handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player quest items"); _IterateNotNormalLootMap(handler, loot.GetPlayerQuestItems(), loot.quest_items); } - + if (!loot.GetPlayerFFAItems().empty()) { handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "FFA items per allowed player"); _IterateNotNormalLootMap(handler, loot.GetPlayerFFAItems(), loot.items); } - + if (!loot.GetPlayerNonQuestNonFFAConditionalItems().empty()) { handler->PSendSysMessage(LANG_COMMAND_NPC_SHOWLOOT_LABEL_2, "Per-player conditional items"); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index faeb77cccec..d40d67fc859 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -129,6 +129,8 @@ public: { "pickpocketing_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_PICKPOCKETING_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesPickpocketingCommand, "" }, { "points_of_interest", rbac::RBAC_PERM_COMMAND_RELOAD_POINTS_OF_INTEREST, true, &HandleReloadPointsOfInterestCommand, "" }, { "prospecting_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_PROSPECTING_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesProspectingCommand, "" }, + { "quest_greeting", rbac::RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING, true, &HandleReloadQuestGreetingCommand, "" }, + { "quest_greeting_locale", rbac::RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING_LOCALE, true, &HandleReloadLocalesQuestGreetingCommand, "" }, { "quest_poi", rbac::RBAC_PERM_COMMAND_RELOAD_QUEST_POI, true, &HandleReloadQuestPOICommand, "" }, { "quest_template", rbac::RBAC_PERM_COMMAND_RELOAD_QUEST_TEMPLATE, true, &HandleReloadQuestTemplateCommand, "" }, { "rbac", rbac::RBAC_PERM_COMMAND_RELOAD_RBAC, true, &HandleReloadRBACCommand, "" }, @@ -243,6 +245,7 @@ public: static bool HandleReloadAllQuestCommand(ChatHandler* handler, char const* /*args*/) { + HandleReloadQuestGreetingCommand(handler, ""); HandleReloadQuestAreaTriggersCommand(handler, "a"); HandleReloadQuestPOICommand(handler, "a"); HandleReloadQuestTemplateCommand(handler, "a"); @@ -317,6 +320,7 @@ public: HandleReloadLocalesPageTextCommand(handler, "a"); HandleReloadLocalesPointsOfInterestCommand(handler, "a"); HandleReloadLocalesQuestCommand(handler, "a"); + HandleReloadLocalesQuestGreetingCommand(handler, ""); return true; } @@ -523,6 +527,22 @@ public: return true; } + static bool HandleReloadQuestGreetingCommand(ChatHandler* handler, char const* /*args*/) + { + TC_LOG_INFO("misc", "Re-Loading Quest Greeting ..."); + sObjectMgr->LoadQuestGreetings(); + handler->SendGlobalGMSysMessage("DB table `quest_greeting` reloaded."); + return true; + } + + static bool HandleReloadLocalesQuestGreetingCommand(ChatHandler* handler, char const* /*args*/) + { + TC_LOG_INFO("misc", "Re-Loading Quest Greeting locales..."); + sObjectMgr->LoadQuestGreetingsLocales(); + handler->SendGlobalGMSysMessage("DB table `quest_greeting_locale` reloaded."); + return true; + } + static bool HandleReloadQuestTemplateCommand(ChatHandler* handler, char const* /*args*/) { TC_LOG_INFO("misc", "Re-Loading Quest Templates..."); diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index efd02314d0e..582cc7b8c79 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -31,6 +31,7 @@ EndScriptData */ #include "MotionMaster.h" #include "Player.h" #include "RBAC.h" +#include "WaypointDefines.h" #include "WaypointManager.h" #include "WorldSession.h" @@ -259,7 +260,7 @@ public: stmt->setUInt32(0, guildLow); WorldDatabase.Execute(stmt); - target->UpdateWaypointID(0); + target->UpdateCurrentWaypointInfo(0, 0); stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_MOVEMENT_TYPE); stmt->setUInt8(0, uint8(IDLE_MOTION_TYPE)); @@ -662,7 +663,7 @@ public: // re-create Creature* wpCreature = new Creature(); - if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation())) + if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, *chr)) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT); delete wpCreature; @@ -671,8 +672,7 @@ public: wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); - /// @todo Should we first use "Create" then use "LoadFromDB"? - if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map)) + if (!wpCreature->LoadFromDB(wpCreature->GetSpawnId(), map, true, true)) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT); delete wpCreature; @@ -874,7 +874,7 @@ public: float o = chr->GetOrientation(); Creature* wpCreature = new Creature(); - if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o)) + if (!wpCreature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, { x, y, z, o })) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete wpCreature; @@ -891,7 +891,7 @@ public: WorldDatabase.Execute(stmt); // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); - if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map)) + if (!wpCreature->LoadFromDB(wpCreature->GetSpawnId(), map, true, true)) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete wpCreature; @@ -937,7 +937,7 @@ public: Map* map = chr->GetMap(); Creature* creature = new Creature(); - if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o)) + if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, { x, y, z, o })) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete creature; @@ -945,7 +945,7 @@ public: } creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); - if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map)) + if (!creature->LoadFromDB(creature->GetSpawnId(), map, true, true)) { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete creature; @@ -986,7 +986,7 @@ public: Map* map = chr->GetMap(); Creature* creature = new Creature(); - if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, x, y, z, o)) + if (!creature->Create(map->GenerateLowGuid<HighGuid::Unit>(), map, chr->GetPhaseMaskForSpawn(), id, { x, y, z, o })) { handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id); delete creature; @@ -994,7 +994,7 @@ public: } creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); - if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map)) + if (!creature->LoadFromDB(creature->GetSpawnId(), map, true, true)) { handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id); delete creature; diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/alterac_valley.cpp b/src/server/scripts/EasternKingdoms/AlteracValley/alterac_valley.cpp index f0edc7af0c7..b58fc0b8d8e 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/alterac_valley.cpp +++ b/src/server/scripts/EasternKingdoms/AlteracValley/alterac_valley.cpp @@ -107,7 +107,7 @@ class npc_av_marshal_or_warmaster : public CreatureScript events.ScheduleEvent(EVENT_CHECK_RESET, 5000); } - void JustRespawned() override + void JustAppeared() override { Reset(); } diff --git a/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp b/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp index cf034ed0ece..ed97d7670a1 100644 --- a/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp +++ b/src/server/scripts/EasternKingdoms/AlteracValley/boss_drekthar.cpp @@ -71,7 +71,7 @@ public: events.ScheduleEvent(EVENT_RANDOM_YELL, urand(20 * IN_MILLISECONDS, 30 * IN_MILLISECONDS)); //20 to 30 seconds } - void JustRespawned() override + void JustAppeared() override { Reset(); Talk(SAY_RESPAWN); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp index bcd4a915595..854f571c18c 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.cpp @@ -135,9 +135,9 @@ public: return GetBlackrockDepthsAI<npc_grimstoneAI>(creature); } - struct npc_grimstoneAI : public npc_escortAI + struct npc_grimstoneAI : public EscortAI { - npc_grimstoneAI(Creature* creature) : npc_escortAI(creature) + npc_grimstoneAI(Creature* creature) : EscortAI(creature) { Initialize(); instance = creature->GetInstanceScript(); @@ -202,7 +202,7 @@ public: MobDeath_Timer = 2500; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -349,7 +349,7 @@ public: } if (CanWalk) - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } }; }; @@ -510,9 +510,9 @@ class npc_rocknot : public CreatureScript public: npc_rocknot() : CreatureScript("npc_rocknot") { } - struct npc_rocknotAI : public npc_escortAI + struct npc_rocknotAI : public EscortAI { - npc_rocknotAI(Creature* creature) : npc_escortAI(creature) + npc_rocknotAI(Creature* creature) : EscortAI(creature) { Initialize(); instance = creature->GetInstanceScript(); @@ -543,7 +543,7 @@ public: go->SetGoState((GOState)state); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -597,7 +597,7 @@ public: } else BreakDoor_Timer -= diff; } - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } void QuestReward(Player* /*player*/, Quest const* quest, uint32 /*item*/) override diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h index 5102c58b9b6..75d4e85e8ed 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h @@ -73,4 +73,6 @@ inline AI* GetBlackrockDepthsAI(T* obj) return GetInstanceAI<AI>(obj, BRDScriptName); } +#define RegisterBlackrockDepthsCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetBlackrockDepthsAI) + #endif diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_coren_direbrew.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_coren_direbrew.cpp index b854239c303..77f3a4cd2e6 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_coren_direbrew.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_coren_direbrew.cpp @@ -114,309 +114,271 @@ Position const AntagonistPos[3] = { 896.2667f, -130.483f, -49.66249f, 2.600541f } }; -class boss_coren_direbrew : public CreatureScript +struct boss_coren_direbrew : public BossAI { -public: - boss_coren_direbrew() : CreatureScript("boss_coren_direbrew") { } + boss_coren_direbrew(Creature* creature) : BossAI(creature, DATA_COREN) { } - struct boss_coren_direbrewAI : public BossAI + bool GossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override { - boss_coren_direbrewAI(Creature* creature) : BossAI(creature, DATA_COREN) { } + if (menuId != GOSSIP_ID) + return false; - bool GossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override + if (gossipListId == GOSSIP_OPTION_FIGHT) { - if (menuId != GOSSIP_ID) - return false; + Talk(SAY_INSULT, player); + DoAction(ACTION_START_FIGHT); + } + else if (gossipListId == GOSSIP_OPTION_APOLOGIZE) + CloseGossipMenuFor(player); - if (gossipListId == GOSSIP_OPTION_FIGHT) - { - Talk(SAY_INSULT, player); - DoAction(ACTION_START_FIGHT); - } - else if (gossipListId == GOSSIP_OPTION_APOLOGIZE) - CloseGossipMenuFor(player); + return false; + } - return false; - } + void Reset() override + { + _Reset(); + me->SetImmuneToPC(true); + me->SetFaction(FACTION_FRIENDLY); + events.SetPhase(PHASE_ALL); - void Reset() override - { - _Reset(); - me->SetImmuneToPC(true); - me->SetFaction(FACTION_FRIENDLY); - events.SetPhase(PHASE_ALL); + for (uint8 i = 0; i < MAX_ANTAGONISTS; ++i) + me->SummonCreature(NPC_ANTAGONIST, AntagonistPos[i], TEMPSUMMON_DEAD_DESPAWN); + } - for (uint8 i = 0; i < MAX_ANTAGONISTS; ++i) - me->SummonCreature(NPC_ANTAGONIST, AntagonistPos[i], TEMPSUMMON_DEAD_DESPAWN); - } + void EnterEvadeMode(EvadeReason /*why*/) override + { + _EnterEvadeMode(); + summons.DespawnAll(); + _DespawnAtEvade(Seconds(10)); + } - void MoveInLineOfSight(Unit* who) override - { - if (!events.IsInPhase(PHASE_ALL) || who->GetTypeId() != TYPEID_PLAYER) - return; + void MoveInLineOfSight(Unit* who) override + { + if (!events.IsInPhase(PHASE_ALL) || who->GetTypeId() != TYPEID_PLAYER) + return; - events.SetPhase(PHASE_INTRO); - events.ScheduleEvent(EVENT_INTRO_1, Seconds(6), 0, PHASE_INTRO); - Talk(SAY_INTRO); - } + events.SetPhase(PHASE_INTRO); + events.ScheduleEvent(EVENT_INTRO_1, Seconds(6), 0, PHASE_INTRO); + Talk(SAY_INTRO); + } - void DoAction(int32 action) override + void DoAction(int32 action) override + { + if (action == ACTION_START_FIGHT) { - if (action == ACTION_START_FIGHT) - { - events.SetPhase(PHASE_ONE); - me->SetImmuneToPC(false); - me->SetFaction(FACTION_GOBLIN_DARK_IRON_BAR_PATRON); - me->SetInCombatWithZone(); + events.SetPhase(PHASE_ONE); + me->SetImmuneToPC(false); + me->SetFaction(FACTION_GOBLIN_DARK_IRON_BAR_PATRON); + me->SetInCombatWithZone(); - EntryCheckPredicate pred(NPC_ANTAGONIST); - summons.DoAction(ACTION_ANTAGONIST_HOSTILE, pred); + EntryCheckPredicate pred(NPC_ANTAGONIST); + summons.DoAction(ACTION_ANTAGONIST_HOSTILE, pred); - events.ScheduleEvent(EVENT_SUMMON_MOLE_MACHINE, Seconds(15)); - events.ScheduleEvent(EVENT_DIREBREW_DISARM, Seconds(20)); - } + events.ScheduleEvent(EVENT_SUMMON_MOLE_MACHINE, Seconds(15)); + events.ScheduleEvent(EVENT_DIREBREW_DISARM, Seconds(20)); } + } - void DamageTaken(Unit* /*attacker*/, uint32& damage) override + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (me->HealthBelowPctDamaged(66, damage) && events.IsInPhase(PHASE_ONE)) { - if (me->HealthBelowPctDamaged(66, damage) && events.IsInPhase(PHASE_ONE)) - { - events.SetPhase(PHASE_TWO); - SummonSister(NPC_ILSA_DIREBREW); - } - else if (me->HealthBelowPctDamaged(33, damage) && events.IsInPhase(PHASE_TWO)) - { - events.SetPhase(PHASE_THREE); - SummonSister(NPC_URSULA_DIREBREW); - } + events.SetPhase(PHASE_TWO); + SummonSister(NPC_ILSA_DIREBREW); } - - void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + else if (me->HealthBelowPctDamaged(33, damage) && events.IsInPhase(PHASE_TWO)) { - if (summon->GetEntry() == NPC_ILSA_DIREBREW) - events.ScheduleEvent(EVENT_RESPAWN_ILSA, Seconds(1)); - else if (summon->GetEntry() == NPC_URSULA_DIREBREW) - events.ScheduleEvent(EVENT_RESPAWN_URSULA, Seconds(1)); + events.SetPhase(PHASE_THREE); + SummonSister(NPC_URSULA_DIREBREW); } + } - void JustDied(Unit* /*killer*/) override - { - _JustDied(); + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + if (summon->GetEntry() == NPC_ILSA_DIREBREW) + events.ScheduleEvent(EVENT_RESPAWN_ILSA, Seconds(1)); + else if (summon->GetEntry() == NPC_URSULA_DIREBREW) + events.ScheduleEvent(EVENT_RESPAWN_URSULA, Seconds(1)); + } - Map::PlayerList const& players = me->GetMap()->GetPlayers(); - if (!players.isEmpty()) - { - if (Group* group = players.begin()->GetSource()->GetGroup()) - if (group->isLFGGroup()) - sLFGMgr->FinishDungeon(group->GetGUID(), 287, me->GetMap()); - } - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); - void SummonSister(uint32 entry) + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + if (!players.isEmpty()) { - if (Creature* sister = me->SummonCreature(entry, me->GetPosition(), TEMPSUMMON_DEAD_DESPAWN)) - sister->SetInCombatWithZone(); + if (Group* group = players.begin()->GetSource()->GetGroup()) + if (group->isLFGGroup()) + sLFGMgr->FinishDungeon(group->GetGUID(), 287, me->GetMap()); } + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) - return; + void SummonSister(uint32 entry) + { + if (Creature* sister = me->SummonCreature(entry, me->GetPosition(), TEMPSUMMON_DEAD_DESPAWN)) + sister->SetInCombatWithZone(); + } - events.Update(diff); + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) + case EVENT_INTRO_1: + Talk(SAY_INTRO1); + events.ScheduleEvent(EVENT_INTRO_2, Seconds(4), 0, PHASE_INTRO); + break; + case EVENT_INTRO_2: { - case EVENT_INTRO_1: - Talk(SAY_INTRO1); - events.ScheduleEvent(EVENT_INTRO_2, Seconds(4), 0, PHASE_INTRO); - break; - case EVENT_INTRO_2: - { - EntryCheckPredicate pred(NPC_ANTAGONIST); - summons.DoAction(ACTION_ANTAGONIST_SAY_1, pred); - events.ScheduleEvent(EVENT_INTRO_3, Seconds(3), 0, PHASE_INTRO); - break; - } - case EVENT_INTRO_3: - { - Talk(SAY_INTRO2); - EntryCheckPredicate pred(NPC_ANTAGONIST); - summons.DoAction(ACTION_ANTAGONIST_SAY_2, pred); - break; - } - case EVENT_RESPAWN_ILSA: - SummonSister(NPC_ILSA_DIREBREW); - break; - case EVENT_RESPAWN_URSULA: - SummonSister(NPC_URSULA_DIREBREW); - break; - case EVENT_SUMMON_MOLE_MACHINE: - DoCastAOE(SPELL_MOLE_MACHINE_TARGET_PICKER); - events.Repeat(Seconds(15)); - break; - case EVENT_DIREBREW_DISARM: - DoCastSelf(SPELL_DIREBREW_DISARM_PRE_CAST, true); - events.Repeat(Seconds(20)); - break; - default: - break; + EntryCheckPredicate pred(NPC_ANTAGONIST); + summons.DoAction(ACTION_ANTAGONIST_SAY_1, pred); + events.ScheduleEvent(EVENT_INTRO_3, Seconds(3), 0, PHASE_INTRO); + break; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + case EVENT_INTRO_3: + { + Talk(SAY_INTRO2); + EntryCheckPredicate pred(NPC_ANTAGONIST); + summons.DoAction(ACTION_ANTAGONIST_SAY_2, pred); + break; + } + case EVENT_RESPAWN_ILSA: + SummonSister(NPC_ILSA_DIREBREW); + break; + case EVENT_RESPAWN_URSULA: + SummonSister(NPC_URSULA_DIREBREW); + break; + case EVENT_SUMMON_MOLE_MACHINE: + me->CastCustomSpell(SPELL_MOLE_MACHINE_TARGET_PICKER, SPELLVALUE_MAX_TARGETS, 1, nullptr, true); + events.Repeat(Seconds(15)); + break; + case EVENT_DIREBREW_DISARM: + DoCastSelf(SPELL_DIREBREW_DISARM_PRE_CAST, true); + events.Repeat(Seconds(20)); + break; + default: + break; } - DoMeleeAttackIfReady(); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackrockDepthsAI<boss_coren_direbrewAI>(creature); + DoMeleeAttackIfReady(); } }; -class npc_coren_direbrew_sisters : public CreatureScript +struct npc_coren_direbrew_sisters : public ScriptedAI { -public: - npc_coren_direbrew_sisters() : CreatureScript("npc_coren_direbrew_sisters") { } + npc_coren_direbrew_sisters(Creature* creature) : ScriptedAI(creature) { } - struct npc_coren_direbrew_sistersAI : public ScriptedAI + void SetGUID(ObjectGuid guid, int32 data) override { - npc_coren_direbrew_sistersAI(Creature* creature) : ScriptedAI(creature) { } - - void SetGUID(ObjectGuid guid, int32 data) override - { - if (data == DATA_TARGET_GUID) - _targetGUID = guid; - } - - ObjectGuid GetGUID(int32 data) const override - { - if (data == DATA_TARGET_GUID) - return _targetGUID; + if (data == DATA_TARGET_GUID) + _targetGUID = guid; + } - return ObjectGuid::Empty; - } + ObjectGuid GetGUID(int32 data) const override + { + if (data == DATA_TARGET_GUID) + return _targetGUID; - void EnterCombat(Unit* /*who*/) override - { - DoCastSelf(SPELL_PORT_TO_COREN); + return ObjectGuid::Empty; + } - if (me->GetEntry() == NPC_URSULA_DIREBREW) - DoCastSelf(SPELL_BARRELED_CONTROL_AURA); - else - DoCastSelf(SPELL_SEND_MUG_CONTROL_AURA); + void EnterCombat(Unit* /*who*/) override + { + DoCastSelf(SPELL_PORT_TO_COREN); - _scheduler - .SetValidator([this] - { - return !me->HasUnitState(UNIT_STATE_CASTING); - }) - .Schedule(Seconds(2), [this](TaskContext mugChuck) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, false, true, -SPELL_HAS_DARK_BREWMAIDENS_BREW)) - DoCast(target, SPELL_CHUCK_MUG); - mugChuck.Repeat(Seconds(4)); - }); - } + if (me->GetEntry() == NPC_URSULA_DIREBREW) + DoCastSelf(SPELL_BARRELED_CONTROL_AURA); + else + DoCastSelf(SPELL_SEND_MUG_CONTROL_AURA); - void UpdateAI(uint32 diff) override + _scheduler + .SetValidator([this] { - _scheduler.Update(diff, [this] - { - DoMeleeAttackIfReady(); - }); - } - - private: - ObjectGuid _targetGUID; - TaskScheduler _scheduler; - }; + return !me->HasUnitState(UNIT_STATE_CASTING); + }) + .Schedule(Seconds(2), [this](TaskContext mugChuck) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, false, true, -SPELL_HAS_DARK_BREWMAIDENS_BREW)) + DoCast(target, SPELL_CHUCK_MUG); + mugChuck.Repeat(Seconds(4)); + }); + } - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetBlackrockDepthsAI<npc_coren_direbrew_sistersAI>(creature); + _scheduler.Update(diff, [this] + { + DoMeleeAttackIfReady(); + }); } + +private: + ObjectGuid _targetGUID; + TaskScheduler _scheduler; }; -class npc_direbrew_minion : public CreatureScript +struct npc_direbrew_minion : public ScriptedAI { -public: - npc_direbrew_minion() : CreatureScript("npc_direbrew_minion") { } + npc_direbrew_minion(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - struct npc_direbrew_minionAI : public ScriptedAI + void Reset() override { - npc_direbrew_minionAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - - void Reset() override - { - me->SetFaction(FACTION_GOBLIN_DARK_IRON_BAR_PATRON); - DoCastAOE(SPELL_MOLE_MACHINE_EMERGE, true); - me->SetInCombatWithZone(); - } - - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* coren = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_COREN))) - coren->AI()->JustSummoned(me); - } - - private: - InstanceScript* _instance; - }; + me->SetFaction(FACTION_GOBLIN_DARK_IRON_BAR_PATRON); + me->SetInCombatWithZone(); + } - CreatureAI* GetAI(Creature* creature) const override + void IsSummonedBy(Unit* /*summoner*/) override { - return GetBlackrockDepthsAI<npc_direbrew_minionAI>(creature); + if (Creature* coren = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_COREN))) + coren->AI()->JustSummoned(me); } + +private: + InstanceScript* _instance; }; -class npc_direbrew_antagonist : public CreatureScript +struct npc_direbrew_antagonist : public ScriptedAI { -public: - npc_direbrew_antagonist() : CreatureScript("npc_direbrew_antagonist") { } + npc_direbrew_antagonist(Creature* creature) : ScriptedAI(creature) { } - struct npc_direbrew_antagonistAI : public ScriptedAI + void DoAction(int32 action) override { - npc_direbrew_antagonistAI(Creature* creature) : ScriptedAI(creature) { } - - void DoAction(int32 action) override - { - switch (action) - { - case ACTION_ANTAGONIST_SAY_1: - Talk(SAY_ANTAGONIST_1); - break; - case ACTION_ANTAGONIST_SAY_2: - Talk(SAY_ANTAGONIST_2); - break; - case ACTION_ANTAGONIST_HOSTILE: - me->SetImmuneToPC(false); - me->SetFaction(FACTION_GOBLIN_DARK_IRON_BAR_PATRON); - me->SetInCombatWithZone(); - break; - default: - break; - } - } - - void EnterCombat(Unit* who) override + switch (action) { - Talk(SAY_ANTAGONIST_COMBAT, who); - ScriptedAI::EnterCombat(who); + case ACTION_ANTAGONIST_SAY_1: + Talk(SAY_ANTAGONIST_1); + break; + case ACTION_ANTAGONIST_SAY_2: + Talk(SAY_ANTAGONIST_2); + break; + case ACTION_ANTAGONIST_HOSTILE: + me->SetImmuneToPC(false); + me->SetFaction(FACTION_GOBLIN_DARK_IRON_BAR_PATRON); + me->SetInCombatWithZone(); + break; + default: + break; } - }; + } - CreatureAI* GetAI(Creature* creature) const override + void EnterCombat(Unit* who) override { - return GetBlackrockDepthsAI<npc_direbrew_antagonistAI>(creature); + Talk(SAY_ANTAGONIST_COMBAT, who); + ScriptedAI::EnterCombat(who); } }; @@ -435,12 +397,12 @@ public: _scheduler .Schedule(Seconds(1), [this](TaskContext /*context*/) { - me->UseDoorOrButton(8); - me->CastSpell((Unit*)nullptr, SPELL_MOLE_MACHINE_EMERGE, true); + me->UseDoorOrButton(10000); + me->CastSpell(nullptr, SPELL_MOLE_MACHINE_EMERGE, true); }) .Schedule(Seconds(4), [this](TaskContext /*context*/) { - if (GameObject* trap = me->FindNearestGameObject(GO_MOLE_MACHINE_TRAP, 3.0f)) + if (GameObject* trap = me->GetLinkedTrap()) { trap->SetLootState(GO_ACTIVATED); trap->UseDoorOrButton(); @@ -463,236 +425,170 @@ public: } }; -// 47407 - Direbrew's Disarm (precast) -class spell_direbrew_disarm : public SpellScriptLoader -{ - public: - spell_direbrew_disarm() : SpellScriptLoader("spell_direbrew_disarm") { } - - class spell_direbrew_disarm_AuraScript : public AuraScript - { - PrepareAuraScript(spell_direbrew_disarm_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DIREBREW_DISARM, SPELL_DIREBREW_DISARM_GROW }); - } - - void PeriodicTick(AuraEffect const* /*aurEff*/) - { - if (Aura* aura = GetTarget()->GetAura(SPELL_DIREBREW_DISARM_GROW)) - { - aura->SetStackAmount(aura->GetStackAmount() + 1); - aura->SetDuration(aura->GetDuration() - 1500); - } - } - - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - GetTarget()->CastSpell(GetTarget(), SPELL_DIREBREW_DISARM_GROW, true); - GetTarget()->CastSpell(GetTarget(), SPELL_DIREBREW_DISARM); - } - - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_direbrew_disarm_AuraScript::PeriodicTick, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY); - OnEffectApply += AuraEffectRemoveFn(spell_direbrew_disarm_AuraScript::OnApply, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_direbrew_disarm_AuraScript(); - } -}; - // 47691 - Summon Mole Machine Target Picker -class spell_direbrew_summon_mole_machine_target_picker : public SpellScriptLoader +class spell_direbrew_summon_mole_machine_target_picker : public SpellScript { - public: - spell_direbrew_summon_mole_machine_target_picker() : SpellScriptLoader("spell_direbrew_summon_mole_machine_target_picker") { } - - class spell_direbrew_summon_mole_machine_target_picker_SpellScript : public SpellScript - { - PrepareSpellScript(spell_direbrew_summon_mole_machine_target_picker_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_MOLE_MACHINE_MINION_SUMMONER }); - } + PrepareSpellScript(spell_direbrew_summon_mole_machine_target_picker); - void HandleScriptEffect(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell(GetHitUnit(), SPELL_MOLE_MACHINE_MINION_SUMMONER, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_MOLE_MACHINE_MINION_SUMMONER }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_direbrew_summon_mole_machine_target_picker_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_MOLE_MACHINE_MINION_SUMMONER, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_direbrew_summon_mole_machine_target_picker_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_direbrew_summon_mole_machine_target_picker::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; // 47370 - Send Mug Target Picker -class spell_send_mug_target_picker : public SpellScriptLoader +class spell_send_mug_target_picker : public SpellScript { - public: - spell_send_mug_target_picker() : SpellScriptLoader("spell_send_mug_target_picker") { } - - class spell_send_mug_target_picker_SpellScript : public SpellScript - { - PrepareSpellScript(spell_send_mug_target_picker_SpellScript); + PrepareSpellScript(spell_send_mug_target_picker); - void FilterTargets(std::list<WorldObject*>& targets) - { - Unit* caster = GetCaster(); - - targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_HAS_DARK_BREWMAIDENS_BREW)); + void FilterTargets(std::list<WorldObject*>& targets) + { + Unit* caster = GetCaster(); - if (targets.size() > 1) - targets.remove_if([caster](WorldObject* obj) - { - if (obj->GetGUID() == caster->GetAI()->GetGUID(DATA_TARGET_GUID)) - return true; - return false; - }); + targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_HAS_DARK_BREWMAIDENS_BREW)); - if (targets.empty()) - return; + if (targets.size() > 1) + targets.remove_if([caster](WorldObject* obj) + { + if (obj->GetGUID() == caster->GetAI()->GetGUID(DATA_TARGET_GUID)) + return true; + return false; + }); - WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); - targets.clear(); - targets.push_back(target); - } + if (targets.empty()) + return; - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - caster->GetAI()->SetGUID(GetHitUnit()->GetGUID(), DATA_TARGET_GUID); - caster->CastSpell(GetHitUnit(), SPELL_SEND_FIRST_MUG, true); - } + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_send_mug_target_picker_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); - OnEffectHitTarget += SpellEffectFn(spell_send_mug_target_picker_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + caster->GetAI()->SetGUID(GetHitUnit()->GetGUID(), DATA_TARGET_GUID); + caster->CastSpell(GetHitUnit(), SPELL_SEND_FIRST_MUG, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_send_mug_target_picker_SpellScript(); - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_send_mug_target_picker::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + OnEffectHitTarget += SpellEffectFn(spell_send_mug_target_picker::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 47344 - Request Second Mug -class spell_request_second_mug : public SpellScriptLoader +class spell_request_second_mug : public SpellScript { - public: - spell_request_second_mug() : SpellScriptLoader("spell_request_second_mug") { } - - class spell_request_second_mug_SpellScript : public SpellScript - { - PrepareSpellScript(spell_request_second_mug_SpellScript); + PrepareSpellScript(spell_request_second_mug); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SEND_SECOND_MUG }); - } - - void HandleScriptEffect(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->CastSpell(GetCaster(), SPELL_SEND_SECOND_MUG, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SEND_SECOND_MUG }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_request_second_mug_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetCaster(), SPELL_SEND_SECOND_MUG, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_request_second_mug_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_request_second_mug::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; // 47369 - Send Mug Control Aura -class spell_send_mug_control_aura : public SpellScriptLoader +class spell_send_mug_control_aura : public AuraScript { - public: - spell_send_mug_control_aura() : SpellScriptLoader("spell_send_mug_control_aura") { } - - class spell_send_mug_control_aura_AuraScript : public AuraScript - { - PrepareAuraScript(spell_send_mug_control_aura_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SEND_MUG_TARGET_PICKER }); - } + PrepareAuraScript(spell_send_mug_control_aura); - void PeriodicTick(AuraEffect const* /*aurEff*/) - { - GetTarget()->CastSpell(GetTarget(), SPELL_SEND_MUG_TARGET_PICKER, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SEND_MUG_TARGET_PICKER }); + } - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_send_mug_control_aura_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); - } - }; + void PeriodicTick(AuraEffect const* /*aurEff*/) + { + GetTarget()->CastSpell(GetTarget(), SPELL_SEND_MUG_TARGET_PICKER, true); + } - AuraScript* GetAuraScript() const override - { - return new spell_send_mug_control_aura_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_send_mug_control_aura::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } }; // 50278 - Barreled Control Aura -class spell_barreled_control_aura : public SpellScriptLoader +class spell_barreled_control_aura : public AuraScript { - public: - spell_barreled_control_aura() : SpellScriptLoader("spell_barreled_control_aura") { } + PrepareAuraScript(spell_barreled_control_aura); - class spell_barreled_control_aura_AuraScript : public AuraScript - { - PrepareAuraScript(spell_barreled_control_aura_AuraScript); + void PeriodicTick(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(nullptr, SPELL_BARRELED, true); + } - void PeriodicTick(AuraEffect const* /*aurEff*/) - { - PreventDefaultAction(); - GetTarget()->CastSpell((Unit*)nullptr, SPELL_BARRELED, true); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_barreled_control_aura::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } +}; - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_barreled_control_aura_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; +// 47407 - Direbrew's Disarm (precast) +class spell_direbrew_disarm : public AuraScript +{ + PrepareAuraScript(spell_direbrew_disarm); - AuraScript* GetAuraScript() const override + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DIREBREW_DISARM, SPELL_DIREBREW_DISARM_GROW }); + } + + void PeriodicTick(AuraEffect const* /*aurEff*/) + { + if (Aura* aura = GetTarget()->GetAura(SPELL_DIREBREW_DISARM_GROW)) { - return new spell_barreled_control_aura_AuraScript(); + aura->SetStackAmount(aura->GetStackAmount() + 1); + aura->SetDuration(aura->GetDuration() - 1500); } + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->CastSpell(GetTarget(), SPELL_DIREBREW_DISARM_GROW, true); + GetTarget()->CastSpell(GetTarget(), SPELL_DIREBREW_DISARM); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_direbrew_disarm::PeriodicTick, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY); + OnEffectApply += AuraEffectRemoveFn(spell_direbrew_disarm::OnApply, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL); + } }; void AddSC_boss_coren_direbrew() { - new boss_coren_direbrew(); - new npc_coren_direbrew_sisters(); - new npc_direbrew_minion(); - new npc_direbrew_antagonist(); + RegisterBlackrockDepthsCreatureAI(boss_coren_direbrew); + RegisterBlackrockDepthsCreatureAI(npc_coren_direbrew_sisters); + RegisterBlackrockDepthsCreatureAI(npc_direbrew_minion); + RegisterBlackrockDepthsCreatureAI(npc_direbrew_antagonist); new go_direbrew_mole_machine(); - new spell_direbrew_disarm(); - new spell_direbrew_summon_mole_machine_target_picker(); - new spell_send_mug_target_picker(); - new spell_request_second_mug(); - new spell_send_mug_control_aura(); - new spell_barreled_control_aura(); + RegisterSpellScript(spell_direbrew_summon_mole_machine_target_picker); + RegisterSpellScript(spell_send_mug_target_picker); + RegisterSpellScript(spell_request_second_mug); + RegisterAuraScript(spell_send_mug_control_aura); + RegisterAuraScript(spell_barreled_control_aura); + RegisterAuraScript(spell_direbrew_disarm); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp index 076966a2acb..105b27e711e 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp @@ -355,7 +355,7 @@ public: nefarian->setActive(true); nefarian->SetCanFly(true); nefarian->SetDisableGravity(true); - nefarian->CastSpell((Unit*)nullptr, SPELL_SHADOWFLAME_INITIAL); + nefarian->CastSpell(nullptr, SPELL_SHADOWFLAME_INITIAL); nefarian->GetMotionMaster()->MovePoint(1, NefarianLoc[1]); } events.CancelEvent(EVENT_MIND_CONTROL); diff --git a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp index 83d0fdcc943..b965e3063e9 100644 --- a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp +++ b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp @@ -94,9 +94,9 @@ public: return GetGnomereganAI<npc_blastmaster_emi_shortfuseAI>(creature); } - struct npc_blastmaster_emi_shortfuseAI : public npc_escortAI + struct npc_blastmaster_emi_shortfuseAI : public EscortAI { - npc_blastmaster_emi_shortfuseAI(Creature* creature) : npc_escortAI(creature) + npc_blastmaster_emi_shortfuseAI(Creature* creature) : EscortAI(creature) { instance = creature->GetInstanceScript(); creature->RestoreFaction(); @@ -219,7 +219,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { //just in case if (GetPlayerForEscort()) diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp index fcd307ada07..2cb87b96262 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp @@ -491,8 +491,7 @@ public: { if (axe->GetVictim()) ResetThreat(axe->GetVictim(), axe); - if (target) - AddThreat(target, 1000000.0f, axe); + AddThreat(target, 1000000.0f, axe); } } } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp index 8c5f57e9a8e..994dc2a989a 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp @@ -131,9 +131,9 @@ class npc_barnes : public CreatureScript public: npc_barnes() : CreatureScript("npc_barnes") { } - struct npc_barnesAI : public npc_escortAI + struct npc_barnesAI : public EscortAI { - npc_barnesAI(Creature* creature) : npc_escortAI(creature) + npc_barnesAI(Creature* creature) : EscortAI(creature) { Initialize(); RaidWiped = false; @@ -184,7 +184,7 @@ public: void EnterCombat(Unit* /*who*/) override { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -288,7 +288,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (HasEscortState(STATE_ESCORT_PAUSED)) { diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp index 5cf03b678f6..3a10b0f5619 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp @@ -392,8 +392,7 @@ public: for (uint8 i = 0; i < 3; ++i) { - Unit* target = nullptr; - target = SelectTarget(SELECT_TARGET_RANDOM, 0); + Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0); Creature* Orb = DoSpawnCreature(CREATURE_ARCANE_SPHERE, 5, 5, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000); if (Orb && target) @@ -564,15 +563,12 @@ public: Rebirth = true; } - if (Rebirth) + if (Death_Timer <= diff) { - if (Death_Timer <= diff) - { - me->SummonCreature(CREATURE_PHOENIX_EGG, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); - me->DisappearAndDie(); - Rebirth = false; - } else Death_Timer -= diff; - } + me->SummonCreature(CREATURE_PHOENIX_EGG, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); + me->DisappearAndDie(); + Rebirth = false; + } else Death_Timer -= diff; } if (!UpdateVictim()) diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp index 86592f5677c..93f10b1a81e 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp @@ -65,6 +65,7 @@ enum Misc ACTION_SWITCH_PHASE = 1 }; +// @todo crystals should really be a DB creature summon group, having them in `creature` like this will cause tons of despawn/respawn bugs class boss_selin_fireheart : public CreatureScript { public: @@ -72,23 +73,15 @@ class boss_selin_fireheart : public CreatureScript struct boss_selin_fireheartAI : public BossAI { - boss_selin_fireheartAI(Creature* creature) : BossAI(creature, DATA_SELIN) - { - _scheduledEvents = false; - } + boss_selin_fireheartAI(Creature* creature) : BossAI(creature, DATA_SELIN), _scheduledEvents(false) { } void Reset() override { - Crystals.clear(); - me->GetCreatureListWithEntryInGrid(Crystals, NPC_FEL_CRYSTAL, 250.0f); + std::list<Creature*> crystals; + me->GetCreatureListWithEntryInGrid(crystals, NPC_FEL_CRYSTAL, 250.0f); - for (Creature* creature : Crystals) - { - if (!creature->IsAlive()) - creature->Respawn(); - - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - } + for (Creature* creature : crystals) + creature->Respawn(true); _Reset(); CrystalGUID.Clear(); @@ -112,21 +105,16 @@ class boss_selin_fireheart : public CreatureScript void SelectNearestCrystal() { - if (Crystals.empty()) - return; - - Crystals.sort(Trinity::ObjectDistanceOrderPred(me)); - if (Creature* CrystalChosen = Crystals.front()) + if (Creature* crystal = me->FindNearestCreature(NPC_FEL_CRYSTAL, 250.0f)) { Talk(SAY_ENERGY); Talk(EMOTE_CRYSTAL); - DoCast(CrystalChosen, SPELL_FEL_CRYSTAL_DUMMY); - CrystalGUID = CrystalChosen->GetGUID(); - Crystals.remove(CrystalChosen); - + DoCast(crystal, SPELL_FEL_CRYSTAL_DUMMY); + CrystalGUID = crystal->GetGUID(); + float x, y, z; - CrystalChosen->GetClosePoint(x, y, z, me->GetCombatReach(), CONTACT_DISTANCE); + crystal->GetClosePoint(x, y, z, me->GetCombatReach(), CONTACT_DISTANCE); events.SetPhase(PHASE_DRAIN); me->SetWalk(false); @@ -136,14 +124,11 @@ class boss_selin_fireheart : public CreatureScript void ShatterRemainingCrystals() { - if (Crystals.empty()) - return; + std::list<Creature*> crystals; + me->GetCreatureListWithEntryInGrid(crystals, NPC_FEL_CRYSTAL, 250.0f); - for (Creature* crystal : Crystals) - { - if (crystal && crystal->IsAlive()) - crystal->KillSelf(); - } + for (Creature* crystal : crystals) + crystal->KillSelf(); } void EnterCombat(Unit* /*who*/) override @@ -259,7 +244,6 @@ class boss_selin_fireheart : public CreatureScript } private: - std::list<Creature*> Crystals; ObjectGuid CrystalGUID; bool _scheduledEvents; }; diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp index aa3b894bbe3..d11f352f6c0 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp @@ -1071,7 +1071,7 @@ class npc_scarlet_miner_cart : public CreatureScript if (apply) { _playerGUID = who->GetGUID(); - me->CastSpell((Unit*)nullptr, SPELL_SUMMON_MINER, true); + me->CastSpell(nullptr, SPELL_SUMMON_MINER, true); } else { @@ -1107,9 +1107,9 @@ class npc_scarlet_miner : public CreatureScript public: npc_scarlet_miner() : CreatureScript("npc_scarlet_miner") { } - struct npc_scarlet_minerAI : public npc_escortAI + struct npc_scarlet_minerAI : public EscortAI { - npc_scarlet_minerAI(Creature* creature) : npc_escortAI(creature) + npc_scarlet_minerAI(Creature* creature) : EscortAI(creature) { Initialize(); me->SetReactState(REACT_PASSIVE); @@ -1138,7 +1138,7 @@ class npc_scarlet_miner : public CreatureScript void InitWaypoint() { - AddWaypoint(1, 2389.03f, -5902.74f, 109.014f, 5000); + AddWaypoint(1, 2389.03f, -5902.74f, 109.014f, 0.f, 5000); AddWaypoint(2, 2341.812012f, -5900.484863f, 102.619743f); AddWaypoint(3, 2306.561279f, -5901.738281f, 91.792419f); AddWaypoint(4, 2300.098389f, -5912.618652f, 86.014885f); @@ -1157,7 +1157,7 @@ class npc_scarlet_miner : public CreatureScript AddWaypoint(14, 2172.516602f, -6146.752441f, 1.074235f); AddWaypoint(15, 2138.918457f, -6158.920898f, 1.342926f); AddWaypoint(16, 2129.866699f, -6174.107910f, 4.380779f); - AddWaypoint(17, 2117.709473f, -6193.830078f, 13.3542f, 10000); + AddWaypoint(17, 2117.709473f, -6193.830078f, 13.3542f, 0.f, 10000); } else { @@ -1165,7 +1165,7 @@ class npc_scarlet_miner : public CreatureScript AddWaypoint(14, 2234.265625f, -6163.741211f, 0.916021f); AddWaypoint(15, 2268.071777f, -6158.750977f, 1.822252f); AddWaypoint(16, 2270.028320f, -6176.505859f, 6.340538f); - AddWaypoint(17, 2271.739014f, -6195.401855f, 13.3542f, 10000); + AddWaypoint(17, 2271.739014f, -6195.401855f, 13.3542f, 0.f, 10000); } } @@ -1176,7 +1176,7 @@ class npc_scarlet_miner : public CreatureScript SetDespawnAtFar(false); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -1224,7 +1224,7 @@ class npc_scarlet_miner : public CreatureScript else IntroTimer -= diff; } - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } }; diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp index 5e02a18caa6..968b0cd0f61 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp @@ -201,9 +201,9 @@ class npc_koltira_deathweaver : public CreatureScript public: npc_koltira_deathweaver() : CreatureScript("npc_koltira_deathweaver") { } - struct npc_koltira_deathweaverAI : public npc_escortAI + struct npc_koltira_deathweaverAI : public EscortAI { - npc_koltira_deathweaverAI(Creature* creature) : npc_escortAI(creature) + npc_koltira_deathweaverAI(Creature* creature) : EscortAI(creature) { Initialize(); me->SetReactState(REACT_DEFENSIVE); @@ -228,7 +228,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -281,7 +281,7 @@ public: void UpdateAI(uint32 uiDiff) override { - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); if (HasEscortState(STATE_ESCORT_PAUSED)) { @@ -687,7 +687,7 @@ public: switch (ExecuteSpeech_Counter) { - case 0: + case 0: Talk(SAY_EXEC_START, player); break; case 1: diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp index 5484bb78c2f..61806772fcd 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp @@ -274,9 +274,9 @@ class npc_highlord_darion_mograine : public CreatureScript public: npc_highlord_darion_mograine() : CreatureScript("npc_highlord_darion_mograine") { } - struct npc_highlord_darion_mograineAI : public npc_escortAI + struct npc_highlord_darion_mograineAI : public EscortAI { - npc_highlord_darion_mograineAI(Creature* creature) : npc_escortAI(creature) + npc_highlord_darion_mograineAI(Creature* creature) : EscortAI(creature) { Reset(); } @@ -453,7 +453,7 @@ public: SetEscortPaused(bOnHold); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -571,12 +571,12 @@ public: void EnterEvadeMode(EvadeReason why) override { if (!bIsBattle)//do not reset self if we are in battle - npc_escortAI::EnterEvadeMode(why); + EscortAI::EnterEvadeMode(why); } void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!bIsBattle) { diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_herod.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_herod.cpp index 509faae7957..35d551afa71 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_herod.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_herod.cpp @@ -149,9 +149,9 @@ public: return GetScarletMonasteryAI<npc_scarlet_traineeAI>(creature); } - struct npc_scarlet_traineeAI : public npc_escortAI + struct npc_scarlet_traineeAI : public EscortAI { - npc_scarlet_traineeAI(Creature* creature) : npc_escortAI(creature) + npc_scarlet_traineeAI(Creature* creature) : EscortAI(creature) { Start_Timer = urand(1000, 6000); } @@ -159,7 +159,6 @@ public: uint32 Start_Timer; void Reset() override { } - void WaypointReached(uint32 /*waypointId*/) override { } void EnterCombat(Unit* /*who*/) override { } void UpdateAI(uint32 diff) override @@ -173,7 +172,7 @@ public: } else Start_Timer -= diff; } - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } }; }; diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_illucia_barov.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_illucia_barov.cpp index 8db6ade664d..ae50fee187a 100644 --- a/src/server/scripts/EasternKingdoms/Scholomance/boss_illucia_barov.cpp +++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_illucia_barov.cpp @@ -28,10 +28,11 @@ Category: Scholomance enum Spells { - SPELL_CURSEOFAGONY = 34794, - SPELL_SHADOWSHOCK = 34799, - SPELL_SILENCE = 34803, - SPELL_FEAR = 34803 + SPELL_CURSEOFAGONY = 18671, + SPELL_DOMINATE = 7645, // UNUSED YET added for documentation + SPELL_FEAR = 12542, + SPELL_SHADOWSHOCK = 17234, + SPELL_SILENCE = 12528 }; enum Events diff --git a/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.cpp b/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.cpp index 86ef68e8c4a..21585333cbf 100644 --- a/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.cpp +++ b/src/server/scripts/EasternKingdoms/ShadowfangKeep/shadowfang_keep.cpp @@ -67,16 +67,16 @@ class npc_shadowfang_prisoner : public CreatureScript public: npc_shadowfang_prisoner() : CreatureScript("npc_shadowfang_prisoner") { } - struct npc_shadowfang_prisonerAI : public npc_escortAI + struct npc_shadowfang_prisonerAI : public EscortAI { - npc_shadowfang_prisonerAI(Creature* creature) : npc_escortAI(creature) + npc_shadowfang_prisonerAI(Creature* creature) : EscortAI(creature) { instance = creature->GetInstanceScript(); } InstanceScript* instance; - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -333,7 +333,7 @@ class spell_shadowfang_keep_haunting_spirits : public SpellScriptLoader void HandleDummyTick(AuraEffect const* aurEff) { - GetTarget()->CastSpell((Unit*)nullptr, aurEff->GetAmount(), true); + GetTarget()->CastSpell(nullptr, aurEff->GetAmount(), true); } void HandleUpdatePeriodic(AuraEffect* aurEff) diff --git a/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp b/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp index f55ed325148..42780658f2b 100644 --- a/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp +++ b/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp @@ -24,6 +24,7 @@ SDCategory: Stratholme EndScriptData */ #include "ScriptMgr.h" +#include "AreaBoundary.h" #include "Creature.h" #include "EventMap.h" #include "GameObject.h" @@ -33,17 +34,15 @@ EndScriptData */ #include "Player.h" #include "stratholme.h" -enum Misc -{ - MAX_ENCOUNTER = 6 -}; - enum InstanceEvents { EVENT_BARON_RUN = 1, EVENT_SLAUGHTER_SQUARE = 2 }; +Position const timmyTheCruelSpawnPosition = { 3625.358f, -3188.108f, 130.3985f, 4.834562f }; +EllipseBoundary const beforeScarletGate(Position(3671.158f, -3181.79f), 60.0f, 40.0f); + class instance_stratholme : public InstanceMapScript { public: @@ -54,16 +53,22 @@ class instance_stratholme : public InstanceMapScript instance_stratholme_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); + for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) EncounterState[i] = NOT_STARTED; for (uint8 i = 0; i < 5; ++i) IsSilverHandDead[i] = false; + + timmySpawned = false; + scarletsKilled = 0; } uint32 EncounterState[MAX_ENCOUNTER]; + uint8 scarletsKilled; bool IsSilverHandDead[5]; + bool timmySpawned; ObjectGuid serviceEntranceGUID; ObjectGuid gauntletGate1GUID; @@ -82,6 +87,33 @@ class instance_stratholme : public InstanceMapScript GuidSet abomnationGUID; EventMap events; + void OnUnitDeath(Unit* who) override + { + switch (who->GetEntry()) + { + case NPC_CRIMSON_GUARDSMAN: + case NPC_CRIMSON_CONJUROR: + case NPC_CRIMSON_INITATE: + case NPC_CRIMSON_GALLANT: + { + if (!timmySpawned) + { + Position pos = who->ToCreature()->GetHomePosition(); + // check if they're in front of the entrance + if (beforeScarletGate.IsWithinBoundary(pos)) + { + if (++scarletsKilled >= TIMMY_THE_CRUEL_CRUSADERS_REQUIRED) + { + instance->SummonCreature(NPC_TIMMY_THE_CRUEL, timmyTheCruelSpawnPosition); + timmySpawned = true; + } + } + } + break; + } + } + } + bool StartSlaugtherSquare() { //change to DONE when crystals implemented diff --git a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h index e30d6feecdc..d82d0849863 100644 --- a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h +++ b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h @@ -57,6 +57,14 @@ enum STRCreatureIds NPC_ABOM_VENOM = 10417, NPC_BLACK_GUARD = 10394, NPC_YSIDA = 16031, + + // Scarlet side creatures + NPC_CRIMSON_GUARDSMAN = 10418, + NPC_CRIMSON_CONJUROR = 10419, + NPC_CRIMSON_INITATE = 10420, + NPC_CRIMSON_GALLANT = 10424, + + NPC_TIMMY_THE_CRUEL = 10808 }; enum STRGameobjectIds @@ -84,6 +92,13 @@ enum STRSpellIds SPELL_BARON_ULTIMATUM = 27861 }; +enum STRMisc +{ + //! amount of crusade monsters required to be killed in order for timmy the cruel to spawn + TIMMY_THE_CRUEL_CRUSADERS_REQUIRED = 15, + MAX_ENCOUNTER = 6 +}; + template <class AI, class T> inline AI* GetStratholmeAI(T* obj) { diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp index 88254211099..c58b8afac7a 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp @@ -186,7 +186,7 @@ public: Talk(YELL_KILL); } - void JustRespawned() override + void JustAppeared() override { Talk(YELL_BIRTH); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp index 5dd4a3248ac..c671878d465 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kalecgos.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,808 +15,782 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_Kalecgos -SD%Complete: 95 -SDComment: -SDCategory: Sunwell_Plateau -EndScriptData */ - #include "ScriptMgr.h" #include "GameObject.h" #include "GameObjectAI.h" #include "InstanceScript.h" -#include "Log.h" -#include "Map.h" #include "MotionMaster.h" -#include "ObjectAccessor.h" #include "Player.h" #include "ScriptedCreature.h" +#include "SpellAuraEffects.h" +#include "SpellScript.h" #include "sunwell_plateau.h" #include "TemporarySummon.h" #include "WorldSession.h" enum Yells { - SAY_SATH_AGGRO = 0, - SAY_SATH_SLAY = 1, - SAY_SATH_DEATH = 2, - SAY_SATH_SPELL1 = 3, - SAY_SATH_SPELL2 = 4, - - SAY_EVIL_AGGRO = 0, - SAY_EVIL_SLAY = 1, - SAY_GOOD_PLRWIN = 2, - SAY_EVIL_ENRAGE = 3, - - SAY_GOOD_AGGRO = 0, - SAY_GOOD_NEAR_DEATH = 1, - SAY_GOOD_NEAR_DEATH2 = 2 + SAY_SATH_AGGRO = 0, + SAY_SATH_SLAY = 1, + SAY_SATH_DEATH = 2, + SAY_SATH_SPELL1 = 3, + SAY_SATH_SPELL2 = 4, + + SAY_EVIL_AGGRO = 0, + SAY_EVIL_SLAY = 1, + SAY_OUTRO_1 = 2, + SAY_OUTRO_2 = 3, + EMOTE_ENRAGE = 4, + SAY_ARCANE_BUFFET = 6, + + SAY_GOOD_NEAR_DEATH_0 = 0, + SAY_GOOD_NEAR_DEATH_1 = 1, + SAY_GOOD_NEAR_DEATH_2 = 2, + SAY_GOOD_DEATH = 3 }; enum Spells { - AURA_SUNWELL_RADIANCE = 45769, - AURA_SPECTRAL_EXHAUSTION = 44867, - AURA_SPECTRAL_REALM = 46021, - AURA_SPECTRAL_INVISIBILITY = 44801, - AURA_DEMONIC_VISUAL = 44800, - - SPELL_SPECTRAL_BLAST = 44869, - SPELL_TELEPORT_SPECTRAL = 46019, - SPELL_ARCANE_BUFFET = 45018, - SPELL_FROST_BREATH = 44799, - SPELL_TAIL_LASH = 45122, - - SPELL_BANISH = 44836, - SPELL_TRANSFORM_KALEC = 44670, - SPELL_ENRAGE = 44807, - - SPELL_CORRUPTION_STRIKE = 45029, - SPELL_AGONY_CURSE = 45032, - SPELL_SHADOW_BOLT = 45031, - - SPELL_HEROIC_STRIKE = 45026, - SPELL_REVITALIZE = 45027 + SPELL_SPECTRAL_BLAST = 44869, + SPELL_ARCANE_BUFFET = 45018, + SPELL_FROST_BREATH = 44799, + SPELL_TAIL_LASH = 45122, + SPELL_WILD_MAGIC_1 = 45001, + SPELL_WILD_MAGIC_2 = 45002, + SPELL_WILD_MAGIC_3 = 45004, + SPELL_WILD_MAGIC_4 = 45006, + SPELL_WILD_MAGIC_5 = 45010, + SPELL_WILD_MAGIC_6 = 44978, + SPELL_BANISH = 44836, + SPELL_ENRAGE = 44807, + SPELL_DEMONIC_VISUAL = 44800, + SPELL_CORRUPTION_STRIKE = 45029, + SPELL_AGONY_CURSE = 45032, + SPELL_SHADOW_BOLT = 45031, + SPELL_TAP_CHECK = 46732, + SPELL_TAP_CHECK_DAMAGE = 46733, + SPELL_AGONY_CURSE_VISUAL_1 = 45083, + SPELL_AGONY_CURSE_VISUAL_2 = 45084, + SPELL_AGONY_CURSE_VISUAL_3 = 45085, + SPELL_AGONY_CURSE_ALLY = 45034, + SPELL_HEROIC_STRIKE = 45026, + SPELL_REVITALIZE = 45027, + SPELL_SPECTRAL_BLAST_EFFECT = 44866, + SPELL_SPECTRAL_BLAST_VISUAL = 46648, + SPELL_SPECTRAL_REALM_TRIGGER = 44811, + SPELL_SPECTRAL_REALM_TELEPORT = 46019, + SPELL_SPECTRAL_REALM_AURA = 46021, + SPELL_SPECTRAL_REALM_2 = 44845, + SPELL_SPECTRAL_REALM_REACTION = 44852, + SPELL_SPECTRAL_EXHAUSTION = 44867, + SPELL_TELEPORT_BACK = 46020 +}; + +enum KalecgosEvents +{ + EVENT_ARCANE_BUFFET = 1, + EVENT_FROST_BREATH, + EVENT_WILD_MAGIC, + EVENT_TAIL_LASH, + EVENT_SPECTRAL_BLAST, + EVENT_CHECK_TIMER, + EVENT_OUTRO_START, + EVENT_OUTRO_1, + EVENT_OUTRO_2, + EVENT_OUTRO_3, + EVENT_REVITALIZE, + EVENT_HEROIC_STRIKE, + EVENT_SHADOWBOLT, + EVENT_AGONY_CURSE, + EVENT_CORRUPTION_STRIKE }; enum SWPActions { - DO_ENRAGE = 1, - DO_BANISH = 2 + ACTION_START_OUTRO = 1, + ACTION_ENRAGE }; -enum Misc +enum KalecSayPhases { - FLY_X = 1679, - FLY_Y = 900, - FLY_Z = 82, - CENTER_X = 1705, - CENTER_Y = 930, - RADIUS = 30, - MAX_PLAYERS_IN_SPECTRAL_REALM = 0 // over this, teleport object won't work, 0 disables check + PHASE_SAY_ONE = 1, + PHASE_SAY_TWO, + PHASE_SAY_THREE, + PHASE_SAY_FOUR, + PHASE_OUTRO }; -#define DRAGON_REALM_Z 53.079f -#define DEMON_REALM_Z -74.558f +enum KalecgosPoints +{ + POINT_OUTRO_1 = 0, + POINT_OUTRO_2 +}; -uint32 WildMagic[] = { 44978, 45001, 45002, 45004, 45006, 45010 }; +Position const KalecgosSummonPos = { 1709.094f, 927.5035f, -74.28364f, 2.932153f }; +Position const FlyPos[2] = +{ + { 1704.18f, 927.999f, 57.888f }, + { 1614.355f, 846.9694f, 119.0971f } +}; -class boss_kalecgos : public CreatureScript +uint32 const WildMagicSpells[6] = { -public: - boss_kalecgos() : CreatureScript("boss_kalecgos") { } + SPELL_WILD_MAGIC_1, + SPELL_WILD_MAGIC_2, + SPELL_WILD_MAGIC_3, + SPELL_WILD_MAGIC_4, + SPELL_WILD_MAGIC_5, + SPELL_WILD_MAGIC_6 +}; - struct boss_kalecgosAI : public ScriptedAI +struct boss_kalecgos : public BossAI +{ + boss_kalecgos(Creature* creature) : BossAI(creature, DATA_KALECGOS), _isEnraged(false), _isBanished(false) { } + + void Reset() override { - boss_kalecgosAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - bJustReset = false; - me->setActive(true); - } + _isEnraged = false; + _isBanished = false; + _Reset(); + events.ScheduleEvent(EVENT_ARCANE_BUFFET, Seconds(8)); + events.ScheduleEvent(EVENT_FROST_BREATH, Seconds(15)); + events.ScheduleEvent(EVENT_WILD_MAGIC, Seconds(10)); + events.ScheduleEvent(EVENT_TAIL_LASH, Seconds(25)); + events.ScheduleEvent(EVENT_SPECTRAL_BLAST, Seconds(20), Seconds(25)); + events.ScheduleEvent(EVENT_CHECK_TIMER, Seconds(1)); + } - void Initialize() - { - SathGUID.Clear(); - ArcaneBuffetTimer = 8000; - FrostBreathTimer = 15000; - WildMagicTimer = 10000; - TailLashTimer = 25000; - SpectralBlastTimer = urand(20000, 25000); - CheckTimer = 1000; - ResetTimer = 30000; - - TalkTimer = 0; - TalkSequence = 0; - isFriendly = false; - isEnraged = false; - isBanished = false; - } + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (events.IsInPhase(PHASE_OUTRO)) + return; - InstanceScript* instance; + _EnterEvadeMode(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SPECTRAL_REALM_AURA); + summons.DespawnAll(); + DespawnPortals(); - uint32 ArcaneBuffetTimer; - uint32 FrostBreathTimer; - uint32 WildMagicTimer; - uint32 SpectralBlastTimer; - uint32 TailLashTimer; - uint32 CheckTimer; - uint32 TalkTimer; - uint32 TalkSequence; - uint32 ResetTimer; + if (Creature* sathrovar = instance->GetCreature(DATA_SATHROVARR)) + _DespawnAtEvade(Seconds(10), sathrovar); - bool isFriendly; - bool isEnraged; - bool isBanished; - bool bJustReset; + _DespawnAtEvade(Seconds(10)); + } - ObjectGuid SathGUID; + void DespawnPortals() + { + std::vector<GameObject*> portals; + me->GetGameObjectListWithEntryInGrid(portals, GO_SPECTRAL_RIFT); + for (GameObject* portal : portals) + portal->Delete(); + } - void Reset() override + void DoAction(int32 action) override + { + switch (action) { - if (Creature* sath = instance->GetCreature(DATA_SATHROVARR)) - SathGUID = sath->GetGUID(); + case ACTION_START_OUTRO: + events.ScheduleEvent(EVENT_OUTRO_START, Seconds(1)); + break; + case ACTION_ENRAGE: + _isEnraged = true; + Talk(EMOTE_ENRAGE); + DoCastSelf(SPELL_ENRAGE, true); + break; + default: + break; + } + } - instance->SetBossState(DATA_KALECGOS, NOT_STARTED); + void DamageTaken(Unit* who, uint32 &damage) override + { + if (damage >= me->GetHealth() && who->GetGUID() != me->GetGUID()) + damage = 0; + } - if (Creature* Sath = ObjectAccessor::GetCreature(*me, SathGUID)) - Sath->AI()->EnterEvadeMode(); + void EnterCombat(Unit* /*who*/) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + Talk(SAY_EVIL_AGGRO); + _EnterCombat(); - me->SetFaction(FACTION_MONSTER); - if (!bJustReset) //first reset at create + if (Creature* kalecgosHuman = me->SummonCreature(NPC_KALECGOS_HUMAN, KalecgosSummonPos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1000)) + if (Creature* sathrovar = instance->GetCreature(DATA_SATHROVARR)) { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - me->SetDisableGravity(false); - me->SetVisible(true); - me->SetStandState(UNIT_STAND_STATE_SLEEP); + sathrovar->SetInCombatWith(kalecgosHuman); + kalecgosHuman->SetInCombatWith(sathrovar); } - me->SetFullHealth(); //dunno why it does not resets health at evade.. - } + } + + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() == TYPEID_PLAYER && roll_chance_i(50)) + Talk(SAY_EVIL_SLAY); + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + return; - void EnterEvadeMode(EvadeReason why) override + switch (id) { - bJustReset = true; - me->SetVisible(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - ScriptedAI::EnterEvadeMode(why); + case POINT_OUTRO_1: + Talk(SAY_OUTRO_1); + events.ScheduleEvent(EVENT_OUTRO_3, Seconds(9)); + break; + case POINT_OUTRO_2: + me->SetVisible(false); + DespawnPortals(); + me->KillSelf(); + break; + default: + break; } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !events.IsInPhase(PHASE_OUTRO)) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void DoAction(int32 param) override + while (uint32 eventId = events.ExecuteEvent()) { - switch (param) + switch (eventId) { - case DO_ENRAGE: - isEnraged = true; - me->CastSpell(me, SPELL_ENRAGE, true); + case EVENT_ARCANE_BUFFET: + if (roll_chance_i(20)) + Talk(SAY_ARCANE_BUFFET); + DoCastAOE(SPELL_ARCANE_BUFFET); + events.Repeat(Seconds(8)); break; - case DO_BANISH: - isBanished = true; - me->CastSpell(me, SPELL_BANISH, true); + case EVENT_FROST_BREATH: + DoCastAOE(SPELL_FROST_BREATH); + events.Repeat(Seconds(15)); break; - } - } - - void UpdateAI(uint32 diff) override - { - if (TalkTimer) - { - if (!TalkSequence) - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - me->InterruptNonMeleeSpells(true); - me->RemoveAllAuras(); - me->GetThreatManager().ClearAllThreat(); - me->CombatStop(); - ++TalkSequence; - } - if (TalkTimer <= diff) - { - if (isFriendly) - GoodEnding(); - else - BadEnding(); - ++TalkSequence; - } else TalkTimer -= diff; - } - else - { - if (bJustReset) - { - if (ResetTimer <= diff) - { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - me->SetDisableGravity(false); - me->SetVisible(true); - me->SetStandState(UNIT_STAND_STATE_SLEEP); - ResetTimer = 10000; - bJustReset = false; - } else ResetTimer -= diff; - return; - } - - if (!UpdateVictim()) - return; + case EVENT_TAIL_LASH: + DoCastAOE(SPELL_TAIL_LASH); + events.Repeat(Seconds(15)); + break; + case EVENT_WILD_MAGIC: + DoCastAOE(WildMagicSpells[urand(0, 5)], true); + events.Repeat(Seconds(20)); + break; + case EVENT_SPECTRAL_BLAST: + DoCastAOE(SPELL_SPECTRAL_BLAST, true); + events.Repeat(Seconds(20), Seconds(25)); + break; + case EVENT_CHECK_TIMER: + if (!_isEnraged && HealthBelowPct(10)) + DoAction(ACTION_ENRAGE); - if (CheckTimer <= diff) - { - if (me->GetDistance(CENTER_X, CENTER_Y, DRAGON_REALM_Z) >= 75) - { - EnterEvadeMode(EVADE_REASON_BOUNDARY); - return; - } - if (HealthBelowPct(10) && !isEnraged) + if (HealthBelowPct(1)) { - if (Creature* Sath = ObjectAccessor::GetCreature(*me, SathGUID)) - Sath->AI()->DoAction(DO_ENRAGE); - DoAction(DO_ENRAGE); - } - if (!isBanished && HealthBelowPct(1)) - { - if (Creature* Sath = ObjectAccessor::GetCreature(*me, SathGUID)) + if (Creature* sathrovarr = instance->GetCreature(DATA_SATHROVARR)) { - if (Sath->HasAura(SPELL_BANISH)) + if (sathrovarr->HasAura(SPELL_BANISH)) { - Sath->DealDamage(Sath, Sath->GetHealth()); - return; + sathrovarr->CastSpell(sathrovarr, SPELL_TAP_CHECK, true); + break; } - else - DoAction(DO_BANISH); - } - else - { - TC_LOG_ERROR("scripts", "Didn't find Shathrowar. Kalecgos event reseted."); - EnterEvadeMode(EVADE_REASON_OTHER); - return; } + if (_isBanished) + break; + + _isBanished = true; + DoCastSelf(SPELL_BANISH, true); + events.Reset(); } - CheckTimer = 1000; - } else CheckTimer -= diff; + events.Repeat(Seconds(1)); + break; + case EVENT_OUTRO_START: + events.Reset(); + events.SetPhase(PHASE_OUTRO); + me->SetRegenerateHealth(false); + me->SetReactState(REACT_PASSIVE); + me->InterruptNonMeleeSpells(true); + me->RemoveAllAttackers(); + me->AttackStop(); + me->SetFaction(FACTION_FRIENDLY); + me->RemoveAllAuras(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + events.ScheduleEvent(EVENT_OUTRO_1, Seconds(3)); + break; + case EVENT_OUTRO_1: + me->SetDisableGravity(true); + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + events.ScheduleEvent(EVENT_OUTRO_2, Seconds(3)); + break; + case EVENT_OUTRO_2: + me->GetMotionMaster()->MovePoint(POINT_OUTRO_1, FlyPos[0]); + break; + case EVENT_OUTRO_3: + Talk(SAY_OUTRO_2); + me->GetMotionMaster()->MovePoint(POINT_OUTRO_2, FlyPos[1], false); + break; + default: + break; + } - if (ArcaneBuffetTimer <= diff) - { - DoCastAOE(SPELL_ARCANE_BUFFET); - ArcaneBuffetTimer = 8000; - } else ArcaneBuffetTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - if (FrostBreathTimer <= diff) - { - DoCastAOE(SPELL_FROST_BREATH); - FrostBreathTimer = 15000; - } else FrostBreathTimer -= diff; + DoMeleeAttackIfReady(); + } - if (TailLashTimer <= diff) - { - DoCastAOE(SPELL_TAIL_LASH); - TailLashTimer = 15000; - } else TailLashTimer -= diff; +private: + bool _isEnraged; + bool _isBanished; +}; - if (WildMagicTimer <= diff) - { - DoCastAOE(WildMagic[rand32() % 6]); - WildMagicTimer = 20000; - } else WildMagicTimer -= diff; +struct boss_kalecgos_human : public ScriptedAI +{ + boss_kalecgos_human(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - if (SpectralBlastTimer <= diff) - { - ThreatContainer::StorageType const& m_threatlist = me->GetThreatManager().getThreatList(); - std::list<Unit*> targetList; - for (ThreatContainer::StorageType::const_iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) - { - Unit* target = (*itr)->getTarget(); - if (target - && target->GetTypeId() == TYPEID_PLAYER - && (!target->GetVictim() || target->GetGUID() != me->EnsureVictim()->GetGUID()) - && target->GetPositionZ() > me->GetPositionZ() - 5 - && !target->HasAura(AURA_SPECTRAL_EXHAUSTION)) - { - targetList.push_back(target); - } - } - if (targetList.empty()) - { - SpectralBlastTimer = 1000; - return; - } + void Reset() override + { + _events.Reset(); + _events.SetPhase(PHASE_SAY_ONE); - std::list<Unit*>::const_iterator i = targetList.begin(); - advance(i, rand32() % targetList.size()); - if ((*i)) - { - (*i)->CastSpell((*i), SPELL_SPECTRAL_BLAST, true); - SpectralBlastTimer = 20000 + rand32() % 5000; - } else SpectralBlastTimer = 1000; - } else SpectralBlastTimer -= diff; + if (Creature* sath = _instance->GetCreature(DATA_SATHROVARR)) + _sathGUID = sath->GetGUID(); - DoMeleeAttackIfReady(); - } - } + _events.ScheduleEvent(EVENT_REVITALIZE, Seconds(5)); + _events.ScheduleEvent(EVENT_HEROIC_STRIKE, Seconds(3)); + } - void MoveInLineOfSight(Unit* who) override - { - if (bJustReset)//boss is invisible, don't attack - return; + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_GOOD_DEATH); + } - if (!me->GetVictim() && me->IsValidAttackTarget(who)) - { - float attackRadius = me->GetAttackDistance(who); - if (me->IsWithinDistInMap(who, attackRadius)) - AttackStart(who); - } - } + void DamageTaken(Unit* who, uint32 &damage) override + { + if (who->GetGUID() != _sathGUID) + damage = 0; - void DamageTaken(Unit* done_by, uint32 &damage) override + if (HealthBelowPct(75) && _events.IsInPhase(PHASE_SAY_ONE)) { - if (damage >= me->GetHealth() && done_by != me) - damage = 0; + Talk(SAY_GOOD_NEAR_DEATH_0); + _events.SetPhase(PHASE_SAY_TWO); } - - void EnterCombat(Unit* /*who*/) override + else if (HealthBelowPct(50) && _events.IsInPhase(PHASE_SAY_TWO)) { - me->SetStandState(UNIT_STAND_STATE_STAND); - Talk(SAY_EVIL_AGGRO); - DoZoneInCombat(); - - instance->SetBossState(DATA_KALECGOS, IN_PROGRESS); + _events.SetPhase(PHASE_SAY_THREE); + Talk(SAY_GOOD_NEAR_DEATH_1); } - - void KilledUnit(Unit* /*victim*/) override + else if (HealthBelowPct(10) && _events.IsInPhase(PHASE_SAY_THREE)) { - Talk(SAY_EVIL_SLAY); + _events.SetPhase(PHASE_SAY_FOUR); + Talk(SAY_GOOD_NEAR_DEATH_2); } + } - void MovementInform(uint32 type, uint32 /*id*/) override - { - if (type != POINT_MOTION_TYPE) - return; - me->SetVisible(false); - if (isFriendly) - { - me->setDeathState(JUST_DIED); - me->GetMap()->ToInstanceMap()->PermBindAllPlayers(); - } - else - { - me->GetMotionMaster()->MoveTargetedHome(); - TalkTimer = 1000; - } - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void GoodEnding() - { - switch (TalkSequence) - { - case 1: - me->SetFaction(FACTION_FRIENDLY); - TalkTimer = 1000; - break; - case 2: - Talk(SAY_GOOD_PLRWIN); - TalkTimer = 10000; - break; - case 3: - me->SetDisableGravity(true); - me->GetMotionMaster()->MovePoint(0, FLY_X, FLY_Y, FLY_Z); - TalkTimer = 600000; - break; - default: - break; - } - } + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void BadEnding() + while (uint32 eventId = _events.ExecuteEvent()) { - switch (TalkSequence) + switch (eventId) { - case 1: - Talk(SAY_EVIL_ENRAGE); - TalkTimer = 3000; + case EVENT_REVITALIZE: + DoCastSelf(SPELL_REVITALIZE); + _events.Repeat(Seconds(5)); break; - case 2: - me->SetDisableGravity(true); - me->GetMotionMaster()->MovePoint(0, FLY_X, FLY_Y, FLY_Z); - TalkTimer = 15000; - break; - case 3: - EnterEvadeMode(EVADE_REASON_OTHER); + case EVENT_HEROIC_STRIKE: + DoCastVictim(SPELL_HEROIC_STRIKE); + _events.Repeat(Seconds(2)); break; default: break; } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetSunwellPlateauAI<boss_kalecgosAI>(creature); + DoMeleeAttackIfReady(); } + +private: + InstanceScript* _instance; + EventMap _events; + ObjectGuid _sathGUID; }; -class boss_kalec : public CreatureScript +class CurseAgonySelector : NonTankTargetSelector { public: - boss_kalec() : CreatureScript("boss_kalec") { } + CurseAgonySelector(Unit* source) : NonTankTargetSelector(source, true) { } - CreatureAI* GetAI(Creature* creature) const override + bool operator()(WorldObject* target) const { - return GetSunwellPlateauAI<boss_kalecAI>(creature); + if (Unit* unitTarget = target->ToUnit()) + return !NonTankTargetSelector::operator()(unitTarget) + || unitTarget->HasAura(SPELL_AGONY_CURSE) || unitTarget->HasAura(SPELL_AGONY_CURSE_ALLY) + || !unitTarget->HasAura(SPELL_SPECTRAL_REALM_AURA); + return false; } +}; - struct boss_kalecAI : public ScriptedAI - { - InstanceScript* instance; +struct boss_sathrovarr : public BossAI +{ + boss_sathrovarr(Creature* creature) : BossAI(creature, DATA_KALECGOS), _isEnraged(false), _isBanished(false) { } - uint32 RevitalizeTimer; - uint32 HeroicStrikeTimer; - uint32 YellTimer; - uint32 YellSequence; + void Reset() override + { + _isEnraged = false; + _isBanished = false; + _Reset(); + events.ScheduleEvent(EVENT_SHADOWBOLT, Seconds(7), Seconds(10)); + events.ScheduleEvent(EVENT_AGONY_CURSE, Seconds(20)); + events.ScheduleEvent(EVENT_CORRUPTION_STRIKE, Seconds(13)); + events.ScheduleEvent(EVENT_CHECK_TIMER, Seconds(1)); + } - ObjectGuid SathGUID; + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_SATH_AGGRO); + } - bool isEnraged; // if demon is enraged + void EnterEvadeMode(EvadeReason why) override + { + if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) + kalecgos->AI()->EnterEvadeMode(why); + } - boss_kalecAI(Creature* creature) : ScriptedAI(creature) + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_TAP_CHECK_DAMAGE) { - Initialize(); - instance = creature->GetInstanceScript(); + DoCastSelf(SPELL_TELEPORT_BACK, true); + caster->Kill(me); } + } - void Initialize() - { - RevitalizeTimer = 5000; - HeroicStrikeTimer = 3000; - YellTimer = 5000; - YellSequence = 0; - - isEnraged = false; - } + void DamageTaken(Unit* who, uint32 &damage) override + { + if (damage >= me->GetHealth() && who->GetGUID() != me->GetGUID()) + damage = 0; + } - void Reset() override + void KilledUnit(Unit* target) override + { + if (target->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SATH_SLAY); + else if (Creature* kalecgosHuman = instance->GetCreature(DATA_KALECGOS_HUMAN)) { - if (Creature* sath = instance->GetCreature(DATA_SATHROVARR)) - SathGUID = sath->GetGUID(); - - Initialize(); + if (kalecgosHuman->GetGUID() == target->GetGUID()) + EnterEvadeMode(EVADE_REASON_OTHER); } + } - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (done_by->GetGUID() != SathGUID) - damage = 0; - else if (isEnraged) - damage *= 3; - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_SATH_DEATH); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SPECTRAL_REALM_AURA); + if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) + kalecgos->AI()->DoAction(ACTION_START_OUTRO); + } - void UpdateAI(uint32 diff) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - if (!me->HasAura(AURA_SPECTRAL_INVISIBILITY)) - me->CastSpell(me, AURA_SPECTRAL_INVISIBILITY, true); - - if (!UpdateVictim()) - return; - - if (YellTimer <= diff) + case EVENT_SHADOWBOLT: + if (roll_chance_i(20)) + Talk(SAY_SATH_SPELL1); + DoCastAOE(SPELL_SHADOW_BOLT); + events.Repeat(Seconds(7), Seconds(10)); + break; + case EVENT_AGONY_CURSE: { - switch (YellSequence) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, CurseAgonySelector(me))) + DoCast(target, SPELL_AGONY_CURSE); + else + DoCastVictim(SPELL_AGONY_CURSE); + events.Repeat(Seconds(20)); + break; + } + case EVENT_CORRUPTION_STRIKE: + if (roll_chance_i(20)) + Talk(SAY_SATH_SPELL2); + DoCastVictim(SPELL_CORRUPTION_STRIKE); + events.Repeat(Seconds(13)); + break; + case EVENT_CHECK_TIMER: + { + if (HealthBelowPct(10) && !_isEnraged) { - case 0: - Talk(SAY_GOOD_AGGRO); - ++YellSequence; - break; - case 1: - if (HealthBelowPct(50)) - { - Talk(SAY_GOOD_NEAR_DEATH); - ++YellSequence; - } - break; - case 2: - if (HealthBelowPct(10)) + _isEnraged = true; + if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) + kalecgos->AI()->DoAction(ACTION_ENRAGE); + } + + if (HealthBelowPct(1)) + { + if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) + { + if (kalecgos->HasAura(SPELL_BANISH)) { - Talk(SAY_GOOD_NEAR_DEATH2); - ++YellSequence; + DoCastSelf(SPELL_TAP_CHECK, true); + break; } + } + if (_isBanished) break; - default: - break; + + _isBanished = true; + DoCastSelf(SPELL_BANISH, true); } - YellTimer = 5000; + events.Repeat(Seconds(1)); + break; } - - if (RevitalizeTimer <= diff) - { - DoCast(me, SPELL_REVITALIZE); - RevitalizeTimer = 5000; - } else RevitalizeTimer -= diff; - - if (HeroicStrikeTimer <= diff) - { - DoCastVictim(SPELL_HEROIC_STRIKE); - HeroicStrikeTimer = 2000; - } else HeroicStrikeTimer -= diff; - - DoMeleeAttackIfReady(); + default: + break; } - }; + } + +private: + bool _isEnraged; + bool _isBanished; }; -class kalecgos_teleporter : public GameObjectScript +class go_kalecgos_spectral_rift : public GameObjectScript { -public: - kalecgos_teleporter() : GameObjectScript("kalecgos_teleporter") { } - - struct kalecgos_teleporterAI : public GameObjectAI - { - kalecgos_teleporterAI(GameObject* go) : GameObjectAI(go) { } + public: + go_kalecgos_spectral_rift() : GameObjectScript("go_kalecgos_spectral_rift") { } - bool GossipHello(Player* player) override + struct go_kalecgos_spectral_riftAI : public GameObjectAI { -#if MAX_PLAYERS_IN_SPECTRAL_REALM > 0 - uint8 SpectralPlayers = 0; - Map::PlayerList const& PlayerList = go->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - { - if (i->GetSource() && i->GetSource()->GetPositionZ() < DEMON_REALM_Z + 5) - ++SpectralPlayers; - } + go_kalecgos_spectral_riftAI(GameObject* go) : GameObjectAI(go) { } - if (player->HasAura(AURA_SPECTRAL_EXHAUSTION) || SpectralPlayers >= MAX_PLAYERS_IN_SPECTRAL_REALM) + bool GossipHello(Player* player) override { + if (!player->HasAura(SPELL_SPECTRAL_EXHAUSTION)) + player->CastSpell(player, SPELL_SPECTRAL_REALM_TRIGGER, true); return true; } -#endif + }; - player->CastSpell(player, SPELL_TELEPORT_SPECTRAL, true); - return true; + GameObjectAI* GetAI(GameObject* go) const override + { + return GetSunwellPlateauAI<go_kalecgos_spectral_riftAI>(go); } - }; - - GameObjectAI* GetAI(GameObject* go) const override - { - return GetSunwellPlateauAI<kalecgos_teleporterAI>(go); - } }; -class boss_sathrovarr : public CreatureScript +// 46732 - Tap Check +class spell_kalecgos_tap_check : public SpellScript { -public: - boss_sathrovarr() : CreatureScript("boss_sathrovarr") { } + PrepareSpellScript(spell_kalecgos_tap_check); - CreatureAI* GetAI(Creature* creature) const override + bool Validate(SpellInfo const* spellInfo) override { - return GetSunwellPlateauAI<boss_sathrovarrAI>(creature); + return ValidateSpellInfo({ uint32(spellInfo->Effects[EFFECT_0].CalcValue()) }); } - struct boss_sathrovarrAI : public ScriptedAI + void HandleDummy(SpellEffIndex /*effIndex*/) { - boss_sathrovarrAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } + GetHitUnit()->CastSpell(GetCaster(), (uint32)GetSpellInfo()->Effects[EFFECT_0].CalcValue(), true); + } - void Initialize() + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_kalecgos_tap_check::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +class SpectralBlastSelector : NonTankTargetSelector +{ + public: + SpectralBlastSelector(Unit* source) : NonTankTargetSelector(source, true) { } + + bool operator()(WorldObject* target) const { - ShadowBoltTimer = urand(7, 10) * 1000; - AgonyCurseTimer = 20000; - CorruptionStrikeTimer = 13000; - CheckTimer = 1000; - ResetThreat = 1000; - isEnraged = false; - isBanished = false; + if (Unit* unitTarget = target->ToUnit()) + return !NonTankTargetSelector::operator()(unitTarget) || + unitTarget->HasAura(SPELL_SPECTRAL_EXHAUSTION) || unitTarget->HasAura(SPELL_SPECTRAL_REALM_AURA); + return false; } +}; - InstanceScript* instance; +// 44869 - Spectral Blast +class spell_kalecgos_spectral_blast : public SpellScript +{ + PrepareSpellScript(spell_kalecgos_spectral_blast); - uint32 CorruptionStrikeTimer; - uint32 AgonyCurseTimer; - uint32 ShadowBoltTimer; - uint32 CheckTimer; - uint32 ResetThreat; + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( + { + SPELL_SPECTRAL_BLAST_EFFECT, + SPELL_SPECTRAL_BLAST_VISUAL, + SPELL_SPECTRAL_REALM_TRIGGER + }); + } - ObjectGuid KalecGUID; - ObjectGuid KalecgosGUID; + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if(SpectralBlastSelector(GetCaster())); + } - bool isEnraged; - bool isBanished; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + Unit* target = GetHitUnit(); - void Reset() override - { - me->SetFullHealth();//dunno why it does not resets health at evade.. - me->setActive(true); - if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS_DRAGON)) - KalecgosGUID = kalecgos->GetGUID(); - instance->SetBossState(DATA_KALECGOS, NOT_STARTED); - if (KalecGUID) - { - if (Creature* Kalec = ObjectAccessor::GetCreature(*me, KalecGUID)) - Kalec->setDeathState(JUST_DIED); - KalecGUID.Clear(); - } + target->CastSpell(target, SPELL_SPECTRAL_BLAST_EFFECT, true); + caster->CastSpell(target, SPELL_SPECTRAL_BLAST_VISUAL, true); + caster->CastSpell(target, SPELL_SPECTRAL_REALM_TRIGGER, true); + } - Initialize(); + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_kalecgos_spectral_blast::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_kalecgos_spectral_blast::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; - me->CastSpell(me, AURA_DEMONIC_VISUAL, true); - TeleportAllPlayersBack(); - } +// 44811 - Spectral Realm +class spell_kalecgos_spectral_realm_trigger : public SpellScript +{ + PrepareSpellScript(spell_kalecgos_spectral_realm_trigger); - void EnterCombat(Unit* /*who*/) override + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - if (Creature* Kalec = me->SummonCreature(NPC_KALECGOS_HUMAN, me->GetPositionX() + 10, me->GetPositionY() + 5, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0)) - { - KalecGUID = Kalec->GetGUID(); - me->CombatStart(Kalec); - AddThreat(Kalec, 100.0f); - Kalec->setActive(true); - } - Talk(SAY_SATH_AGGRO); - } + SPELL_SPECTRAL_REALM_TELEPORT, + SPELL_SPECTRAL_REALM_AURA, + SPELL_SPECTRAL_REALM_2, + SPELL_SPECTRAL_REALM_REACTION + }); + } - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (damage >= me->GetHealth() && done_by != me) - damage = 0; - } + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + target->CastSpell(target, SPELL_SPECTRAL_REALM_TELEPORT, true); + target->CastSpell(target, SPELL_SPECTRAL_REALM_AURA, true); + target->CastSpell(target, SPELL_SPECTRAL_REALM_2, true); + target->CastSpell(target, SPELL_SPECTRAL_REALM_REACTION, true); + } - void KilledUnit(Unit* target) override - { - if (target->GetGUID() == KalecGUID) - { - TeleportAllPlayersBack(); - if (Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID)) - { - ENSURE_AI(boss_kalecgos::boss_kalecgosAI, Kalecgos->AI())->TalkTimer = 1; - ENSURE_AI(boss_kalecgos::boss_kalecgosAI, Kalecgos->AI())->isFriendly = false; - } - EnterEvadeMode(); - return; - } - Talk(SAY_SATH_SLAY); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_kalecgos_spectral_realm_trigger::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 46021 - Spectral Realm +class spell_kalecgos_spectral_realm_aura : public AuraScript +{ + PrepareAuraScript(spell_kalecgos_spectral_realm_aura); - void JustDied(Unit* /*killer*/) override + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - Talk(SAY_SATH_DEATH); - me->UpdatePosition(me->GetPositionX(), me->GetPositionY(), DRAGON_REALM_Z, me->GetOrientation()); - TeleportAllPlayersBack(); - if (Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID)) - { - ENSURE_AI(boss_kalecgos::boss_kalecgosAI, Kalecgos->AI())->TalkTimer = 1; - ENSURE_AI(boss_kalecgos::boss_kalecgosAI, Kalecgos->AI())->isFriendly = true; - } + SPELL_SPECTRAL_REALM_REACTION, + SPELL_TELEPORT_BACK, + SPELL_SPECTRAL_EXHAUSTION + }); + } - instance->SetBossState(DATA_KALECGOS, DONE); - } + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->RemoveAurasDueToSpell(SPELL_SPECTRAL_REALM_REACTION); + target->CastSpell(target, SPELL_TELEPORT_BACK, true); + target->CastSpell(target, SPELL_SPECTRAL_EXHAUSTION, true); + } - void TeleportAllPlayersBack() - { - Map::PlayerList const& playerList = me->GetMap()->GetPlayers(); - Position const& homePos = me->GetHomePosition(); - for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) - { - Player* player = itr->GetSource(); - if (player->IsInDist(&homePos, 50.0f) && player->GetPositionZ() <= DEMON_REALM_Z + 10.f) - { - player->RemoveAura(AURA_SPECTRAL_REALM); - player->TeleportTo(me->GetMap()->GetId(), player->GetPositionX(), - player->GetPositionY(), DRAGON_REALM_Z + 5, player->GetOrientation()); - } - } - } + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_kalecgos_spectral_realm_aura::OnRemove, EFFECT_0, SPELL_AURA_MOD_INVISIBILITY_DETECT, AURA_EFFECT_HANDLE_REAL); + } +}; - void DoAction(int32 param) override - { - switch (param) - { - case DO_ENRAGE: - isEnraged = true; - me->CastSpell(me, SPELL_ENRAGE, true); - break; - case DO_BANISH: - isBanished = true; - me->CastSpell(me, SPELL_BANISH, true); - break; - } - } +// 45032, 45034 - Curse of Boundless Agony +class spell_kalecgos_curse_of_boundless_agony : public AuraScript +{ + PrepareAuraScript(spell_kalecgos_curse_of_boundless_agony); - void UpdateAI(uint32 diff) override + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - if (!me->HasAura(AURA_SPECTRAL_INVISIBILITY)) - me->CastSpell(me, AURA_SPECTRAL_INVISIBILITY, true); + SPELL_AGONY_CURSE_VISUAL_1, + SPELL_AGONY_CURSE_VISUAL_2, + SPELL_AGONY_CURSE_VISUAL_3, + SPELL_AGONY_CURSE_ALLY + }); + } - if (!UpdateVictim()) + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (InstanceScript* instance = GetTarget()->GetInstanceScript()) + if (instance->GetBossState(DATA_KALECGOS) == IN_PROGRESS) return; - if (CheckTimer <= diff) - { - Creature* Kalec = ObjectAccessor::GetCreature(*me, KalecGUID); - if (!Kalec || !Kalec->IsAlive()) - { - if (Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID)) - Kalecgos->AI()->EnterEvadeMode(); - return; - } - - if (HealthBelowPct(10) && !isEnraged) - { - if (Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID)) - Kalecgos->AI()->DoAction(DO_ENRAGE); - DoAction(DO_ENRAGE); - } - - Creature* Kalecgos = ObjectAccessor::GetCreature(*me, KalecgosGUID); - if (Kalecgos && !Kalecgos->IsInCombat()) - { - EnterEvadeMode(); - return; - } - - if (!isBanished && HealthBelowPct(1)) - { - if (Kalecgos) - { - if (Kalecgos->HasAura(SPELL_BANISH)) - { - me->DealDamage(me, me->GetHealth()); - return; - } - DoAction(DO_BANISH); - } - else - { - EnterEvadeMode(); - return; - } - } - CheckTimer = 1000; - } else CheckTimer -= diff; + Remove(AURA_REMOVE_BY_CANCEL); + } - if (ResetThreat <= diff) - { - ThreatContainer::StorageType threatlist = me->GetThreatManager().getThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) - { - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) - if (unit->GetPositionZ() > me->GetPositionZ() + 5) - me->GetThreatManager().ModifyThreatByPercent(unit, -100); - } - ResetThreat = 1000; - } else ResetThreat -= diff; + void OnPeriodic(AuraEffect const* aurEff) + { + if (aurEff->GetTickNumber() <= 5) + GetTarget()->CastSpell(GetTarget(), SPELL_AGONY_CURSE_VISUAL_1, true); + else if (aurEff->GetTickNumber() <= 10) + GetTarget()->CastSpell(GetTarget(), SPELL_AGONY_CURSE_VISUAL_2, true); + else + GetTarget()->CastSpell(GetTarget(), SPELL_AGONY_CURSE_VISUAL_3, true); + } - if (ShadowBoltTimer <= diff) - { - if (!(rand32() % 5)) - Talk(SAY_SATH_SPELL1); - DoCast(me, SPELL_SHADOW_BOLT); - ShadowBoltTimer = 7000 + (rand32() % 3000); - } else ShadowBoltTimer -= diff; + void HandleEffectPeriodicUpdate(AuraEffect* aurEff) + { + if (aurEff->GetTickNumber() > 1 && aurEff->GetTickNumber() % 5 == 1) + aurEff->SetAmount(aurEff->GetAmount() * 2); + } - if (AgonyCurseTimer <= diff) - { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1); - if (!target) - target = me->GetVictim(); - DoCast(target, SPELL_AGONY_CURSE); - AgonyCurseTimer = 20000; - } else AgonyCurseTimer -= diff; - - if (CorruptionStrikeTimer <= diff) - { - if (!(rand32() % 5))Talk(SAY_SATH_SPELL2); - DoCastVictim(SPELL_CORRUPTION_STRIKE); - CorruptionStrikeTimer = 13000; - } else CorruptionStrikeTimer -= diff; + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_CANCEL) + GetTarget()->CastSpell(GetTarget(), SPELL_AGONY_CURSE_ALLY, true); + } - DoMeleeAttackIfReady(); - } - }; + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_kalecgos_curse_of_boundless_agony::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_kalecgos_curse_of_boundless_agony::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_kalecgos_curse_of_boundless_agony::HandleEffectPeriodicUpdate, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + AfterEffectRemove += AuraEffectRemoveFn(spell_kalecgos_curse_of_boundless_agony::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } }; void AddSC_boss_kalecgos() { - new boss_kalecgos(); - new boss_sathrovarr(); - new boss_kalec(); - new kalecgos_teleporter(); + RegisterSunwellPlateauCreatureAI(boss_kalecgos); + RegisterSunwellPlateauCreatureAI(boss_sathrovarr); + RegisterSunwellPlateauCreatureAI(boss_kalecgos_human); + new go_kalecgos_spectral_rift(); + RegisterSpellScript(spell_kalecgos_tap_check); + RegisterSpellScript(spell_kalecgos_spectral_blast); + RegisterSpellScript(spell_kalecgos_spectral_realm_trigger); + RegisterAuraScript(spell_kalecgos_spectral_realm_aura); + RegisterAuraScript(spell_kalecgos_curse_of_boundless_agony); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp index f0bcb9dd3de..ae9725525a1 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp @@ -124,7 +124,7 @@ class VoidSpawnSummon : public BasicEvent bool Execute(uint64 /*time*/, uint32 /*diff*/) { - _owner->CastSpell((Unit*)nullptr, SPELL_SUMMON_VOID_SENTINEL, true); + _owner->CastSpell(nullptr, SPELL_SUMMON_VOID_SENTINEL, true); return true; } @@ -589,7 +589,7 @@ class spell_summon_blood_elves_script : SpellScriptLoader void HandleScript(SpellEffIndex /*effIndex*/) { for (uint8 i = 0; i < MAX_SUMMON_BLOOD_ELVES; ++i) - GetCaster()->CastSpell((Unit*)nullptr, SummonBloodElvesSpells[urand(0,3)], true); + GetCaster()->CastSpell(nullptr, SummonBloodElvesSpells[urand(0,3)], true); } void Register() override @@ -621,7 +621,7 @@ class spell_muru_darkness : SpellScriptLoader void HandleAfterCast() { for (uint8 i = 0; i < MAX_SUMMON_DARK_FIEND; ++i) - GetCaster()->CastSpell((Unit*)nullptr, SummonDarkFiendSpells[i], true); + GetCaster()->CastSpell(nullptr, SummonDarkFiendSpells[i], true); } void Register() override @@ -683,7 +683,7 @@ class spell_transform_visual_missile_periodic : public SpellScriptLoader void OnPeriodic(AuraEffect const* /*aurEff*/) { - GetTarget()->CastSpell((Unit*)nullptr, RAND(TRANSFORM_VISUAL_MISSILE_1, TRANSFORM_VISUAL_MISSILE_2), true); + GetTarget()->CastSpell(nullptr, RAND(TRANSFORM_VISUAL_MISSILE_1, TRANSFORM_VISUAL_MISSILE_2), true); } void Register() override @@ -709,7 +709,7 @@ class spell_summon_blood_elves_periodic : public SpellScriptLoader void OnPeriodic(AuraEffect const* /*aurEff*/) { - GetTarget()->CastSpell((Unit*)nullptr, SPELL_SUMMON_BLOOD_ELVES_SCRIPT, true); + GetTarget()->CastSpell(nullptr, SPELL_SUMMON_BLOOD_ELVES_SCRIPT, true); } void Register() override diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp index 6b3c2b1bcaf..e5df84f8ba1 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/instance_sunwell_plateau.cpp @@ -16,6 +16,7 @@ */ #include "ScriptMgr.h" +#include "AreaBoundary.h" #include "InstanceScript.h" #include "Log.h" #include "Map.h" @@ -60,6 +61,11 @@ ObjectData const creatureData[] = { 0, 0 } // END }; +BossBoundaryData const boundaries = +{ + { DATA_KALECGOS, new BoundaryUnionBoundary(new CircleBoundary(Position(1704.9f, 928.4f), 34.0), new RectangleBoundary(1689.2f, 1713.3f, 762.2f, 1074.8f)) } +}; + class instance_sunwell_plateau : public InstanceMapScript { public: @@ -73,6 +79,7 @@ class instance_sunwell_plateau : public InstanceMapScript SetBossNumber(EncounterCount); LoadDoorData(doorData); LoadObjectData(creatureData, nullptr); + LoadBossBoundaries(boundaries); } Player const* GetPlayerInMap() const diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h index cec43582958..85fbde015fb 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h @@ -111,7 +111,8 @@ enum SWPGameObjectIds GO_BOSS_COLLISION_2 = 188524, GO_FIRE_BARRIER = 188075, GO_MURUS_GATE_1 = 187990, - GO_MURUS_GATE_2 = 188118 + GO_MURUS_GATE_2 = 188118, + GO_SPECTRAL_RIFT = 187055 }; template <class AI, class T> @@ -120,4 +121,6 @@ AI* GetSunwellPlateauAI(T* obj) return GetInstanceAI<AI>(obj, SunwellPlateauScriptName); } +#define RegisterSunwellPlateauCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetSunwellPlateauAI) + #endif // SUNWELL_PLATEAU_H diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h index ae1323af6aa..57d738698c7 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h +++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h @@ -33,7 +33,7 @@ enum ZADataTypes BOSS_HEXLORD = 4, BOSS_ZULJIN = 5, MAX_ENCOUNTER, - + DATA_GONGEVENT, DATA_CHESTLOOTED, TYPE_RAND_VENDOR_1, diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp index 371be75b9f6..047467d0338 100644 --- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp +++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp @@ -175,16 +175,13 @@ void AddSC_instance_zulgurub(); //void AddSC_alterac_mountains(); void AddSC_arathi_highlands(); void AddSC_blasted_lands(); -void AddSC_burning_steppes(); void AddSC_duskwood(); //void AddSC_eastern_plaguelands(); void AddSC_ghostlands(); void AddSC_hinterlands(); -void AddSC_isle_of_queldanas(); void AddSC_redridge_mountains(); void AddSC_silverpine_forest(); void AddSC_stormwind_city(); -void AddSC_stranglethorn_vale(); void AddSC_swamp_of_sorrows(); void AddSC_tirisfal_glades(); void AddSC_undercity(); @@ -354,16 +351,13 @@ void AddEasternKingdomsScripts() //AddSC_alterac_mountains(); AddSC_arathi_highlands(); AddSC_blasted_lands(); - AddSC_burning_steppes(); AddSC_duskwood(); //AddSC_eastern_plaguelands(); AddSC_ghostlands(); AddSC_hinterlands(); - AddSC_isle_of_queldanas(); AddSC_redridge_mountains(); AddSC_silverpine_forest(); AddSC_stormwind_city(); - AddSC_stranglethorn_vale(); AddSC_swamp_of_sorrows(); AddSC_tirisfal_glades(); AddSC_undercity(); diff --git a/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp b/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp index 501838888ef..2d3d36d425a 100644 --- a/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_arathi_highlands.cpp @@ -60,11 +60,11 @@ class npc_professor_phizzlethorpe : public CreatureScript public: npc_professor_phizzlethorpe() : CreatureScript("npc_professor_phizzlethorpe") { } - struct npc_professor_phizzlethorpeAI : public npc_escortAI + struct npc_professor_phizzlethorpeAI : public EscortAI { - npc_professor_phizzlethorpeAI(Creature* creature) : npc_escortAI(creature) { } + npc_professor_phizzlethorpeAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -118,14 +118,14 @@ class npc_professor_phizzlethorpe : public CreatureScript if (quest->GetQuestId() == QUEST_SUNKEN_TREASURE) { Talk(SAY_PROGRESS_1, player); - npc_escortAI::Start(false, false, player->GetGUID(), quest); + EscortAI::Start(false, false, player->GetGUID(), quest); me->SetFaction(FACTION_ESCORTEE_N_NEUTRAL_PASSIVE); } } void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } }; diff --git a/src/server/scripts/EasternKingdoms/zone_burning_steppes.cpp b/src/server/scripts/EasternKingdoms/zone_burning_steppes.cpp deleted file mode 100644 index e6170d0e65e..00000000000 --- a/src/server/scripts/EasternKingdoms/zone_burning_steppes.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* ScriptData -SDName: Burning_Steppes -SD%Complete: 100 -SDComment: Quest support: 4866 -SDCategory: Burning Steppes -EndScriptData */ - -/* ContentData -npc_ragged_john -EndContentData */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "ScriptedGossip.h" -#include "Player.h" - -/*###### -## npc_ragged_john -######*/ - -enum RaggedJohn -{ - QUEST_MOTHERS_MILK = 4866, - SPELL_MOTHERS_MILK = 16468, - SPELL_WICKED_MILKING = 16472 -}; - -class npc_ragged_john : public CreatureScript -{ - public: - npc_ragged_john() : CreatureScript("npc_ragged_john") { } - - struct npc_ragged_johnAI : public ScriptedAI - { - npc_ragged_johnAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override { } - - void MoveInLineOfSight(Unit* who) override - { - if (who->HasAura(SPELL_MOTHERS_MILK)) - { - if (who->GetTypeId() == TYPEID_PLAYER && me->IsWithinDistInMap(who, 15) && who->isInAccessiblePlaceFor(me)) - { - DoCast(who, SPELL_WICKED_MILKING); - if (Player* player = who->ToPlayer()) - player->AreaExploredOrEventHappens(QUEST_MOTHERS_MILK); - } - } - - ScriptedAI::MoveInLineOfSight(who); - } - - void EnterCombat(Unit* /*who*/) override { } - - bool GossipHello(Player* player) override - { - if (me->IsQuestGiver()) - { - player->PrepareQuestMenu(me->GetGUID()); - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - } - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_ragged_johnAI(creature); - } -}; - -void AddSC_burning_steppes() -{ - new npc_ragged_john(); -} diff --git a/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp b/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp index 5a8197d8f5f..28d942c0716 100644 --- a/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp @@ -60,11 +60,11 @@ class npc_ranger_lilatha : public CreatureScript public: npc_ranger_lilatha() : CreatureScript("npc_ranger_lilatha") { } - struct npc_ranger_lilathaAI : public npc_escortAI + struct npc_ranger_lilathaAI : public EscortAI { - npc_ranger_lilathaAI(Creature* creature) : npc_escortAI(creature) { } + npc_ranger_lilathaAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) diff --git a/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp b/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp index af79fd66ea5..519bfe5f619 100644 --- a/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_hinterlands.cpp @@ -55,9 +55,9 @@ class npc_oox09hl : public CreatureScript public: npc_oox09hl() : CreatureScript("npc_oox09hl") { } - struct npc_oox09hlAI : public npc_escortAI + struct npc_oox09hlAI : public EscortAI { - npc_oox09hlAI(Creature* creature) : npc_escortAI(creature) { } + npc_oox09hlAI(Creature* creature) : EscortAI(creature) { } void Reset() override { } @@ -81,11 +81,11 @@ public: me->SetStandState(UNIT_STAND_STATE_STAND); me->SetFaction(player->GetTeam() == ALLIANCE ? FACTION_ESCORTEE_A_PASSIVE : FACTION_ESCORTEE_H_PASSIVE); Talk(SAY_OOX_START, player); - npc_escortAI::Start(false, false, player->GetGUID(), quest); + EscortAI::Start(false, false, player->GetGUID(), quest); } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -103,7 +103,7 @@ public: } } - void WaypointStart(uint32 pointId) override + void WaypointStarted(uint32 pointId, uint32 /*pathId*/) override { switch (pointId) { @@ -168,9 +168,9 @@ class npc_rinji : public CreatureScript public: npc_rinji() : CreatureScript("npc_rinji") { } - struct npc_rinjiAI : public npc_escortAI + struct npc_rinjiAI : public EscortAI { - npc_rinjiAI(Creature* creature) : npc_escortAI(creature) + npc_rinjiAI(Creature* creature) : EscortAI(creature) { Initialize(); _IsByOutrunner = false; @@ -188,12 +188,12 @@ public: Initialize(); } - void JustRespawned() override + void JustAppeared() override { _IsByOutrunner = false; spawnId = 0; - npc_escortAI::JustRespawned(); + EscortAI::JustAppeared(); } void EnterCombat(Unit* who) override @@ -241,11 +241,11 @@ public: if (GameObject* go = me->FindNearestGameObject(GO_RINJI_CAGE, INTERACTION_DISTANCE)) go->UseDoorOrButton(); - npc_escortAI::Start(false, false, player->GetGUID(), quest); + EscortAI::Start(false, false, player->GetGUID(), quest); } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) diff --git a/src/server/scripts/EasternKingdoms/zone_isle_of_queldanas.cpp b/src/server/scripts/EasternKingdoms/zone_isle_of_queldanas.cpp deleted file mode 100644 index db23c49f437..00000000000 --- a/src/server/scripts/EasternKingdoms/zone_isle_of_queldanas.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* ScriptData -SDName: Isle_of_Queldanas -SD%Complete: 100 -SDComment: Quest support: 11541 -SDCategory: Isle Of Quel'Danas -EndScriptData */ - -/* ContentData -npc_greengill_slave -EndContentData */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "Player.h" -#include "SpellInfo.h" - -/*###### -## npc_greengill_slave -######*/ - -enum GreengillSlave -{ - NPC_DARKSPINE_MYRIDON = 25060, - QUEST_GREENGILL_COAST = 11541, - SPELL_ENRAGE = 45111, - SPELL_ORB_MURLOC_CONTROL = 45109, - SPELL_GREENGILL_SLAVE_FREED = 45110 -}; - -class npc_greengill_slave : public CreatureScript -{ -public: - npc_greengill_slave() : CreatureScript("npc_greengill_slave") { } - - struct npc_greengill_slaveAI : public ScriptedAI - { - npc_greengill_slaveAI(Creature* creature) : ScriptedAI(creature) { } - - void SpellHit(Unit* caster, SpellInfo const* spellInfo) override - { - Player* player = caster->ToPlayer(); - - if (!player) - return; - - if (spellInfo->Id == SPELL_ORB_MURLOC_CONTROL && !me->HasAura(SPELL_ENRAGE)) - { - if (player->GetQuestStatus(QUEST_GREENGILL_COAST) == QUEST_STATUS_INCOMPLETE) - DoCast(player, SPELL_GREENGILL_SLAVE_FREED, true); - - DoCast(me, SPELL_ENRAGE); - - if (Creature* Myrmidon = me->FindNearestCreature(NPC_DARKSPINE_MYRIDON, 70)) - { - AddThreat(Myrmidon, 100000.0f); - AttackStart(Myrmidon); - } - } - } - - void UpdateAI(uint32 /*diff*/) override - { - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_greengill_slaveAI(creature); - } -}; - -void AddSC_isle_of_queldanas() -{ - new npc_greengill_slave(); -} diff --git a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp index bcd4f9bcb37..4e4f767d989 100644 --- a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp +++ b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp @@ -43,9 +43,9 @@ class npc_corporal_keeshan : public CreatureScript public: npc_corporal_keeshan() : CreatureScript("npc_corporal_keeshan") { } - struct npc_corporal_keeshanAI : public npc_escortAI + struct npc_corporal_keeshanAI : public EscortAI { - npc_corporal_keeshanAI(Creature* creature) : npc_escortAI(creature) + npc_corporal_keeshanAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -69,11 +69,11 @@ public: { Talk(SAY_CORPORAL_1, player); me->SetFaction(FACTION_ESCORTEE_N_NEUTRAL_ACTIVE); - npc_escortAI::Start(true, false, player->GetGUID(), quest); + EscortAI::Start(true, false, player->GetGUID(), quest); } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -105,7 +105,7 @@ public: if (HasEscortState(STATE_ESCORT_NONE)) return; - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (phase) { diff --git a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp index 4a4e19d0225..bad77139853 100644 --- a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp +++ b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp @@ -65,11 +65,11 @@ class npc_deathstalker_erland : public CreatureScript public: npc_deathstalker_erland() : CreatureScript("npc_deathstalker_erland") { } - struct npc_deathstalker_erlandAI : public npc_escortAI + struct npc_deathstalker_erlandAI : public EscortAI { - npc_deathstalker_erlandAI(Creature* creature) : npc_escortAI(creature) { } + npc_deathstalker_erlandAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) diff --git a/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp b/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp index d883f282549..e7f5ed8866f 100644 --- a/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp +++ b/src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp @@ -128,9 +128,9 @@ public: return new npc_lord_gregor_lescovarAI(creature); } - struct npc_lord_gregor_lescovarAI : public npc_escortAI + struct npc_lord_gregor_lescovarAI : public EscortAI { - npc_lord_gregor_lescovarAI(Creature* creature) : npc_escortAI(creature) + npc_lord_gregor_lescovarAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -173,7 +173,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -264,7 +264,7 @@ public: } } else uiTimer -= uiDiff; } - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); if (!UpdateVictim()) return; @@ -389,9 +389,9 @@ public: return new npc_tyrion_spybotAI(creature); } - struct npc_tyrion_spybotAI : public npc_escortAI + struct npc_tyrion_spybotAI : public EscortAI { - npc_tyrion_spybotAI(Creature* creature) : npc_escortAI(creature) + npc_tyrion_spybotAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -410,7 +410,7 @@ public: Initialize(); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -506,7 +506,7 @@ public: } } else uiTimer -= uiDiff; } - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); if (!UpdateVictim()) return; diff --git a/src/server/scripts/EasternKingdoms/zone_stranglethorn_vale.cpp b/src/server/scripts/EasternKingdoms/zone_stranglethorn_vale.cpp deleted file mode 100644 index 0d399cb907e..00000000000 --- a/src/server/scripts/EasternKingdoms/zone_stranglethorn_vale.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* ScriptData -SDName: Stranglethorn_Vale -SD%Complete: 100 -SDComment: Quest support: 592 -SDCategory: Stranglethorn Vale -EndScriptData */ - -/* ContentData -npc_yenniku -EndContentData */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "Player.h" -#include "SpellInfo.h" - -/*###### -## npc_yenniku -######*/ - -class npc_yenniku : public CreatureScript -{ -public: - npc_yenniku() : CreatureScript("npc_yenniku") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_yennikuAI(creature); - } - - struct npc_yennikuAI : public ScriptedAI - { - npc_yennikuAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - bReset = false; - } - - void Initialize() - { - Reset_Timer = 0; - } - - uint32 Reset_Timer; - bool bReset; - - void Reset() override - { - Initialize(); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); - } - - void SpellHit(Unit* caster, SpellInfo const* spell) override - { - if (bReset || spell->Id != 3607) - return; - - if (Player* player = caster->ToPlayer()) - { - if (player->GetQuestStatus(592) == QUEST_STATUS_INCOMPLETE) //Yenniku's Release - { - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); - me->CombatStop(); //stop combat - me->GetThreatManager().ClearAllThreat(); //unsure of this - me->SetFaction(FACTION_HORDE_GENERIC); - - bReset = true; - Reset_Timer = 60000; - } - } - } - - void EnterCombat(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override - { - if (bReset) - { - if (Reset_Timer <= diff) - { - EnterEvadeMode(); - bReset = false; - me->SetFaction(FACTION_TROLL_BLOODSCALP); - return; - } - - Reset_Timer -= diff; - - if (me->IsInCombat() && me->GetVictim()) - { - if (Player* player = me->EnsureVictim()->ToPlayer()) - { - if (player->GetTeam() == HORDE) - { - me->CombatStop(); - me->GetThreatManager().ClearAllThreat(); - } - } - } - } - - //Return since we have no target - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); - } - }; -}; - -/*###### -## -######*/ - -void AddSC_stranglethorn_vale() -{ - new npc_yenniku(); -} diff --git a/src/server/scripts/EasternKingdoms/zone_swamp_of_sorrows.cpp b/src/server/scripts/EasternKingdoms/zone_swamp_of_sorrows.cpp index e0396397889..fcc2ea370ec 100644 --- a/src/server/scripts/EasternKingdoms/zone_swamp_of_sorrows.cpp +++ b/src/server/scripts/EasternKingdoms/zone_swamp_of_sorrows.cpp @@ -43,9 +43,9 @@ class npc_galen_goodward : public CreatureScript public: npc_galen_goodward() : CreatureScript("npc_galen_goodward") { } - struct npc_galen_goodwardAI : public npc_escortAI + struct npc_galen_goodwardAI : public EscortAI { - npc_galen_goodwardAI(Creature* creature) : npc_escortAI(creature) + npc_galen_goodwardAI(Creature* creature) : EscortAI(creature) { galensCageGUID.Clear(); Reset(); @@ -67,11 +67,11 @@ public: if (quest->GetQuestId() == QUEST_GALENS_ESCAPE) { Talk(SAY_QUEST_ACCEPTED, player); - npc_escortAI::Start(false, false, player->GetGUID(), quest); + EscortAI::Start(false, false, player->GetGUID(), quest); } } - void WaypointStart(uint32 uiPointId) override + void WaypointStarted(uint32 uiPointId, uint32 /*pathId*/) override { switch (uiPointId) { @@ -95,7 +95,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -118,7 +118,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (HasEscortState(STATE_ESCORT_NONE)) return; diff --git a/src/server/scripts/EasternKingdoms/zone_tirisfal_glades.cpp b/src/server/scripts/EasternKingdoms/zone_tirisfal_glades.cpp index 374dd3ef5c4..2a7178d6fcb 100644 --- a/src/server/scripts/EasternKingdoms/zone_tirisfal_glades.cpp +++ b/src/server/scripts/EasternKingdoms/zone_tirisfal_glades.cpp @@ -42,9 +42,15 @@ EndContentData */ enum Calvin { - SAY_COMPLETE = 0, - SPELL_DRINK = 2639, // possibly not correct spell (but iconId is correct) - QUEST_590 = 590 + SAY_COMPLETE = 0, + SPELL_DRINK = 7737, // Possibly incorrect spell, but both duration and icon are correct + QUEST_590 = 590, + + EVENT_EMOTE_RUDE = 1, + EVENT_TALK = 2, + EVENT_DRINK = 3, + EVENT_SET_QUESTGIVER_FLAG = 4, + EVENT_STAND = 5 }; class npc_calvin_montague : public CreatureScript @@ -54,82 +60,59 @@ public: struct npc_calvin_montagueAI : public ScriptedAI { - npc_calvin_montagueAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - m_uiPhase = 0; - m_uiPhaseTimer = 5000; - m_uiPlayerGUID.Clear(); - } - - uint32 m_uiPhase; - uint32 m_uiPhaseTimer; - ObjectGuid m_uiPlayerGUID; + npc_calvin_montagueAI(Creature* creature) : ScriptedAI(creature) { } void Reset() override { - Initialize(); - me->RestoreFaction(); - if (!me->IsImmuneToPC()) me->SetImmuneToPC(true); } void EnterCombat(Unit* /*who*/) override { } - void DamageTaken(Unit* pDoneBy, uint32 &uiDamage) override + void DamageTaken(Unit* /*attacker*/, uint32 &damage) override { - if (uiDamage > me->GetHealth() || me->HealthBelowPctDamaged(15, uiDamage)) + if (damage > me->GetHealth() || me->HealthBelowPctDamaged(15, damage)) { - uiDamage = 0; - - me->RestoreFaction(); - me->SetImmuneToPC(true); + damage = 0; me->CombatStop(true); - - m_uiPhase = 1; - - if (pDoneBy->GetTypeId() == TYPEID_PLAYER) - m_uiPlayerGUID = pDoneBy->GetGUID(); + EnterEvadeMode(); + _events.ScheduleEvent(EVENT_EMOTE_RUDE, Seconds(3)); } } void UpdateAI(uint32 diff) override { - if (m_uiPhase) - { - if (m_uiPhaseTimer <= diff) - m_uiPhaseTimer = 7500; - else - { - m_uiPhaseTimer -= diff; - return; - } + _events.Update(diff); - switch (m_uiPhase) + if (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - case 1: + case EVENT_EMOTE_RUDE: + me->HandleEmoteCommand(EMOTE_ONESHOT_RUDE); + _events.ScheduleEvent(EVENT_TALK, Seconds(2)); + break; + case EVENT_TALK: Talk(SAY_COMPLETE); - ++m_uiPhase; + _events.ScheduleEvent(EVENT_DRINK, Seconds(5)); break; - case 2: - if (Player* player = ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID)) + case EVENT_DRINK: + if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) player->AreaExploredOrEventHappens(QUEST_590); - - DoCast(me, SPELL_DRINK, true); - ++m_uiPhase; + _playerGUID.Clear(); + DoCastSelf(SPELL_DRINK); + _events.ScheduleEvent(EVENT_SET_QUESTGIVER_FLAG, Seconds(12)); + break; + case EVENT_SET_QUESTGIVER_FLAG: + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + _events.ScheduleEvent(EVENT_STAND, Seconds(3)); break; - case 3: - EnterEvadeMode(); + case EVENT_STAND: + me->SetStandState(UNIT_STAND_STATE_STAND); break; } - - return; } if (!UpdateVictim()) @@ -142,11 +125,17 @@ public: { if (quest->GetQuestId() == QUEST_590) { + _playerGUID = player->GetGUID(); me->SetFaction(FACTION_ENEMY); me->SetImmuneToPC(false); AttackStart(player); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); } } + + private: + EventMap _events; + ObjectGuid _playerGUID; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/EasternKingdoms/zone_undercity.cpp b/src/server/scripts/EasternKingdoms/zone_undercity.cpp index 37fe5cec1f9..4eaf6ce0002 100644 --- a/src/server/scripts/EasternKingdoms/zone_undercity.cpp +++ b/src/server/scripts/EasternKingdoms/zone_undercity.cpp @@ -82,6 +82,11 @@ enum Sylvanas GUID_EVENT_INVOKER = 1, }; +enum Sounds +{ + SOUND_AGGRO = 5886 +}; + float HighborneLoc[4][3]= { {1285.41f, 312.47f, 0.51f}, @@ -120,6 +125,7 @@ public: void EnterCombat(Unit* /*who*/) override { + DoPlaySoundToSet(me, SOUND_AGGRO); _events.ScheduleEvent(EVENT_FADE, 30000); _events.ScheduleEvent(EVENT_SUMMON_SKELETON, 20000); _events.ScheduleEvent(EVENT_BLACK_ARROW, 15000); diff --git a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp index 7ba95268562..159a37812a9 100644 --- a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp @@ -19,12 +19,11 @@ /* ScriptData SDName: Western_Plaguelands SD%Complete: 90 -SDComment: Quest support: 5097, 5098, 5216, 5219, 5222, 5225, 5229, 5231, 5233, 5235. To obtain Vitreous Focuser (could use more spesifics about gossip items) +SDComment: Quest support: 5097, 5098, 5216, 5219, 5222, 5225, 5229, 5231, 5233, 5235. SDCategory: Western Plaguelands EndScriptData */ /* ContentData -npcs_dithers_and_arbington npc_the_scourge_cauldron npc_andorhal_tower EndContentData */ @@ -36,99 +35,6 @@ EndContentData */ #include "WorldSession.h" /*###### -## npcs_dithers_and_arbington -######*/ - -enum DithersAndArbington -{ - GOSSIP_ITEM_ID_FELSTONE_FIELD = 0, - GOSSIP_ITEM_ID_DALSON_S_TEARS = 1, - GOSSIP_ITEM_ID_WRITHING_HAUNT = 2, - GOSSIP_ITEM_ID_GAHRRON_S_WITH = 3, - GOSSIP_MENU_ID_LETS_GET_TO_WORK = 3223, - GOSSIP_MENU_ID_VITREOUS_FOCUSER = 3229, - NPC_TEXT_OSSEOUS_AGITATORS = 3980, - NPC_TEXT_SOMATIC_INTENSIFIERS_1 = 3981, - NPC_TEXT_SOMATIC_INTENSIFIERS_2 = 3982, - NPC_TEXT_ECTOPLASMIC_RESONATORS = 3983, - NPC_TEXT_LET_S_GET_TO_WORK = 3985, - QUEST_MISSION_ACCOMPLISHED_H = 5237, - QUEST_MISSION_ACCOMPLISHED_A = 5238, - CREATE_ITEM_VITREOUS_FOCUSER = 17529 -}; - -class npcs_dithers_and_arbington : public CreatureScript -{ - public: - npcs_dithers_and_arbington() : CreatureScript("npcs_dithers_and_arbington") { } - - struct npcs_dithers_and_arbingtonAI : public ScriptedAI - { - npcs_dithers_and_arbingtonAI(Creature* creature) : ScriptedAI(creature) { } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_TRADE: - player->GetSession()->SendListInventory(me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 1: - AddGossipItemFor(player, GOSSIP_MENU_ID_VITREOUS_FOCUSER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); - SendGossipMenuFor(player, NPC_TEXT_OSSEOUS_AGITATORS, me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - AddGossipItemFor(player, GOSSIP_MENU_ID_VITREOUS_FOCUSER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); - SendGossipMenuFor(player, NPC_TEXT_SOMATIC_INTENSIFIERS_1, me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - AddGossipItemFor(player, GOSSIP_MENU_ID_VITREOUS_FOCUSER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); - SendGossipMenuFor(player, NPC_TEXT_SOMATIC_INTENSIFIERS_2, me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 4: - AddGossipItemFor(player, GOSSIP_MENU_ID_VITREOUS_FOCUSER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); - SendGossipMenuFor(player, NPC_TEXT_ECTOPLASMIC_RESONATORS, me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 5: - CloseGossipMenuFor(player); - DoCast(player, CREATE_ITEM_VITREOUS_FOCUSER, false); - break; - } - return true; - } - - bool GossipHello(Player* player) override - { - if (me->IsQuestGiver()) - player->PrepareQuestMenu(me->GetGUID()); - - if (me->IsVendor()) - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - if (player->GetQuestRewardStatus(QUEST_MISSION_ACCOMPLISHED_H) || player->GetQuestRewardStatus(QUEST_MISSION_ACCOMPLISHED_A)) - { - AddGossipItemFor(player, GOSSIP_MENU_ID_LETS_GET_TO_WORK, GOSSIP_ITEM_ID_FELSTONE_FIELD, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - AddGossipItemFor(player, GOSSIP_MENU_ID_LETS_GET_TO_WORK, GOSSIP_ITEM_ID_DALSON_S_TEARS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - AddGossipItemFor(player, GOSSIP_MENU_ID_LETS_GET_TO_WORK, GOSSIP_ITEM_ID_WRITHING_HAUNT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - AddGossipItemFor(player, GOSSIP_MENU_ID_LETS_GET_TO_WORK, GOSSIP_ITEM_ID_GAHRRON_S_WITH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); - SendGossipMenuFor(player, NPC_TEXT_LET_S_GET_TO_WORK, me->GetGUID()); - } - else - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npcs_dithers_and_arbingtonAI(creature); - } -}; - -/*###### ## npc_the_scourge_cauldron ######*/ @@ -248,7 +154,6 @@ public: void AddSC_western_plaguelands() { - new npcs_dithers_and_arbington(); new npc_the_scourge_cauldron(); new npc_andorhal_tower(); } diff --git a/src/server/scripts/EasternKingdoms/zone_wetlands.cpp b/src/server/scripts/EasternKingdoms/zone_wetlands.cpp index 51dbdf19a03..6120b89ee2b 100644 --- a/src/server/scripts/EasternKingdoms/zone_wetlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_wetlands.cpp @@ -56,9 +56,9 @@ public: return new npc_tapoke_slim_jahnAI(creature); } - struct npc_tapoke_slim_jahnAI : public npc_escortAI + struct npc_tapoke_slim_jahnAI : public EscortAI { - npc_tapoke_slim_jahnAI(Creature* creature) : npc_escortAI(creature) + npc_tapoke_slim_jahnAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -76,7 +76,7 @@ public: Initialize(); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { diff --git a/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp b/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp index 921af01bfb4..a026506bb9d 100644 --- a/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp +++ b/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp @@ -223,16 +223,16 @@ class npc_morridune : public CreatureScript public: npc_morridune() : CreatureScript("npc_morridune") { } - struct npc_morriduneAI : public npc_escortAI + struct npc_morriduneAI : public EscortAI { - npc_morriduneAI(Creature* creature) : npc_escortAI(creature) + npc_morriduneAI(Creature* creature) : EscortAI(creature) { Talk(SAY_MORRIDUNE_1); me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); Start(false); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp index b41eb13c46c..885d611e3bc 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp @@ -99,7 +99,7 @@ public: Talk(SAY_ONSLAY); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7) { @@ -121,8 +121,8 @@ public: { if (IsEvent) { - //Must update npc_escortAI - npc_escortAI::UpdateAI(diff); + //Must update EscortAI + EscortAI::UpdateAI(diff); if (!go) { go = true; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp index 985f4e214a8..5e3ba51b1ca 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp @@ -102,7 +102,7 @@ public: Talk(SAY_ONSLAY); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7 && instance) { @@ -124,8 +124,8 @@ public: { if (IsEvent) { - //Must update npc_escortAI - npc_escortAI::UpdateAI(diff); + //Must update EscortAI + EscortAI::UpdateAI(diff); if (!go) { go = true; @@ -225,10 +225,6 @@ public: { } - void WaypointReached(uint32 /*waypointId*/) override - { - } - void MoveInLineOfSight(Unit* who) override { diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp index 25c174cb26a..fc26c35a314 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp @@ -97,7 +97,7 @@ public: Talk(SAY_ONSLAY); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7 && instance) { @@ -119,8 +119,8 @@ public: { if (IsEvent) { - //Must update npc_escortAI - npc_escortAI::UpdateAI(diff); + //Must update EscortAI + EscortAI::UpdateAI(diff); if (!go) { go = true; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp index d1677b2c8ef..746350853b9 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp @@ -92,7 +92,7 @@ public: Talk(SAY_ONSLAY); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7 && instance) { @@ -114,8 +114,8 @@ public: { if (IsEvent) { - //Must update npc_escortAI - npc_escortAI::UpdateAI(diff); + //Must update EscortAI + EscortAI::UpdateAI(diff); if (!go) { go = true; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp index 48b8e59a5a7..ec918cbb3a4 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp @@ -316,7 +316,7 @@ float HordeFirePos[65][8]=//spawn points for the fire visuals (GO) in the horde {5545.43f, -2647.82f, 1483.05f, 5.38848f, 0, 0, 0.432578f, -0.901596f} }; -hyjalAI::hyjalAI(Creature* creature) : npc_escortAI(creature), Summons(me) +hyjalAI::hyjalAI(Creature* creature) : EscortAI(creature), Summons(me) { Initialize(); instance = creature->GetInstanceScript(); @@ -447,7 +447,7 @@ void hyjalAI::MoveInLineOfSight(Unit* who) if (IsDummy) return; - npc_escortAI::MoveInLineOfSight(who); + EscortAI::MoveInLineOfSight(who); } void hyjalAI::SummonCreature(uint32 entry, float Base[4][3]) @@ -930,7 +930,7 @@ void hyjalAI::RespawnNearPos(float x, float y) Cell::VisitGridObjects(x, y, me->GetMap(), worker, me->GetGridActivationRange()); } -void hyjalAI::WaypointReached(uint32 waypointId) +void hyjalAI::WaypointReached(uint32 waypointId, uint32 /*pathId*/) { if (waypointId == 1 || (waypointId == 0 && me->GetEntry() == THRALL)) { @@ -979,7 +979,7 @@ void hyjalAI::WaypointReached(uint32 waypointId) } void hyjalAI::DoOverrun(uint32 faction, const uint32 diff) { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (WaitForTeleport) { if (TeleportTimer <= diff) diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.h b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.h index a61a568f61b..9a0c1f78b92 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.h +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.h @@ -115,7 +115,7 @@ enum YellId DEATH = 6, // Used on death }; -struct hyjalAI : public npc_escortAI +struct hyjalAI : public EscortAI { hyjalAI(Creature* creature); @@ -144,7 +144,7 @@ struct hyjalAI : public npc_escortAI void SummonedCreatureDespawn(Creature* summoned) override; void HideNearPos(float x, float y); void RespawnNearPos(float x, float y); - void WaypointReached(uint32 waypointId) override; + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override; void DoOverrun(uint32 faction, const uint32 diff); void MoveInLineOfSight(Unit* who) override; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp index f9cd454c67e..6d05e6094b1 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp @@ -176,7 +176,7 @@ float HordeOverrunWP[21][3]=//waypoints in the horde base used in the end in the {5429.91f, -2718.44f, 1493.42f}//20 end 2 }; -hyjal_trashAI::hyjal_trashAI(Creature* creature) : npc_escortAI(creature) +hyjal_trashAI::hyjal_trashAI(Creature* creature) : EscortAI(creature) { instance = creature->GetInstanceScript(); IsEvent = false; @@ -443,7 +443,7 @@ public: void EnterCombat(Unit* /*who*/) override { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 0 && !IsOverrun) { @@ -498,7 +498,7 @@ public: if (!CanMove)return; hyjal_trashAI::UpdateAI(diff); if (IsEvent || IsOverrun) - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (IsEvent) { if (!go) @@ -558,7 +558,7 @@ public: KnockDownTimer = 10000; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7 && !IsOverrun) { @@ -590,7 +590,7 @@ public: { hyjal_trashAI::UpdateAI(diff); if (IsEvent || IsOverrun) - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (IsEvent) { if (!go) @@ -656,7 +656,7 @@ public: RandomMove = false; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7 && !IsOverrun) { @@ -689,7 +689,7 @@ public: { hyjal_trashAI::UpdateAI(diff); if (IsEvent || IsOverrun) - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (IsEvent) { if (!go) @@ -766,7 +766,7 @@ public: summons.Despawn(summon); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7 && !IsOverrun) { @@ -810,7 +810,7 @@ public: hyjal_trashAI::UpdateAI(diff); if (IsEvent || IsOverrun) - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (IsEvent) { @@ -879,7 +879,7 @@ public: ShellTimer = 50000 + rand32() % 10000; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7 && !IsOverrun) { @@ -904,7 +904,7 @@ public: { hyjal_trashAI::UpdateAI(diff); if (IsEvent || IsOverrun) - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (IsEvent) { if (!go) @@ -975,7 +975,7 @@ public: WebTimer = 20000 + rand32() % 5000; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7 && !IsOverrun) { @@ -1000,7 +1000,7 @@ public: { hyjal_trashAI::UpdateAI(diff); if (IsEvent || IsOverrun) - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (IsEvent) { if (!go) @@ -1061,7 +1061,7 @@ public: ManaBurnTimer = 9000 + rand32() % 5000; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 7 && !IsOverrun) { @@ -1086,7 +1086,7 @@ public: { hyjal_trashAI::UpdateAI(diff); if (IsEvent || IsOverrun) - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (IsEvent) { if (!go) @@ -1155,7 +1155,7 @@ public: me->SetDisableGravity(true); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 2 && !IsOverrun) { @@ -1189,7 +1189,7 @@ public: if (IsEvent || IsOverrun) { ENSURE_AI(hyjal_trashAI, me->AI())->SetCanAttack(false); - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } if (IsEvent) @@ -1280,7 +1280,7 @@ public: me->SetDisableGravity(true); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 2 && !IsOverrun) { @@ -1310,7 +1310,7 @@ public: if (IsEvent || IsOverrun) { ENSURE_AI(hyjal_trashAI, me->AI())->SetCanAttack(false); - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } if (IsEvent) diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.h b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.h index 66189f9c71d..d3ccbecf7ea 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.h +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.h @@ -21,7 +21,7 @@ #include "hyjal.h" #include "ScriptedEscortAI.h" -struct hyjal_trashAI : public npc_escortAI +struct hyjal_trashAI : public EscortAI { hyjal_trashAI(Creature* creature); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp index 26c718c3930..860b4a38968 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp @@ -258,9 +258,9 @@ class npc_arthas : public CreatureScript public: npc_arthas() : CreatureScript("npc_arthas") { } - struct npc_arthasAI : public npc_escortAI + struct npc_arthasAI : public EscortAI { - npc_arthasAI(Creature* creature) : npc_escortAI(creature) + npc_arthasAI(Creature* creature) : EscortAI(creature) { Initialize(); instance = creature->GetInstanceScript(); @@ -336,7 +336,7 @@ public: void AttackStart(Unit* who) override { if (who && !who->IsImmuneToPC()) - npc_escortAI::AttackStart(who); + EscortAI::AttackStart(who); } void EnterCombat(Unit* /*who*/) override @@ -391,7 +391,7 @@ public: ++step; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -511,7 +511,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (bStepping) { @@ -901,7 +901,7 @@ public: } } else if (instance->GetBossState(bossEvent) == FAIL) - npc_escortAI::EnterEvadeMode(); + EscortAI::EnterEvadeMode(); else phaseTimer = 10000; break; @@ -1061,7 +1061,7 @@ public: JumpToNextStep(15000); } else if (instance->GetBossState(DATA_EPOCH) == FAIL) - npc_escortAI::EnterEvadeMode(); + EscortAI::EnterEvadeMode(); else phaseTimer = 10000; break; @@ -1106,7 +1106,7 @@ public: JumpToNextStep(1000); } else if (instance->GetBossState(DATA_MAL_GANIS) == FAIL) - npc_escortAI::EnterEvadeMode(); + EscortAI::EnterEvadeMode(); else phaseTimer = 10000; break; @@ -1196,7 +1196,7 @@ public: return false; AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_ARTHAS_0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); SendGossipMenuFor(player, 907, me->GetGUID()); - break; + break; } case 1: AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_ARTHAS_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp index 29e94b2bd9f..c4ba72f2563 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp @@ -197,9 +197,9 @@ class npc_thrall_old_hillsbrad : public CreatureScript public: npc_thrall_old_hillsbrad() : CreatureScript("npc_thrall_old_hillsbrad") { } - struct npc_thrall_old_hillsbradAI : public npc_escortAI + struct npc_thrall_old_hillsbradAI : public EscortAI { - npc_thrall_old_hillsbradAI(Creature* creature) : npc_escortAI(creature) + npc_thrall_old_hillsbradAI(Creature* creature) : EscortAI(creature) { Initialize(); instance = creature->GetInstanceScript(); @@ -217,7 +217,7 @@ public: bool LowHp; bool HadMount; - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -354,7 +354,7 @@ public: if (Creature* Taretha = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_TARETHA))) { if (Player* player = GetPlayerForEscort()) - ENSURE_AI(npc_escortAI, (Taretha->AI()))->Start(false, true, player->GetGUID()); + ENSURE_AI(EscortAI, (Taretha->AI()))->Start(false, true, player->GetGUID()); } //kill credit Creature for quest @@ -455,7 +455,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!UpdateVictim()) return; @@ -564,16 +564,16 @@ class npc_taretha : public CreatureScript public: npc_taretha() : CreatureScript("npc_taretha") { } - struct npc_tarethaAI : public npc_escortAI + struct npc_tarethaAI : public EscortAI { - npc_tarethaAI(Creature* creature) : npc_escortAI(creature) + npc_tarethaAI(Creature* creature) : EscortAI(creature) { instance = creature->GetInstanceScript(); } InstanceScript* instance; - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -591,7 +591,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override diff --git a/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp b/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp index b2ee684d0f5..131087fb683 100644 --- a/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp +++ b/src/server/scripts/Kalimdor/RazorfenKraul/razorfen_kraul.cpp @@ -46,9 +46,9 @@ class npc_willix : public CreatureScript public: npc_willix() : CreatureScript("npc_willix") { } - struct npc_willixAI : public npc_escortAI + struct npc_willixAI : public EscortAI { - npc_willixAI(Creature* creature) : npc_escortAI(creature) { } + npc_willixAI(Creature* creature) : EscortAI(creature) { } void QuestAccept(Player* player, Quest const* quest) override { @@ -60,7 +60,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp index 2a775ecf9fd..77e1ef0e77e 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp @@ -204,9 +204,7 @@ public: //Charge_Timer if (Charge_Timer <= diff) { - Unit* target = nullptr; - target = SelectTarget(SELECT_TARGET_RANDOM, 0); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) { DoCast(target, SPELL_CHARGE); //me->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true, 1); diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_ouro.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_ouro.cpp index 3a154f40eed..0e6a24b3ee3 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_ouro.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_ouro.cpp @@ -122,10 +122,7 @@ public: //ChangeTarget_Timer if (Submerged && ChangeTarget_Timer <= diff) { - Unit* target = nullptr; - target = SelectTarget(SELECT_TARGET_RANDOM, 0); - - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) DoTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()); ChangeTarget_Timer = urand(10000, 20000); diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_twinemperors.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_twinemperors.cpp index bbcf18d5b84..602f5652e4a 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_twinemperors.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_twinemperors.cpp @@ -558,9 +558,7 @@ public: //Blizzard_Timer if (Blizzard_Timer <= diff) { - Unit* target = nullptr; - target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45, true); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45, true)) DoCast(target, SPELL_BLIZZARD); Blizzard_Timer = 15000 + rand32() % 15000; } else Blizzard_Timer -= diff; diff --git a/src/server/scripts/Kalimdor/WailingCaverns/wailing_caverns.cpp b/src/server/scripts/Kalimdor/WailingCaverns/wailing_caverns.cpp index 0286de05581..48308c9bc16 100644 --- a/src/server/scripts/Kalimdor/WailingCaverns/wailing_caverns.cpp +++ b/src/server/scripts/Kalimdor/WailingCaverns/wailing_caverns.cpp @@ -81,9 +81,9 @@ class npc_disciple_of_naralex : public CreatureScript public: npc_disciple_of_naralex() : CreatureScript("npc_disciple_of_naralex") { } - struct npc_disciple_of_naralexAI : public npc_escortAI + struct npc_disciple_of_naralexAI : public EscortAI { - npc_disciple_of_naralexAI(Creature* creature) : npc_escortAI(creature) + npc_disciple_of_naralexAI(Creature* creature) : EscortAI(creature) { instance = creature->GetInstanceScript(); eventTimer = 0; @@ -98,7 +98,7 @@ public: uint32 eventProgress; InstanceScript* instance; - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -153,7 +153,7 @@ public: void UpdateAI(uint32 diff) override { if (currentEvent != TYPE_NARALEX_PART3) - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (eventTimer <= diff) { @@ -177,7 +177,7 @@ public: ++eventProgress; Talk(SAY_BANISH_THE_SPIRITS); DoCast(me, SPELL_SERPENTINE_CLEANSING); - //CAST_AI(npc_escort::npc_escortAI, me->AI())->SetCanDefend(false); + //CAST_AI(EscortAI, me->AI())->SetCanDefend(false); eventTimer = 30000; me->SummonCreature(NPC_DEVIATE_VIPER, -61.5261f, 273.676f, -92.8442f, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000); me->SummonCreature(NPC_DEVIATE_VIPER, -58.4658f, 280.799f, -92.8393f, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000); @@ -186,7 +186,7 @@ public: else if (eventProgress == 2) { - //CAST_AI(npc_escort::npc_escortAI, me->AI())->SetCanDefend(true); + //CAST_AI(EscortAI, me->AI())->SetCanDefend(true); Talk(SAY_CAVERNS_PURIFIED); instance->SetData(TYPE_NARALEX_PART2, DONE); if (me->HasAura(SPELL_SERPENTINE_CLEANSING)) @@ -206,7 +206,7 @@ public: { ++eventProgress; eventTimer = 15000; - //CAST_AI(npc_escort::npc_escortAI, me->AI())->SetCanDefend(false); + //CAST_AI(EscortAI, me->AI())->SetCanDefend(false); if (Creature* naralex = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_NARALEX))) DoCast(naralex, SPELL_NARALEXS_AWAKENING, true); Talk(EMOTE_AWAKENING_RITUAL); diff --git a/src/server/scripts/Kalimdor/zone_ashenvale.cpp b/src/server/scripts/Kalimdor/zone_ashenvale.cpp index f5680664e27..964c8506113 100644 --- a/src/server/scripts/Kalimdor/zone_ashenvale.cpp +++ b/src/server/scripts/Kalimdor/zone_ashenvale.cpp @@ -64,9 +64,9 @@ class npc_ruul_snowhoof : public CreatureScript public: npc_ruul_snowhoof() : CreatureScript("npc_ruul_snowhoof") { } - struct npc_ruul_snowhoofAI : public npc_escortAI + struct npc_ruul_snowhoofAI : public EscortAI { - npc_ruul_snowhoofAI(Creature* creature) : npc_escortAI(creature) { } + npc_ruul_snowhoofAI(Creature* creature) : EscortAI(creature) { } void Reset() override { @@ -86,11 +86,11 @@ public: if (quest->GetQuestId() == QUEST_FREEDOM_TO_RUUL) { me->SetFaction(FACTION_ESCORTEE_N_NEUTRAL_PASSIVE); - npc_escortAI::Start(true, false, player->GetGUID()); + EscortAI::Start(true, false, player->GetGUID()); } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -121,7 +121,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } }; @@ -181,9 +181,9 @@ class npc_muglash : public CreatureScript public: npc_muglash() : CreatureScript("npc_muglash") { } - struct npc_muglashAI : public npc_escortAI + struct npc_muglashAI : public EscortAI { - npc_muglashAI(Creature* creature) : npc_escortAI(creature) + npc_muglashAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -229,11 +229,11 @@ public: { Talk(SAY_MUG_START1); me->SetFaction(FACTION_ESCORTEE_N_NEUTRAL_PASSIVE); - npc_escortAI::Start(true, false, player->GetGUID()); + EscortAI::Start(true, false, player->GetGUID()); } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (Player* player = GetPlayerForEscort()) { @@ -291,7 +291,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!me->GetVictim()) { diff --git a/src/server/scripts/Kalimdor/zone_azshara.cpp b/src/server/scripts/Kalimdor/zone_azshara.cpp index 0813b05705f..bea3ac7dd73 100644 --- a/src/server/scripts/Kalimdor/zone_azshara.cpp +++ b/src/server/scripts/Kalimdor/zone_azshara.cpp @@ -24,104 +24,6 @@ #include "ScriptedGossip.h" #include "SpellInfo.h" -/*###### -## npc_spitelashes -######*/ - -enum Spitelashes -{ - SPELL_POLYMORPH_RANK1 = 118, - SPELL_POLYMORPH_RANK2 = 12824, - SPELL_POLYMORPH_RANK3 = 12825, - SPELL_POLYMORPH_RANK4 = 12826, - SPELL_POLYMORPH = 29124, - SPELL_POLYMORPH_BACKFIRE = 28406, - SPELL_REMOVE_POLYMORPH = 6924 -}; - -class npc_spitelashes : public CreatureScript -{ -public: - npc_spitelashes() : CreatureScript("npc_spitelashes") { } - - struct npc_spitelashesAI : public ScriptedAI - { - npc_spitelashesAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - morphtimer = 0; - spellhit = false; - } - - uint32 morphtimer; - bool spellhit; - - void Reset() override - { - Initialize(); - } - - void EnterCombat(Unit* /*who*/) override { } - - void SpellHit(Unit* unit, SpellInfo const* spell) override - { - if (spellhit) - return; - - switch (spell->Id) - { - case SPELL_POLYMORPH_RANK1: - case SPELL_POLYMORPH_RANK2: - case SPELL_POLYMORPH_RANK3: - case SPELL_POLYMORPH_RANK4: - if (Player* player = unit->ToPlayer()) - if (player->GetQuestStatus(9364) == QUEST_STATUS_INCOMPLETE) - { - spellhit = true; - DoCast(me, SPELL_POLYMORPH); - } - break; - default: - break; - } - } - - void UpdateAI(uint32 diff) override - { - // we mustn't remove the Creature in the same round in which we cast the summon spell, otherwise there will be no summons - if (spellhit && morphtimer >= 5000) - { - me->DespawnOrUnsummon(); - return; - } - // walk 5 seconds before summoning - if (spellhit && morphtimer < 5000) - { - morphtimer += diff; - if (morphtimer >= 5000) - { - DoCast(me, SPELL_POLYMORPH_BACKFIRE); // summon copies - DoCast(me, SPELL_REMOVE_POLYMORPH); // visual explosion - } - } - if (!UpdateVictim()) - return; - - /// @todo add abilities for the different creatures - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_spitelashesAI(creature); - } -}; - /*#### # npc_rizzle_sprysprocket ####*/ @@ -468,7 +370,6 @@ public: void AddSC_azshara() { - new npc_spitelashes(); new npc_rizzle_sprysprocket(); new npc_depth_charge(); } diff --git a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp index c4036fbe836..3ce50e2f2f4 100644 --- a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp @@ -348,9 +348,9 @@ class npc_magwin : public CreatureScript public: npc_magwin() : CreatureScript("npc_magwin") { } - struct npc_magwinAI : public npc_escortAI + struct npc_magwinAI : public EscortAI { - npc_magwinAI(Creature* creature) : npc_escortAI(creature) { } + npc_magwinAI(Creature* creature) : EscortAI(creature) { } void Reset() override { @@ -371,7 +371,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (Player* player = GetPlayerForEscort()) { @@ -410,7 +410,7 @@ public: break; case EVENT_START_ESCORT: if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) - npc_escortAI::Start(true, false, player->GetGUID()); + EscortAI::Start(true, false, player->GetGUID()); _events.ScheduleEvent(EVENT_STAND, Seconds(2)); break; case EVENT_STAND: // Remove kneel standstate. Using a separate delayed event because it causes unwanted delay before starting waypoint movement. @@ -428,7 +428,7 @@ public: } } - npc_escortAI::UpdateEscortAI(diff); + EscortAI::UpdateEscortAI(diff); } private: @@ -718,128 +718,6 @@ public: } }; -/*######## -## Quest: The Prophecy of Akida -########*/ - -enum BristlelimbCage -{ - QUEST_THE_PROPHECY_OF_AKIDA = 9544, - NPC_STILLPINE_CAPITIVE = 17375, - GO_BRISTELIMB_CAGE = 181714, - - CAPITIVE_SAY = 0, - - POINT_INIT = 1, - EVENT_DESPAWN = 1, -}; - -class npc_stillpine_capitive : public CreatureScript -{ - public: - npc_stillpine_capitive() : CreatureScript("npc_stillpine_capitive") { } - - struct npc_stillpine_capitiveAI : public ScriptedAI - { - npc_stillpine_capitiveAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - _playerGUID.Clear(); - _movementComplete = false; - } - - void Reset() override - { - if (GameObject* cage = me->FindNearestGameObject(GO_BRISTELIMB_CAGE, 5.0f)) - { - cage->SetLootState(GO_JUST_DEACTIVATED); - cage->SetGoState(GO_STATE_READY); - } - _events.Reset(); - Initialize(); - } - - void StartMoving(Player* owner) - { - if (owner) - { - Talk(CAPITIVE_SAY, owner); - _playerGUID = owner->GetGUID(); - } - Position pos = me->GetNearPosition(3.0f, 0.0f); - me->GetMotionMaster()->MovePoint(POINT_INIT, pos); - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE || id != POINT_INIT) - return; - - if (Player* _player = ObjectAccessor::GetPlayer(*me, _playerGUID)) - _player->KilledMonsterCredit(me->GetEntry(), me->GetGUID()); - - _movementComplete = true; - _events.ScheduleEvent(EVENT_DESPAWN, 3500); - } - - void UpdateAI(uint32 diff) override - { - if (!_movementComplete) - return; - - _events.Update(diff); - - if (_events.ExecuteEvent() == EVENT_DESPAWN) - me->DespawnOrUnsummon(); - } - - private: - ObjectGuid _playerGUID; - EventMap _events; - bool _movementComplete; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_stillpine_capitiveAI(creature); - } -}; - -class go_bristlelimb_cage : public GameObjectScript -{ - public: - go_bristlelimb_cage() : GameObjectScript("go_bristlelimb_cage") { } - - struct go_bristlelimb_cageAI : public GameObjectAI - { - go_bristlelimb_cageAI(GameObject* go) : GameObjectAI(go) { } - - bool GossipHello(Player* player) override - { - me->SetGoState(GO_STATE_READY); - if (player->GetQuestStatus(QUEST_THE_PROPHECY_OF_AKIDA) == QUEST_STATUS_INCOMPLETE) - { - if (Creature* capitive = me->FindNearestCreature(NPC_STILLPINE_CAPITIVE, 5.0f, true)) - { - me->ResetDoorOrButton(); - ENSURE_AI(npc_stillpine_capitive::npc_stillpine_capitiveAI, capitive->AI())->StartMoving(player); - return false; - } - } - return true; - } - }; - - GameObjectAI* GetAI(GameObject* go) const override - { - return new go_bristlelimb_cageAI(go); - } -}; - void AddSC_azuremyst_isle() { new npc_draenei_survivor(); @@ -849,6 +727,4 @@ void AddSC_azuremyst_isle() new npc_geezle(); new npc_death_ravager(); new go_ravager_cage(); - new npc_stillpine_capitive(); - new go_bristlelimb_cage(); } diff --git a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp index 86eae7f4b40..3a92623ce15 100644 --- a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp @@ -355,9 +355,9 @@ class npc_demolitionist_legoso : public CreatureScript public: npc_demolitionist_legoso() : CreatureScript("npc_demolitionist_legoso") { } - struct npc_demolitionist_legosoAI : public npc_escortAI + struct npc_demolitionist_legosoAI : public EscortAI { - npc_demolitionist_legosoAI(Creature* creature) : npc_escortAI(creature) + npc_demolitionist_legosoAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -466,7 +466,7 @@ public: if (HasEscortState(STATE_ESCORT_NONE)) return; - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (_phase) { @@ -710,7 +710,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) diff --git a/src/server/scripts/Kalimdor/zone_darkshore.cpp b/src/server/scripts/Kalimdor/zone_darkshore.cpp index b2bdd76c137..8d07fd1e506 100644 --- a/src/server/scripts/Kalimdor/zone_darkshore.cpp +++ b/src/server/scripts/Kalimdor/zone_darkshore.cpp @@ -204,9 +204,9 @@ class npc_prospector_remtravel : public CreatureScript public: npc_prospector_remtravel() : CreatureScript("npc_prospector_remtravel") { } - struct npc_prospector_remtravelAI : public npc_escortAI + struct npc_prospector_remtravelAI : public EscortAI { - npc_prospector_remtravelAI(Creature* creature) : npc_escortAI(creature) { } + npc_prospector_remtravelAI(Creature* creature) : EscortAI(creature) { } void Reset() override { } @@ -222,7 +222,7 @@ public: //pSummoned->AI()->AttackStart(me); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (Player* player = GetPlayerForEscort()) { diff --git a/src/server/scripts/Kalimdor/zone_desolace.cpp b/src/server/scripts/Kalimdor/zone_desolace.cpp index 4360574a65f..74786200a43 100644 --- a/src/server/scripts/Kalimdor/zone_desolace.cpp +++ b/src/server/scripts/Kalimdor/zone_desolace.cpp @@ -176,9 +176,9 @@ class npc_dalinda : public CreatureScript public: npc_dalinda() : CreatureScript("npc_dalinda") { } - struct npc_dalindaAI : public npc_escortAI + struct npc_dalindaAI : public EscortAI { - npc_dalindaAI(Creature* creature) : npc_escortAI(creature) { } + npc_dalindaAI(Creature* creature) : EscortAI(creature) { } void Reset() override { } @@ -191,7 +191,7 @@ public: return; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); @@ -209,7 +209,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!UpdateVictim()) return; diff --git a/src/server/scripts/Kalimdor/zone_feralas.cpp b/src/server/scripts/Kalimdor/zone_feralas.cpp index 16c30f6fadd..0d0f75d4d4d 100644 --- a/src/server/scripts/Kalimdor/zone_feralas.cpp +++ b/src/server/scripts/Kalimdor/zone_feralas.cpp @@ -63,11 +63,11 @@ class npc_oox22fe : public CreatureScript public: npc_oox22fe() : CreatureScript("npc_oox22fe") { } - struct npc_oox22feAI : public npc_escortAI + struct npc_oox22feAI : public EscortAI { - npc_oox22feAI(Creature* creature) : npc_escortAI(creature) { } + npc_oox22feAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index e19d8797bc9..792fe737c8b 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -111,10 +111,10 @@ public: return new npc_clintar_spiritAI(creature); } - struct npc_clintar_spiritAI : public npc_escortAI + struct npc_clintar_spiritAI : public EscortAI { public: - npc_clintar_spiritAI(Creature* creature) : npc_escortAI(creature) + npc_clintar_spiritAI(Creature* creature) : EscortAI(creature) { Initialize(); PlayerGUID.Clear(); @@ -189,7 +189,7 @@ public: AttackStart(player->getAttackerForHelper()); return; } - npc_escortAI::EnterEvadeMode(why); + EscortAI::EnterEvadeMode(why); } void StartEvent(Player* player) @@ -198,7 +198,7 @@ public: { for (uint8 i = 0; i < 41; ++i) { - AddWaypoint(i, Clintar_spirit_WP[i][0], Clintar_spirit_WP[i][1], Clintar_spirit_WP[i][2], (uint32)Clintar_spirit_WP[i][4]); + AddWaypoint(i, Clintar_spirit_WP[i][0], Clintar_spirit_WP[i][1], Clintar_spirit_WP[i][2], Clintar_spirit_WP[i][3], (uint32)Clintar_spirit_WP[i][4]); } PlayerGUID = player->GetGUID(); Start(true, false, PlayerGUID); @@ -210,7 +210,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!PlayerGUID) { @@ -370,7 +370,7 @@ public: } else if (EventOnWait) EventTimer -= diff; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { CurrWP = waypointId; EventTimer = 0; diff --git a/src/server/scripts/Kalimdor/zone_orgrimmar.cpp b/src/server/scripts/Kalimdor/zone_orgrimmar.cpp index 2603173e992..fc0d5120015 100644 --- a/src/server/scripts/Kalimdor/zone_orgrimmar.cpp +++ b/src/server/scripts/Kalimdor/zone_orgrimmar.cpp @@ -170,6 +170,11 @@ enum ThrallWarchief SPELL_SHOCK = 16034 }; +enum Sounds +{ + SOUND_AGGRO = 5880 +}; + /// @todo verify abilities/timers class npc_thrall_warchief : public CreatureScript { @@ -197,7 +202,10 @@ public: Initialize(); } - void EnterCombat(Unit* /*who*/) override { } + void EnterCombat(Unit* /*who*/) override + { + DoPlaySoundToSet(me, SOUND_AGGRO); + } void UpdateAI(uint32 diff) override { diff --git a/src/server/scripts/Kalimdor/zone_silithus.cpp b/src/server/scripts/Kalimdor/zone_silithus.cpp index f5b6ebba946..2ac6d96b883 100644 --- a/src/server/scripts/Kalimdor/zone_silithus.cpp +++ b/src/server/scripts/Kalimdor/zone_silithus.cpp @@ -1226,13 +1226,13 @@ class go_wind_stone : public GameObjectScript case NPC_DUKE_WATER: case NPC_DUKE_EARTH: case NPC_DUKE_AIR: - summons->AI()->Talk(SAY_DUKE_AGGRO); + summons->AI()->Talk(SAY_DUKE_AGGRO, player); break; case NPC_ROYAL_FIRE: case NPC_ROYAL_AIR: case NPC_ROYAL_EARTH: case NPC_ROYAL_WATER: - summons->AI()->Talk(YELL_ROYAL_AGGRO); + summons->AI()->Talk(YELL_ROYAL_AGGRO, player); break; } summons->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); diff --git a/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp b/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp index fa9164fced8..9095ac29830 100644 --- a/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp +++ b/src/server/scripts/Kalimdor/zone_stonetalon_mountains.cpp @@ -46,11 +46,11 @@ class npc_kaya_flathoof : public CreatureScript public: npc_kaya_flathoof() : CreatureScript("npc_kaya_flathoof") { } - struct npc_kaya_flathoofAI : public npc_escortAI + struct npc_kaya_flathoofAI : public EscortAI { - npc_kaya_flathoofAI(Creature* creature) : npc_escortAI(creature) { } + npc_kaya_flathoofAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) diff --git a/src/server/scripts/Kalimdor/zone_tanaris.cpp b/src/server/scripts/Kalimdor/zone_tanaris.cpp index b1e44a4ac55..1898b9406b0 100644 --- a/src/server/scripts/Kalimdor/zone_tanaris.cpp +++ b/src/server/scripts/Kalimdor/zone_tanaris.cpp @@ -24,7 +24,6 @@ SDCategory: Tanaris EndScriptData */ /* ContentData -npc_aquementas npc_custodian_of_time npc_OOX17 npc_tooga @@ -40,127 +39,6 @@ EndContentData */ #include "WorldSession.h" /*###### -## npc_aquementas -######*/ - -enum Aquementas -{ - AGGRO_YELL_AQUE = 0, - - SPELL_AQUA_JET = 13586, - SPELL_FROST_SHOCK = 15089, - - ITEM_BOOK_OF_AQUOR = 11169, - ITEM_SILVERY_CLAWS = 11172, - ITEM_IRONTREE_HEART = 11173, - ITEM_SILVER_TOTEM = 11522 -}; - -class npc_aquementas : public CreatureScript -{ -public: - npc_aquementas() : CreatureScript("npc_aquementas") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_aquementasAI(creature); - } - - struct npc_aquementasAI : public ScriptedAI - { - npc_aquementasAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - SendItemTimer = 0; - SwitchFactionTimer = 10000; - - isFriendly = true; - - AquaJetTimer = 5000; - FrostShockTimer = 1000; - } - - uint32 SendItemTimer; - uint32 SwitchFactionTimer; - bool isFriendly; - - uint32 FrostShockTimer; - uint32 AquaJetTimer; - - void Reset() override - { - Initialize(); - me->SetFaction(FACTION_FRIENDLY); - } - - void SendItem(Unit* receiver) - { - Player* player = receiver->ToPlayer(); - - if (player && player->HasItemCount(ITEM_BOOK_OF_AQUOR, 1, false) && - player->HasItemCount(ITEM_SILVERY_CLAWS, 11, false) && - player->HasItemCount(ITEM_IRONTREE_HEART, 1, false) && - !player->HasItemCount(ITEM_SILVER_TOTEM, 1, true)) - { - ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 11522, 1, nullptr); - if (msg == EQUIP_ERR_OK) - player->StoreNewItem(dest, ITEM_SILVER_TOTEM, true); - } - } - - void EnterCombat(Unit* who) override - { - Talk(AGGRO_YELL_AQUE, who); - } - - void UpdateAI(uint32 diff) override - { - if (isFriendly) - { - if (SwitchFactionTimer <= diff) - { - me->SetFaction(FACTION_ELEMENTAL); - isFriendly = false; - } else SwitchFactionTimer -= diff; - } - - if (!UpdateVictim()) - return; - - if (!isFriendly) - { - if (SendItemTimer <= diff) - { - if (me->GetVictim() && me->EnsureVictim()->GetTypeId() == TYPEID_PLAYER) - SendItem(me->GetVictim()); - SendItemTimer = 5000; - } else SendItemTimer -= diff; - } - - if (FrostShockTimer <= diff) - { - DoCastVictim(SPELL_FROST_SHOCK); - FrostShockTimer = 15000; - } else FrostShockTimer -= diff; - - if (AquaJetTimer <= diff) - { - DoCast(me, SPELL_AQUA_JET); - AquaJetTimer = 15000; - } else AquaJetTimer -= diff; - - DoMeleeAttackIfReady(); - } - }; - -}; - -/*###### ## npc_custodian_of_time ######*/ @@ -195,11 +73,11 @@ public: return new npc_custodian_of_timeAI(creature); } - struct npc_custodian_of_timeAI : public npc_escortAI + struct npc_custodian_of_timeAI : public EscortAI { - npc_custodian_of_timeAI(Creature* creature) : npc_escortAI(creature) { } + npc_custodian_of_timeAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (Player* player = GetPlayerForEscort()) { @@ -290,7 +168,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } }; @@ -319,11 +197,11 @@ class npc_OOX17 : public CreatureScript public: npc_OOX17() : CreatureScript("npc_OOX17") { } - struct npc_OOX17AI : public npc_escortAI + struct npc_OOX17AI : public EscortAI { - npc_OOX17AI(Creature* creature) : npc_escortAI(creature) { } + npc_OOX17AI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (Player* player = GetPlayerForEscort()) { @@ -551,7 +429,6 @@ public: void AddSC_tanaris() { - new npc_aquementas(); new npc_custodian_of_time(); new npc_OOX17(); new npc_tooga(); diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index 4e1c211ddde..2029ee3fbba 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -101,13 +101,13 @@ class npc_gilthares : public CreatureScript public: npc_gilthares() : CreatureScript("npc_gilthares") { } - struct npc_giltharesAI : public npc_escortAI + struct npc_giltharesAI : public EscortAI { - npc_giltharesAI(Creature* creature) : npc_escortAI(creature) { } + npc_giltharesAI(Creature* creature) : EscortAI(creature) { } void Reset() override { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -530,9 +530,9 @@ class npc_wizzlecrank_shredder : public CreatureScript public: npc_wizzlecrank_shredder() : CreatureScript("npc_wizzlecrank_shredder") { } - struct npc_wizzlecrank_shredderAI : public npc_escortAI + struct npc_wizzlecrank_shredderAI : public EscortAI { - npc_wizzlecrank_shredderAI(Creature* creature) : npc_escortAI(creature) + npc_wizzlecrank_shredderAI(Creature* creature) : EscortAI(creature) { IsPostEvent = false; PostEventTimer = 1000; @@ -556,7 +556,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -579,7 +579,7 @@ public: } } - void WaypointStart(uint32 PointId) override + void WaypointStarted(uint32 PointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); diff --git a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp index a1439f4cc00..7823e535bb2 100644 --- a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp +++ b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp @@ -56,13 +56,13 @@ class npc_kanati : public CreatureScript public: npc_kanati() : CreatureScript("npc_kanati") { } - struct npc_kanatiAI : public npc_escortAI + struct npc_kanatiAI : public EscortAI { - npc_kanatiAI(Creature* creature) : npc_escortAI(creature) { } + npc_kanatiAI(Creature* creature) : EscortAI(creature) { } void Reset() override { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -136,13 +136,13 @@ class npc_lakota_windsong : public CreatureScript public: npc_lakota_windsong() : CreatureScript("npc_lakota_windsong") { } - struct npc_lakota_windsongAI : public npc_escortAI + struct npc_lakota_windsongAI : public EscortAI { - npc_lakota_windsongAI(Creature* creature) : npc_escortAI(creature) { } + npc_lakota_windsongAI(Creature* creature) : EscortAI(creature) { } void Reset() override { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -216,13 +216,13 @@ class npc_paoka_swiftmountain : public CreatureScript public: npc_paoka_swiftmountain() : CreatureScript("npc_paoka_swiftmountain") { } - struct npc_paoka_swiftmountainAI : public npc_escortAI + struct npc_paoka_swiftmountainAI : public EscortAI { - npc_paoka_swiftmountainAI(Creature* creature) : npc_escortAI(creature) { } + npc_paoka_swiftmountainAI(Creature* creature) : EscortAI(creature) { } void Reset() override { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { diff --git a/src/server/scripts/Kalimdor/zone_thunder_bluff.cpp b/src/server/scripts/Kalimdor/zone_thunder_bluff.cpp index 0f894631345..cca5311c8b8 100644 --- a/src/server/scripts/Kalimdor/zone_thunder_bluff.cpp +++ b/src/server/scripts/Kalimdor/zone_thunder_bluff.cpp @@ -41,6 +41,11 @@ enum CairneBloodhoof SPELL_UPPERCUT = 22916 }; +enum Sounds +{ + SOUND_AGGRO = 5884 +}; + #define GOSSIP_HCB "I know this is rather silly but a young ward who is a bit shy would like your hoofprint." /// @todo verify abilities/timers class npc_cairne_bloodhoof : public CreatureScript @@ -75,7 +80,10 @@ public: Initialize(); } - void EnterCombat(Unit* /*who*/) override { } + void EnterCombat(Unit* /*who*/) override + { + DoPlaySoundToSet(me, SOUND_AGGRO); + } void UpdateAI(uint32 diff) override { diff --git a/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp b/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp index 4a1456a18c0..f7f7c5cd1dd 100644 --- a/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp +++ b/src/server/scripts/Kalimdor/zone_ungoro_crater.cpp @@ -57,9 +57,9 @@ class npc_ame : public CreatureScript public: npc_ame() : CreatureScript("npc_ame") { } - struct npc_ameAI : public npc_escortAI + struct npc_ameAI : public EscortAI { - npc_ameAI(Creature* creature) : npc_escortAI(creature) + npc_ameAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -71,7 +71,7 @@ public: uint32 DemoralizingShoutTimer; - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (Player* player = GetPlayerForEscort()) { @@ -118,7 +118,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!UpdateVictim()) return; diff --git a/src/server/scripts/Kalimdor/zone_winterspring.cpp b/src/server/scripts/Kalimdor/zone_winterspring.cpp index 9bc3711e830..5e7080ae621 100644 --- a/src/server/scripts/Kalimdor/zone_winterspring.cpp +++ b/src/server/scripts/Kalimdor/zone_winterspring.cpp @@ -19,12 +19,11 @@ /* ScriptData SDName: Winterspring SD%Complete: Almost Completely Emptied -SDComment: Vendor Rivern Frostwind. Quest Support 4901 +SDComment: Quest Support 4901 SDCategory: Winterspring EndScriptData */ /* ContentData -npc_rivern_frostwind npc_ranshalla go_elune_fire EndContentData */ @@ -36,52 +35,9 @@ EndContentData */ #include "ObjectAccessor.h" #include "Player.h" #include "ScriptedEscortAI.h" -#include "ScriptedGossip.h" #include "TemporarySummon.h" #include "WorldSession.h" -/*###### -## npc_rivern_frostwind -######*/ - -class npc_rivern_frostwind : public CreatureScript -{ -public: - npc_rivern_frostwind() : CreatureScript("npc_rivern_frostwind") { } - - struct npc_rivern_frostwindAI : public ScriptedAI - { - npc_rivern_frostwindAI(Creature* creature) : ScriptedAI(creature) { } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(me->GetGUID()); - - return true; - } - - bool GossipHello(Player* player) override - { - if (me->IsQuestGiver()) - player->PrepareQuestMenu(me->GetGUID()); - - if (me->IsVendor() && player->GetReputationRank(589) == REP_EXALTED) - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_rivern_frostwindAI(creature); - } -}; - enum Says { // Escort texts @@ -307,9 +263,9 @@ class npc_ranshalla : public CreatureScript public: npc_ranshalla() : CreatureScript("npc_ranshalla") { } - struct npc_ranshallaAI : public npc_escortAI, private DialogueHelper + struct npc_ranshallaAI : public EscortAI, private DialogueHelper { - npc_ranshallaAI(Creature* creature) : npc_escortAI(creature), DialogueHelper(introDialogue) + npc_ranshallaAI(Creature* creature) : EscortAI(creature), DialogueHelper(introDialogue) { Initialize(); } @@ -393,7 +349,7 @@ public: StartNextDialogueText(SAY_PRIESTESS_ALTAR_3); } - void WaypointReached(uint32 pointId) override + void WaypointReached(uint32 pointId, uint32 /*pathId*/) override { switch (pointId) { @@ -580,7 +536,7 @@ public: if (events.ExecuteEvent() == EVENT_RESUME) StartNextDialogueText(SAY_PRIESTESS_ALTAR_3); - npc_escortAI::UpdateEscortAI(diff); + EscortAI::UpdateEscortAI(diff); } void QuestAccept(Player* player, Quest const* quest) override @@ -643,7 +599,6 @@ public: void AddSC_winterspring() { - new npc_rivern_frostwind(); new npc_ranshalla(); new go_elune_fire(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/ahnkahet.h b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/ahnkahet.h index 00bd46ce2d5..ba70be7f02d 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/ahnkahet.h +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/ahnkahet.h @@ -37,13 +37,7 @@ enum AKDataTypes // Additional Data DATA_SPHERE_1 = 5, DATA_SPHERE_2 = 6, - DATA_PRINCE_TALDARAM_PLATFORM = 7, - DATA_PL_JEDOGA_TARGET = 8, - DATA_ADD_JEDOGA_OPFER = 9, - DATA_ADD_JEDOGA_INITIAND = 10, - DATA_JEDOGA_TRIGGER_SWITCH = 11, - DATA_JEDOGA_RESET_INITIANDS = 12, - DATA_ALL_INITIAND_DEAD = 13 + DATA_PRINCE_TALDARAM_PLATFORM = 7 }; enum AKCreatureIds @@ -59,7 +53,9 @@ enum AKCreatureIds NPC_AHNKAHAR_SWARMER = 30178, // Jedoga Shadowseeker - NPC_INITIAND = 30114, + NPC_TWILIGHT_INITIATE = 30114, + NPC_TWILIGHT_VOLUNTEER = 30385, + NPC_TWILIGHT_WORSHIPPER = 30111, NPC_JEDOGA_CONTROLLER = 30181, // Amanitar @@ -88,4 +84,6 @@ inline AI* GetAhnKahetAI(T* obj) return GetInstanceAI<AI>(obj, AhnKahetScriptName); } +#define RegisterAhnKahetCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetAhnKahetAI) + #endif // AHNKAHET_H_ diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp index f4881aaa9ed..514e8a44742 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp @@ -19,218 +19,266 @@ #include "ahnkahet.h" #include "InstanceScript.h" #include "ScriptedCreature.h" -#include "TemporarySummon.h" +#include "SpellScript.h" -enum Spells +enum AmanitarSpells { - SPELL_BASH = 57094, // Victim - SPELL_ENTANGLING_ROOTS = 57095, // Random Victim 100Y - SPELL_MINI = 57055, // Self - SPELL_VENOM_BOLT_VOLLEY = 57088, // Random Victim 100Y - SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS = 56648, // Killer 3Y - SPELL_POISONOUS_MUSHROOM_POISON_CLOUD = 57061, // Self - Duration 8 Sec - SPELL_POISONOUS_MUSHROOM_VISUAL_AREA = 61566, // Self - SPELL_POISONOUS_MUSHROOM_VISUAL_AURA = 56741, // Self - SPELL_PUTRID_MUSHROOM = 31690, // To make the mushrooms visible - SPELL_POWER_MUSHROOM_VISUAL_AURA = 56740, + //Amanitar + SPELL_BASH = 57094, + SPELL_ENTANGLING_ROOTS = 57095, + SPELL_MINI = 57055, + SPELL_VENOM_BOLT_VOLLEY = 57088, + SPELL_REMOVE_MUSHROOM_POWER = 57283, + //Mushrooms + SPELL_POTENT_FUNGUS = 56648, + SPELL_POISONOUS_MUSHROOM_POISON_CLOUD = 57061, + SPELL_POISONOUS_MUSHROOM_VISUAL_AURA = 56741, + SPELL_POWER_MUSHROOM_VISUAL_AURA = 56740, + SPELL_PUTRID_MUSHROOM = 31690, + SPELL_GROW = 57059, + SPELL_SHRINK = 31691 }; -enum Creatures -{ - NPC_TRIGGER = 19656 -}; - -enum Events +enum AmanitarEvents { EVENT_SPAWN = 1, EVENT_MINI, EVENT_ROOT, EVENT_BASH, EVENT_BOLT, - EVENT_AURA + EVENT_RESPAWN }; -class boss_amanitar : public CreatureScript +Position const MushroomPositions[32] = { - public: - boss_amanitar() : CreatureScript("boss_amanitar") { } + { 373.4807f, -856.5301f, -74.30518f, 0.2094395f }, + { 358.4792f, -879.3193f, -75.9463f, 5.166174f }, + { 356.5531f, -846.3022f, -72.1796f, 3.193953f }, + { 332.369f, -846.081f, -74.30516f, 4.834562f }, + { 360.2234f, -862.055f, -75.22755f, 1.658063f }, + { 351.7189f, -890.9619f, -76.54617f, 1.064651f }, + { 345.8126f, -869.1772f, -77.17728f, 1.361357f }, + { 367.5179f, -884.0129f, -77.32881f, 4.276057f }, + { 370.6044f, -868.4305f, -74.19881f, 0.8901179f }, + { 381.3156f, -873.2377f, -74.82656f, 1.099557f }, + { 371.5869f, -873.8141f, -74.72424f, 1.082104f }, + { 340.4079f, -891.6375f, -74.99128f, 1.134464f }, + { 368.21f, -851.5953f, -73.99741f, 4.694936f }, + { 328.7047f, -853.9812f, -75.51253f, 0.5759587f }, + { 366.4145f, -876.39f, -75.52739f, 5.253441f }, + { 380.1362f, -861.4344f, -73.45917f, 3.787364f }, + { 373.3007f, -888.8057f, -79.03593f, 5.602507f }, + { 348.3599f, -848.0839f, -73.54117f, 1.745329f }, + { 352.5586f, -882.6624f, -75.68202f, 3.822271f }, + { 357.8967f, -871.179f, -75.77553f, 2.443461f }, + { 360.1034f, -842.3351f, -71.08852f, 4.34587f }, + { 348.1334f, -861.5244f, -74.61307f, 2.565634f }, + { 401.4896f, -866.7059f, -73.22395f, 0.8901179f }, + { 360.1683f, -889.1515f, -76.74798f, 3.612832f }, + { 350.1828f, -907.7313f, -74.94678f, 5.044002f }, + { 340.6278f, -856.5973f, -74.23862f, 4.415683f }, + { 366.4849f, -859.7621f, -74.82679f, 1.500983f }, + { 359.1482f, -853.3346f, -74.47543f, 5.654867f }, + { 374.9992f, -879.0921f, -75.56115f, 1.867502f }, + { 339.5252f, -850.4612f, -74.45442f, 4.764749f }, + { 337.0534f, -864.002f, -75.72749f, 4.642576f }, + { 398.2797f, -851.8694f, -68.84419f, 0.5759587f } +}; - struct boss_amanitarAI : public BossAI - { - boss_amanitarAI(Creature* creature) : BossAI(creature, DATA_AMANITAR) { } +struct boss_amanitar : public BossAI +{ + boss_amanitar(Creature* creature) : BossAI(creature, DATA_AMANITAR) { } - void Reset() override - { - _Reset(); - me->SetMeleeDamageSchool(SPELL_SCHOOL_NATURE); - me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true); - } + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + events.ScheduleEvent(EVENT_ROOT, Seconds(5), Seconds(9)); + events.ScheduleEvent(EVENT_BASH, Seconds(10), Seconds(14)); + events.ScheduleEvent(EVENT_BOLT, Seconds(15), Seconds(20)); + events.ScheduleEvent(EVENT_MINI, Seconds(12), Seconds(18)); + events.ScheduleEvent(EVENT_SPAWN, Seconds(1)); + events.ScheduleEvent(EVENT_RESPAWN, Seconds(40), Seconds(60)); + } - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); + void EnterEvadeMode(EvadeReason /*why*/) override + { + _EnterEvadeMode(); + summons.DespawnAll(); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI); + _DespawnAtEvade(); + } - events.ScheduleEvent(EVENT_ROOT, urand(5, 9) * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_BASH, urand(10, 14) * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_BOLT, urand(15, 20) * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_MINI, urand(12, 18) * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SPAWN, 5 * IN_MILLISECONDS); - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + DoCastAOE(SPELL_REMOVE_MUSHROOM_POWER); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI); + } - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI); - } + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + } - void SpawnAdds() - { - uint8 u = 0; + void SummonedCreatureDies(Creature* summon, Unit* killer) override + { + _mushroomsDeque.push_back(summon->GetPosition()); - for (uint8 i = 0; i < 30; ++i) - { - Position pos = me->GetRandomNearPosition(30.0f); - me->UpdateGroundPositionZ(pos.GetPositionX(), pos.GetPositionY(), pos.m_positionZ); + BossAI::SummonedCreatureDies(summon, killer); + } - if (Creature* trigger = me->SummonCreature(NPC_TRIGGER, pos)) - { - Creature* temp1 = trigger->FindNearestCreature(NPC_HEALTHY_MUSHROOM, 4.0f, true); - Creature* temp2 = trigger->FindNearestCreature(NPC_POISONOUS_MUSHROOM, 4.0f, true); - if (!temp1 && !temp2) - { - u = 1 - u; - me->SummonCreature(u > 0 ? NPC_POISONOUS_MUSHROOM : NPC_HEALTHY_MUSHROOM, pos, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60 * IN_MILLISECONDS); - } - trigger->DespawnOrUnsummon(); - } - } - } + void SpawnMushroom(Position const pos) + { + me->SummonCreature(roll_chance_i(40) ? NPC_HEALTHY_MUSHROOM : NPC_POISONOUS_MUSHROOM, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 4000); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - events.Update(diff); + events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SPAWN: + for (Position const pos : MushroomPositions) + SpawnMushroom(pos); + break; + case EVENT_MINI: + if (SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, true, -SPELL_MINI)) { - case EVENT_SPAWN: - SpawnAdds(); - events.ScheduleEvent(EVENT_SPAWN, 20 * IN_MILLISECONDS); - break; - case EVENT_MINI: - DoCast(SPELL_MINI); - events.ScheduleEvent(EVENT_MINI, urand(25, 30) * IN_MILLISECONDS); - break; - case EVENT_ROOT: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_ENTANGLING_ROOTS, true); - events.ScheduleEvent(EVENT_ROOT, urand(10, 15) * IN_MILLISECONDS); - break; - case EVENT_BASH: - DoCastVictim(SPELL_BASH); - events.ScheduleEvent(EVENT_BASH, urand(7, 12) * IN_MILLISECONDS); - break; - case EVENT_BOLT: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_VENOM_BOLT_VOLLEY, true); - events.ScheduleEvent(EVENT_BOLT, urand(18, 22) * IN_MILLISECONDS); - break; - default: - break; + DoCastAOE(SPELL_MINI); + events.Repeat(Seconds(30)); } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - DoMeleeAttackIfReady(); + else + events.Repeat(Seconds(1)); + break; + case EVENT_ROOT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100.0f, true)) + DoCast(target, SPELL_ENTANGLING_ROOTS, true); + events.Repeat(Seconds(10), Seconds(15)); + break; + case EVENT_BASH: + DoCastVictim(SPELL_BASH); + events.Repeat(Seconds(7), Seconds(12)); + break; + case EVENT_BOLT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_VENOM_BOLT_VOLLEY, true); + events.Repeat(Seconds(18), Seconds(22)); + break; + case EVENT_RESPAWN: + while (!_mushroomsDeque.empty()) + { + SpawnMushroom(_mushroomsDeque.front()); + _mushroomsDeque.pop_front(); + } + events.Repeat(Seconds(40), Seconds(60)); + break; + default: + break; } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetAhnKahetAI<boss_amanitarAI>(creature); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } + + DoMeleeAttackIfReady(); + } + + private: + std::deque<Position> _mushroomsDeque; }; -class npc_amanitar_mushrooms : public CreatureScript +struct npc_amanitar_mushrooms : public ScriptedAI { -public: - npc_amanitar_mushrooms() : CreatureScript("npc_amanitar_mushrooms") { } + npc_amanitar_mushrooms(Creature* creature) : ScriptedAI(creature), _active(false) { } - struct npc_amanitar_mushroomsAI : public ScriptedAI + void Reset() override { - npc_amanitar_mushroomsAI(Creature* creature) : ScriptedAI(creature) { } + me->SetReactState(REACT_PASSIVE); + me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); + DoCastSelf(SPELL_PUTRID_MUSHROOM); + DoCastSelf(SPELL_SHRINK, true); + DoCastSelf(SPELL_GROW, true); - EventMap events; + if (me->GetEntry() == NPC_HEALTHY_MUSHROOM) + { + DoCastSelf(SPELL_POWER_MUSHROOM_VISUAL_AURA); + _active = true; + } + else + DoCastSelf(SPELL_POISONOUS_MUSHROOM_VISUAL_AURA); - void Reset() override + _scheduler.Schedule(Milliseconds(800), [this](TaskContext /*context*/) { - events.Reset(); - events.ScheduleEvent(EVENT_AURA, 1 * IN_MILLISECONDS); + DoCastSelf(SPELL_GROW, true); + }); + } + + void MoveInLineOfSight(Unit* target) override + { + if (_active || target->GetTypeId() != TYPEID_PLAYER || me->GetDistance2d(target) > 2.0f) + return; - me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); - DoCast(SPELL_PUTRID_MUSHROOM); + _active = true; - if (me->GetEntry() == NPC_POISONOUS_MUSHROOM) - DoCast(SPELL_POISONOUS_MUSHROOM_VISUAL_AURA); - else - DoCast(SPELL_POWER_MUSHROOM_VISUAL_AURA); - } + target->RemoveAurasDueToSpell(SPELL_POTENT_FUNGUS); + DoCastAOE(SPELL_POISONOUS_MUSHROOM_POISON_CLOUD); - void DamageTaken(Unit* /*attacker*/, uint32 &damage) override + _scheduler.Schedule(Seconds(1), [this](TaskContext /*context*/) { - if (damage >= me->GetHealth() && me->GetEntry() == NPC_HEALTHY_MUSHROOM) - DoCast(me, SPELL_HEALTHY_MUSHROOM_POTENT_FUNGUS, true); - } + me->SetObjectScale(0.1f); + me->DespawnOrUnsummon(Seconds(4)); + }); + } - void EnterCombat(Unit* /*who*/) override { } - void AttackStart(Unit* /*victim*/) override { } + void JustDied(Unit* /*killer*/) override + { + if (me->GetEntry() == NPC_HEALTHY_MUSHROOM) + DoCastAOE(SPELL_POTENT_FUNGUS, true); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } - events.Update(diff); +private: + TaskScheduler _scheduler; + bool _active; +}; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; +// 56648 - Potent Fungus +class spell_amanitar_potent_fungus : public AuraScript +{ + PrepareAuraScript(spell_amanitar_potent_fungus); - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_AURA: - if (me->GetEntry() == NPC_POISONOUS_MUSHROOM) - { - DoCast(me, SPELL_POISONOUS_MUSHROOM_VISUAL_AREA, true); - DoCast(me, SPELL_POISONOUS_MUSHROOM_POISON_CLOUD); - } - events.ScheduleEvent(EVENT_AURA, 7 * IN_MILLISECONDS); - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - } - }; + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + if (!target->HasAura(SPELL_MINI)) + return; + + target->RemoveAurasDueToSpell(SPELL_MINI); + Remove(); + } - CreatureAI* GetAI(Creature* creature) const override + void Register() override { - return GetAhnKahetAI<npc_amanitar_mushroomsAI>(creature); + AfterEffectApply += AuraEffectApplyFn(spell_amanitar_potent_fungus::OnApply, EFFECT_0, SPELL_AURA_MOD_SCALE, AURA_EFFECT_HANDLE_REAL); } }; void AddSC_boss_amanitar() { - new boss_amanitar(); - new npc_amanitar_mushrooms(); + RegisterAhnKahetCreatureAI(boss_amanitar); + RegisterAhnKahetCreatureAI(npc_amanitar_mushrooms); + RegisterAuraScript(spell_amanitar_potent_fungus); } diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp index 882b3c22410..7ab94f69186 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp @@ -15,589 +15,484 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* - * Comment: Visuals missing, de-germanize code, wow 3.3.5a-ize - * Patch 3.3.2 (2010-01-02): Jedoga Shadowseeker now only ascends once during the encounter. - */ - #include "ScriptMgr.h" #include "ahnkahet.h" #include "InstanceScript.h" #include "MotionMaster.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" +#include "Spell.h" +#include "SpellScript.h" +#include "TemporarySummon.h" enum Yells { - TEXT_AGGRO = 0, - TEXT_SACRIFICE_1 = 1, - TEXT_SACRIFICE_2 = 2, - TEXT_SLAY = 3, - TEXT_DEATH = 4, - TEXT_PREACHING = 5 + SAY_AGGRO = 0, + SAY_CHOOSE = 1, + SAY_SACRIFICE = 2, + SAY_SLAY = 3, + SAY_DEATH = 4, + SAY_PREACHING = 5, + + SAY_CHOSEN = 0, + SAY_SACRIFICED = 1 }; -enum Spells +enum JedogaSpells { - SPELL_SPHERE_VISUAL = 56075, - SPELL_GIFT_OF_THE_HERALD = 56219, - SPELL_CYCLONE_STRIKE = 56855, // Self - SPELL_LIGHTNING_BOLT = 56891, // 40Y - SPELL_THUNDERSHOCK = 56926 // 30Y + SPELL_RANDOM_LIGHTNING_VISUAL = 56327, + SPELL_HOVER_FALL_1 = 56100, + SPELL_HOVER_FALL_2 = 56157, + SPELL_SPHERE_VISUAL = 56075, + SPELL_LIGHTNING_BOLT = 56891, + SPELL_THUNDERSHOCK = 56926, + SPELL_CYCLONE_STRIKE = 56855, + SPELL_SACRIFICE_BEAM = 56150, + + //Jedoga Controller + SPELL_BEAM_VISUAL_JEDOGA = 56312, + SPELL_SACRIFICE_VISUAL = 56133, + + //Twilight Volunteer + SPELL_SPHERE_VISUAL_VOLUNTEER = 56102, + SPELL_PILLAR_OF_LIGHTNING = 56868 }; -const Position JedogaPosition[2] = +enum JedogaEvents { - {372.330994f, -705.278015f, -0.624178f, 5.427970f}, - {372.330994f, -705.278015f, -16.179716f, 5.427970f} + EVENT_INTRO_SAY = 1, + EVENT_START_FIGHT_1, + EVENT_START_FIGHT_2, + EVENT_CYCLONE_STRIKE, + EVENT_LIGHTNING_BOLT, + EVENT_THUNDERSHOCK, + EVENT_START_PHASE_TWO, + EVENT_FLY_DELAY, + EVENT_END_PHASE_TWO, + EVENT_CHOOSE_VOLUNTEER, + EVENT_SUMMON_VOLUNTEER }; -enum Misc +enum JedogaPhases { - ACTION_INITIAND_KILLED = 1, - DATA_VOLUNTEER_WORK = 2 + PHASE_INTRO = 0, + PHASE_ONE, + PHASE_TWO, + PHASE_THREE }; -class boss_jedoga_shadowseeker : public CreatureScript +enum JedogaPoints { -public: - boss_jedoga_shadowseeker() : CreatureScript("boss_jedoga_shadowseeker") { } - - struct boss_jedoga_shadowseekerAI : public ScriptedAI - { - boss_jedoga_shadowseekerAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - bFirstTime = true; - bPreDone = false; - } - - void Initialize() - { - uiOpFerTimer = urand(15 * IN_MILLISECONDS, 20 * IN_MILLISECONDS); + POINT_INITIAL_POSITION = 0, + POINT_SACRIFICE, + POINT_GROUND, + POINT_PHASE_TWO, + POINT_PHASE_TWO_FLY +}; - uiCycloneTimer = 3 * IN_MILLISECONDS; - uiBoltTimer = 7 * IN_MILLISECONDS; - uiThunderTimer = 12 * IN_MILLISECONDS; +Position const SacrificePosition = { 376.5385f, -707.3567f, -16.14124f }; +Position const JedogaGroundPosition = { 371.6281f, -704.4836f, -16.17967f }; +Position const JedogaFlyPosition = { 371.627f, -704.4217f, -6.707521f }; +Position const JedogaControllerPositions[3] = +{ + { 402.7893f, -748.5251f, 29.39399f, 2.024582f }, + { 420.1999f, -727.0132f, 28.88036f, 2.042035f }, + { 375.4977f, -707.3635f, -16.0964f, 2.426008f } +}; - bOpFerok = false; - bOpFerokFail = false; - bOnGround = false; - bCanDown = false; - volunteerWork = true; - } +typedef std::pair<Position, Position> VolunteerPositionPair; +std::vector<VolunteerPositionPair> const VolunteerSpotPositions = +{ + { { 400.7701f, -784.8928f, -31.60143f }, { 365.9514f, -719.1235f, -16.17974f } }, + { { 397.3595f, -788.5157f, -31.59679f }, { 359.7433f, -715.017f, -16.17974f } }, + { { 399.3177f, -787.2599f, -31.59631f }, { 362.0263f, -719.1036f, -16.17974f } }, + { { 460.4623f, -719.2227f, -31.58718f }, { 389.266f, -679.3693f, -16.17973f } }, + { { 456.0909f, -724.3412f, -31.58718f }, { 400.5992f, -691.7954f, -16.17973f } }, + { { 452.6613f, -726.9518f, -31.58718f }, { 400.3423f, -701.5115f, -16.17974f } }, + { { 447.8852f, -732.3298f, -31.58718f }, { 389.861f, -710.6993f, -16.17974f } }, + { { 457.562f, -721.1855f, -31.58718f }, { 395.4494f, -684.5345f, -16.17973f } }, + { { 451.7243f, -730.2181f, -31.58718f }, { 397.0945f, -708.4188f, -15.99747f } }, + { { 413.9582f, -777.132f, -31.58716f }, { 388.1394f, -723.124f, -15.9938f } }, + { { 411.5661f, -781.2356f, -31.58716f }, { 381.7102f, -730.0745f, -15.99554f } }, + { { 407.395f, -786.793f, -31.58716f }, { 366.9791f, -737.3303f, -16.17974f } }, + { { 404.9166f, -788.3472f, -31.58716f }, { 358.6124f, -735.9944f, -15.9855f } }, + { { 401.5697f, -791.2033f, -31.58717f }, { 351.9383f, -729.6436f, -16.17974f } }, + { { 410.1105f, -785.4691f, -31.58716f }, { 373.1659f, -736.2893f, -16.17974f } }, + { { 442.5644f, -730.2499f, -31.59826f }, { 390.5955f, -714.6851f, -16.17974f } }, + { { 445.5233f, -725.9542f, -31.60173f }, { 393.9694f, -708.1727f, -16.17974f } }, + { { 448.5531f, -722.5888f, -31.60066f }, { 395.2702f, -702.556f, -16.17974f } }, + { { 449.8521f, -719.7265f, -31.58849f }, { 394.5757f, -695.1004f, -16.17974f } }, + { { 453.5134f, -717.7018f, -31.59883f }, { 387.6152f, -690.1782f, -16.17974f } }, + { { 457.8564f, -711.7424f, -31.59773f }, { 378.6874f, -687.1343f, -16.17973f } }, + { { 410.0583f, -774.4119f, -31.60115f }, { 383.8151f, -723.4276f, -16.17974f } }, + { { 408.7458f, -777.955f, -31.59873f }, { 376.9857f, -725.0735f, -16.17974f } }, + { { 405.2404f, -779.6614f, -31.60512f }, { 373.3736f, -722.7498f, -16.17974f } }, + { { 404.0797f, -783.829f, -31.59497f }, { 367.8631f, -722.5212f, -16.17974f } } +}; - InstanceScript* instance; +enum JedogaMisc +{ + SUMMON_GROUP_INITIATES = 1, + SUMMON_GROUP_WORSHIPPERS = 2, + DATA_VOLUNTEER_WORK = 1, + ACTION_CHOSEN = 1, + ACTION_SACRIFICE = 2, + TWILIGHT_INITIATES_SIZE = 15 +}; - uint32 uiOpFerTimer; - uint32 uiCycloneTimer; - uint32 uiBoltTimer; - uint32 uiThunderTimer; +struct boss_jedoga_shadowseeker : public BossAI +{ + boss_jedoga_shadowseeker(Creature* creature) : BossAI(creature, DATA_JEDOGA_SHADOWSEEKER), _initiatesKilled(0), _volunteerWork(true) { } - bool bPreDone; - bool bOpFerok; - bool bOnGround; - bool bOpFerokFail; - bool bCanDown; - bool volunteerWork; - bool bFirstTime; + void Reset() override + { + _Reset(); + events.SetPhase(PHASE_INTRO); + me->SetReactState(REACT_PASSIVE); + me->SummonCreatureGroup(SUMMON_GROUP_INITIATES); - void Reset() override - { - Initialize(); + if (TempSummon* controller = me->SummonCreature(NPC_JEDOGA_CONTROLLER, JedogaControllerPositions[0], TEMPSUMMON_MANUAL_DESPAWN)) + controller->CastSpell(me, SPELL_BEAM_VISUAL_JEDOGA); + if (TempSummon* controller = me->SummonCreature(NPC_JEDOGA_CONTROLLER, JedogaControllerPositions[1], TEMPSUMMON_MANUAL_DESPAWN)) + controller->CastSpell(me, SPELL_BEAM_VISUAL_JEDOGA); - if (!bFirstTime) - instance->SetBossState(DATA_JEDOGA_SHADOWSEEKER, FAIL); + events.ScheduleEvent(EVENT_INTRO_SAY, Minutes(2), 0, PHASE_INTRO); + } - instance->SetGuidData(DATA_PL_JEDOGA_TARGET, ObjectGuid::Empty); - instance->SetGuidData(DATA_ADD_JEDOGA_OPFER, ObjectGuid::Empty); - instance->SetData(DATA_JEDOGA_RESET_INITIANDS, 0); - MoveUp(); + void EnterCombat(Unit* /*who*/) override + { + me->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); + me->RemoveAurasDueToSpell(SPELL_RANDOM_LIGHTNING_VISUAL); + me->SummonCreatureGroup(SUMMON_GROUP_WORSHIPPERS); - bFirstTime = false; - } + _EnterCombat(); + Talk(SAY_AGGRO); + events.SetPhase(PHASE_ONE); - void EnterCombat(Unit* who) override + for (VolunteerPositionPair posPair : VolunteerSpotPositions) { - if (!instance || (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_JEDOGA_CONTROLLER)) - return; - - Talk(TEXT_AGGRO); - me->SetInCombatWithZone(); - instance->SetBossState(DATA_JEDOGA_SHADOWSEEKER, IN_PROGRESS); + if (TempSummon* volunteer = me->SummonCreature(NPC_TWILIGHT_VOLUNTEER, posPair.first, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000)) + { + volunteer->GetMotionMaster()->MovePoint(POINT_INITIAL_POSITION, posPair.second); + _volunteerGUIDS.push_back(volunteer->GetGUID()); + } } + } - void AttackStart(Unit* who) override - { - if (!who || (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_JEDOGA_CONTROLLER)) - return; - - ScriptedAI::AttackStart(who); - } + void EnterEvadeMode(EvadeReason /*why*/) override + { + summons.DespawnAll(); + _EnterEvadeMode(); + _DespawnAtEvade(Seconds(15)); + } - void KilledUnit(Unit* Victim) override - { - if (!Victim || Victim->GetTypeId() != TYPEID_PLAYER) - return; + void KilledUnit(Unit* Victim) override + { + if (Victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - Talk(TEXT_SLAY); - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } - void JustDied(Unit* /*killer*/) override - { - Talk(TEXT_DEATH); - instance->SetBossState(DATA_JEDOGA_SHADOWSEEKER, DONE); - } + uint32 GetData(uint32 type) const override + { + if (type == DATA_VOLUNTEER_WORK) + return _volunteerWork ? 1 : 0; - void DoAction(int32 action) override - { - if (action == ACTION_INITIAND_KILLED) - volunteerWork = false; - } + return 0; + } - uint32 GetData(uint32 type) const override + void DamageTaken(Unit* /*done_by*/, uint32& /*damage*/) override + { + if (HealthBelowPct(55) && events.IsInPhase(PHASE_ONE)) { - if (type == DATA_VOLUNTEER_WORK) - return volunteerWork ? 1 : 0; - - return 0; + events.Reset(); + events.SetPhase(PHASE_TWO); + events.ScheduleEvent(EVENT_START_PHASE_TWO, Seconds(1)); } + } - void MoveInLineOfSight(Unit* who) override + void DoAction(int32 action) override + { + if (action == ACTION_SACRIFICE) { - if (!instance || !who || (who->GetTypeId() == TYPEID_UNIT && who->GetEntry() == NPC_JEDOGA_CONTROLLER)) - return; - - if (!bPreDone && who->GetTypeId() == TYPEID_PLAYER && me->GetDistance(who) < 100.0f) - { - Talk(TEXT_PREACHING); - bPreDone = true; - } - - if (instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) != IN_PROGRESS || !bOnGround) - return; - - if (!me->GetVictim() && me->CanCreatureAttack(who)) - { - float attackRadius = me->GetAttackDistance(who); - if (me->IsWithinDistInMap(who, attackRadius) && me->IsWithinLOSInMap(who)) - { - if (!me->GetVictim()) - { - who->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); - AttackStart(who); - } - else - { - who->SetInCombatWith(me); - AddThreat(who, 0.0f); - } - } - } + Talk(SAY_SACRIFICE); + DoCastAOE(SPELL_SACRIFICE_BEAM); + events.ScheduleEvent(EVENT_END_PHASE_TWO, Seconds(3)); + events.RescheduleEvent(EVENT_SUMMON_VOLUNTEER, Seconds(15)); } + } - void MoveDown() + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_TWILIGHT_WORSHIPPER) { - bOpFerokFail = false; - - instance->SetData(DATA_JEDOGA_TRIGGER_SWITCH, 0); - me->GetMotionMaster()->MovePoint(1, JedogaPosition[1]); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, false); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, false); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); - - me->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); - - bOnGround = true; - - if (UpdateVictim()) - { - AttackStart(me->GetVictim()); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - } - else - { - if (Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_PL_JEDOGA_TARGET))) - { - AttackStart(target); - instance->SetData(DATA_JEDOGA_RESET_INITIANDS, 0); - if (instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) != IN_PROGRESS) - EnterCombat(target); - } - else if (!me->IsInCombat()) - EnterEvadeMode(); - } + summon->SetStandState(UNIT_STAND_STATE_KNEEL); + summons.Summon(summon); + return; } - void MoveUp() - { - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); - - me->AttackStop(); - me->RemoveAllAuras(); - me->LoadCreaturesAddon(); - me->GetMotionMaster()->MovePoint(0, JedogaPosition[0]); - - instance->SetData(DATA_JEDOGA_TRIGGER_SWITCH, 1); - if (instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) == IN_PROGRESS) - OpferRufen(); - - bOnGround = false; - uiOpFerTimer = urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS); - } + BossAI::JustSummoned(summon); + } - void OpferRufen() + void SummonedCreatureDies(Creature* summon, Unit* killer) override + { + if (summon->GetEntry() == NPC_TWILIGHT_INITIATE) { - ObjectGuid opfer = instance->GetGuidData(DATA_ADD_JEDOGA_INITIAND); - - if (opfer) + if (++_initiatesKilled == TWILIGHT_INITIATES_SIZE) { - Talk(TEXT_SACRIFICE_1); - instance->SetGuidData(DATA_ADD_JEDOGA_OPFER, opfer); + DoCastSelf(SPELL_HOVER_FALL_1); + me->RemoveByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + events.ScheduleEvent(EVENT_START_FIGHT_1, Seconds(1)); } - else - bCanDown = true; } - - void Opfern() + else if (summon->GetEntry() == NPC_TWILIGHT_VOLUNTEER) { - Talk(TEXT_SACRIFICE_2); - - me->InterruptNonMeleeSpells(false); - DoCast(me, SPELL_GIFT_OF_THE_HERALD, false); - - bOpFerok = false; - bCanDown = true; - } - - void UpdateAI(uint32 diff) override - { - if (instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) != IN_PROGRESS && instance->GetData(DATA_ALL_INITIAND_DEAD)) - MoveDown(); - - if (bOpFerok && !bOnGround && !bCanDown) Opfern(); + _volunteerWork = false; - if (bOpFerokFail && !bOnGround && !bCanDown) - bCanDown = true; - - if (bCanDown) + if (events.IsInPhase(PHASE_TWO)) { - MoveDown(); - bCanDown = false; + events.SetPhase(PHASE_THREE); + events.RescheduleEvent(EVENT_END_PHASE_TWO, Seconds(1)); } - if (bOnGround) - { - if (!UpdateVictim()) - return; - - if (uiCycloneTimer <= diff) - { - DoCast(me, SPELL_CYCLONE_STRIKE, false); - uiCycloneTimer = urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS); - } else uiCycloneTimer -= diff; - - if (uiBoltTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - me->CastSpell(target, SPELL_LIGHTNING_BOLT, false); - - uiBoltTimer = urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS); - } else uiBoltTimer -= diff; - - if (uiThunderTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - me->CastSpell(target, SPELL_THUNDERSHOCK, false); - - uiThunderTimer = urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS); - } else uiThunderTimer -= diff; - - if (uiOpFerTimer <= diff) - MoveUp(); - else - uiOpFerTimer -= diff; - - DoMeleeAttackIfReady(); - } + events.RescheduleEvent(EVENT_SUMMON_VOLUNTEER, Seconds(10)); } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetAhnKahetAI<boss_jedoga_shadowseekerAI>(creature); + BossAI::SummonedCreatureDies(summon, killer); } -}; - -class npc_jedoga_initiand : public CreatureScript -{ -public: - npc_jedoga_initiand() : CreatureScript("npc_jedoga_initiand") { } - struct npc_jedoga_initiandAI : public ScriptedAI + void MovementInform(uint32 type, uint32 pointId) override { - npc_jedoga_initiandAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - instance = creature->GetInstanceScript(); - } + if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE) + return; - void Initialize() + switch (pointId) { - bWalking = false; - bCheckTimer = 2 * IN_MILLISECONDS; + case POINT_GROUND: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + DoZoneInCombat(); + events.ScheduleEvent(EVENT_CYCLONE_STRIKE, Seconds(3)); + events.ScheduleEvent(EVENT_LIGHTNING_BOLT, Seconds(7)); + events.ScheduleEvent(EVENT_THUNDERSHOCK, Seconds(12)); + break; + case POINT_PHASE_TWO: + events.ScheduleEvent(EVENT_FLY_DELAY, Seconds(2)); + break; + case POINT_PHASE_TWO_FLY: + events.ScheduleEvent(EVENT_CHOOSE_VOLUNTEER, Seconds(2)); + break; + default: + break; } + } - InstanceScript* instance; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) + return; - uint32 bCheckTimer; + events.Update(diff); - bool bWalking; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void Reset() override + while (uint32 eventId = events.ExecuteEvent()) { - Initialize(); - - if (instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) != IN_PROGRESS) - { - me->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, false); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, false); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); - } - else + switch (eventId) { - DoCast(me, SPELL_SPHERE_VISUAL, false); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); - } - } + case EVENT_INTRO_SAY: + if (instance->GetBossState(DATA_PRINCE_TALDARAM) == DONE) + Talk(SAY_PREACHING); + events.Repeat(Minutes(2)); + break; + case EVENT_START_FIGHT_1: + me->RemoveAurasDueToSpell(SPELL_BEAM_VISUAL_JEDOGA); + events.ScheduleEvent(EVENT_START_FIGHT_2, Seconds(1)); + break; + case EVENT_START_FIGHT_2: + summons.DespawnEntry(NPC_JEDOGA_CONTROLLER); + me->SetDisableGravity(false); + me->RemoveByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + me->GetMotionMaster()->MoveLand(POINT_GROUND, JedogaGroundPosition); + break; + case EVENT_START_PHASE_TWO: + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->InterruptNonMeleeSpells(true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->GetMotionMaster()->MovePoint(POINT_PHASE_TWO, JedogaGroundPosition); + break; + case EVENT_FLY_DELAY: + me->SetDisableGravity(true); + me->SetByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + me->GetMotionMaster()->MoveTakeoff(POINT_PHASE_TWO_FLY, JedogaFlyPosition); + break; + case EVENT_CHOOSE_VOLUNTEER: + if (TempSummon* controller = me->SummonCreature(NPC_JEDOGA_CONTROLLER, JedogaControllerPositions[2], TEMPSUMMON_MANUAL_DESPAWN)) + { + me->SetFacingToObject(controller); + controller->CastSpell(controller, SPELL_SACRIFICE_VISUAL); + } - void JustDied(Unit* killer) override - { - if (!killer || !instance) - return; + Talk(SAY_CHOOSE); - if (bWalking) - { - if (Creature* boss = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_JEDOGA_SHADOWSEEKER))) - { - if (!ENSURE_AI(boss_jedoga_shadowseeker::boss_jedoga_shadowseekerAI, boss->AI())->bOpFerok) - ENSURE_AI(boss_jedoga_shadowseeker::boss_jedoga_shadowseekerAI, boss->AI())->bOpFerokFail = true; + if (_volunteerGUIDS.empty()) + break; - if (killer->GetTypeId() == TYPEID_PLAYER) - boss->AI()->DoAction(ACTION_INITIAND_KILLED); + _selectedVolunteerGUID = Trinity::Containers::SelectRandomContainerElement(_volunteerGUIDS); + if (Creature* volunteer = ObjectAccessor::GetCreature(*me, _selectedVolunteerGUID)) + volunteer->AI()->DoAction(ACTION_CHOSEN); + break; + case EVENT_SUMMON_VOLUNTEER: + { + uint32 pos = std::distance(_volunteerGUIDS.begin(), std::find(_volunteerGUIDS.begin(), _volunteerGUIDS.end(), _selectedVolunteerGUID)); + if (pos < VolunteerSpotPositions.size()) + { + VolunteerPositionPair posPair = VolunteerSpotPositions.at(pos); + if (TempSummon* volunteer = me->SummonCreature(NPC_TWILIGHT_VOLUNTEER, posPair.first, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000)) + volunteer->GetMotionMaster()->MovePoint(POINT_INITIAL_POSITION, posPair.second); + } + break; } - - instance->SetGuidData(DATA_ADD_JEDOGA_OPFER, ObjectGuid::Empty); - - bWalking = false; + case EVENT_END_PHASE_TWO: + summons.DespawnEntry(NPC_JEDOGA_CONTROLLER); + DoCastSelf(SPELL_HOVER_FALL_2); + me->SetDisableGravity(false); + me->RemoveByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + me->GetMotionMaster()->MoveLand(POINT_GROUND, JedogaGroundPosition); + break; + case EVENT_CYCLONE_STRIKE: + DoCastSelf(SPELL_CYCLONE_STRIKE); + events.Repeat(Seconds(15), Seconds(30)); + break; + case EVENT_LIGHTNING_BOLT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_LIGHTNING_BOLT); + events.Repeat(Seconds(15), Seconds(30)); + break; + case EVENT_THUNDERSHOCK: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_THUNDERSHOCK); + events.Repeat(Seconds(15), Seconds(30)); + break; + default: + break; } - if (killer->GetTypeId() == TYPEID_PLAYER) - instance->SetGuidData(DATA_PL_JEDOGA_TARGET, killer->GetGUID()); - } - - void EnterCombat(Unit* /*who*/) override - { - } - void AttackStart(Unit* victim) override - { - if ((instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) == IN_PROGRESS) || !victim) + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - - ScriptedAI::AttackStart(victim); } - void MoveInLineOfSight(Unit* who) override + DoMeleeAttackIfReady(); + } - { - if ((instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) == IN_PROGRESS) || !who) - return; +private: + uint8 _initiatesKilled; + bool _volunteerWork; + GuidVector _volunteerGUIDS; + ObjectGuid _selectedVolunteerGUID; +}; - ScriptedAI::MoveInLineOfSight(who); - } +struct npc_twilight_volunteer : public ScriptedAI +{ + npc_twilight_volunteer(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) + { + me->SetReactState(REACT_PASSIVE); + } - void MovementInform(uint32 uiType, uint32 uiPointId) override + void DoAction(int32 action) override + { + if (action == ACTION_CHOSEN) { - if (uiType != POINT_MOTION_TYPE || !instance) - return; - - switch (uiPointId) - { - case 1: - { - Creature* boss = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_JEDOGA_SHADOWSEEKER)); - if (boss) - { - ENSURE_AI(boss_jedoga_shadowseeker::boss_jedoga_shadowseekerAI, boss->AI())->bOpFerok = true; - ENSURE_AI(boss_jedoga_shadowseeker::boss_jedoga_shadowseekerAI, boss->AI())->bOpFerokFail = false; - me->KillSelf(); - } - } - break; - } + DoCastSelf(SPELL_PILLAR_OF_LIGHTNING); + me->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL_VOLUNTEER); + Talk(SAY_CHOSEN); + me->SetStandState(UNIT_STAND_STATE_STAND); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(POINT_SACRIFICE, SacrificePosition); } + } - void UpdateAI(uint32 diff) override + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != POINT_MOTION_TYPE) + return; + + switch (pointId) { - if (bCheckTimer <= diff) - { - if (me->GetGUID() == instance->GetGuidData(DATA_ADD_JEDOGA_OPFER) && !bWalking) + case POINT_INITIAL_POSITION: + if (Creature* jedoga = _instance->GetCreature(DATA_JEDOGA_SHADOWSEEKER)) + me->SetFacingToObject(jedoga); + + DoCastSelf(SPELL_SPHERE_VISUAL_VOLUNTEER); + me->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case POINT_SACRIFICE: + if (Creature* jedoga = _instance->GetCreature(DATA_JEDOGA_SHADOWSEEKER)) { - me->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, false); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, false); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); - - float distance = me->GetDistance(JedogaPosition[1]); - - if (distance < 9.0f) - me->SetSpeedRate(MOVE_WALK, 0.5f); - else if (distance < 15.0f) - me->SetSpeedRate(MOVE_WALK, 0.75f); - else if (distance < 20.0f) - me->SetSpeedRate(MOVE_WALK, 1.0f); - - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MovePoint(1, JedogaPosition[1]); - bWalking = true; - } - if (!bWalking) - { - if (instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) != IN_PROGRESS && me->HasAura(SPELL_SPHERE_VISUAL)) - { - me->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, false); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, false); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); - } - if (instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) == IN_PROGRESS && !me->HasAura(SPELL_SPHERE_VISUAL)) + me->SetStandState(UNIT_STAND_STATE_KNEEL); + jedoga->AI()->DoAction(ACTION_SACRIFICE); + Talk(SAY_SACRIFICED); + + _scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) { - DoCast(me, SPELL_SPHERE_VISUAL, false); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); - } + me->SetStandState(UNIT_STAND_STATE_DEAD); + me->DespawnOrUnsummon(Seconds(5)); + }); } - bCheckTimer = 2*IN_MILLISECONDS; - } else bCheckTimer -= diff; - - //Return since we have no target - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); + break; + default: + break; } - }; + } - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetAhnKahetAI<npc_jedoga_initiandAI>(creature); + _scheduler.Update(diff); } -}; -// ------------------------------------------------------------------------------------------------------------ -// Jedogas Aufseher - Entry: 30181 -// ------------------------------------------------------------------------------------------------------------ -enum AufseherSpell -{ - SPELL_BEAM_VISUAL_JEDOGAS_AUFSEHER_1 = 60342, - SPELL_BEAM_VISUAL_JEDOGAS_AUFSEHER_2 = 56312 +private: + InstanceScript* _instance; + TaskScheduler _scheduler; }; -class npc_jedogas_aufseher_trigger : public CreatureScript +// 56328 - Random Lightning Visual Effect +class spell_random_lightning_visual_effect : public SpellScript { -public: - npc_jedogas_aufseher_trigger() : CreatureScript("npc_jedogas_aufseher_trigger") { } + PrepareSpellScript(spell_random_lightning_visual_effect); - struct npc_jedogas_aufseher_triggerAI : public ScriptedAI + void ModDestHeight(SpellDestination& dest) { - npc_jedogas_aufseher_triggerAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - bRemoved = false; - bRemoved2 = false; - bCast = false; - bCast2 = false; - - SetCombatMovement(false); - } - - InstanceScript* instance; - - bool bRemoved; - bool bRemoved2; - bool bCast; - bool bCast2; - - void Reset() override { } - void EnterCombat(Unit* /*who*/) override { } - void AttackStart(Unit* /*victim*/) override { } - void MoveInLineOfSight(Unit* /*who*/) override { } - - - void UpdateAI(uint32 /*diff*/) override - { - if (!bRemoved && me->GetPositionX() > 440.0f) - { - if (instance->GetBossState(DATA_PRINCE_TALDARAM) == DONE) - { - me->InterruptNonMeleeSpells(true); - bRemoved = true; - return; - } - if (!bCast) - { - DoCast(me, SPELL_BEAM_VISUAL_JEDOGAS_AUFSEHER_1, false); - bCast = true; - } - } - if (!bRemoved2 && me->GetPositionX() < 440.0f) - { - if (!bCast2 && instance->GetData(DATA_JEDOGA_TRIGGER_SWITCH)) - { - DoCast(me, SPELL_BEAM_VISUAL_JEDOGAS_AUFSEHER_2, false); - bCast2 = true; - } - if (bCast2 && !instance->GetData(DATA_JEDOGA_TRIGGER_SWITCH)) - { - me->InterruptNonMeleeSpells(true); - bCast2 = false; - } - if (!bRemoved2 && instance->GetBossState(DATA_JEDOGA_SHADOWSEEKER) == DONE) - { - me->InterruptNonMeleeSpells(true); - bRemoved2 = true; - } - } - } - }; + Position const offset = { 0.0f, 0.0f, -19.0f, 0.0f }; + dest.RelocateOffset(offset); + } - CreatureAI* GetAI(Creature* creature) const override + void Register() override { - return GetAhnKahetAI<npc_jedogas_aufseher_triggerAI>(creature); + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_random_lightning_visual_effect::ModDestHeight, EFFECT_0, TARGET_DEST_CASTER_RANDOM); } + }; class achievement_volunteer_work : public AchievementCriteriaScript { public: - achievement_volunteer_work() : AchievementCriteriaScript("achievement_volunteer_work") - { - } + achievement_volunteer_work() : AchievementCriteriaScript("achievement_volunteer_work") { } bool OnCheck(Player* /*player*/, Unit* target) override { if (!target) return false; - if (Creature* Jedoga = target->ToCreature()) - if (Jedoga->AI()->GetData(DATA_VOLUNTEER_WORK)) + if (Creature* jedoga = target->ToCreature()) + if (jedoga->AI()->GetData(DATA_VOLUNTEER_WORK) == 1) return true; return false; @@ -606,8 +501,8 @@ class achievement_volunteer_work : public AchievementCriteriaScript void AddSC_boss_jedoga_shadowseeker() { - new boss_jedoga_shadowseeker(); - new npc_jedoga_initiand(); - new npc_jedogas_aufseher_trigger(); + RegisterAhnKahetCreatureAI(boss_jedoga_shadowseeker); + RegisterAhnKahetCreatureAI(npc_twilight_volunteer); + RegisterSpellScript(spell_random_lightning_visual_effect); new achievement_volunteer_work(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp index fcb37f56a87..8c7f36c3231 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp @@ -53,7 +53,8 @@ enum Spells enum Misc { DATA_EMBRACE_DMG = 20000, - H_DATA_EMBRACE_DMG = 40000 + H_DATA_EMBRACE_DMG = 40000, + SUMMON_GROUP_CONTROLLERS = 1 }; #define DATA_SPHERE_DISTANCE 25.0f @@ -104,6 +105,9 @@ class boss_prince_taldaram : public CreatureScript _flameSphereTargetGUID.Clear(); _embraceTargetGUID.Clear(); _embraceTakenDamage = 0; + + if (!CheckSpheres()) + me->SummonCreatureGroup(SUMMON_GROUP_CONTROLLERS); } void EnterCombat(Unit* /*who*/) override @@ -125,6 +129,10 @@ class boss_prince_taldaram : public CreatureScript case NPC_FLAME_SPHERE_2: case NPC_FLAME_SPHERE_3: summon->AI()->SetGUID(_flameSphereTargetGUID); + break; + case NPC_JEDOGA_CONTROLLER: + summon->CastSpell(me, SPELL_BEAM_VISUAL); + break; default: return; } @@ -266,13 +274,15 @@ class boss_prince_taldaram : public CreatureScript void RemovePrison() { me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + summons.DespawnEntry(NPC_JEDOGA_CONTROLLER); me->RemoveAurasDueToSpell(SPELL_BEAM_VISUAL); me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), DATA_GROUND_POSITION_Z, me->GetOrientation()); DoCast(SPELL_HOVER_FALL); me->SetDisableGravity(false); me->GetMotionMaster()->MoveLand(0, me->GetHomePosition()); Talk(SAY_WARNING); - instance->HandleGameObject(instance->GetGuidData(DATA_PRINCE_TALDARAM_PLATFORM), true); + if (GameObject* platform = instance->GetGameObject(DATA_PRINCE_TALDARAM_PLATFORM)) + instance->HandleGameObject(platform->GetGUID(), true); } private: @@ -395,7 +405,7 @@ class go_prince_taldaram_sphere : public GameObjectScript bool GossipHello(Player* /*player*/) override { - Creature* princeTaldaram = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_PRINCE_TALDARAM)); + Creature* princeTaldaram = instance->GetCreature(DATA_PRINCE_TALDARAM); if (princeTaldaram && princeTaldaram->IsAlive()) { me->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp index c629a5e66e7..9315cf3fddd 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp @@ -16,9 +16,9 @@ */ #include "ScriptMgr.h" +#include "AreaBoundary.h" #include "ahnkahet.h" #include "Creature.h" -#include "CreatureAI.h" #include "GameObject.h" #include "InstanceScript.h" #include "Map.h" @@ -29,6 +29,27 @@ DoorData const doorData[] = { 0, 0, DOOR_TYPE_ROOM } // END }; +ObjectData const creatureData[] = +{ + { NPC_ELDER_NADOX, DATA_ELDER_NADOX }, + { NPC_PRINCE_TALDARAM, DATA_PRINCE_TALDARAM }, + { NPC_JEDOGA_SHADOWSEEKER, DATA_JEDOGA_SHADOWSEEKER }, + { NPC_AMANITAR, DATA_AMANITAR }, + { NPC_HERALD_VOLAZJ, DATA_HERALD_VOLAZJ }, + { 0, 0 } +}; + +ObjectData const gameObjectData[] = +{ + { GO_PRINCE_TALDARAM_PLATFORM, DATA_PRINCE_TALDARAM_PLATFORM }, + { 0, 0 } //END +}; + +BossBoundaryData const boundaries = +{ + { DATA_JEDOGA_SHADOWSEEKER, new ParallelogramBoundary(Position(460.365f, -661.997f, -20.985f), Position(364.958f,-790.211f, -14.207f), Position(347.436f,-657.978f,14.478f)) } +}; + class instance_ahnkahet : public InstanceMapScript { public: @@ -41,46 +62,20 @@ class instance_ahnkahet : public InstanceMapScript SetHeaders(DataHeader); SetBossNumber(EncounterCount); LoadDoorData(doorData); - - SwitchTrigger = 0; + LoadObjectData(creatureData, gameObjectData); + LoadBossBoundaries(boundaries); SpheresState[0] = 0; SpheresState[1] = 0; } - void OnCreatureCreate(Creature* creature) override - { - switch (creature->GetEntry()) - { - case NPC_ELDER_NADOX: - ElderNadoxGUID = creature->GetGUID(); - break; - case NPC_PRINCE_TALDARAM: - PrinceTaldaramGUID = creature->GetGUID(); - break; - case NPC_JEDOGA_SHADOWSEEKER: - JedogaShadowseekerGUID = creature->GetGUID(); - break; - case NPC_AMANITAR: - AmanitarGUID = creature->GetGUID(); - break; - case NPC_HERALD_VOLAZJ: - HeraldVolazjGUID = creature->GetGUID(); - break; - case NPC_INITIAND: - InitiandGUIDs.insert(creature->GetGUID()); - break; - default: - break; - } - } - void OnGameObjectCreate(GameObject* go) override { + InstanceScript::OnGameObjectCreate(go); + switch (go->GetEntry()) { case GO_PRINCE_TALDARAM_PLATFORM: - PrinceTaldaramPlatformGUID = go->GetGUID(); if (GetBossState(DATA_PRINCE_TALDARAM) == DONE) HandleGameObject(ObjectGuid::Empty, true, go); break; @@ -102,21 +97,6 @@ class instance_ahnkahet : public InstanceMapScript else go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); break; - case GO_PRINCE_TALDARAM_GATE: - AddDoor(go, true); - break; - default: - break; - } - } - - void OnGameObjectRemove(GameObject* go) override - { - switch (go->GetEntry()) - { - case GO_PRINCE_TALDARAM_GATE: - AddDoor(go, false); - break; default: break; } @@ -130,20 +110,6 @@ class instance_ahnkahet : public InstanceMapScript case DATA_SPHERE_2: SpheresState[type - DATA_SPHERE_1] = data; break; - case DATA_JEDOGA_TRIGGER_SWITCH: - SwitchTrigger = data; - break; - case DATA_JEDOGA_RESET_INITIANDS: - for (ObjectGuid guid : InitiandGUIDs) - { - if (Creature* creature = instance->GetCreature(guid)) - { - creature->Respawn(); - if (!creature->IsInEvadeMode()) - creature->AI()->EnterEvadeMode(); - } - } - break; default: break; } @@ -156,99 +122,12 @@ class instance_ahnkahet : public InstanceMapScript case DATA_SPHERE_1: case DATA_SPHERE_2: return SpheresState[type - DATA_SPHERE_1]; - case DATA_ALL_INITIAND_DEAD: - for (ObjectGuid guid : InitiandGUIDs) - { - Creature* cr = instance->GetCreature(guid); - if (!cr || cr->IsAlive()) - return 0; - } - return 1; - case DATA_JEDOGA_TRIGGER_SWITCH: - return SwitchTrigger; default: break; } return 0; } - void SetGuidData(uint32 type, ObjectGuid data) override - { - switch (type) - { - case DATA_ADD_JEDOGA_OPFER: - JedogaSacrifices = data; - break; - case DATA_PL_JEDOGA_TARGET: - JedogaTarget = data; - break; - default: - break; - } - } - - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - case DATA_ELDER_NADOX: - return ElderNadoxGUID; - case DATA_PRINCE_TALDARAM: - return PrinceTaldaramGUID; - case DATA_JEDOGA_SHADOWSEEKER: - return JedogaShadowseekerGUID; - case DATA_AMANITAR: - return AmanitarGUID; - case DATA_HERALD_VOLAZJ: - return HeraldVolazjGUID; - case DATA_PRINCE_TALDARAM_PLATFORM: - return PrinceTaldaramPlatformGUID; - case DATA_ADD_JEDOGA_INITIAND: - { - GuidVector vInitiands; - vInitiands.reserve(InitiandGUIDs.size()); - for (ObjectGuid guid : InitiandGUIDs) - { - Creature* cr = instance->GetCreature(guid); - if (cr && cr->IsAlive()) - vInitiands.push_back(guid); - } - if (vInitiands.empty()) - return ObjectGuid::Empty; - - return Trinity::Containers::SelectRandomContainerElement(vInitiands); - } - case DATA_ADD_JEDOGA_OPFER: - return JedogaSacrifices; - case DATA_PL_JEDOGA_TARGET: - return JedogaTarget; - default: - break; - } - return ObjectGuid::Empty; - } - - bool SetBossState(uint32 type, EncounterState state) override - { - if (!InstanceScript::SetBossState(type, state)) - return false; - - switch (type) - { - case DATA_JEDOGA_SHADOWSEEKER: - if (state == DONE) - { - for (ObjectGuid guid : InitiandGUIDs) - if (Creature* cr = instance->GetCreature(guid)) - cr->DespawnOrUnsummon(); - } - break; - default: - break; - } - return true; - } - void WriteSaveDataMore(std::ostringstream& data) override { data << SpheresState[0] << ' ' << SpheresState[1]; @@ -261,20 +140,7 @@ class instance_ahnkahet : public InstanceMapScript } protected: - ObjectGuid ElderNadoxGUID; - ObjectGuid PrinceTaldaramGUID; - ObjectGuid JedogaShadowseekerGUID; - ObjectGuid AmanitarGUID; - ObjectGuid HeraldVolazjGUID; - - ObjectGuid PrinceTaldaramPlatformGUID; - ObjectGuid JedogaSacrifices; - ObjectGuid JedogaTarget; - - GuidSet InitiandGUIDs; - uint32 SpheresState[2]; - uint8 SwitchTrigger; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h index 7e42df81f21..f4e8e99b820 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h @@ -37,7 +37,8 @@ enum ANDataTypes DATA_WATCHER_GASHRA, DATA_WATCHER_SILTHIK, DATA_ANUBARAK_WALL, - DATA_ANUBARAK_WALL_2 + DATA_ANUBARAK_WALL_2, + DATA_GATEWATCHER_GREET }; enum ANCreatureIds diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp index 89b5268684d..b10add03865 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp @@ -18,6 +18,7 @@ #include "ScriptMgr.h" #include "azjol_nerub.h" #include "InstanceScript.h" +#include "Map.h" #include "MotionMaster.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" @@ -278,29 +279,22 @@ public: _anubar.push_back(guid); } - void Initialize() + void InitializeAI() override { + BossAI::InitializeAI(); me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 9.0f); me->SetFloatValue(UNIT_FIELD_COMBATREACH, 9.0f); _enteredCombat = false; _doorsWebbed = false; _lastPlayerCombatState = false; SetStep(0); - SetCombatMovement(true); - SummonCrusherPack(SUMMON_GROUP_CRUSHER_1); - } - - void InitializeAI() override - { - BossAI::InitializeAI(); - if (me->IsAlive()) - Initialize(); } - void JustRespawned() override + void JustAppeared() override { - BossAI::JustRespawned(); - Initialize(); + BossAI::JustAppeared(); + SetCombatMovement(true); + SummonCrusherPack(SUMMON_GROUP_CRUSHER_1); } void UpdateAI(uint32 diff) override @@ -975,6 +969,8 @@ class spell_hadronox_periodic_summon_template_AuraScript : public AuraScript InstanceScript* instance = caster->GetInstanceScript(); if (!instance) return; + if (!instance->instance->HavePlayers()) + return; if (instance->GetBossState(DATA_HADRONOX) == DONE) GetAura()->Remove(); else diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp index 506ddd8cad2..1df855f2ab0 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp @@ -132,7 +132,7 @@ class boss_krik_thir : public CreatureScript struct boss_krik_thirAI : public BossAI { - boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR), _hadGreet(false), _hadFrenzy(false), _petsInCombat(false), _watchersActive(0) { } + boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR), _hadFrenzy(false), _petsInCombat(false), _watchersActive(0) { } void SummonAdds() { @@ -157,15 +157,9 @@ class boss_krik_thir : public CreatureScript me->SetReactState(REACT_PASSIVE); } - void InitializeAI() override - { - BossAI::InitializeAI(); - SummonAdds(); - } - - void JustRespawned() override + void JustAppeared() override { - BossAI::JustRespawned(); + BossAI::JustAppeared(); SummonAdds(); } @@ -218,9 +212,9 @@ class boss_krik_thir : public CreatureScript switch (action) { case -ACTION_GATEWATCHER_GREET: - if (!_hadGreet && me->IsAlive() && !me->IsInCombat() && !_petsInCombat) + if (!instance->GetData(DATA_GATEWATCHER_GREET) && me->IsAlive() && !me->IsInCombat() && !_petsInCombat) { - _hadGreet = true; + instance->SetData(DATA_GATEWATCHER_GREET, 1); Talk(SAY_PREFIGHT); } break; @@ -313,7 +307,6 @@ class boss_krik_thir : public CreatureScript } private: - bool _hadGreet; bool _hadFrenzy; bool _petsInCombat; uint8 _watchersActive; diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp index eb6692f0251..49da45c2586 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp @@ -71,14 +71,20 @@ class instance_azjol_nerub : public InstanceMapScript LoadBossBoundaries(boundaries); LoadDoorData(doorData); LoadObjectData(creatureData, gameobjectData); + GateWatcherGreet = 0; } void OnUnitDeath(Unit* who) override { InstanceScript::OnUnitDeath(who); + + if (who->GetTypeId() != TYPEID_UNIT || GetBossState(DATA_KRIKTHIR) == DONE) + return; + Creature* creature = who->ToCreature(); - if (!creature || creature->IsCritter() || creature->IsControlledByPlayer()) + if (creature->IsCritter() || creature->IsCharmedOwnedByPlayerOrPlayer()) return; + if (Creature* gatewatcher = GetCreature(DATA_KRIKTHIR)) gatewatcher->AI()->DoAction(-ACTION_GATEWATCHER_GREET); } @@ -93,6 +99,32 @@ class instance_azjol_nerub : public InstanceMapScript return true; } + + uint32 GetData(uint32 type) const override + { + switch (type) + { + case DATA_GATEWATCHER_GREET: + return GateWatcherGreet; + default: + return 0; + } + } + + void SetData(uint32 type, uint32 data) override + { + switch (type) + { + case DATA_GATEWATCHER_GREET: + GateWatcherGreet = data; + break; + default: + break; + } + } + + protected: + uint8 GateWatcherGreet; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp index 16955384cb8..24f35115e04 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp @@ -73,8 +73,7 @@ class boss_baltharus_the_warborn : public CreatureScript struct boss_baltharus_the_warbornAI : public BossAI { - boss_baltharus_the_warbornAI(Creature* creature) : BossAI(creature, DATA_BALTHARUS_THE_WARBORN), - _cloneCount(0), _introDone(false) { } + boss_baltharus_the_warbornAI(Creature* creature) : BossAI(creature, DATA_BALTHARUS_THE_WARBORN), _cloneCount(0) { } void Reset() override { @@ -91,9 +90,6 @@ class boss_baltharus_the_warborn : public CreatureScript switch (action) { case ACTION_INTRO_BALTHARUS: - if (_introDone) - return; - _introDone = true; me->setActive(true); events.ScheduleEvent(EVENT_INTRO_TALK, Seconds(7), 0, PHASE_INTRO); break; @@ -232,7 +228,6 @@ class boss_baltharus_the_warborn : public CreatureScript private: uint8 _cloneCount; - bool _introDone; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp index 472dc16191f..3957ad4793c 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -566,7 +566,7 @@ class npc_halion_controller : public CreatureScript _twilightDamageTaken = 0; } - void JustRespawned() override + void JustAppeared() override { if (_instance->GetGuidData(DATA_HALION) || _instance->GetBossState(DATA_GENERAL_ZARITHRIAN) != DONE) return; @@ -733,7 +733,7 @@ class npc_halion_controller : public CreatureScript case EVENT_TWILIGHT_MENDING: if (_instance->GetCreature(DATA_HALION)) // Just check if physical Halion is spawned if (Creature* twilightHalion = _instance->GetCreature(DATA_TWILIGHT_HALION)) - twilightHalion->CastSpell((Unit*)nullptr, SPELL_TWILIGHT_MENDING, true); + twilightHalion->CastSpell(nullptr, SPELL_TWILIGHT_MENDING, true); break; case EVENT_TRIGGER_BERSERK: for (uint8 i = DATA_HALION; i <= DATA_TWILIGHT_HALION; i++) @@ -921,7 +921,7 @@ class npc_orb_carrier : public CreatureScript /// However, refreshing it looks bad, so just cast the spell if /// we are not channeling it. if (!me->HasUnitState(UNIT_STATE_CASTING)) - me->CastSpell((Unit*)nullptr, SPELL_TRACK_ROTATION, false); + me->CastSpell(nullptr, SPELL_TRACK_ROTATION, false); scheduler.Update(diff); diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp index e43b93e484d..20d8333bbab 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp @@ -152,12 +152,12 @@ class npc_xerestrasza : public CreatureScript } }; -class at_baltharus_plateau : public AreaTriggerScript +class at_baltharus_plateau : public OnlyOnceAreaTriggerScript { public: - at_baltharus_plateau() : AreaTriggerScript("at_baltharus_plateau") { } + at_baltharus_plateau() : OnlyOnceAreaTriggerScript("at_baltharus_plateau") { } - bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + bool _OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { // Only trigger once if (InstanceScript* instance = player->GetInstanceScript()) diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp index 1061dc37aff..3b997fe9516 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp @@ -505,9 +505,9 @@ public: npc_argent_soldier() : CreatureScript("npc_argent_soldier") { } // THIS AI NEEDS MORE IMPROVEMENTS - struct npc_argent_soldierAI : public npc_escortAI + struct npc_argent_soldierAI : public EscortAI { - npc_argent_soldierAI(Creature* creature) : npc_escortAI(creature) + npc_argent_soldierAI(Creature* creature) : EscortAI(creature) { instance = creature->GetInstanceScript(); me->SetReactState(REACT_DEFENSIVE); @@ -519,7 +519,7 @@ public: uint8 uiWaypoint; - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == 0) { @@ -592,7 +592,7 @@ public: void UpdateAI(uint32 uiDiff) override { - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); if (!UpdateVictim()) return; diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp index be1c5b69bd8..c64222cc61c 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp @@ -354,18 +354,16 @@ class npc_black_knight_skeletal_gryphon : public CreatureScript public: npc_black_knight_skeletal_gryphon() : CreatureScript("npc_black_knight_skeletal_gryphon") { } - struct npc_black_knight_skeletal_gryphonAI : public npc_escortAI + struct npc_black_knight_skeletal_gryphonAI : public EscortAI { - npc_black_knight_skeletal_gryphonAI(Creature* creature) : npc_escortAI(creature) + npc_black_knight_skeletal_gryphonAI(Creature* creature) : EscortAI(creature) { Start(false, true); } - void WaypointReached(uint32 /*waypointId*/) override { } - void UpdateAI(uint32 uiDiff) override { - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); UpdateVictim(); } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp index e0d59e6b4d5..fb2e461da0e 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp @@ -114,7 +114,7 @@ void AggroAllPlayers(Creature* temp) temp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); temp->SetImmuneToPC(true); temp->SetReactState(REACT_AGGRESSIVE); - temp->EngageWithTarget(player); + temp->EngageWithTarget(player); } } } @@ -152,9 +152,9 @@ class generic_vehicleAI_toc5 : public CreatureScript public: generic_vehicleAI_toc5() : CreatureScript("generic_vehicleAI_toc5") { } - struct generic_vehicleAI_toc5AI : public npc_escortAI + struct generic_vehicleAI_toc5AI : public EscortAI { - generic_vehicleAI_toc5AI(Creature* creature) : npc_escortAI(creature) + generic_vehicleAI_toc5AI(Creature* creature) : EscortAI(creature) { Initialize(); SetDespawnAtEnd(false); @@ -212,7 +212,7 @@ public: Start(false, true); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -239,7 +239,7 @@ public: void UpdateAI(uint32 uiDiff) override { - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); if (!UpdateVictim()) return; diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/trial_of_the_champion.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/trial_of_the_champion.cpp index efedf67069f..955e7ff313c 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/trial_of_the_champion.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/trial_of_the_champion.cpp @@ -463,7 +463,7 @@ public: instance->GetData(BOSS_ARGENT_CHALLENGE_P) == NOT_STARTED && instance->GetData(BOSS_BLACK_KNIGHT) == NOT_STARTED) AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_START_EVENT1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - else if (instance) + else AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_START_EVENT2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp index 72ac4f0e499..4d3f243a4a7 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp @@ -168,7 +168,7 @@ class boss_anubarak_trial : public CreatureScript struct boss_anubarak_trialAI : public BossAI { - boss_anubarak_trialAI(Creature* creature) : BossAI(creature, BOSS_ANUBARAK) + boss_anubarak_trialAI(Creature* creature) : BossAI(creature, DATA_ANUBARAK) { Initialize(); } @@ -223,7 +223,7 @@ class boss_anubarak_trial : public CreatureScript void JustReachedHome() override { - instance->SetBossState(BOSS_ANUBARAK, FAIL); + instance->SetBossState(DATA_ANUBARAK, FAIL); //Summon Scarab Swarms neutral at random places for (int i = 0; i < 10; i++) if (Creature* scarab = me->SummonCreature(NPC_SCARAB, AnubarakLoc[1].GetPositionX()+urand(0, 50)-25, AnubarakLoc[1].GetPositionY()+urand(0, 50)-25, AnubarakLoc[1].GetPositionZ())) @@ -461,8 +461,8 @@ class npc_swarm_scarab : public CreatureScript DoCast(me, SPELL_ACID_MANDIBLE); me->SetInCombatWithZone(); if (me->IsInCombat()) - if (Creature* Anubarak = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_ANUBARAK))) - Anubarak->AI()->JustSummoned(me); + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) + anubarak->AI()->JustSummoned(me); } void DoAction(int32 actionId) override @@ -485,7 +485,7 @@ class npc_swarm_scarab : public CreatureScript void UpdateAI(uint32 diff) override { - if (_instance->GetBossState(BOSS_ANUBARAK) != IN_PROGRESS) + if (_instance->GetBossState(DATA_ANUBARAK) != IN_PROGRESS) me->DisappearAndDie(); if (!UpdateVictim()) @@ -541,8 +541,8 @@ class npc_nerubian_burrower : public CreatureScript DoCast(me, SPELL_AWAKENED); me->SetInCombatWithZone(); if (me->IsInCombat()) - if (Creature* Anubarak = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_ANUBARAK))) - Anubarak->AI()->JustSummoned(me); + if (Creature* anubarak = _instance->GetCreature(DATA_ANUBARAK)) + anubarak->AI()->JustSummoned(me); } void DoAction(int32 actionId) override @@ -561,7 +561,7 @@ class npc_nerubian_burrower : public CreatureScript void UpdateAI(uint32 diff) override { - if (_instance->GetBossState(BOSS_ANUBARAK) != IN_PROGRESS) + if (_instance->GetBossState(DATA_ANUBARAK) != IN_PROGRESS) me->DisappearAndDie(); if (!UpdateVictim() && !me->HasAura(SPELL_SUBMERGE_EFFECT)) diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp index fc2d5c466cc..be5772f191f 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp @@ -375,12 +375,11 @@ class boss_toc_champion_controller : public CreatureScript public: boss_toc_champion_controller() : CreatureScript("boss_toc_champion_controller") { } - struct boss_toc_champion_controllerAI : public ScriptedAI + struct boss_toc_champion_controllerAI : public BossAI { - boss_toc_champion_controllerAI(Creature* creature) : ScriptedAI(creature), _summons(me) + boss_toc_champion_controllerAI(Creature* creature) : BossAI(creature, DATA_FACTION_CRUSADERS) { Initialize(); - _instance = creature->GetInstanceScript(); } void Initialize() @@ -396,6 +395,8 @@ class boss_toc_champion_controller : public CreatureScript Initialize(); } + void JustSummoned(Creature* /*summon*/) override { } + std::vector<uint32> SelectChampions(Team playerTeam) { std::vector<uint32> vHealersEntries; @@ -415,7 +416,7 @@ class boss_toc_champion_controller : public CreatureScript vOtherEntries.push_back(playerTeam == ALLIANCE ? NPC_HORDE_WARRIOR : NPC_ALLIANCE_WARRIOR); uint8 healersSubtracted = 2; - if (_instance->instance->GetSpawnMode() == RAID_DIFFICULTY_25MAN_NORMAL || _instance->instance->GetSpawnMode() == RAID_DIFFICULTY_25MAN_HEROIC) + if (instance->instance->GetSpawnMode() == RAID_DIFFICULTY_25MAN_NORMAL || instance->instance->GetSpawnMode() == RAID_DIFFICULTY_25MAN_HEROIC) healersSubtracted = 1; for (uint8 i = 0; i < healersSubtracted; ++i) { @@ -452,7 +453,7 @@ class boss_toc_champion_controller : public CreatureScript vHealersEntries.erase(vHealersEntries.begin() + pos); } - if (_instance->instance->GetSpawnMode() == RAID_DIFFICULTY_10MAN_NORMAL || _instance->instance->GetSpawnMode() == RAID_DIFFICULTY_10MAN_HEROIC) + if (instance->instance->GetSpawnMode() == RAID_DIFFICULTY_10MAN_NORMAL || instance->instance->GetSpawnMode() == RAID_DIFFICULTY_10MAN_HEROIC) for (uint8 i = 0; i < 4; ++i) vOtherEntries.erase(vOtherEntries.begin() + urand(0, vOtherEntries.size() - 1)); @@ -486,7 +487,7 @@ class boss_toc_champion_controller : public CreatureScript uint8 pos = urand(0, vChampionJumpTarget.size()-1); if (Creature* champion = me->SummonCreature(vChampionEntries[i], vChampionJumpOrigin[urand(0, vChampionJumpOrigin.size()-1)], TEMPSUMMON_MANUAL_DESPAWN)) { - _summons.Summon(champion); + summons.Summon(champion); champion->SetReactState(REACT_PASSIVE); champion->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); champion->SetImmuneToPC(false); @@ -515,7 +516,7 @@ class boss_toc_champion_controller : public CreatureScript SummonChampions((Team)uiData); break; case 1: - for (SummonList::iterator i = _summons.begin(); i != _summons.end(); ++i) + for (SummonList::iterator i = summons.begin(); i != summons.end(); ++i) { if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) { @@ -530,10 +531,10 @@ class boss_toc_champion_controller : public CreatureScript { case FAIL: _championsFailed++; - if (_championsFailed + _championsKilled >= _summons.size()) + if (_championsFailed + _championsKilled >= summons.size()) { - _instance->SetBossState(BOSS_CRUSADERS, FAIL); - _summons.DespawnAll(); + instance->SetBossState(DATA_FACTION_CRUSADERS, FAIL); + summons.DespawnAll(); me->DespawnOrUnsummon(); } break; @@ -544,21 +545,23 @@ class boss_toc_champion_controller : public CreatureScript _championsFailed = 0; _championsKilled = 0; _inProgress = true; - _summons.DoZoneInCombat(); - _instance->SetBossState(BOSS_CRUSADERS, IN_PROGRESS); + summons.DoZoneInCombat(); + instance->SetBossState(DATA_FACTION_CRUSADERS, IN_PROGRESS); } break; case DONE: + { _championsKilled++; if (_championsKilled == 1) - _instance->SetBossState(BOSS_CRUSADERS, SPECIAL); - else if (_championsKilled >= _summons.size()) + instance->SetBossState(DATA_FACTION_CRUSADERS, SPECIAL); + else if (_championsKilled >= summons.size()) { - _instance->SetBossState(BOSS_CRUSADERS, DONE); - _summons.DespawnAll(); + instance->SetBossState(DATA_FACTION_CRUSADERS, DONE); + summons.DespawnAll(); me->DespawnOrUnsummon(); } break; + } default: break; } @@ -568,8 +571,6 @@ class boss_toc_champion_controller : public CreatureScript } } private: - InstanceScript* _instance; - SummonList _summons; uint32 _championsNotStarted; uint32 _championsFailed; uint32 _championsKilled; @@ -584,9 +585,10 @@ class boss_toc_champion_controller : public CreatureScript struct boss_faction_championsAI : public BossAI { - boss_faction_championsAI(Creature* creature, uint32 aitype) : BossAI(creature, BOSS_CRUSADERS) + boss_faction_championsAI(Creature* creature, uint32 aitype) : BossAI(creature, DATA_FACTION_CHAMPIONS) { _aiType = aitype; + SetBoundary(instance->GetBossBoundary(DATA_FACTION_CRUSADERS)); } void Reset() override @@ -598,7 +600,7 @@ struct boss_faction_championsAI : public BossAI void JustReachedHome() override { - if (Creature* pChampionController = ObjectAccessor::GetCreature((*me), instance->GetGuidData(NPC_CHAMPIONS_CONTROLLER))) + if (Creature* pChampionController = instance->GetCreature(DATA_FACTION_CRUSADERS)) pChampionController->AI()->SetData(2, FAIL); me->DespawnOrUnsummon(); } @@ -637,15 +639,17 @@ struct boss_faction_championsAI : public BossAI void JustDied(Unit* /*killer*/) override { if (_aiType != AI_PET) - if (Creature* pChampionController = ObjectAccessor::GetCreature((*me), instance->GetGuidData(NPC_CHAMPIONS_CONTROLLER))) + if (Creature* pChampionController = instance->GetCreature(DATA_FACTION_CRUSADERS)) pChampionController->AI()->SetData(2, DONE); } void EnterCombat(Unit* /*who*/) override { DoCast(me, SPELL_ANTI_AOE, true); - _EnterCombat(); - if (Creature* pChampionController = ObjectAccessor::GetCreature((*me), instance->GetGuidData(NPC_CHAMPIONS_CONTROLLER))) + me->SetCombatPulseDelay(5); + me->setActive(true); + DoZoneInCombat(); + if (Creature* pChampionController = instance->GetCreature(DATA_FACTION_CRUSADERS)) pChampionController->AI()->SetData(2, IN_PROGRESS); } @@ -662,11 +666,11 @@ struct boss_faction_championsAI : public BossAI if (TeamInInstance == ALLIANCE) { - if (Creature* varian = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_VARIAN))) + if (Creature* varian = instance->GetCreature(DATA_VARIAN)) varian->AI()->Talk(SAY_KILL_PLAYER); } else - if (Creature* garrosh = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GARROSH))) + if (Creature* garrosh = instance->GetCreature(DATA_GARROSH)) garrosh->AI()->Talk(SAY_KILL_PLAYER); } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp index 8f50ed0e365..8b32acdadb5 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp @@ -102,7 +102,7 @@ class boss_jaraxxus : public CreatureScript struct boss_jaraxxusAI : public BossAI { - boss_jaraxxusAI(Creature* creature) : BossAI(creature, BOSS_JARAXXUS) { } + boss_jaraxxusAI(Creature* creature) : BossAI(creature, DATA_JARAXXUS) { } void Reset() override { @@ -119,7 +119,7 @@ class boss_jaraxxus : public CreatureScript void JustReachedHome() override { _JustReachedHome(); - instance->SetBossState(BOSS_JARAXXUS, FAIL); + instance->SetBossState(DATA_JARAXXUS, FAIL); DoCast(me, SPELL_JARAXXUS_CHAINS); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); me->SetImmuneToPC(true); @@ -238,7 +238,7 @@ class npc_legion_flame : public CreatureScript void UpdateAI(uint32 /*diff*/) override { UpdateVictim(); - if (_instance->GetBossState(BOSS_JARAXXUS) != IN_PROGRESS) + if (_instance->GetBossState(DATA_JARAXXUS) != IN_PROGRESS) me->DespawnOrUnsummon(); } private: @@ -330,7 +330,7 @@ class npc_fel_infernal : public CreatureScript void UpdateAI(uint32 diff) override { - if (_instance->GetBossState(BOSS_JARAXXUS) != IN_PROGRESS) + if (_instance->GetBossState(DATA_JARAXXUS) != IN_PROGRESS) { me->DespawnOrUnsummon(); return; @@ -438,7 +438,7 @@ class npc_mistress_of_pain : public CreatureScript void UpdateAI(uint32 diff) override { - if (_instance->GetBossState(BOSS_JARAXXUS) != IN_PROGRESS) + if (_instance->GetBossState(DATA_JARAXXUS) != IN_PROGRESS) { me->DespawnOrUnsummon(); return; diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp index 17ce9458905..772edda63c6 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp @@ -179,7 +179,10 @@ class boss_gormok : public CreatureScript struct boss_gormokAI : public BossAI { - boss_gormokAI(Creature* creature) : BossAI(creature, BOSS_BEASTS) { } + boss_gormokAI(Creature* creature) : BossAI(creature, DATA_GORMOK_THE_IMPALER) + { + SetBoundary(instance->GetBossBoundary(DATA_NORTHREND_BEASTS)); + } void Reset() override { @@ -192,7 +195,7 @@ class boss_gormok : public CreatureScript void EnterEvadeMode(EvadeReason why) override { - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); ScriptedAI::EnterEvadeMode(why); } @@ -204,7 +207,7 @@ class boss_gormok : public CreatureScript switch (pointId) { case 0: - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); me->SetImmuneToPC(false); me->SetReactState(REACT_AGGRESSIVE); @@ -222,7 +225,7 @@ class boss_gormok : public CreatureScript void JustReachedHome() override { - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); instance->SetData(TYPE_NORTHREND_BEASTS, FAIL); me->DespawnOrUnsummon(); @@ -230,8 +233,11 @@ class boss_gormok : public CreatureScript void EnterCombat(Unit* /*who*/) override { - _EnterCombat(); + me->SetCombatPulseDelay(5); + me->setActive(true); + //DoZoneInCombat(); instance->SetData(TYPE_NORTHREND_BEASTS, GORMOK_IN_PROGRESS); + instance->SetBossState(DATA_NORTHREND_BEASTS, IN_PROGRESS); } void DamageTaken(Unit* /*who*/, uint32& damage) override @@ -387,7 +393,7 @@ class npc_snobold_vassal : public CreatureScript void MountOnBoss() { - Unit* gormok = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_GORMOK)); + Unit* gormok = _instance->GetCreature(DATA_GORMOK_THE_IMPALER); if (gormok && gormok->IsAlive()) { me->AttackStop(); @@ -511,7 +517,7 @@ class npc_firebomb : public CreatureScript struct boss_jormungarAI : public BossAI { - boss_jormungarAI(Creature* creature) : BossAI(creature, BOSS_BEASTS) + boss_jormungarAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId) { OtherWormEntry = 0; ModelStationary = 0; @@ -525,6 +531,7 @@ struct boss_jormungarAI : public BossAI Phase = PHASE_MOBILE; Enraged = false; WasMobile = false; + SetBoundary(instance->GetBossBoundary(DATA_NORTHREND_BEASTS)); } void Reset() override @@ -539,9 +546,14 @@ struct boss_jormungarAI : public BossAI events.ScheduleEvent(EVENT_SLIME_POOL, 15*IN_MILLISECONDS, 0, PHASE_MOBILE); } + uint32 GetOtherWormData(uint32 wormEntry) + { + return wormEntry == NPC_ACIDMAW ? DATA_ACIDMAW : DATA_DREADSCALE; + } + void JustDied(Unit* /*killer*/) override { - if (Creature* otherWorm = ObjectAccessor::GetCreature(*me, instance->GetGuidData(OtherWormEntry))) + if (Creature* otherWorm = instance->GetCreature(GetOtherWormData(OtherWormEntry))) { if (!otherWorm->IsAlive()) { @@ -714,7 +726,7 @@ class boss_acidmaw : public CreatureScript struct boss_acidmawAI : public boss_jormungarAI { - boss_acidmawAI(Creature* creature) : boss_jormungarAI(creature) { } + boss_acidmawAI(Creature* creature) : boss_jormungarAI(creature, DATA_ACIDMAW) { } void Reset() override { @@ -745,7 +757,7 @@ class boss_dreadscale : public CreatureScript struct boss_dreadscaleAI : public boss_jormungarAI { - boss_dreadscaleAI(Creature* creature) : boss_jormungarAI(creature) { } + boss_dreadscaleAI(Creature* creature) : boss_jormungarAI(creature, DATA_DREADSCALE) { } void Reset() override { @@ -772,7 +784,7 @@ class boss_dreadscale : public CreatureScript switch (pointId) { case 0: - instance->DoCloseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoCloseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); me->SetImmuneToPC(false); me->SetReactState(REACT_AGGRESSIVE); @@ -785,13 +797,13 @@ class boss_dreadscale : public CreatureScript void EnterEvadeMode(EvadeReason why) override { - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); boss_jormungarAI::EnterEvadeMode(why); } void JustReachedHome() override { - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); boss_jormungarAI::JustReachedHome(); } @@ -887,9 +899,10 @@ class boss_icehowl : public CreatureScript struct boss_icehowlAI : public BossAI { - boss_icehowlAI(Creature* creature) : BossAI(creature, BOSS_BEASTS) + boss_icehowlAI(Creature* creature) : BossAI(creature, DATA_ICEHOWL) { Initialize(); + SetBoundary(instance->GetBossBoundary(DATA_NORTHREND_BEASTS)); } void Initialize() @@ -946,7 +959,7 @@ class boss_icehowl : public CreatureScript _movementFinish = true; break; case 2: - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); me->SetImmuneToPC(false); me->SetReactState(REACT_AGGRESSIVE); @@ -959,13 +972,13 @@ class boss_icehowl : public CreatureScript void EnterEvadeMode(EvadeReason why) override { - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); ScriptedAI::EnterEvadeMode(why); } void JustReachedHome() override { - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); instance->SetData(TYPE_NORTHREND_BEASTS, FAIL); me->DespawnOrUnsummon(); } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp index 49bd28f7e56..7050f20c1c9 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp @@ -158,9 +158,14 @@ class OrbsDespawner : public BasicEvent Creature* _creature; }; +static uint32 GetSisterData(uint32 sisterEntry) +{ + return sisterEntry == NPC_FJOLA_LIGHTBANE ? DATA_FJOLA_LIGHTBANE : DATA_EYDIS_DARKBANE; +} + struct boss_twin_baseAI : public BossAI { - boss_twin_baseAI(Creature* creature) : BossAI(creature, BOSS_VALKIRIES) + boss_twin_baseAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId) { AuraState = AURA_STATE_NONE; Weapon = 0; @@ -173,6 +178,7 @@ struct boss_twin_baseAI : public BossAI TwinPactSpellId = 0; SpikeSpellId = 0; TouchSpellId = 0; + SetBoundary(instance->GetBossBoundary(DATA_TWIN_VALKIRIES)); } void Reset() override @@ -189,7 +195,7 @@ struct boss_twin_baseAI : public BossAI void JustReachedHome() override { - instance->SetBossState(BOSS_VALKIRIES, FAIL); + instance->SetBossState(DATA_TWIN_VALKIRIES, FAIL); summons.DespawnAll(); me->DespawnOrUnsummon(); @@ -248,12 +254,14 @@ struct boss_twin_baseAI : public BossAI { me->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); pSister->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - _JustDied(); + events.Reset(); + summons.DespawnAll(); + instance->SetBossState(DATA_TWIN_VALKIRIES, DONE); } else { me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); - instance->SetBossState(BOSS_VALKIRIES, SPECIAL); + instance->SetBossState(DATA_TWIN_VALKIRIES, SPECIAL); } } summons.DespawnAll(); @@ -262,7 +270,7 @@ struct boss_twin_baseAI : public BossAI // Called when sister pointer needed Creature* GetSister() { - return ObjectAccessor::GetCreature((*me), instance->GetGuidData(SisterNpcId)); + return instance->GetCreature(GetSisterData(SisterNpcId)); } void EnterCombat(Unit* /*who*/) override @@ -273,10 +281,12 @@ struct boss_twin_baseAI : public BossAI me->AddAura(MyEmphatySpellId, pSister); pSister->SetInCombatWithZone(); } - instance->SetBossState(BOSS_VALKIRIES, IN_PROGRESS); + instance->SetBossState(DATA_TWIN_VALKIRIES, IN_PROGRESS); Talk(SAY_AGGRO); DoCast(me, SurgeSpellId); + me->SetCombatPulseDelay(5); + me->setActive(true); events.ScheduleEvent(EVENT_TWIN_SPIKE, 20 * IN_MILLISECONDS); events.ScheduleEvent(EVENT_BERSERK, IsHeroic() ? 6 * MINUTE*IN_MILLISECONDS : 10 * MINUTE*IN_MILLISECONDS); @@ -354,7 +364,7 @@ class boss_fjola : public CreatureScript struct boss_fjolaAI : public boss_twin_baseAI { - boss_fjolaAI(Creature* creature) : boss_twin_baseAI(creature) + boss_fjolaAI(Creature* creature) : boss_twin_baseAI(creature, DATA_FJOLA_LIGHTBANE) { GenerateStageSequence(); } @@ -364,7 +374,7 @@ class boss_fjola : public CreatureScript SetEquipmentSlots(false, EQUIP_MAIN_1, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); Weapon = EQUIP_MAIN_1; AuraState = AURA_STATE_UNKNOWN22; - SisterNpcId = NPC_DARKBANE; + SisterNpcId = NPC_EYDIS_DARKBANE; MyEmphatySpellId = SPELL_TWIN_EMPATHY_DARK; OtherEssenceSpellId = SPELL_DARK_ESSENCE_HELPER; SurgeSpellId = SPELL_LIGHT_SURGE; @@ -421,13 +431,13 @@ class boss_fjola : public CreatureScript void EnterEvadeMode(EvadeReason why) override { - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); boss_twin_baseAI::EnterEvadeMode(why); } void JustReachedHome() override { - instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); + instance->DoUseDoorOrButton(instance->GetGuidData(DATA_MAIN_GATE)); boss_twin_baseAI::JustReachedHome(); } @@ -465,14 +475,14 @@ class boss_eydis : public CreatureScript struct boss_eydisAI : public boss_twin_baseAI { - boss_eydisAI(Creature* creature) : boss_twin_baseAI(creature) { } + boss_eydisAI(Creature* creature) : boss_twin_baseAI(creature, DATA_EYDIS_DARKBANE) { } void Reset() override { SetEquipmentSlots(false, EQUIP_MAIN_2, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); Weapon = EQUIP_MAIN_2; AuraState = AURA_STATE_UNKNOWN19; - SisterNpcId = NPC_LIGHTBANE; + SisterNpcId = NPC_FJOLA_LIGHTBANE; MyEmphatySpellId = SPELL_TWIN_EMPATHY_LIGHT; OtherEssenceSpellId = SPELL_LIGHT_ESSENCE_HELPER; SurgeSpellId = SPELL_DARK_SURGE; @@ -860,8 +870,8 @@ class spell_power_of_the_twins : public SpellScriptLoader { if (InstanceScript* instance = GetCaster()->GetInstanceScript()) { - if (Creature* Valk = ObjectAccessor::GetCreature(*GetCaster(), instance->GetGuidData(GetCaster()->GetEntry()))) - ENSURE_AI(boss_twin_baseAI, Valk->AI())->EnableDualWield(true); + if (Creature* valk = instance->GetCreature(GetSisterData(GetCaster()->GetEntry()))) + ENSURE_AI(boss_twin_baseAI, valk->AI())->EnableDualWield(true); } } @@ -869,8 +879,8 @@ class spell_power_of_the_twins : public SpellScriptLoader { if (InstanceScript* instance = GetCaster()->GetInstanceScript()) { - if (Creature* Valk = ObjectAccessor::GetCreature(*GetCaster(), instance->GetGuidData(GetCaster()->GetEntry()))) - ENSURE_AI(boss_twin_baseAI, Valk->AI())->EnableDualWield(false); + if (Creature* valk = instance->GetCreature(GetSisterData(GetCaster()->GetEntry()))) + ENSURE_AI(boss_twin_baseAI, valk->AI())->EnableDualWield(false); } } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp index 506f076be33..28891678aef 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp @@ -29,11 +29,53 @@ BossBoundaryData const boundaries = { - { BOSS_BEASTS, new CircleBoundary(Position(563.26f, 139.6f), 75.0) }, - { BOSS_JARAXXUS, new CircleBoundary(Position(563.26f, 139.6f), 75.0) }, - { BOSS_CRUSADERS, new CircleBoundary(Position(563.26f, 139.6f), 75.0) }, - { BOSS_VALKIRIES, new CircleBoundary(Position(563.26f, 139.6f), 75.0) }, - { BOSS_ANUBARAK, new EllipseBoundary(Position(746.0f, 135.0f), 100.0, 75.0) } + { DATA_NORTHREND_BEASTS, new CircleBoundary(Position(563.26f, 139.6f), 75.0) }, + { DATA_JARAXXUS, new CircleBoundary(Position(563.26f, 139.6f), 75.0) }, + { DATA_FACTION_CRUSADERS, new CircleBoundary(Position(563.26f, 139.6f), 75.0) }, + { DATA_TWIN_VALKIRIES, new CircleBoundary(Position(563.26f, 139.6f), 75.0) }, + { DATA_ANUBARAK, new EllipseBoundary(Position(746.0f, 135.0f), 100.0, 75.0) } +}; + +ObjectData const creatureData[] = +{ + { NPC_GORMOK, DATA_GORMOK_THE_IMPALER }, + { NPC_ACIDMAW, DATA_ACIDMAW }, + { NPC_DREADSCALE, DATA_DREADSCALE }, + { NPC_ICEHOWL, DATA_ICEHOWL }, + { NPC_JARAXXUS, DATA_JARAXXUS }, + { NPC_CHAMPIONS_CONTROLLER, DATA_FACTION_CRUSADERS }, + { NPC_FJOLA_LIGHTBANE, DATA_FJOLA_LIGHTBANE }, + { NPC_EYDIS_DARKBANE, DATA_EYDIS_DARKBANE }, + { NPC_LICH_KING, DATA_LICH_KING }, + { NPC_ANUBARAK, DATA_ANUBARAK }, + { NPC_BARRET_RAMSEY, DATA_BARRET_RAMSEY }, + { NPC_TIRION_FORDRING, DATA_FORDRING }, + { NPC_TIRION_FORDRING_ANUBARAK, DATA_FORDRING_ANUBARAK }, + { NPC_VARIAN, DATA_VARIAN }, + { NPC_GARROSH, DATA_GARROSH }, + { NPC_FIZZLEBANG, DATA_FIZZLEBANG }, + { 0, 0 } // END +}; + +ObjectData const gameObjectData[] = +{ + { GO_CRUSADERS_CACHE_10, DATA_CRUSADERS_CHEST }, + { GO_CRUSADERS_CACHE_25, DATA_CRUSADERS_CHEST }, + { GO_CRUSADERS_CACHE_10_H, DATA_CRUSADERS_CHEST }, + { GO_CRUSADERS_CACHE_25_H, DATA_CRUSADERS_CHEST }, + { GO_ARGENT_COLISEUM_FLOOR, DATA_COLISEUM_FLOOR }, + { GO_MAIN_GATE_DOOR, DATA_MAIN_GATE }, + { GO_EAST_PORTCULLIS, DATA_EAST_PORTCULLIS }, + { GO_WEB_DOOR, DATA_WEB_DOOR }, + { GO_TRIBUTE_CHEST_10H_25, DATA_TRIBUTE_CHEST }, + { GO_TRIBUTE_CHEST_10H_45, DATA_TRIBUTE_CHEST }, + { GO_TRIBUTE_CHEST_10H_50, DATA_TRIBUTE_CHEST }, + { GO_TRIBUTE_CHEST_10H_99, DATA_TRIBUTE_CHEST }, + { GO_TRIBUTE_CHEST_25H_25, DATA_TRIBUTE_CHEST }, + { GO_TRIBUTE_CHEST_25H_45, DATA_TRIBUTE_CHEST }, + { GO_TRIBUTE_CHEST_25H_50, DATA_TRIBUTE_CHEST }, + { GO_TRIBUTE_CHEST_25H_99, DATA_TRIBUTE_CHEST }, + { 0, 0 } // END }; class instance_trial_of_the_crusader : public InstanceMapScript @@ -46,8 +88,9 @@ class instance_trial_of_the_crusader : public InstanceMapScript instance_trial_of_the_crusader_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); - SetBossNumber(MAX_ENCOUNTERS); + SetBossNumber(EncounterCount); LoadBossBoundaries(boundaries); + LoadObjectData(creatureData, gameObjectData); TrialCounter = 50; EventStage = 0; NorthrendBeasts = NOT_STARTED; @@ -62,12 +105,12 @@ class instance_trial_of_the_crusader : public InstanceMapScript bool IsEncounterInProgress() const override { - for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) + for (uint8 i = 0; i < EncounterCount; ++i) if (GetBossState(i) == IN_PROGRESS) return true; // Special state is set at Faction Champions after first champ dead, encounter is still in combat - if (GetBossState(BOSS_CRUSADERS) == SPECIAL) + if (GetBossState(DATA_FACTION_CRUSADERS) == SPECIAL) return true; return false; @@ -84,8 +127,8 @@ class instance_trial_of_the_crusader : public InstanceMapScript player->SendUpdateWorldState(UPDATE_STATE_UI_SHOW, 0); // make sure Anub'arak isnt missing - if (GetBossState(BOSS_LICH_KING) == DONE && TrialCounter && GetBossState(BOSS_ANUBARAK) != DONE) - if (!ObjectAccessor::GetCreature(*player, GetGuidData(NPC_ANUBARAK))) + if (GetBossState(DATA_LICH_KING) == DONE && TrialCounter && GetBossState(DATA_ANUBARAK) != DONE) + if (!GetCreature(DATA_ANUBARAK)) player->SummonCreature(NPC_ANUBARAK, AnubarakLoc[0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME); } @@ -109,100 +152,18 @@ class instance_trial_of_the_crusader : public InstanceMapScript void OnCreatureCreate(Creature* creature) override { - switch (creature->GetEntry()) - { - case NPC_BARRENT: - BarrentGUID = creature->GetGUID(); - if (!TrialCounter) - creature->DespawnOrUnsummon(); - break; - case NPC_TIRION: - TirionGUID = creature->GetGUID(); - break; - case NPC_TIRION_FORDRING: - TirionFordringGUID = creature->GetGUID(); - break; - case NPC_FIZZLEBANG: - FizzlebangGUID = creature->GetGUID(); - break; - case NPC_GARROSH: - GarroshGUID = creature->GetGUID(); - break; - case NPC_VARIAN: - VarianGUID = creature->GetGUID(); - break; - - case NPC_GORMOK: - GormokGUID = creature->GetGUID(); - break; - case NPC_ACIDMAW: - AcidmawGUID = creature->GetGUID(); - break; - case NPC_DREADSCALE: - DreadscaleGUID = creature->GetGUID(); - break; - case NPC_ICEHOWL: - IcehowlGUID = creature->GetGUID(); - break; - case NPC_JARAXXUS: - JaraxxusGUID = creature->GetGUID(); - break; - case NPC_CHAMPIONS_CONTROLLER: - ChampionsControllerGUID = creature->GetGUID(); - break; - case NPC_DARKBANE: - DarkbaneGUID = creature->GetGUID(); - break; - case NPC_LIGHTBANE: - LightbaneGUID = creature->GetGUID(); - break; - case NPC_ANUBARAK: - AnubarakGUID = creature->GetGUID(); - creature->SetRespawnDelay(7 * DAY); - break; - default: - break; - } + InstanceScript::OnCreatureCreate(creature); + if (creature->GetEntry() == NPC_BARRET_RAMSEY) + if (!TrialCounter) + creature->DespawnOrUnsummon(); } void OnGameObjectCreate(GameObject* go) override { - switch (go->GetEntry()) - { - case GO_CRUSADERS_CACHE_10: - case GO_CRUSADERS_CACHE_25: - case GO_CRUSADERS_CACHE_10_H: - case GO_CRUSADERS_CACHE_25_H: - CrusadersCacheGUID = go->GetGUID(); - break; - case GO_ARGENT_COLISEUM_FLOOR: - FloorGUID = go->GetGUID(); - if (GetBossState(BOSS_LICH_KING) == DONE) - go->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED); - break; - case GO_MAIN_GATE_DOOR: - MainGateDoorGUID = go->GetGUID(); - break; - case GO_EAST_PORTCULLIS: - EastPortcullisGUID = go->GetGUID(); - break; - case GO_WEB_DOOR: - WebDoorGUID = go->GetGUID(); - break; - - case GO_TRIBUTE_CHEST_10H_25: - case GO_TRIBUTE_CHEST_10H_45: - case GO_TRIBUTE_CHEST_10H_50: - case GO_TRIBUTE_CHEST_10H_99: - case GO_TRIBUTE_CHEST_25H_25: - case GO_TRIBUTE_CHEST_25H_45: - case GO_TRIBUTE_CHEST_25H_50: - case GO_TRIBUTE_CHEST_25H_99: - TributeChestGUID = go->GetGUID(); - break; - default: - break; - } + InstanceScript::OnGameObjectCreate(go); + if (go->GetEntry() == GO_ARGENT_COLISEUM_FLOOR) + if (GetBossState(DATA_LICH_KING) == DONE) + go->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED); } void OnUnitDeath(Unit* unit) override @@ -219,20 +180,20 @@ class instance_trial_of_the_crusader : public InstanceMapScript switch (type) { - case BOSS_BEASTS: + case DATA_NORTHREND_BEASTS: break; - case BOSS_JARAXXUS: + case DATA_JARAXXUS: // Cleanup Icehowl - if (Creature* icehowl = instance->GetCreature(IcehowlGUID)) + if (Creature* icehowl = GetCreature(DATA_ICEHOWL)) icehowl->DespawnOrUnsummon(); if (state == DONE) EventStage = 2000; break; - case BOSS_CRUSADERS: + case DATA_FACTION_CRUSADERS: // Cleanup Jaraxxus - if (Creature* jaraxxus = instance->GetCreature(JaraxxusGUID)) + if (Creature* jaraxxus = GetCreature(DATA_JARAXXUS)) jaraxxus->DespawnOrUnsummon(); - if (Creature* fizzlebang = instance->GetCreature(FizzlebangGUID)) + if (Creature* fizzlebang = GetCreature(DATA_FIZZLEBANG)) fizzlebang->DespawnOrUnsummon(); switch (state) { @@ -247,8 +208,8 @@ class instance_trial_of_the_crusader : public InstanceMapScript DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, SPELL_DEFEAT_FACTION_CHAMPIONS); if (ResilienceWillFixItTimer > 0) DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, SPELL_CHAMPIONS_KILLED_IN_MINUTE); - DoRespawnGameObject(CrusadersCacheGUID, 7*DAY); - if (GameObject* cache = instance->GetGameObject(CrusadersCacheGUID)) + DoRespawnGameObject(GetGuidData(DATA_CRUSADERS_CHEST), 7*DAY); + if (GameObject* cache = GetGameObject(DATA_CRUSADERS_CHEST)) cache->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); EventStage = 3100; break; @@ -256,27 +217,27 @@ class instance_trial_of_the_crusader : public InstanceMapScript break; } break; - case BOSS_VALKIRIES: + case DATA_TWIN_VALKIRIES: // Cleanup chest - if (GameObject* cache = instance->GetGameObject(CrusadersCacheGUID)) + if (GameObject* cache = GetGameObject(DATA_CRUSADERS_CHEST)) cache->Delete(); switch (state) { case FAIL: - if (GetBossState(BOSS_VALKIRIES) == NOT_STARTED) + if (GetBossState(DATA_TWIN_VALKIRIES) == NOT_STARTED) state = NOT_STARTED; break; case SPECIAL: - if (GetBossState(BOSS_VALKIRIES) == SPECIAL) + if (GetBossState(DATA_TWIN_VALKIRIES) == SPECIAL) state = DONE; break; default: break; } break; - case BOSS_LICH_KING: + case DATA_LICH_KING: break; - case BOSS_ANUBARAK: + case DATA_ANUBARAK: switch (state) { case DONE: @@ -319,7 +280,7 @@ class instance_trial_of_the_crusader : public InstanceMapScript } if (tributeChest) - if (Creature* tirion = instance->GetCreature(TirionGUID)) + if (Creature* tirion = GetCreature(DATA_FORDRING)) if (GameObject* chest = tirion->SummonGameObject(tributeChest, 805.62f, 134.87f, 142.16f, 3.27f, QuaternionData(), WEEK)) chest->SetRespawnTime(chest->GetRespawnDelay()); break; @@ -334,16 +295,16 @@ class instance_trial_of_the_crusader : public InstanceMapScript if (IsEncounterInProgress()) { - CloseDoor(GetGuidData(GO_EAST_PORTCULLIS)); - CloseDoor(GetGuidData(GO_WEB_DOOR)); + CloseDoor(GetGuidData(DATA_EAST_PORTCULLIS)); + CloseDoor(GetGuidData(DATA_WEB_DOOR)); } else { - OpenDoor(GetGuidData(GO_EAST_PORTCULLIS)); - OpenDoor(GetGuidData(GO_WEB_DOOR)); + OpenDoor(GetGuidData(DATA_EAST_PORTCULLIS)); + OpenDoor(GetGuidData(DATA_WEB_DOOR)); } - if (type < MAX_ENCOUNTERS) + if (type < EncounterCount) { TC_LOG_DEBUG("scripts", "[ToCr] BossState(type %u) %u = state %u;", type, GetBossState(type), state); if (state == FAIL) @@ -360,21 +321,21 @@ class instance_trial_of_the_crusader : public InstanceMapScript // if theres no more attemps allowed if (!TrialCounter) { - if (Unit* announcer = instance->GetCreature(GetGuidData(NPC_BARRENT))) + if (Unit* announcer = GetCreature(DATA_BARRET_RAMSEY)) announcer->ToCreature()->DespawnOrUnsummon(); - if (Creature* anubArak = instance->GetCreature(GetGuidData(NPC_ANUBARAK))) - anubArak->DespawnOrUnsummon(); + if (Creature* anubarak = GetCreature(DATA_ANUBARAK)) + anubarak->DespawnOrUnsummon(); } } NeedSave = true; - EventStage = (type == BOSS_BEASTS ? 666 : 0); + EventStage = (type == DATA_NORTHREND_BEASTS ? 666 : 0); state = NOT_STARTED; } if (state == DONE || NeedSave) { - if (Unit* announcer = instance->GetCreature(GetGuidData(NPC_BARRENT))) + if (Unit* announcer = GetCreature(DATA_BARRET_RAMSEY)) announcer->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); Save(); } @@ -421,10 +382,10 @@ class instance_trial_of_the_crusader : public InstanceMapScript case ICEHOWL_DONE: EventStage = 400; SetData(TYPE_NORTHREND_BEASTS, DONE); - SetBossState(BOSS_BEASTS, DONE); + SetBossState(DATA_NORTHREND_BEASTS, DONE); break; case FAIL: - SetBossState(BOSS_BEASTS, FAIL); + SetBossState(DATA_NORTHREND_BEASTS, FAIL); break; default: break; @@ -448,57 +409,6 @@ class instance_trial_of_the_crusader : public InstanceMapScript } } - ObjectGuid GetGuidData(uint32 type) const override - { - switch (type) - { - case NPC_BARRENT: - return BarrentGUID; - case NPC_TIRION: - return TirionGUID; - case NPC_TIRION_FORDRING: - return TirionFordringGUID; - case NPC_FIZZLEBANG: - return FizzlebangGUID; - case NPC_GARROSH: - return GarroshGUID; - case NPC_VARIAN: - return VarianGUID; - - case NPC_GORMOK: - return GormokGUID; - case NPC_ACIDMAW: - return AcidmawGUID; - case NPC_DREADSCALE: - return DreadscaleGUID; - case NPC_ICEHOWL: - return IcehowlGUID; - case NPC_JARAXXUS: - return JaraxxusGUID; - case NPC_CHAMPIONS_CONTROLLER: - return ChampionsControllerGUID; - case NPC_DARKBANE: - return DarkbaneGUID; - case NPC_LIGHTBANE: - return LightbaneGUID; - case NPC_ANUBARAK: - return AnubarakGUID; - - case GO_ARGENT_COLISEUM_FLOOR: - return FloorGUID; - case GO_MAIN_GATE_DOOR: - return MainGateDoorGUID; - case GO_EAST_PORTCULLIS: - return EastPortcullisGUID; - case GO_WEB_DOOR: - return WebDoorGUID; - default: - break; - } - - return ObjectGuid::Empty; - } - uint32 GetData(uint32 type) const override { switch (type) @@ -553,7 +463,7 @@ class instance_trial_of_the_crusader : public InstanceMapScript case 6000: case 6005: case 6010: - return NPC_TIRION; + return NPC_TIRION_FORDRING; break; case 5010: case 5030: @@ -595,7 +505,7 @@ class instance_trial_of_the_crusader : public InstanceMapScript return NPC_FIZZLEBANG; break; default: - return NPC_TIRION; + return NPC_TIRION_FORDRING; break; }; default: @@ -615,7 +525,7 @@ class instance_trial_of_the_crusader : public InstanceMapScript NotOneButTwoJormungarsTimer -= diff; } - if (GetBossState(BOSS_CRUSADERS) == SPECIAL && ResilienceWillFixItTimer) + if (GetBossState(DATA_FACTION_CRUSADERS) == SPECIAL && ResilienceWillFixItTimer) { if (ResilienceWillFixItTimer <= diff) ResilienceWillFixItTimer = 0; @@ -630,7 +540,7 @@ class instance_trial_of_the_crusader : public InstanceMapScript std::ostringstream saveStream; - for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) + for (uint8 i = 0; i < EncounterCount; ++i) saveStream << GetBossState(i) << ' '; saveStream << TrialCounter; @@ -658,7 +568,7 @@ class instance_trial_of_the_crusader : public InstanceMapScript std::istringstream loadStream(strIn); - for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) + for (uint8 i = 0; i < EncounterCount; ++i) { uint32 tmpState; loadStream >> tmpState; @@ -718,30 +628,6 @@ class instance_trial_of_the_crusader : public InstanceMapScript bool NeedSave; std::string SaveDataBuffer; - ObjectGuid BarrentGUID; - ObjectGuid TirionGUID; - ObjectGuid TirionFordringGUID; - ObjectGuid FizzlebangGUID; - ObjectGuid GarroshGUID; - ObjectGuid VarianGUID; - - ObjectGuid GormokGUID; - ObjectGuid AcidmawGUID; - ObjectGuid DreadscaleGUID; - ObjectGuid IcehowlGUID; - ObjectGuid JaraxxusGUID; - ObjectGuid ChampionsControllerGUID; - ObjectGuid DarkbaneGUID; - ObjectGuid LightbaneGUID; - ObjectGuid AnubarakGUID; - - ObjectGuid CrusadersCacheGUID; - ObjectGuid FloorGUID; - ObjectGuid TributeChestGUID; - ObjectGuid MainGateDoorGUID; - ObjectGuid EastPortcullisGUID; - ObjectGuid WebDoorGUID; - // Achievement stuff uint32 NotOneButTwoJormungarsTimer; uint32 ResilienceWillFixItTimer; diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp index fdc93f56c39..de24ab4a352 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp @@ -164,12 +164,12 @@ struct _Messages static _Messages _GossipMessage[]= { - {MSG_BEASTS, GOSSIP_ACTION_INFO_DEF + 1, false, BOSS_BEASTS}, - {MSG_JARAXXUS, GOSSIP_ACTION_INFO_DEF + 2, false, BOSS_JARAXXUS}, - {MSG_CRUSADERS, GOSSIP_ACTION_INFO_DEF + 3, false, BOSS_CRUSADERS}, - {MSG_VALKIRIES, GOSSIP_ACTION_INFO_DEF + 4, false, BOSS_VALKIRIES}, - {MSG_LICH_KING, GOSSIP_ACTION_INFO_DEF + 5, false, BOSS_ANUBARAK}, - {MSG_ANUBARAK, GOSSIP_ACTION_INFO_DEF + 6, true, BOSS_ANUBARAK} + {MSG_BEASTS, GOSSIP_ACTION_INFO_DEF + 1, false, DATA_NORTHREND_BEASTS}, + {MSG_JARAXXUS, GOSSIP_ACTION_INFO_DEF + 2, false, DATA_JARAXXUS}, + {MSG_CRUSADERS, GOSSIP_ACTION_INFO_DEF + 3, false, DATA_FACTION_CRUSADERS}, + {MSG_VALKIRIES, GOSSIP_ACTION_INFO_DEF + 4, false, DATA_TWIN_VALKIRIES}, + {MSG_LICH_KING, GOSSIP_ACTION_INFO_DEF + 5, false, DATA_ANUBARAK}, + {MSG_ANUBARAK, GOSSIP_ACTION_INFO_DEF + 6, true, DATA_ANUBARAK} }; enum Messages @@ -229,16 +229,16 @@ class npc_announcer_toc10 : public CreatureScript ClearGossipMenuFor(player); CloseGossipMenuFor(player); - if (instance->GetBossState(BOSS_BEASTS) != DONE) + if (instance->GetBossState(DATA_NORTHREND_BEASTS) != DONE) { instance->SetData(TYPE_EVENT, 110); instance->SetData(TYPE_NORTHREND_BEASTS, NOT_STARTED); - instance->SetBossState(BOSS_BEASTS, NOT_STARTED); + instance->SetBossState(DATA_NORTHREND_BEASTS, NOT_STARTED); } - else if (instance->GetBossState(BOSS_JARAXXUS) != DONE) + else if (instance->GetBossState(DATA_JARAXXUS) != DONE) { // if Jaraxxus is spawned, but the raid wiped - if (Creature* jaraxxus = ObjectAccessor::GetCreature(*player, instance->GetGuidData(NPC_JARAXXUS))) + if (Creature* jaraxxus = instance->GetCreature(DATA_JARAXXUS)) { jaraxxus->RemoveAurasDueToSpell(SPELL_JARAXXUS_CHAINS); jaraxxus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); @@ -249,29 +249,29 @@ class npc_announcer_toc10 : public CreatureScript else { instance->SetData(TYPE_EVENT, 1010); - instance->SetBossState(BOSS_JARAXXUS, NOT_STARTED); + instance->SetBossState(DATA_JARAXXUS, NOT_STARTED); } } - else if (instance->GetBossState(BOSS_CRUSADERS) != DONE) + else if (instance->GetBossState(DATA_FACTION_CRUSADERS) != DONE) { if (player->GetTeam() == ALLIANCE) instance->SetData(TYPE_EVENT, 3000); else instance->SetData(TYPE_EVENT, 3001); - instance->SetBossState(BOSS_CRUSADERS, NOT_STARTED); + instance->SetBossState(DATA_FACTION_CRUSADERS, NOT_STARTED); } - else if (instance->GetBossState(BOSS_VALKIRIES) != DONE) + else if (instance->GetBossState(DATA_TWIN_VALKIRIES) != DONE) { instance->SetData(TYPE_EVENT, 4000); - instance->SetBossState(BOSS_VALKIRIES, NOT_STARTED); + instance->SetBossState(DATA_TWIN_VALKIRIES, NOT_STARTED); } - else if (instance->GetBossState(BOSS_LICH_KING) != DONE) + else if (instance->GetBossState(DATA_LICH_KING) != DONE) { if (me->GetMap()->GetPlayers().getFirst()->GetSource()->GetTeam() == ALLIANCE) instance->SetData(TYPE_EVENT, 4020); else instance->SetData(TYPE_EVENT, 4030); - instance->SetBossState(BOSS_LICH_KING, NOT_STARTED); + instance->SetBossState(DATA_LICH_KING, NOT_STARTED); } me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); return true; @@ -305,7 +305,7 @@ class boss_lich_king_toc : public CreatureScript summoned->SetDisplayId(summoned->GetCreatureTemplate()->Modelid2); } - _instance->SetBossState(BOSS_LICH_KING, IN_PROGRESS); + _instance->SetBossState(DATA_LICH_KING, IN_PROGRESS); me->SetWalk(true); } @@ -376,15 +376,15 @@ class boss_lich_king_toc : public CreatureScript break; case 5080: { - if (GameObject* go = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(GO_ARGENT_COLISEUM_FLOOR))) + if (GameObject* go = _instance->GetGameObject(DATA_COLISEUM_FLOOR)) go->SetDestructibleState(GO_DESTRUCTIBLE_DAMAGED); me->CastSpell(me, SPELL_CORPSE_TELEPORT, false); me->CastSpell(me, SPELL_DESTROY_FLOOR_KNOCKUP, false); - _instance->SetBossState(BOSS_LICH_KING, DONE); + _instance->SetBossState(DATA_LICH_KING, DONE); - if (!ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_ANUBARAK))) + if (!_instance->GetCreature(DATA_ANUBARAK)) me->SummonCreature(NPC_ANUBARAK, AnubarakLoc[0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME); _instance->SetData(TYPE_EVENT, 0); @@ -429,7 +429,7 @@ class npc_fizzlebang_toc : public CreatureScript { Talk(SAY_STAGE_1_06, killer); _instance->SetData(TYPE_EVENT, 1180); - if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) + if (Creature* jaraxxus = _instance->GetCreature(DATA_JARAXXUS)) { jaraxxus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); jaraxxus->SetImmuneToPC(false); @@ -454,7 +454,7 @@ class npc_fizzlebang_toc : public CreatureScript { case 1: me->SetWalk(false); - _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); + _instance->DoUseDoorOrButton(_instance->GetGuidData(DATA_MAIN_GATE)); _instance->SetData(TYPE_EVENT, 1120); _instance->SetData(TYPE_EVENT_TIMER, 1*IN_MILLISECONDS); break; @@ -538,7 +538,7 @@ class npc_fizzlebang_toc : public CreatureScript _updateTimer = 5*IN_MILLISECONDS; break; case 1142: - if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) + if (Creature* jaraxxus = _instance->GetCreature(DATA_JARAXXUS)) jaraxxus->SetTarget(me->GetGUID()); if (Creature* pTrigger = ObjectAccessor::GetCreature(*me, _triggerGUID)) pTrigger->DespawnOrUnsummon(); @@ -548,13 +548,13 @@ class npc_fizzlebang_toc : public CreatureScript _updateTimer = 10*IN_MILLISECONDS; break; case 1144: - if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) + if (Creature* jaraxxus = _instance->GetCreature(DATA_JARAXXUS)) jaraxxus->AI()->Talk(SAY_STAGE_1_05); _instance->SetData(TYPE_EVENT, 1150); _updateTimer = 5*IN_MILLISECONDS; break; case 1150: - if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) + if (Creature* jaraxxus = _instance->GetCreature(DATA_JARAXXUS)) { //1-shot Fizzlebang jaraxxus->CastSpell(me, 67888, false); // 67888 - Fel Lightning @@ -605,7 +605,7 @@ class npc_tirion_toc : public CreatureScript if (!_instance) return; - if (_instance->GetData(TYPE_EVENT_NPC) != NPC_TIRION) + if (_instance->GetData(TYPE_EVENT_NPC) != NPC_TIRION_FORDRING) return; uint32 _updateTimer = _instance->GetData(TYPE_EVENT_TIMER); @@ -627,9 +627,9 @@ class npc_tirion_toc : public CreatureScript break; case 150: me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); - if (_instance->GetBossState(BOSS_BEASTS) != DONE) + if (_instance->GetBossState(DATA_NORTHREND_BEASTS) != DONE) { - _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); + _instance->DoUseDoorOrButton(_instance->GetGuidData(DATA_MAIN_GATE)); if (Creature* gormok = me->SummonCreature(NPC_GORMOK, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30*IN_MILLISECONDS)) { @@ -649,9 +649,9 @@ class npc_tirion_toc : public CreatureScript break; case 200: Talk(SAY_STAGE_0_04); - if (_instance->GetBossState(BOSS_BEASTS) != DONE) + if (_instance->GetBossState(DATA_NORTHREND_BEASTS) != DONE) { - _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); + _instance->DoUseDoorOrButton(_instance->GetGuidData(DATA_MAIN_GATE)); if (Creature* dreadscale = me->SummonCreature(NPC_DREADSCALE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_MANUAL_DESPAWN)) { dreadscale->GetMotionMaster()->MovePoint(0, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ()); @@ -667,9 +667,9 @@ class npc_tirion_toc : public CreatureScript break; case 300: Talk(SAY_STAGE_0_05); - if (_instance->GetBossState(BOSS_BEASTS) != DONE) + if (_instance->GetBossState(DATA_NORTHREND_BEASTS) != DONE) { - _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); + _instance->DoUseDoorOrButton(_instance->GetGuidData(DATA_MAIN_GATE)); if (Creature* icehowl = me->SummonCreature(NPC_ICEHOWL, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_DEAD_DESPAWN)) { icehowl->GetMotionMaster()->MovePoint(2, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ()); @@ -697,7 +697,7 @@ class npc_tirion_toc : public CreatureScript case 1010: Talk(SAY_STAGE_1_01); _updateTimer = 7*IN_MILLISECONDS; - _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); + _instance->DoUseDoorOrButton(_instance->GetGuidData(DATA_MAIN_GATE)); me->SummonCreature(NPC_FIZZLEBANG, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 2, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME); _instance->SetData(TYPE_EVENT, 0); break; @@ -751,7 +751,7 @@ class npc_tirion_toc : public CreatureScript _instance->SetData(TYPE_EVENT, 3092); break; case 3092: - if (Creature* pChampionController = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_CHAMPIONS_CONTROLLER))) + if (Creature* pChampionController = _instance->GetCreature(DATA_FACTION_CRUSADERS)) pChampionController->AI()->SetData(1, NOT_STARTED); _instance->SetData(TYPE_EVENT, 3095); break; @@ -768,14 +768,14 @@ class npc_tirion_toc : public CreatureScript break; case 4010: Talk(SAY_STAGE_3_02); - if (Creature* lightbane = me->SummonCreature(NPC_LIGHTBANE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) + if (Creature* lightbane = me->SummonCreature(NPC_FJOLA_LIGHTBANE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) { lightbane->SetVisible(false); lightbane->SetReactState(REACT_PASSIVE); lightbane->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[0].GetPositionX(), TwinValkyrsLoc[0].GetPositionY(), TwinValkyrsLoc[0].GetPositionZ()); lightbane->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[1].GetPositionX(), TwinValkyrsLoc[1].GetPositionY(), TwinValkyrsLoc[1].GetPositionZ()); } - if (Creature* darkbane = me->SummonCreature(NPC_DARKBANE, ToCSpawnLoc[2].GetPositionX(), ToCSpawnLoc[2].GetPositionY(), ToCSpawnLoc[2].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) + if (Creature* darkbane = me->SummonCreature(NPC_EYDIS_DARKBANE, ToCSpawnLoc[2].GetPositionX(), ToCSpawnLoc[2].GetPositionY(), ToCSpawnLoc[2].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) { darkbane->SetVisible(false); darkbane->SetReactState(REACT_PASSIVE); @@ -786,13 +786,13 @@ class npc_tirion_toc : public CreatureScript _instance->SetData(TYPE_EVENT, 4015); break; case 4015: - _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); - if (Creature* lightbane = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_LIGHTBANE))) + _instance->DoUseDoorOrButton(_instance->GetGuidData(DATA_MAIN_GATE)); + if (Creature* lightbane = _instance->GetCreature(DATA_FJOLA_LIGHTBANE)) { lightbane->GetMotionMaster()->MovePoint(1, ToCCommonLoc[8].GetPositionX(), ToCCommonLoc[8].GetPositionY(), ToCCommonLoc[8].GetPositionZ()); lightbane->SetVisible(true); } - if (Creature* darkbane = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_DARKBANE))) + if (Creature* darkbane = _instance->GetCreature(DATA_EYDIS_DARKBANE)) { darkbane->GetMotionMaster()->MovePoint(1, ToCCommonLoc[9].GetPositionX(), ToCCommonLoc[9].GetPositionY(), ToCCommonLoc[9].GetPositionZ()); darkbane->SetVisible(true); @@ -801,7 +801,7 @@ class npc_tirion_toc : public CreatureScript _instance->SetData(TYPE_EVENT, 4016); break; case 4016: - _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); + _instance->DoUseDoorOrButton(_instance->GetGuidData(DATA_MAIN_GATE)); _instance->SetData(TYPE_EVENT, 4017); break; case 4040: @@ -824,14 +824,14 @@ class npc_tirion_toc : public CreatureScript _instance->SetData(TYPE_EVENT, 0); break; case 6000: - me->SummonCreature(NPC_TIRION_FORDRING, EndSpawnLoc[0]); + me->SummonCreature(NPC_TIRION_FORDRING_ANUBARAK, EndSpawnLoc[0]); me->SummonCreature(NPC_ARGENT_MAGE, EndSpawnLoc[1]); me->SummonGameObject(GO_PORTAL_TO_DALARAN, EndSpawnLoc[2], QuaternionData(), 0); _updateTimer = 20*IN_MILLISECONDS; _instance->SetData(TYPE_EVENT, 6005); break; case 6005: - if (Creature* tirionFordring = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_TIRION_FORDRING))) + if (Creature* tirionFordring = _instance->GetCreature(DATA_FORDRING_ANUBARAK)) tirionFordring->AI()->Talk(SAY_STAGE_4_06); _updateTimer = 20*IN_MILLISECONDS; _instance->SetData(TYPE_EVENT, 6010); @@ -839,10 +839,10 @@ class npc_tirion_toc : public CreatureScript case 6010: if (IsHeroic()) { - if (Creature* tirionFordring = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_TIRION_FORDRING))) + if (Creature* tirionFordring = _instance->GetCreature(DATA_FORDRING_ANUBARAK)) tirionFordring->AI()->Talk(SAY_STAGE_4_07); _updateTimer = 1*MINUTE*IN_MILLISECONDS; - _instance->SetBossState(BOSS_ANUBARAK, SPECIAL); + _instance->SetBossState(DATA_ANUBARAK, SPECIAL); _instance->SetData(TYPE_EVENT, 6020); } else diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h index d744c7e1cd2..d3565fe27eb 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h @@ -25,19 +25,42 @@ #define DataHeader "TCR" struct Position; +uint32 const EncounterCount = 6; enum TCRDataTypes { - BOSS_BEASTS = 0, - BOSS_JARAXXUS = 1, - BOSS_CRUSADERS = 2, - BOSS_VALKIRIES = 3, - BOSS_LICH_KING = 4, // not really a boss but oh well - BOSS_ANUBARAK = 5, - MAX_ENCOUNTERS = 6, - - TYPE_COUNTER = 8, - TYPE_EVENT = 9, + // Encounter States + DATA_NORTHREND_BEASTS = 0, + DATA_JARAXXUS = 1, + DATA_FACTION_CRUSADERS = 2, + DATA_TWIN_VALKIRIES = 3, + DATA_LICH_KING = 4, + DATA_ANUBARAK = 5, + + // Additional Data + DATA_GORMOK_THE_IMPALER = 5, + DATA_ACIDMAW = 6, + DATA_DREADSCALE = 7, + DATA_ICEHOWL = 8, + DATA_FJOLA_LIGHTBANE = 9, + DATA_EYDIS_DARKBANE = 10, + DATA_BARRET_RAMSEY = 11, + DATA_FORDRING = 12, + DATA_FORDRING_ANUBARAK = 13, + DATA_VARIAN = 14, + DATA_GARROSH = 15, + DATA_FIZZLEBANG = 16, + DATA_FACTION_CHAMPIONS = 17, + + DATA_CRUSADERS_CHEST = 18, + DATA_COLISEUM_FLOOR = 19, + DATA_MAIN_GATE = 20, + DATA_EAST_PORTCULLIS = 21, + DATA_WEB_DOOR = 22, + DATA_TRIBUTE_CHEST = 23, + + TYPE_COUNTER = 24, + TYPE_EVENT = 25, TYPE_EVENT_TIMER = 101, TYPE_EVENT_NPC = 102, @@ -60,7 +83,7 @@ enum TCRSpellIds enum TCRMiscData { - DESPAWN_TIME = 1200000 + DESPAWN_TIME = 1200000 }; extern Position const ToCCommonLoc[]; @@ -68,121 +91,121 @@ extern Position const AnubarakLoc[]; enum TCRWorldStateIds { - UPDATE_STATE_UI_SHOW = 4390, - UPDATE_STATE_UI_COUNT = 4389 + UPDATE_STATE_UI_SHOW = 4390, + UPDATE_STATE_UI_COUNT = 4389 }; enum NorthrendBeasts { - GORMOK_IN_PROGRESS = 1000, - GORMOK_DONE = 1001, - SNAKES_IN_PROGRESS = 2000, - DREADSCALE_SUBMERGED = 2001, - ACIDMAW_SUBMERGED = 2002, - SNAKES_SPECIAL = 2003, - SNAKES_DONE = 2004, - ICEHOWL_IN_PROGRESS = 3000, - ICEHOWL_DONE = 3001 + GORMOK_IN_PROGRESS = 1000, + GORMOK_DONE = 1001, + SNAKES_IN_PROGRESS = 2000, + DREADSCALE_SUBMERGED = 2001, + ACIDMAW_SUBMERGED = 2002, + SNAKES_SPECIAL = 2003, + SNAKES_DONE = 2004, + ICEHOWL_IN_PROGRESS = 3000, + ICEHOWL_DONE = 3001 }; enum AnnouncerMessages { - MSG_BEASTS = 724001, - MSG_JARAXXUS = 724002, - MSG_CRUSADERS = 724003, - MSG_VALKIRIES = 724004, - MSG_LICH_KING = 724005, - MSG_ANUBARAK = 724006 + MSG_BEASTS = 724001, + MSG_JARAXXUS = 724002, + MSG_CRUSADERS = 724003, + MSG_VALKIRIES = 724004, + MSG_LICH_KING = 724005, + MSG_ANUBARAK = 724006 }; enum TCRCreatureIds { - NPC_BARRENT = 34816, - NPC_TIRION = 34996, - NPC_TIRION_FORDRING = 36095, - NPC_ARGENT_MAGE = 36097, - NPC_FIZZLEBANG = 35458, - NPC_GARROSH = 34995, - NPC_VARIAN = 34990, - NPC_LICH_KING = 35877, - - NPC_THRALL = 34994, - NPC_PROUDMOORE = 34992, - NPC_WILFRED_PORTAL = 17965, - NPC_TRIGGER = 35651, - - NPC_ICEHOWL = 34797, - NPC_GORMOK = 34796, - NPC_DREADSCALE = 34799, - NPC_ACIDMAW = 35144, - - NPC_JARAXXUS = 34780, - - NPC_CHAMPIONS_CONTROLLER = 34781, - - NPC_ALLIANCE_DEATH_KNIGHT = 34461, - NPC_ALLIANCE_DRUID_BALANCE = 34460, - NPC_ALLIANCE_DRUID_RESTORATION = 34469, - NPC_ALLIANCE_HUNTER = 34467, - NPC_ALLIANCE_MAGE = 34468, - NPC_ALLIANCE_PALADIN_HOLY = 34465, - NPC_ALLIANCE_PALADIN_RETRIBUTION = 34471, - NPC_ALLIANCE_PRIEST_DISCIPLINE = 34466, - NPC_ALLIANCE_PRIEST_SHADOW = 34473, - NPC_ALLIANCE_ROGUE = 34472, - NPC_ALLIANCE_SHAMAN_ENHANCEMENT = 34463, - NPC_ALLIANCE_SHAMAN_RESTORATION = 34470, - NPC_ALLIANCE_WARLOCK = 34474, - NPC_ALLIANCE_WARRIOR = 34475, - - NPC_HORDE_DEATH_KNIGHT = 34458, - NPC_HORDE_DRUID_BALANCE = 34451, - NPC_HORDE_DRUID_RESTORATION = 34459, - NPC_HORDE_HUNTER = 34448, - NPC_HORDE_MAGE = 34449, - NPC_HORDE_PALADIN_HOLY = 34445, - NPC_HORDE_PALADIN_RETRIBUTION = 34456, - NPC_HORDE_PRIEST_DISCIPLINE = 34447, - NPC_HORDE_PRIEST_SHADOW = 34441, - NPC_HORDE_ROGUE = 34454, - NPC_HORDE_SHAMAN_ENHANCEMENT = 34455, - NPC_HORDE_SHAMAN_RESTORATION = 34444, - NPC_HORDE_WARLOCK = 34450, - NPC_HORDE_WARRIOR = 34453, - - NPC_LIGHTBANE = 34497, - NPC_DARKBANE = 34496, - - NPC_DARK_ESSENCE = 34567, - NPC_LIGHT_ESSENCE = 34568, - - NPC_ANUBARAK = 34564 + NPC_BARRET_RAMSEY = 34816, + NPC_TIRION_FORDRING = 34996, + NPC_TIRION_FORDRING_ANUBARAK = 36095, + NPC_ARGENT_MAGE = 36097, + NPC_FIZZLEBANG = 35458, + NPC_GARROSH = 34995, + NPC_VARIAN = 34990, + NPC_LICH_KING = 35877, + + NPC_THRALL = 34994, + NPC_PROUDMOORE = 34992, + NPC_WILFRED_PORTAL = 17965, + NPC_TRIGGER = 35651, + + NPC_ICEHOWL = 34797, + NPC_GORMOK = 34796, + NPC_DREADSCALE = 34799, + NPC_ACIDMAW = 35144, + + NPC_JARAXXUS = 34780, + + NPC_CHAMPIONS_CONTROLLER = 34781, + + NPC_ALLIANCE_DEATH_KNIGHT = 34461, + NPC_ALLIANCE_DRUID_BALANCE = 34460, + NPC_ALLIANCE_DRUID_RESTORATION = 34469, + NPC_ALLIANCE_HUNTER = 34467, + NPC_ALLIANCE_MAGE = 34468, + NPC_ALLIANCE_PALADIN_HOLY = 34465, + NPC_ALLIANCE_PALADIN_RETRIBUTION = 34471, + NPC_ALLIANCE_PRIEST_DISCIPLINE = 34466, + NPC_ALLIANCE_PRIEST_SHADOW = 34473, + NPC_ALLIANCE_ROGUE = 34472, + NPC_ALLIANCE_SHAMAN_ENHANCEMENT = 34463, + NPC_ALLIANCE_SHAMAN_RESTORATION = 34470, + NPC_ALLIANCE_WARLOCK = 34474, + NPC_ALLIANCE_WARRIOR = 34475, + + NPC_HORDE_DEATH_KNIGHT = 34458, + NPC_HORDE_DRUID_BALANCE = 34451, + NPC_HORDE_DRUID_RESTORATION = 34459, + NPC_HORDE_HUNTER = 34448, + NPC_HORDE_MAGE = 34449, + NPC_HORDE_PALADIN_HOLY = 34445, + NPC_HORDE_PALADIN_RETRIBUTION = 34456, + NPC_HORDE_PRIEST_DISCIPLINE = 34447, + NPC_HORDE_PRIEST_SHADOW = 34441, + NPC_HORDE_ROGUE = 34454, + NPC_HORDE_SHAMAN_ENHANCEMENT = 34455, + NPC_HORDE_SHAMAN_RESTORATION = 34444, + NPC_HORDE_WARLOCK = 34450, + NPC_HORDE_WARRIOR = 34453, + + NPC_FJOLA_LIGHTBANE = 34497, + NPC_EYDIS_DARKBANE = 34496, + + NPC_DARK_ESSENCE = 34567, + NPC_LIGHT_ESSENCE = 34568, + + NPC_ANUBARAK = 34564 }; enum TCRGameObjectIds { - GO_CRUSADERS_CACHE_10 = 195631, - GO_CRUSADERS_CACHE_25 = 195632, - GO_CRUSADERS_CACHE_10_H = 195633, - GO_CRUSADERS_CACHE_25_H = 195635, + GO_CRUSADERS_CACHE_10 = 195631, + GO_CRUSADERS_CACHE_25 = 195632, + GO_CRUSADERS_CACHE_10_H = 195633, + GO_CRUSADERS_CACHE_25_H = 195635, // Tribute Chest (heroic) // 10-man modes - GO_TRIBUTE_CHEST_10H_25 = 195668, // 10man 01-24 attempts - GO_TRIBUTE_CHEST_10H_45 = 195667, // 10man 25-44 attempts - GO_TRIBUTE_CHEST_10H_50 = 195666, // 10man 45-49 attempts - GO_TRIBUTE_CHEST_10H_99 = 195665, // 10man 50 attempts + GO_TRIBUTE_CHEST_10H_25 = 195668, // 10man 01-24 attempts + GO_TRIBUTE_CHEST_10H_45 = 195667, // 10man 25-44 attempts + GO_TRIBUTE_CHEST_10H_50 = 195666, // 10man 45-49 attempts + GO_TRIBUTE_CHEST_10H_99 = 195665, // 10man 50 attempts // 25-man modes - GO_TRIBUTE_CHEST_25H_25 = 195672, // 25man 01-24 attempts - GO_TRIBUTE_CHEST_25H_45 = 195671, // 25man 25-44 attempts - GO_TRIBUTE_CHEST_25H_50 = 195670, // 25man 45-49 attempts - GO_TRIBUTE_CHEST_25H_99 = 195669, // 25man 50 attempts - - GO_ARGENT_COLISEUM_FLOOR = 195527, //20943 - GO_MAIN_GATE_DOOR = 195647, - GO_EAST_PORTCULLIS = 195648, - GO_WEB_DOOR = 195485, - GO_PORTAL_TO_DALARAN = 195682 + GO_TRIBUTE_CHEST_25H_25 = 195672, // 25man 01-24 attempts + GO_TRIBUTE_CHEST_25H_45 = 195671, // 25man 25-44 attempts + GO_TRIBUTE_CHEST_25H_50 = 195670, // 25man 45-49 attempts + GO_TRIBUTE_CHEST_25H_99 = 195669, // 25man 50 attempts + + GO_ARGENT_COLISEUM_FLOOR = 195527, //20943 + GO_MAIN_GATE_DOOR = 195647, + GO_EAST_PORTCULLIS = 195648, + GO_WEB_DOOR = 195485, + GO_PORTAL_TO_DALARAN = 195682 }; enum TCRAchievementData diff --git a/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp b/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp index 2bcfd7a70d6..bbfc6ac0924 100644 --- a/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp +++ b/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp @@ -243,7 +243,7 @@ public: if (Creature* crystalChannelTarget = crystal->FindNearestCreature(NPC_CRYSTAL_CHANNEL_TARGET, 5.0f)) { if (active) - crystalChannelTarget->CastSpell((Unit*)nullptr, SPELL_BEAM_CHANNEL); + crystalChannelTarget->CastSpell(nullptr, SPELL_BEAM_CHANNEL); else if (crystalChannelTarget->HasUnitState(UNIT_STATE_CASTING)) crystalChannelTarget->CastStop(); } @@ -384,7 +384,7 @@ class spell_novos_summon_minions : public SpellScriptLoader void HandleScript(SpellEffIndex /*effIndex*/) { for (uint8 i = 0; i < 2; ++i) - GetCaster()->CastSpell((Unit*)nullptr, SPELL_SUMMON_COPY_OF_MINIONS, true); + GetCaster()->CastSpell(nullptr, SPELL_SUMMON_COPY_OF_MINIONS, true); } void Register() override diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp index abab9f7ead7..e0ac981b69c 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_bronjahm.cpp @@ -246,7 +246,7 @@ class npc_corrupted_soul_fragment : public CreatureScript if (instance->GetGuidData(DATA_BRONJAHM).GetCounter() != id) return; - me->CastSpell((Unit*)nullptr, SPELL_CONSUME_SOUL, true); + me->CastSpell(nullptr, SPELL_CONSUME_SOUL, true); me->DespawnOrUnsummon(); } 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 7d620ae5a60..0545d3d19f6 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -2824,9 +2824,9 @@ class spell_hor_gunship_cannon_fire : public SpellScriptLoader if (!urand(0, 2)) { if (GetTarget()->GetEntry() == NPC_GUNSHIP_CANNON_HORDE) - GetTarget()->CastSpell((Unit*)nullptr, SPELL_GUNSHIP_CANNON_FIRE_MISSILE_HORDE, true); + GetTarget()->CastSpell(nullptr, SPELL_GUNSHIP_CANNON_FIRE_MISSILE_HORDE, true); else - GetTarget()->CastSpell((Unit*)nullptr, SPELL_GUNSHIP_CANNON_FIRE_MISSILE_ALLIANCE, true); + GetTarget()->CastSpell(nullptr, SPELL_GUNSHIP_CANNON_FIRE_MISSILE_ALLIANCE, true); } } 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 89f95a32cc8..ab439788c90 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 @@ -346,7 +346,7 @@ class instance_halls_of_reflection : public InstanceMapScript if (Creature* lichking = instance->GetCreature(TheLichKingEscapeGUID)) { - lichking->CastSpell((Unit*)nullptr, SPELL_ACHIEV_CHECK, true); + lichking->CastSpell(nullptr, SPELL_ACHIEV_CHECK, true); lichking->DespawnOrUnsummon(1); } break; @@ -453,7 +453,7 @@ class instance_halls_of_reflection : public InstanceMapScript if (_quelDelarState == NOT_STARTED) { if (Creature* bunny = instance->GetCreature(FrostmourneAltarBunnyGUID)) - bunny->CastSpell((Unit*)nullptr, SPELL_ESSENCE_OF_CAPTURED); + bunny->CastSpell(nullptr, SPELL_ESSENCE_OF_CAPTURED); events.ScheduleEvent(EVENT_QUEL_DELAR_SUMMON_UTHER, 2000); } } diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp index f21d4fa007c..8b558d8e913 100644 --- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp @@ -114,7 +114,9 @@ enum KrickPhase enum Actions { - ACTION_OUTRO = 1 + ACTION_OUTRO = 1, + ACTION_STORE_OLD_TARGET, + ACTION_RESET_THREAT }; enum Points @@ -144,13 +146,15 @@ class boss_ick : public CreatureScript { boss_ickAI(Creature* creature) : BossAI(creature, DATA_ICK) { - _tempThreat = 0; + _tempThreat = 0.0f; } void Reset() override { events.Reset(); instance->SetBossState(DATA_ICK, NOT_STARTED); + _oldTargetGUID.Clear(); + _tempThreat = 0.0f; } Creature* GetKrick() @@ -190,15 +194,29 @@ class boss_ick : public CreatureScript instance->SetBossState(DATA_ICK, DONE); } - void SetTempThreat(float threat) + void DoAction(int32 actionId) override { - _tempThreat = threat; - } + if (actionId == ACTION_STORE_OLD_TARGET) + { + if (Unit* victim = me->GetVictim()) + { + _oldTargetGUID = victim->GetGUID(); + _tempThreat = GetThreat(victim); + } + } + else if (actionId == ACTION_RESET_THREAT) + { + if (Unit* oldTarget = ObjectAccessor::GetUnit(*me, _oldTargetGUID)) + { + if (Unit* current = me->GetVictim()) + ModifyThreatByPercent(current, -100); - void _ResetThreat(Unit* target) - { - ModifyThreatByPercent(target, -100); - AddThreat(target, _tempThreat); + AddThreat(oldTarget, _tempThreat); + AttackStart(oldTarget); + _oldTargetGUID.Clear(); + _tempThreat = 0.0f; + } + } } void UpdateAI(uint32 diff) override @@ -262,7 +280,7 @@ class boss_ick : public CreatureScript case EVENT_PURSUIT: if (Creature* krick = GetKrick()) krick->AI()->Talk(SAY_KRICK_CHASE); - DoCast(me, SPELL_PURSUIT); + me->CastCustomSpell(SPELL_PURSUIT, SPELLVALUE_MAX_TARGETS, 1, me); break; default: break; @@ -277,6 +295,7 @@ class boss_ick : public CreatureScript private: float _tempThreat; + ObjectGuid _oldTargetGUID; }; CreatureAI* GetAI(Creature* creature) const override @@ -631,18 +650,15 @@ class spell_krick_pursuit : public SpellScriptLoader void HandleScriptEffect(SpellEffIndex /*effIndex*/) { - if (GetCaster()) - if (Creature* ick = GetCaster()->ToCreature()) - { - if (Unit* target = ick->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true)) - { - ick->AI()->Talk(SAY_ICK_CHASE_1, target); - ick->AddAura(GetSpellInfo()->Id, target); - ENSURE_AI(boss_ick::boss_ickAI, ick->AI())->SetTempThreat(ick->GetThreatManager().GetThreat(target)); - ick->GetThreatManager().AddThreat(target, float(GetEffectValue()), GetSpellInfo(), true, true); - target->GetThreatManager().AddThreat(ick, float(GetEffectValue()), GetSpellInfo(), true, true); - } - } + Unit* target = GetHitUnit(); + if (Creature* ick = GetCaster()->ToCreature()) + { + ick->AI()->Talk(SAY_ICK_CHASE_1, target); + ick->AddAura(GetSpellInfo()->Id, target); + ick->AI()->DoAction(ACTION_STORE_OLD_TARGET); + ick->GetThreatManager().AddThreat(target, float(GetEffectValue()), GetSpellInfo(), true, true); + ick->AI()->AttackStart(target); + } } void Register() override @@ -659,7 +675,7 @@ class spell_krick_pursuit : public SpellScriptLoader { if (Unit* caster = GetCaster()) if (Creature* creCaster = caster->ToCreature()) - ENSURE_AI(boss_ick::boss_ickAI, creCaster->AI())->_ResetThreat(GetTarget()); + creCaster->AI()->DoAction(ACTION_RESET_THREAT); } void Register() override diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp index 4e62380b983..187ab353803 100644 --- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp @@ -539,18 +539,18 @@ class spell_tyrannus_rimefang_icy_blast : public SpellScriptLoader } }; -class at_tyrannus_event_starter : public AreaTriggerScript +class at_tyrannus_event_starter : public OnlyOnceAreaTriggerScript { public: - at_tyrannus_event_starter() : AreaTriggerScript("at_tyrannus_event_starter") { } + at_tyrannus_event_starter() : OnlyOnceAreaTriggerScript("at_tyrannus_event_starter") { } - bool OnTrigger(Player* player, AreaTriggerEntry const* /*at*/) override + bool _OnTrigger(Player* player, AreaTriggerEntry const* /*at*/) override { InstanceScript* instance = player->GetInstanceScript(); if (player->IsGameMaster() || !instance) return false; - if (instance->GetBossState(DATA_TYRANNUS) != IN_PROGRESS && instance->GetBossState(DATA_TYRANNUS) != DONE) + if (instance->GetBossState(DATA_TYRANNUS) != DONE) if (Creature* tyrannus = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_TYRANNUS))) { tyrannus->AI()->DoAction(ACTION_START_INTRO); diff --git a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp index c9e5848d2f5..4bd67f3e232 100644 --- a/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp +++ b/src/server/scripts/Northrend/Gundrak/instance_gundrak.cpp @@ -322,7 +322,7 @@ class instance_gundrak : public InstanceMapScript if (GameObject* altar = GetGameObject(altarId)) if (Creature* trigger = altar->FindNearestCreature(NPC_ALTAR_TRIGGER, 10.0f)) - trigger->CastSpell((Unit*)nullptr, spellId, true); + trigger->CastSpell(nullptr, spellId, true); // eventId equals statueId ToggleGameObject(eventId, GO_STATE_READY); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp index f038f653782..ffbc0901f31 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp @@ -222,7 +222,7 @@ class boss_blood_council_controller : public CreatureScript struct boss_blood_council_controllerAI : public BossAI { - boss_blood_council_controllerAI(Creature* creature) : BossAI(creature, DATA_BLOOD_PRINCE_COUNCIL), _intro(true) + boss_blood_council_controllerAI(Creature* creature) : BossAI(creature, DATA_BLOOD_PRINCE_COUNCIL) { Initialize(); SetCombatMovement(false); @@ -240,7 +240,7 @@ class boss_blood_council_controller : public CreatureScript Initialize(); me->SummonCreatureGroup(SUMMON_PRINCES_GROUP); - if (!_intro) + if (!instance->GetData(DATA_BLOOD_PRINCE_COUNCIL_INTRO)) for (uint32 bossData : PrincesData) if (Creature* prince = ObjectAccessor::GetCreature(*me, instance->GetGuidData(bossData))) { @@ -311,7 +311,7 @@ class boss_blood_council_controller : public CreatureScript uint32 GetData(uint32 data) const override { - if (data == DATA_INTRO && !_intro) + if (data == DATA_INTRO && !instance->GetData(DATA_BLOOD_PRINCE_COUNCIL_INTRO)) return DATA_INTRO_DONE; return 0; } @@ -338,9 +338,9 @@ class boss_blood_council_controller : public CreatureScript void DoAction(int32 actionId) override { - if (actionId == ACTION_START_INTRO && _intro && instance->GetBossState(DATA_BLOOD_PRINCE_COUNCIL) != DONE) + if (actionId == ACTION_START_INTRO && instance->GetData(DATA_BLOOD_PRINCE_COUNCIL_INTRO) && instance->GetBossState(DATA_BLOOD_PRINCE_COUNCIL) != DONE) { - _intro = false; + instance->SetData(DATA_BLOOD_PRINCE_COUNCIL_INTRO, 0); if (Creature* bloodQueen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_BLOOD_QUEEN_LANA_THEL_COUNCIL))) bloodQueen->AI()->DoAction(ACTION_START_INTRO); } @@ -425,7 +425,6 @@ class boss_blood_council_controller : public CreatureScript uint32 _invocationStage; uint32 _resetCounter; - bool _intro; }; CreatureAI* GetAI(Creature* creature) const override @@ -445,7 +444,7 @@ struct BloodPrincesBossAI : public BossAI { _spawnHealth = 1; if (!me->isDead()) - JustRespawned(); + JustAppeared(); } void Reset() override @@ -493,7 +492,7 @@ struct BloodPrincesBossAI : public BossAI } } - void JustRespawned() override + void JustAppeared() override { if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_BLOOD_PRINCES_CONTROL))) if (controller->AI()->GetData(DATA_INTRO) != DATA_INTRO_DONE) 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 41b836922e1..e344a3181fc 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -550,7 +550,7 @@ struct gunship_npc_AI : public ScriptedAI Instance(creature->GetInstanceScript()), Slot(nullptr), Index(uint32(-1)) { BurningPitchId = Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_BURNING_PITCH_A : SPELL_BURNING_PITCH_H; - me->setRegeneratingHealth(false); + me->SetRegenerateHealth(false); } void SetData(uint32 type, uint32 data) override @@ -676,7 +676,7 @@ class npc_gunship : public CreatureScript _teamInInstance(creature->GetInstanceScript()->GetData(DATA_TEAM_IN_INSTANCE)), _summonedFirstMage(false), _died(false) { - me->setRegeneratingHealth(false); + me->SetRegenerateHealth(false); } void DamageTaken(Unit* /*source*/, uint32& damage) override @@ -857,7 +857,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript { _controller.ResetSlots(HORDE); _controller.SetTransport(creature->GetTransport()); - me->setRegeneratingHealth(false); + me->SetRegenerateHealth(false); me->m_CombatDistance = 70.0f; _firstMageCooldown = time(nullptr) + 60; _axethrowersYellCooldown = time_t(0); @@ -1126,7 +1126,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript { _controller.ResetSlots(ALLIANCE); _controller.SetTransport(creature->GetTransport()); - me->setRegeneratingHealth(false); + me->SetRegenerateHealth(false); me->m_CombatDistance = 70.0f; _firstMageCooldown = time(nullptr) + 60; _riflemanYellCooldown = time_t(0); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp index 8a444b3d933..bf6920e741d 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp @@ -203,7 +203,7 @@ class boss_lady_deathwhisper : public CreatureScript struct boss_lady_deathwhisperAI : public BossAI { boss_lady_deathwhisperAI(Creature* creature) : BossAI(creature, DATA_LADY_DEATHWHISPER), - _dominateMindCount(RAID_MODE<uint8>(0, 1, 1, 3)), _introDone(false) + _dominateMindCount(RAID_MODE<uint8>(0, 1, 1, 3)) { Initialize(); } @@ -237,43 +237,39 @@ class boss_lady_deathwhisper : public CreatureScript if (action != ACTION_START_INTRO) return; - if (!_introDone) + Talk(SAY_INTRO_1); + _phase = PHASE_INTRO; + scheduler.Schedule(Seconds(10), GROUP_INTRO, [this](TaskContext context) { - _introDone = true; - Talk(SAY_INTRO_1); - _phase = PHASE_INTRO; - scheduler.Schedule(Seconds(10), GROUP_INTRO, [this](TaskContext context) + switch (context.GetRepeatCounter()) { - switch (context.GetRepeatCounter()) - { - case 0: - Talk(SAY_INTRO_2); - context.Repeat(Seconds(21)); - break; - case 1: - Talk(SAY_INTRO_3); - context.Repeat(Seconds(11)); - break; - case 2: - Talk(SAY_INTRO_4); - context.Repeat(Seconds(9)); - break; - case 3: - Talk(SAY_INTRO_5); - context.Repeat(Seconds(21)); - break; - case 4: - Talk(SAY_INTRO_6); - context.Repeat(Seconds(10)); - break; - case 5: - Talk(SAY_INTRO_7); - return; - default: - break; - } - }); - } + case 0: + Talk(SAY_INTRO_2); + context.Repeat(Seconds(21)); + break; + case 1: + Talk(SAY_INTRO_3); + context.Repeat(Seconds(11)); + break; + case 2: + Talk(SAY_INTRO_4); + context.Repeat(Seconds(9)); + break; + case 3: + Talk(SAY_INTRO_5); + context.Repeat(Seconds(21)); + break; + case 4: + Talk(SAY_INTRO_6); + context.Repeat(Seconds(10)); + break; + case 5: + Talk(SAY_INTRO_7); + return; + default: + break; + } + }); } void AttackStart(Unit* victim) override @@ -585,7 +581,6 @@ class boss_lady_deathwhisper : public CreatureScript uint32 _waveCounter; uint8 const _dominateMindCount; uint8 _phase; - bool _introDone; }; CreatureAI* GetAI(Creature* creature) const override @@ -1022,12 +1017,12 @@ class spell_deathwhisper_mana_barrier : public SpellScriptLoader } }; -class at_lady_deathwhisper_entrance : public AreaTriggerScript +class at_lady_deathwhisper_entrance : public OnlyOnceAreaTriggerScript { public: - at_lady_deathwhisper_entrance() : AreaTriggerScript("at_lady_deathwhisper_entrance") { } + at_lady_deathwhisper_entrance() : OnlyOnceAreaTriggerScript("at_lady_deathwhisper_entrance") { } - bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + bool _OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (InstanceScript* instance = player->GetInstanceScript()) if (instance->GetBossState(DATA_LADY_DEATHWHISPER) != DONE) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp index ca8a9356db6..7e7b372337e 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp @@ -140,7 +140,6 @@ class boss_lord_marrowgar : public CreatureScript _boneStormDuration = RAID_MODE<uint32>(20000, 30000, 20000, 30000); _baseSpeed = creature->GetSpeedRate(MOVE_RUN); _coldflameLastPos.Relocate(creature); - _introDone = false; _boneSlice = false; } @@ -155,7 +154,6 @@ class boss_lord_marrowgar : public CreatureScript events.ScheduleEvent(EVENT_COLDFLAME, 5000, EVENT_GROUP_SPECIAL); events.ScheduleEvent(EVENT_WARN_BONE_STORM, urand(45000, 50000)); events.ScheduleEvent(EVENT_ENRAGE, 600000); - _introDone = false; _boneSlice = false; _boneSpikeImmune.clear(); } @@ -333,11 +331,7 @@ class boss_lord_marrowgar : public CreatureScript _boneSpikeImmune.clear(); break; case ACTION_TALK_ENTER_ZONE: - if (!_introDone) - { Talk(SAY_ENTER_ZONE); - _introDone = true; - } break; default: break; @@ -350,7 +344,6 @@ class boss_lord_marrowgar : public CreatureScript ObjectGuid _coldflameTarget; uint32 _boneStormDuration; float _baseSpeed; - bool _introDone; bool _boneSlice; }; @@ -654,6 +647,15 @@ class spell_marrowgar_bone_spike_graveyard : public SpellScriptLoader { Unit* target = *itr; target->CastSpell(target, BoneSpikeSummonId[i], true); + if (!target->IsAlive()) // make sure we don't get any stuck spikes on dead targets + { + if (Aura* aura = target->GetAura(SPELL_IMPALED)) + { + if (Creature* spike = ObjectAccessor::GetCreature(*target, aura->GetCasterGUID())) + spike->DespawnOrUnsummon(); + aura->Remove(); + } + } } marrowgarAI->Talk(SAY_BONESPIKE); @@ -752,12 +754,12 @@ class spell_marrowgar_bone_slice : public SpellScriptLoader } }; -class at_lord_marrowgar_entrance : public AreaTriggerScript +class at_lord_marrowgar_entrance : public OnlyOnceAreaTriggerScript { public: - at_lord_marrowgar_entrance() : AreaTriggerScript("at_lord_marrowgar_entrance") { } + at_lord_marrowgar_entrance() : OnlyOnceAreaTriggerScript("at_lord_marrowgar_entrance") { } - bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + bool _OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (InstanceScript* instance = player->GetInstanceScript()) if (Creature* lordMarrowgar = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_LORD_MARROWGAR))) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp index b23b3ca9917..e19b4187138 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp @@ -1118,7 +1118,7 @@ class spell_putricide_ooze_tank_protection : public SpellScriptLoader PreventDefaultAction(); Unit* actionTarget = eventInfo.GetActionTarget(); - actionTarget->CastSpell((Unit*)nullptr, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true, nullptr, aurEff); + actionTarget->CastSpell(nullptr, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index 370578a3c47..c64e42762d2 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -194,7 +194,7 @@ class FrostBombExplosion : public BasicEvent bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override { - _owner->CastSpell((Unit*)nullptr, SPELL_FROST_BOMB, false, nullptr, nullptr, _sindragosaGUID); + _owner->CastSpell(nullptr, SPELL_FROST_BOMB, false, nullptr, nullptr, _sindragosaGUID); _owner->RemoveAurasDueToSpell(SPELL_FROST_BOMB_VISUAL); return true; } @@ -226,7 +226,7 @@ class boss_sindragosa : public CreatureScript struct boss_sindragosaAI : public BossAI { - boss_sindragosaAI(Creature* creature) : BossAI(creature, DATA_SINDRAGOSA), _summoned(false) + boss_sindragosaAI(Creature* creature) : BossAI(creature, DATA_SINDRAGOSA) { Initialize(); } @@ -251,7 +251,7 @@ class boss_sindragosa : public CreatureScript events.ScheduleEvent(EVENT_AIR_PHASE, 50000); Initialize(); - if (!_summoned) + if (instance->GetData(DATA_SINDRAGOSA_INTRO)) { me->SetCanFly(true); me->SetDisableGravity(true); @@ -311,10 +311,8 @@ class boss_sindragosa : public CreatureScript { if (action == ACTION_START_FROSTWYRM) { - if (_summoned) - return; - _summoned = true; + instance->SetData(DATA_SINDRAGOSA_INTRO, 0); if (TempSummon* summon = me->ToTempSummon()) summon->SetTempSummonType(TEMPSUMMON_DEAD_DESPAWN); @@ -565,7 +563,6 @@ class boss_sindragosa : public CreatureScript uint8 _mysticBuffetStack; bool _isInAirPhase; bool _isThirdPhase; - bool _summoned; }; CreatureAI* GetAI(Creature* creature) const override @@ -688,9 +685,9 @@ class npc_spinestalker : public CreatureScript } } - void JustRespawned() override + void JustAppeared() override { - ScriptedAI::JustRespawned(); + ScriptedAI::JustAppeared(); _instance->SetData(DATA_SINDRAGOSA_FROSTWYRMS, me->GetSpawnId()); // this cannot be in Reset because reset also happens on evade } @@ -825,9 +822,9 @@ class npc_rimefang : public CreatureScript } } - void JustRespawned() override + void JustAppeared() override { - ScriptedAI::JustRespawned(); + ScriptedAI::JustAppeared(); _instance->SetData(DATA_SINDRAGOSA_FROSTWYRMS, me->GetSpawnId()); // this cannot be in Reset because reset also happens on evade } @@ -991,9 +988,9 @@ class npc_sindragosa_trash : public CreatureScript Initialize(); } - void JustRespawned() override + void JustAppeared() override { - ScriptedAI::JustRespawned(); + ScriptedAI::JustAppeared(); // Increase add count if (me->GetEntry() == NPC_FROSTWING_WHELP) @@ -1616,12 +1613,12 @@ public: } }; -class at_sindragosa_lair : public AreaTriggerScript +class at_sindragosa_lair : public OnlyOnceAreaTriggerScript { public: - at_sindragosa_lair() : AreaTriggerScript("at_sindragosa_lair") { } + at_sindragosa_lair() : OnlyOnceAreaTriggerScript("at_sindragosa_lair") { } - bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + bool _OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { if (InstanceScript* instance = player->GetInstanceScript()) { 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 d00e56606b5..7b1de4c8bfe 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -268,7 +268,10 @@ enum Events // Strangulate Vehicle (Harvest Soul) EVENT_TELEPORT, EVENT_MOVE_TO_LICH_KING, - EVENT_DESPAWN_SELF + EVENT_DESPAWN_SELF, + + //Spirit Bomb + EVENT_BOMB_EXPLOSION }; enum EventGroups @@ -472,7 +475,7 @@ class VileSpiritActivateEvent : public BasicEvent { _owner->SetReactState(REACT_AGGRESSIVE); _owner->CastSpell(_owner, SPELL_VILE_SPIRIT_MOVE_SEARCH, true); - _owner->CastSpell((Unit*)nullptr, SPELL_VILE_SPIRIT_DAMAGE_SEARCH, true); + _owner->CastSpell(nullptr, SPELL_VILE_SPIRIT_DAMAGE_SEARCH, true); return true; } @@ -626,8 +629,8 @@ class boss_the_lich_king : public CreatureScript me->GetMap()->SetZoneOverrideLight(AREA_ICECROWN_CITADEL, 0, 5000); break; case ACTION_BREAK_FROSTMOURNE: - me->CastSpell((Unit*)nullptr, SPELL_SUMMON_BROKEN_FROSTMOURNE, TRIGGERED_IGNORE_CAST_IN_PROGRESS); - me->CastSpell((Unit*)nullptr, SPELL_SUMMON_BROKEN_FROSTMOURNE_2, TRIGGERED_IGNORE_CAST_IN_PROGRESS); + me->CastSpell(nullptr, SPELL_SUMMON_BROKEN_FROSTMOURNE, TRIGGERED_IGNORE_CAST_IN_PROGRESS); + me->CastSpell(nullptr, SPELL_SUMMON_BROKEN_FROSTMOURNE_2, TRIGGERED_IGNORE_CAST_IN_PROGRESS); SetEquipmentSlots(false, EQUIP_BROKEN_FROSTMOURNE); events.ScheduleEvent(EVENT_OUTRO_TALK_6, 2500, 0, PHASE_OUTRO); break; @@ -709,7 +712,7 @@ class boss_the_lich_king : public CreatureScript summons.DespawnAll(); me->GetMap()->SetZoneMusic(AREA_ICECROWN_CITADEL, MUSIC_FURY_OF_FROSTMOURNE); me->InterruptNonMeleeSpells(true); - me->CastSpell((Unit*)nullptr, SPELL_FURY_OF_FROSTMOURNE, TRIGGERED_NONE); + me->CastSpell(nullptr, SPELL_FURY_OF_FROSTMOURNE, TRIGGERED_NONE); me->SetWalk(true); events.ScheduleEvent(EVENT_OUTRO_TALK_1, 2600, 0, PHASE_OUTRO); events.ScheduleEvent(EVENT_OUTRO_EMOTE_TALK, 6600, 0, PHASE_OUTRO); @@ -756,7 +759,7 @@ class boss_the_lich_king : public CreatureScript break; case NPC_FROSTMOURNE_TRIGGER: { - summon->CastSpell((Unit*)nullptr, SPELL_BROKEN_FROSTMOURNE, true); + summon->CastSpell(nullptr, SPELL_BROKEN_FROSTMOURNE, true); me->GetMap()->SetZoneOverrideLight(AREA_ICECROWN_CITADEL, LIGHT_SOULSTORM, 10000); me->GetMap()->SetZoneWeather(AREA_ICECROWN_CITADEL, WEATHER_STATE_BLACKSNOW, 0.5f); @@ -815,6 +818,7 @@ class boss_the_lich_king : public CreatureScript { me->GetMap()->SetZoneOverrideLight(AREA_ICECROWN_CITADEL, LIGHT_SNOWSTORM, 5000); me->GetMap()->SetZoneWeather(AREA_ICECROWN_CITADEL, WEATHER_STATE_LIGHT_SNOW, 0.5f); + summons.DespawnEntry(NPC_SHADOW_TRAP); } } @@ -842,8 +846,6 @@ class boss_the_lich_king : public CreatureScript Talk(SAY_LK_REMORSELESS_WINTER); me->GetMap()->SetZoneMusic(AREA_ICECROWN_CITADEL, MUSIC_SPECIAL); DoCast(me, SPELL_REMORSELESS_WINTER_1); - summons.DespawnEntry(NPC_SHADOW_TRAP); - events.DelayEvents(62500, EVENT_GROUP_BERSERK); // delay berserk timer, its not ticking during phase transitions events.ScheduleEvent(EVENT_QUAKE, 62500, 0, PHASE_TRANSITION); events.ScheduleEvent(EVENT_PAIN_AND_SUFFERING, 4000, 0, PHASE_TRANSITION); events.ScheduleEvent(EVENT_SUMMON_ICE_SPHERE, 8000, 0, PHASE_TRANSITION); @@ -859,7 +861,6 @@ class boss_the_lich_king : public CreatureScript me->GetMap()->SetZoneMusic(AREA_ICECROWN_CITADEL, MUSIC_SPECIAL); DoCast(me, SPELL_REMORSELESS_WINTER_2); summons.DespawnEntry(NPC_VALKYR_SHADOWGUARD); - events.DelayEvents(62500, EVENT_GROUP_BERSERK); // delay berserk timer, its not ticking during phase transitions events.ScheduleEvent(EVENT_QUAKE_2, 62500, 0, PHASE_TRANSITION); events.ScheduleEvent(EVENT_PAIN_AND_SUFFERING, 6000, 0, PHASE_TRANSITION); events.ScheduleEvent(EVENT_SUMMON_ICE_SPHERE, 8000, 0, PHASE_TRANSITION); @@ -963,7 +964,7 @@ class boss_the_lich_king : public CreatureScript events.ScheduleEvent(EVENT_NECROTIC_PLAGUE, urand(30000, 33000), 0, PHASE_ONE); break; case EVENT_SHADOW_TRAP: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, NonTankTargetSelector(me))) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, SpellTargetSelector(me, SPELL_SHADOW_TRAP))) DoCast(target, SPELL_SHADOW_TRAP); events.ScheduleEvent(EVENT_SHADOW_TRAP, 15500, 0, PHASE_ONE); break; @@ -972,7 +973,7 @@ class boss_the_lich_king : public CreatureScript events.ScheduleEvent(EVENT_SOUL_REAPER, urand(33000, 35000), 0, PHASE_TWO_THREE); break; case EVENT_DEFILE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, true, -SPELL_HARVEST_SOUL_VALKYR)) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, true, -SPELL_HARVEST_SOUL_VALKYR)) { Talk(EMOTE_DEFILE_WARNING); DoCast(target, SPELL_DEFILE); @@ -1059,7 +1060,6 @@ class boss_the_lich_king : public CreatureScript summon->RemoveAurasDueToSpell(SPELL_VILE_SPIRIT_MOVE_SEARCH); summon->RemoveAurasDueToSpell(SPELL_VILE_SPIRIT_DAMAGE_SEARCH); summon->GetMotionMaster()->MoveTargetedHome(); - summon->GetMotionMaster()->MoveRandom(10.0f); } else if (summon->GetEntry() == NPC_RAGING_SPIRIT) summon->AI()->DoAction(ACTION_DISABLE_RAGING); @@ -1076,6 +1076,7 @@ class boss_the_lich_king : public CreatureScript { triggers.sort(Trinity::ObjectDistanceOrderPred(terenas, true)); Creature* spawner = triggers.front(); + spawner->setActive(true); spawner->CastSpell(spawner, SPELL_SUMMON_SPIRIT_BOMB_1, true); // summons bombs randomly spawner->CastSpell(spawner, SPELL_SUMMON_SPIRIT_BOMB_2, true); // summons bombs on players spawner->m_Events.AddEvent(new TriggerWickedSpirit(spawner), spawner->m_Events.CalculateTime(3000)); @@ -1119,11 +1120,11 @@ class boss_the_lich_king : public CreatureScript Talk(SAY_LK_OUTRO_6); if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HIGHLORD_TIRION_FORDRING))) tirion->SetFacingToObject(me); - me->CastSpell((Unit*)nullptr, SPELL_SUMMON_BROKEN_FROSTMOURNE_3, TRIGGERED_IGNORE_CAST_IN_PROGRESS); + me->CastSpell(nullptr, SPELL_SUMMON_BROKEN_FROSTMOURNE_3, TRIGGERED_IGNORE_CAST_IN_PROGRESS); SetEquipmentSlots(false, EQUIP_UNEQUIP); break; case EVENT_OUTRO_SOUL_BARRAGE: - me->CastSpell((Unit*)nullptr, SPELL_SOUL_BARRAGE, TRIGGERED_IGNORE_CAST_IN_PROGRESS); + me->CastSpell(nullptr, SPELL_SOUL_BARRAGE, TRIGGERED_IGNORE_CAST_IN_PROGRESS); sCreatureTextMgr->SendSound(me, SOUND_PAIN, CHAT_MSG_MONSTER_YELL, 0, TEXT_RANGE_NORMAL, TEAM_OTHER, false); // set flight me->SetDisableGravity(true); @@ -1693,7 +1694,7 @@ class npc_strangulate_vehicle : public CreatureScript { if (Unit* summoner = summ->GetSummoner()) { - summoner->CastSpell((Unit*)nullptr, SPELL_HARVEST_SOUL_VISUAL, true); + summoner->CastSpell(nullptr, SPELL_HARVEST_SOUL_VISUAL, true); summoner->ExitVehicle(summoner); if (!IsHeroic()) summoner->CastSpell(summoner, SPELL_HARVEST_SOUL_TELEPORT, true); @@ -1775,7 +1776,7 @@ class npc_terenas_menethil : public CreatureScript } break; case ACTION_TELEPORT_BACK: - me->CastSpell((Unit*)nullptr, SPELL_RESTORE_SOUL, TRIGGERED_NONE); + me->CastSpell(nullptr, SPELL_RESTORE_SOUL, TRIGGERED_NONE); me->DespawnOrUnsummon(3000); break; default: @@ -1805,7 +1806,7 @@ class npc_terenas_menethil : public CreatureScript _events.ScheduleEvent(EVENT_TELEPORT_BACK, 1000); if (Creature* warden = me->FindNearestCreature(NPC_SPIRIT_WARDEN, 20.0f)) { - warden->CastSpell((Unit*)nullptr, SPELL_DESTROY_SOUL, TRIGGERED_NONE); + warden->CastSpell(nullptr, SPELL_DESTROY_SOUL, TRIGGERED_NONE); warden->DespawnOrUnsummon(2000); } @@ -1863,7 +1864,7 @@ class npc_terenas_menethil : public CreatureScript case EVENT_DESTROY_SOUL: me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); if (Creature* warden = me->FindNearestCreature(NPC_SPIRIT_WARDEN, 20.0f)) - warden->CastSpell((Unit*)nullptr, SPELL_DESTROY_SOUL, TRIGGERED_NONE); + warden->CastSpell(nullptr, SPELL_DESTROY_SOUL, TRIGGERED_NONE); DoCast(SPELL_TERENAS_LOSES_INSIDE); _events.ScheduleEvent(EVENT_TELEPORT_BACK, 1000); break; @@ -1976,20 +1977,29 @@ class npc_spirit_bomb : public CreatureScript if (type != POINT_MOTION_TYPE || point != POINT_GROUND) return; - me->RemoveAllAuras(); - DoCastAOE(SPELL_EXPLOSION); - me->DespawnOrUnsummon(1000); + _events.ScheduleEvent(EVENT_BOMB_EXPLOSION, 3000); } void AttackStart(Unit* /*victim*/) override { } - void UpdateAI(uint32 /*diff*/) override + void UpdateAI(uint32 diff) override { UpdateVictim(); - // no melee attacks + + _events.Update(diff); + + if (_events.ExecuteEvent() == EVENT_BOMB_EXPLOSION) + { + me->RemoveAllAuras(); + DoCastAOE(SPELL_EXPLOSION); + me->DespawnOrUnsummon(1000); + } } + + private: + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override @@ -2318,7 +2328,7 @@ class spell_the_lich_king_shadow_trap_periodic : public SpellScriptLoader if (targets.empty()) return; - GetCaster()->CastSpell((Unit*)nullptr, SPELL_SHADOW_TRAP_KNOCKBACK, true); + GetCaster()->CastSpell(nullptr, SPELL_SHADOW_TRAP_KNOCKBACK, true); } void Register() override @@ -2511,8 +2521,9 @@ class spell_the_lich_king_summon_into_air : public SpellScriptLoader // spirit bombs get higher if (GetSpellInfo()->Effects[effIndex].MiscValue == NPC_SPIRIT_BOMB) { - dest->RelocateOffset(offset); - GetHitDest()->RelocateOffset(offset); + static Position const offsetExtra = { 0.0f, 0.0f, 5.0f, 0.0f }; + dest->RelocateOffset(offsetExtra); + GetHitDest()->RelocateOffset(offsetExtra); } } @@ -2700,7 +2711,7 @@ class spell_the_lich_king_vile_spirits : public SpellScriptLoader void OnPeriodic(AuraEffect const* aurEff) { if (_is25Man || ((aurEff->GetTickNumber() - 1) % 5)) - GetTarget()->CastSpell((Unit*)nullptr, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true, nullptr, aurEff, GetCasterGUID()); + GetTarget()->CastSpell(nullptr, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true, nullptr, aurEff, GetCasterGUID()); } void Register() override @@ -2823,7 +2834,7 @@ class spell_the_lich_king_vile_spirit_damage_target_search : public SpellScriptL if (TempSummon* summon = GetCaster()->ToTempSummon()) if (Unit* summoner = summon->GetSummoner()) summoner->GetAI()->SetData(DATA_VILE, 1); - GetCaster()->CastSpell((Unit*)nullptr, SPELL_SPIRIT_BURST, true); + GetCaster()->CastSpell(nullptr, SPELL_SPIRIT_BURST, true); GetCaster()->ToCreature()->DespawnOrUnsummon(3000); GetCaster()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } @@ -2858,7 +2869,7 @@ class spell_the_lich_king_harvest_soul : public SpellScriptLoader { // m_originalCaster to allow stacking from different casters, meh if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH) - GetTarget()->CastSpell((Unit*)nullptr, SPELL_HARVESTED_SOUL, true, nullptr, nullptr, GetTarget()->GetInstanceScript()->GetGuidData(DATA_THE_LICH_KING)); + GetTarget()->CastSpell(nullptr, SPELL_HARVESTED_SOUL, true, nullptr, nullptr, GetTarget()->GetInstanceScript()->GetGuidData(DATA_THE_LICH_KING)); } void Register() override @@ -3054,7 +3065,7 @@ class spell_the_lich_king_in_frostmourne_room : public SpellScriptLoader { // m_originalCaster to allow stacking from different casters, meh if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH) - GetTarget()->CastSpell((Unit*)nullptr, SPELL_HARVESTED_SOUL, true, nullptr, nullptr, GetTarget()->GetInstanceScript()->GetGuidData(DATA_THE_LICH_KING)); + GetTarget()->CastSpell(nullptr, SPELL_HARVESTED_SOUL, true, nullptr, nullptr, GetTarget()->GetInstanceScript()->GetGuidData(DATA_THE_LICH_KING)); } void Register() override @@ -3081,7 +3092,7 @@ class spell_the_lich_king_summon_spirit_bomb : public SpellScriptLoader void HandleScript(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); - GetHitUnit()->CastSpell((Unit*)nullptr, uint32(GetEffectValue()), true); + GetHitUnit()->CastSpell(nullptr, uint32(GetEffectValue()), true); } void Register() override @@ -3139,7 +3150,7 @@ class spell_the_lich_king_jump : public SpellScriptLoader { PreventHitDefaultEffect(effIndex); GetHitUnit()->RemoveAurasDueToSpell(SPELL_RAISE_DEAD); - GetHitUnit()->CastSpell((Unit*)nullptr, SPELL_JUMP_2, true); + GetHitUnit()->CastSpell(nullptr, SPELL_JUMP_2, true); if (Creature* creature = GetHitCreature()) creature->AI()->DoAction(ACTION_BREAK_FROSTMOURNE); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp index 493b759e0f7..d2cc08dcc6e 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp @@ -264,7 +264,7 @@ class ValithriaDespawner : public BasicEvent creature->SetRespawnDelay(10); if (CreatureData const* data = creature->GetCreatureData()) - creature->UpdatePosition(data->posX, data->posY, data->posZ, data->orientation); + creature->UpdatePosition(data->spawnPoint); creature->DespawnOrUnsummon(); creature->SetCorpseDelay(corpseDelay); @@ -1467,7 +1467,7 @@ class spell_dreamwalker_twisted_nightmares : public SpellScriptLoader // return; if (InstanceScript* instance = GetHitUnit()->GetInstanceScript()) - GetHitUnit()->CastSpell((Unit*)nullptr, GetSpellInfo()->Effects[effIndex].TriggerSpell, true, nullptr, nullptr, instance->GetGuidData(DATA_VALITHRIA_DREAMWALKER)); + GetHitUnit()->CastSpell(nullptr, GetSpellInfo()->Effects[effIndex].TriggerSpell, true, nullptr, nullptr, instance->GetGuidData(DATA_VALITHRIA_DREAMWALKER)); } void Register() override diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index ed85ee3fa17..59a95ae0b9c 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -354,7 +354,7 @@ class FrostwingGauntletRespawner creature->SetRespawnDelay(2); if (CreatureData const* data = creature->GetCreatureData()) - creature->UpdatePosition(data->posX, data->posY, data->posZ, data->orientation); + creature->UpdatePosition(data->spawnPoint); creature->DespawnOrUnsummon(); creature->SetCorpseDelay(corpseDelay); @@ -955,9 +955,9 @@ class npc_crok_scourgebane : public CreatureScript public: npc_crok_scourgebane() : CreatureScript("npc_crok_scourgebane") { } - struct npc_crok_scourgebaneAI : public npc_escortAI + struct npc_crok_scourgebaneAI : public EscortAI { - npc_crok_scourgebaneAI(Creature* creature) : npc_escortAI(creature), + npc_crok_scourgebaneAI(Creature* creature) : EscortAI(creature), _instance(creature->GetInstanceScript()), _respawnTime(creature->GetRespawnDelay()), _corpseDelay(creature->GetCorpseDelay()) { @@ -1036,7 +1036,7 @@ class npc_crok_scourgebane : public CreatureScript } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -1067,7 +1067,7 @@ class npc_crok_scourgebane : public CreatureScript } } - void WaypointStart(uint32 waypointId) override + void WaypointStarted(uint32 waypointId, uint32 /*pathId*/) override { _currentWPid = waypointId; switch (waypointId) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h index e40ba687312..a9ea390e834 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h @@ -120,7 +120,9 @@ enum ICDataTypes DATA_TERENAS_MENETHIL = 39, DATA_ENEMY_GUNSHIP = 40, DATA_UPPERSPIRE_TELE_ACT = 41, /// also used by conditions - DATA_BLOOD_QUEEN_LANA_THEL_COUNCIL = 42 + DATA_BLOOD_QUEEN_LANA_THEL_COUNCIL = 42, + DATA_BLOOD_PRINCE_COUNCIL_INTRO = 43, + DATA_SINDRAGOSA_INTRO = 44 }; enum ICCreaturesIds diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index ffc899b4d0d..c986bb25c6b 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -147,6 +147,8 @@ class instance_icecrown_citadel : public InstanceMapScript UpperSpireTeleporterActiveState = NOT_STARTED; BloodQuickeningState = NOT_STARTED; BloodQuickeningMinutes = 0; + BloodPrinceIntro = 1; + SindragosaIntro = 1; } // A function to help reduce the number of lines for teleporter management. @@ -392,13 +394,13 @@ class instance_icecrown_citadel : public InstanceMapScript break; case NPC_ZAFOD_BOOMBOX: if (GameObjectTemplate const* go = sObjectMgr->GetGameObjectTemplate(GO_THE_SKYBREAKER_A)) - if ((TeamInInstance == ALLIANCE && data->mapid == go->moTransport.mapID) || - (TeamInInstance == HORDE && data->mapid != go->moTransport.mapID)) + if ((TeamInInstance == ALLIANCE && data->spawnPoint.GetMapId() == go->moTransport.mapID) || + (TeamInInstance == HORDE && data->spawnPoint.GetMapId() != go->moTransport.mapID)) return entry; return 0; case NPC_IGB_MURADIN_BRONZEBEARD: - if ((TeamInInstance == ALLIANCE && data->posX > 10.0f) || - (TeamInInstance == HORDE && data->posX < 10.0f)) + if ((TeamInInstance == ALLIANCE && data->spawnPoint.GetPositionX() > 10.0f) || + (TeamInInstance == HORDE && data->spawnPoint.GetPositionX() < 10.0f)) return entry; return 0; default: @@ -739,6 +741,10 @@ class instance_icecrown_citadel : public InstanceMapScript return BloodQuickeningState; case DATA_HEROIC_ATTEMPTS: return HeroicAttempts; + case DATA_BLOOD_PRINCE_COUNCIL_INTRO: + return BloodPrinceIntro; + case DATA_SINDRAGOSA_INTRO: + return SindragosaIntro; default: break; } @@ -1116,6 +1122,12 @@ class instance_icecrown_citadel : public InstanceMapScript SaveToDB(); } break; + case DATA_BLOOD_PRINCE_COUNCIL_INTRO: + BloodPrinceIntro = data; + break; + case DATA_SINDRAGOSA_INTRO: + SindragosaIntro = data; + break; default: break; } @@ -1308,7 +1320,7 @@ class instance_icecrown_citadel : public InstanceMapScript return; stalkers.sort(Trinity::ObjectDistanceOrderPred(teleporter)); - stalkers.front()->CastSpell((Unit*)nullptr, SPELL_ARTHAS_TELEPORTER_CEREMONY, false); + stalkers.front()->CastSpell(nullptr, SPELL_ARTHAS_TELEPORTER_CEREMONY, false); stalkers.pop_front(); for (std::list<Creature*>::iterator itr = stalkers.begin(); itr != stalkers.end(); ++itr) (*itr)->AI()->Reset(); @@ -1530,6 +1542,8 @@ class instance_icecrown_citadel : public InstanceMapScript uint32 BloodQuickeningState; uint32 HeroicAttempts; uint16 BloodQuickeningMinutes; + uint8 BloodPrinceIntro; + uint8 SindragosaIntro; bool IsBonedEligible; bool IsOozeDanceEligible; bool IsNauseaEligible; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp index 5454ff7c848..8a481bff9ce 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp @@ -96,7 +96,7 @@ public: void InitializeAI() override { - if (!me->isDead()) + if (!me->isDead() && instance->GetBossState(BOSS_ANUBREKHAN) != DONE) { Reset(); SummonGuards(); @@ -239,20 +239,19 @@ public: }; -class at_anubrekhan_entrance : public AreaTriggerScript +class at_anubrekhan_entrance : public OnlyOnceAreaTriggerScript { public: - at_anubrekhan_entrance() : AreaTriggerScript("at_anubrekhan_entrance") { } + at_anubrekhan_entrance() : OnlyOnceAreaTriggerScript("at_anubrekhan_entrance") { } - bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + bool _OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { InstanceScript* instance = player->GetInstanceScript(); - if (!instance || instance->GetData(DATA_HAD_ANUBREKHAN_GREET) || instance->GetBossState(BOSS_ANUBREKHAN) != NOT_STARTED) + if (!instance || instance->GetBossState(BOSS_ANUBREKHAN) != NOT_STARTED) return true; if (Creature* anub = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_ANUBREKHAN))) anub->AI()->Talk(SAY_GREET); - instance->SetData(DATA_HAD_ANUBREKHAN_GREET, 1u); return true; } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp index da3d95a5173..9b7df0d4f40 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp @@ -83,7 +83,7 @@ class boss_faerlina : public CreatureScript void InitializeAI() override { - if (!me->isDead()) + if (!me->isDead() && instance->GetBossState(BOSS_FAERLINA) != DONE) { Reset(); SummonAdds(); @@ -260,20 +260,19 @@ class achievement_momma_said_knock_you_out : public AchievementCriteriaScript } }; -class at_faerlina_entrance : public AreaTriggerScript +class at_faerlina_entrance : public OnlyOnceAreaTriggerScript { public: - at_faerlina_entrance() : AreaTriggerScript("at_faerlina_entrance") { } + at_faerlina_entrance() : OnlyOnceAreaTriggerScript("at_faerlina_entrance") { } - bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + bool _OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { InstanceScript* instance = player->GetInstanceScript(); - if (!instance || instance->GetData(DATA_HAD_FAERLINA_GREET) || instance->GetBossState(BOSS_FAERLINA) != NOT_STARTED) + if (!instance || instance->GetBossState(BOSS_FAERLINA) != NOT_STARTED) return true; if (Creature* faerlina = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_FAERLINA))) faerlina->AI()->Talk(SAY_GREET); - instance->SetData(DATA_HAD_FAERLINA_GREET, 1u); return true; } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp index 0fb00d18fd3..51deb5b4375 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp @@ -265,14 +265,9 @@ struct boss_four_horsemen_baseAI : public BossAI for (Horseman boss : horsemen) { if (Creature* cBoss = getHorsemanHandle(boss)) - { - cBoss->DespawnOrUnsummon(); - cBoss->SetRespawnTime(15); - } + cBoss->DespawnOrUnsummon(0, Seconds(15)); else - { TC_LOG_WARN("scripts", "FourHorsemenAI: Encounter resetting but horseman with id %u is not present", uint32(boss)); - } } } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp index add15715165..75e1a5b2386 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp @@ -123,7 +123,7 @@ public: events.ScheduleEvent(EVENT_DISRUPT, randtime(Seconds(15), Seconds(20)), 0, PHASE_FIGHT); events.ScheduleEvent(EVENT_FEVER, randtime(Seconds(10), Seconds(20)), 0, PHASE_FIGHT); events.ScheduleEvent(EVENT_DANCE, Minutes(1) + Seconds(30), 0, PHASE_FIGHT); - events.ScheduleEvent(EVENT_ERUPT, Seconds(15), 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_ERUPT, Seconds(15)); _safetyDance = true; @@ -172,7 +172,7 @@ public: DoCast(SPELL_TELEPORT_SELF); DoCastAOE(SPELL_PLAGUE_CLOUD); events.ScheduleEvent(EVENT_DANCE_END, Seconds(45), 0, PHASE_DANCE); - events.ScheduleEvent(EVENT_ERUPT, Seconds(10)); + events.RescheduleEvent(EVENT_ERUPT, Seconds(10)); break; case EVENT_DANCE_END: events.SetPhase(PHASE_FIGHT); @@ -181,7 +181,7 @@ public: events.ScheduleEvent(EVENT_DISRUPT, randtime(Seconds(10), Seconds(25)), 0, PHASE_FIGHT); events.ScheduleEvent(EVENT_FEVER, randtime(Seconds(15), Seconds(20)), 0, PHASE_FIGHT); events.ScheduleEvent(EVENT_DANCE, Minutes(1) + Seconds(30), 0, PHASE_FIGHT); - events.ScheduleEvent(EVENT_ERUPT, Seconds(15), 0, PHASE_FIGHT); + events.RescheduleEvent(EVENT_ERUPT, Seconds(15)); me->CastStop(); me->SetReactState(REACT_AGGRESSIVE); DoZoneInCombat(); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp index 46ab982c8a9..adce3c76070 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp @@ -127,29 +127,29 @@ public: auto list = mgr.GetModifiableThreatList(); auto it = list.begin(), end = list.end(); if (it == end) + { EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + return; + } if ((*it)->GetVictim() != currentVictim) secondThreat = *it; - if ((!secondThreat || Is25ManRaid()) && (++it != end)) + if ((!secondThreat || Is25ManRaid()) && (++it != end && (*it)->IsAvailable())) { if ((*it)->GetVictim() != currentVictim) (secondThreat ? thirdThreat : secondThreat) = *it; - if (!thirdThreat && Is25ManRaid() && (++it != end)) + if (!thirdThreat && Is25ManRaid() && (++it != end && (*it)->IsAvailable())) thirdThreat = *it; } Unit* pHatefulTarget = nullptr; - if (!thirdThreat) + if (!secondThreat) + pHatefulTarget = currentVictim; + else if (!thirdThreat) pHatefulTarget = secondThreat->GetVictim(); - else if (secondThreat) + else pHatefulTarget = (secondThreat->GetVictim()->GetHealth() < thirdThreat->GetVictim()->GetHealth()) ? thirdThreat->GetVictim() : secondThreat->GetVictim(); - if (!pHatefulTarget) - pHatefulTarget = me->GetVictim(); - - DoCast(pHatefulTarget, SPELL_HATEFUL_STRIKE, true); - // add threat to highest threat targets AddThreat(currentVictim, HATEFUL_THREAT_AMT); if (secondThreat) @@ -157,6 +157,8 @@ public: if (thirdThreat) thirdThreat->AddThreat(HATEFUL_THREAT_AMT); + DoCast(pHatefulTarget, SPELL_HATEFUL_STRIKE, true); + events.Repeat(Seconds(1)); break; } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp index 58da5a92295..ac27c44d1de 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp @@ -79,7 +79,7 @@ public: void InitializeAI() override { - if (!me->isDead()) + if (!me->isDead() && instance->GetBossState(BOSS_RAZUVIOUS) != DONE) { Reset(); SummonAdds(); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp index 9f82e3ce4b3..1f279323591 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp @@ -31,15 +31,12 @@ enum Phases PHASE_NOT_ENGAGED = 1, PHASE_PETS, PHASE_TRANSITION, - PHASE_THADDIUS, - PHASE_RESETTING + PHASE_THADDIUS }; enum AIActions { - ACTION_RESET_ENCOUNTER_TIMER = -1, // sent from instance AI ACTION_BEGIN_RESET_ENCOUNTER = 0, // sent from thaddius to pets to trigger despawn and encounter reset - ACTION_RESET_ENCOUNTER, // sent from thaddius to pets to trigger respawn and full reset ACTION_FEUGEN_DIED, // sent from respective pet to thaddius to indicate death ACTION_STALAGG_DIED, // ^ ACTION_FEUGEN_RESET, // pet to thaddius @@ -160,321 +157,291 @@ enum ThaddiusSpells SPELL_NEGATIVE_CHARGE_AMP = 29660, }; -class boss_thaddius : public CreatureScript +struct boss_thaddius : public BossAI { -public: - boss_thaddius() : CreatureScript("boss_thaddius") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetNaxxramasAI<boss_thaddiusAI>(creature); - } - - struct boss_thaddiusAI : public BossAI - { - public: - boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningUnlocked(false), ballLightningEnabled(false), shockingEligibility(true) {} + public: + boss_thaddius(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningUnlocked(false), ballLightningEnabled(false), shockingEligibility(true) {} - void InitializeAI() override + void InitializeAI() override + { + if (instance->GetBossState(BOSS_THADDIUS) != DONE) { - if (instance->GetBossState(BOSS_THADDIUS) != DONE) - { - events.SetPhase(PHASE_NOT_ENGAGED); - SetCombatMovement(false); - - // initialize everything properly, and ensure that the coils are loaded by the time we initialize - BeginResetEncounter(true); - } + events.SetPhase(PHASE_NOT_ENGAGED); + SetCombatMovement(false); } + } - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - void Reset() override { } + void Reset() override { } - void EnterEvadeMode(EvadeReason why) override + void EnterEvadeMode(EvadeReason why) override + { + if (!ballLightningEnabled && why == EVADE_REASON_NO_HOSTILES) { - if (!ballLightningEnabled && why == EVADE_REASON_NO_HOSTILES) - { - ballLightningEnabled = true; - return; // try again - } - if (events.IsInPhase(PHASE_TRANSITION) || (events.IsInPhase(PHASE_THADDIUS) && me->IsAlive())) - BeginResetEncounter(); + ballLightningEnabled = true; + return; // try again } + if (events.IsInPhase(PHASE_TRANSITION) || (events.IsInPhase(PHASE_THADDIUS) && me->IsAlive())) + BeginResetEncounter(); + } - bool CanAIAttack(Unit const* who) const override - { - if (ballLightningEnabled || me->IsWithinMeleeRange(who)) - return BossAI::CanAIAttack(who); - else - return false; - } + bool CanAIAttack(Unit const* who) const override + { + if (ballLightningEnabled || me->IsWithinMeleeRange(who)) + return BossAI::CanAIAttack(who); + else + return false; + } - void JustRespawned() override - { - if (events.IsInPhase(PHASE_RESETTING)) - ResetEncounter(); - } + void JustAppeared() override + { + if (instance->GetBossState(BOSS_THADDIUS) != DONE) + ResetEncounter(); + } - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - me->setActive(false); - if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - stalagg->setActive(false); - if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - feugen->setActive(false); - Talk(SAY_DEATH); - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + me->setActive(false); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->setActive(false); + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->setActive(false); + Talk(SAY_DEATH); + } - void DoAction(int32 action) override + void DoAction(int32 action) override + { + switch (action) { - switch (action) - { - case ACTION_RESET_ENCOUNTER_TIMER: - if (events.IsInPhase(PHASE_RESETTING)) - ResetEncounter(); - break; - case ACTION_FEUGEN_RESET: - case ACTION_STALAGG_RESET: - if (!events.IsInPhase(PHASE_NOT_ENGAGED) && !events.IsInPhase(PHASE_RESETTING)) - BeginResetEncounter(); - break; - case ACTION_FEUGEN_AGGRO: - case ACTION_STALAGG_AGGRO: - if (events.IsInPhase(PHASE_RESETTING)) - { - BeginResetEncounter(); - return; - } - if (!events.IsInPhase(PHASE_NOT_ENGAGED)) - return; - events.SetPhase(PHASE_PETS); - - shockingEligibility = true; - - if (!instance->CheckRequiredBosses(BOSS_THADDIUS)) - { - BeginResetEncounter(); - return; - } - instance->SetBossState(BOSS_THADDIUS, IN_PROGRESS); + case ACTION_FEUGEN_RESET: + case ACTION_STALAGG_RESET: + if (!events.IsInPhase(PHASE_NOT_ENGAGED)) + BeginResetEncounter(); + break; + case ACTION_FEUGEN_AGGRO: + case ACTION_STALAGG_AGGRO: + if (!events.IsInPhase(PHASE_NOT_ENGAGED)) + return; + events.SetPhase(PHASE_PETS); - me->setActive(true); - DoZoneInCombat(); - if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - stalagg->setActive(true); - if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - feugen->setActive(true); - break; - case ACTION_FEUGEN_DIED: - if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - feugen->AI()->DoAction(ACTION_FEUGEN_REVIVING_FX); - feugenAlive = false; - if (stalaggAlive) - events.ScheduleEvent(EVENT_REVIVE_FEUGEN, Seconds(5), 0, PHASE_PETS); - else - Transition(); + shockingEligibility = true; - break; - case ACTION_STALAGG_DIED: - if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - stalagg->AI()->DoAction(ACTION_STALAGG_REVIVING_FX); - stalaggAlive = false; - if (feugenAlive) - events.ScheduleEvent(EVENT_REVIVE_STALAGG, Seconds(5), 0, PHASE_PETS); - else - Transition(); - - break; + if (!instance->CheckRequiredBosses(BOSS_THADDIUS)) + { + BeginResetEncounter(); + return; + } + instance->SetBossState(BOSS_THADDIUS, IN_PROGRESS); + + me->setActive(true); + DoZoneInCombat(); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->setActive(true); + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->setActive(true); + break; + case ACTION_FEUGEN_DIED: + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_FEUGEN_REVIVING_FX); + feugenAlive = false; + if (stalaggAlive) + events.ScheduleEvent(EVENT_REVIVE_FEUGEN, Seconds(5), 0, PHASE_PETS); + else + Transition(); + + break; + case ACTION_STALAGG_DIED: + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_STALAGG_REVIVING_FX); + stalaggAlive = false; + if (feugenAlive) + events.ScheduleEvent(EVENT_REVIVE_STALAGG, Seconds(5), 0, PHASE_PETS); + else + Transition(); - case ACTION_POLARITY_CROSSED: - shockingEligibility = false; - break; - default: - break; - } - } + break; - uint32 GetData(uint32 id) const override - { - return (id == DATA_POLARITY_CROSSED && shockingEligibility) ? 1u : 0u; + case ACTION_POLARITY_CROSSED: + shockingEligibility = false; + break; + default: + break; } + } - void Transition() // initiate transition between pet phase and thaddius phase - { - events.SetPhase(PHASE_TRANSITION); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - - events.ScheduleEvent(EVENT_TRANSITION_1, Seconds(10), 0, PHASE_TRANSITION); - events.ScheduleEvent(EVENT_TRANSITION_2, Seconds(12), 0, PHASE_TRANSITION); - events.ScheduleEvent(EVENT_TRANSITION_3, Seconds(14), 0, PHASE_TRANSITION); - } + uint32 GetData(uint32 id) const override + { + return (id == DATA_POLARITY_CROSSED && shockingEligibility) ? 1u : 0u; + } - void BeginResetEncounter(bool initial = false) - { - if (instance->GetBossState(BOSS_THADDIUS) == DONE) - return; - if (events.IsInPhase(PHASE_RESETTING)) - return; + void Transition() // initiate transition between pet phase and thaddius phase + { + events.SetPhase(PHASE_TRANSITION); - // remove polarity shift debuffs on reset - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY); - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->DespawnOrUnsummon(); - me->SetRespawnTime(initial ? 5 : 30); + events.ScheduleEvent(EVENT_TRANSITION_1, Seconds(10), 0, PHASE_TRANSITION); + events.ScheduleEvent(EVENT_TRANSITION_2, Seconds(12), 0, PHASE_TRANSITION); + events.ScheduleEvent(EVENT_TRANSITION_3, Seconds(14), 0, PHASE_TRANSITION); + } - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); - me->SetImmuneToPC(true); - events.SetPhase(PHASE_RESETTING); - if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - feugen->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER); - if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - stalagg->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER); + void BeginResetEncounter() + { + if (instance->GetBossState(BOSS_THADDIUS) == DONE) + return; + + // remove polarity shift debuffs on reset + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY); + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY); + + me->DespawnOrUnsummon(0, Seconds(30)); + + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED); + me->SetImmuneToPC(true); + me->setActive(false); + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER); + } - me->setActive(false); - } + void ResetEncounter() + { + feugenAlive = true; + stalaggAlive = true; - void ResetEncounter() - { - feugenAlive = true; - stalaggAlive = true; + _Reset(); + events.SetPhase(PHASE_NOT_ENGAGED); + me->SetReactState(REACT_PASSIVE); - _Reset(); - events.SetPhase(PHASE_NOT_ENGAGED); - me->SetReactState(REACT_PASSIVE); + // @todo these guys should really be moved to a summon group - this is merely a hack to make them work in dynamic_spawning + instance->instance->RemoveRespawnTime(SPAWN_TYPE_CREATURE, 130958, true); // Stalagg + instance->instance->RemoveRespawnTime(SPAWN_TYPE_CREATURE, 130959, true); // Feugen + } - if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - feugen->AI()->DoAction(ACTION_RESET_ENCOUNTER); - if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - stalagg->AI()->DoAction(ACTION_RESET_ENCOUNTER); - } + void UpdateAI(uint32 diff) override + { + if (events.IsInPhase(PHASE_NOT_ENGAGED)) + return; + if (events.IsInPhase(PHASE_THADDIUS) && !UpdateVictim()) + return; - void UpdateAI(uint32 diff) override + events.Update(diff); + while (uint32 eventId = events.ExecuteEvent()) { - if (events.IsInPhase(PHASE_NOT_ENGAGED)) - return; - if (events.IsInPhase(PHASE_THADDIUS) && !UpdateVictim()) - return; - - events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + switch (eventId) { - switch (eventId) - { - case EVENT_REVIVE_FEUGEN: - feugenAlive = true; - if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - feugen->AI()->DoAction(ACTION_FEUGEN_REVIVED); - break; - case EVENT_REVIVE_STALAGG: - stalaggAlive = true; - if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - stalagg->AI()->DoAction(ACTION_STALAGG_REVIVED); - break; - case EVENT_TRANSITION_1: // tesla coils overload - if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - feugen->AI()->DoAction(ACTION_TRANSITION); - if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - stalagg->AI()->DoAction(ACTION_TRANSITION); - break; - case EVENT_TRANSITION_2: // tesla coils shock thaddius - me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true); - if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - feugen->AI()->DoAction(ACTION_TRANSITION_2); - if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - stalagg->AI()->DoAction(ACTION_TRANSITION_2); - break; - case EVENT_TRANSITION_3: // thaddius becomes active - me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true); - ballLightningUnlocked = false; - me->RemoveAura(SPELL_THADDIUS_INACTIVE_VISUAL); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - me->SetImmuneToPC(false); - me->SetReactState(REACT_AGGRESSIVE); - - DoZoneInCombat(); - if (Unit* closest = SelectTarget(SELECT_TARGET_MINDISTANCE, 0, 500.0f)) - AttackStart(closest); - else // if there is no nearest target, then there is no target, meaning we should reset - { - BeginResetEncounter(); - return; - } + case EVENT_REVIVE_FEUGEN: + feugenAlive = true; + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_FEUGEN_REVIVED); + break; + case EVENT_REVIVE_STALAGG: + stalaggAlive = true; + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_STALAGG_REVIVED); + break; + case EVENT_TRANSITION_1: // tesla coils overload + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_TRANSITION); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_TRANSITION); + break; + case EVENT_TRANSITION_2: // tesla coils shock thaddius + me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true); + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_TRANSITION_2); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_TRANSITION_2); + break; + case EVENT_TRANSITION_3: // thaddius becomes active + me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true); + ballLightningUnlocked = false; + me->RemoveAura(SPELL_THADDIUS_INACTIVE_VISUAL); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); + me->SetImmuneToPC(false); + me->SetReactState(REACT_AGGRESSIVE); - if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) - feugen->AI()->DoAction(ACTION_TRANSITION_3); - if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) - stalagg->AI()->DoAction(ACTION_TRANSITION_3); + DoZoneInCombat(); + if (Unit* closest = SelectTarget(SELECT_TARGET_MINDISTANCE, 0, 500.0f)) + AttackStart(closest); + else // if there is no nearest target, then there is no target, meaning we should reset + { + BeginResetEncounter(); + return; + } - events.SetPhase(PHASE_THADDIUS); + if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN))) + feugen->AI()->DoAction(ACTION_TRANSITION_3); + if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG))) + stalagg->AI()->DoAction(ACTION_TRANSITION_3); - Talk(SAY_AGGRO); + events.SetPhase(PHASE_THADDIUS); - events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, Seconds(5), 0, PHASE_THADDIUS); - events.ScheduleEvent(EVENT_SHIFT, Seconds(10), 0, PHASE_THADDIUS); - events.ScheduleEvent(EVENT_CHAIN, randtime(Seconds(10), Seconds(20)), 0, PHASE_THADDIUS); - events.ScheduleEvent(EVENT_BERSERK, Minutes(6), 0, PHASE_THADDIUS); + Talk(SAY_AGGRO); - break; - case EVENT_ENABLE_BALL_LIGHTNING: - ballLightningUnlocked = true; - break; - case EVENT_SHIFT: - me->CastStop(); // shift overrides all other spells - DoCastAOE(SPELL_POLARITY_SHIFT); - events.ScheduleEvent(EVENT_SHIFT_TALK, Seconds(3), PHASE_THADDIUS); - events.ScheduleEvent(EVENT_SHIFT, Seconds(30), PHASE_THADDIUS); - break; - case EVENT_SHIFT_TALK: - Talk(SAY_ELECT); - Talk(EMOTE_POLARITY_SHIFTED); - break; - case EVENT_CHAIN: - if (me->FindCurrentSpellBySpellId(SPELL_POLARITY_SHIFT)) // delay until shift is over - events.Repeat(Seconds(3)); - else - { - me->CastStop(); - DoCastVictim(SPELL_CHAIN_LIGHTNING); - events.Repeat(randtime(Seconds(10), Seconds(20))); - } - break; - case EVENT_BERSERK: + events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, Seconds(5), 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_SHIFT, Seconds(10), 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_CHAIN, randtime(Seconds(10), Seconds(20)), 0, PHASE_THADDIUS); + events.ScheduleEvent(EVENT_BERSERK, Minutes(6), 0, PHASE_THADDIUS); + + break; + case EVENT_ENABLE_BALL_LIGHTNING: + ballLightningUnlocked = true; + break; + case EVENT_SHIFT: + me->CastStop(); // shift overrides all other spells + DoCastAOE(SPELL_POLARITY_SHIFT); + events.ScheduleEvent(EVENT_SHIFT_TALK, Seconds(3), PHASE_THADDIUS); + events.ScheduleEvent(EVENT_SHIFT, Seconds(30), PHASE_THADDIUS); + break; + case EVENT_SHIFT_TALK: + Talk(SAY_ELECT); + Talk(EMOTE_POLARITY_SHIFTED); + break; + case EVENT_CHAIN: + if (me->FindCurrentSpellBySpellId(SPELL_POLARITY_SHIFT)) // delay until shift is over + events.Repeat(Seconds(3)); + else + { me->CastStop(); - DoCast(me, SPELL_BERSERK); - break; - default: - break; - } + DoCastVictim(SPELL_CHAIN_LIGHTNING); + events.Repeat(randtime(Seconds(10), Seconds(20))); + } + break; + case EVENT_BERSERK: + me->CastStop(); + DoCast(me, SPELL_BERSERK); + break; + default: + break; } - if (events.IsInPhase(PHASE_THADDIUS) && !me->HasUnitState(UNIT_STATE_CASTING) && me->isAttackReady()) + } + if (events.IsInPhase(PHASE_THADDIUS) && !me->HasUnitState(UNIT_STATE_CASTING) && me->isAttackReady()) + { + if (me->IsWithinMeleeRange(me->GetVictim())) { - if (me->IsWithinMeleeRange(me->GetVictim())) - { - ballLightningEnabled = false; - DoMeleeAttackIfReady(); - } - else if (ballLightningUnlocked) - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) - DoCast(target, SPELL_BALL_LIGHTNING); + ballLightningEnabled = false; + DoMeleeAttackIfReady(); } + else if (ballLightningUnlocked) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) + DoCast(target, SPELL_BALL_LIGHTNING); } + } - private: - bool stalaggAlive; - bool feugenAlive; - bool ballLightningUnlocked; // whether the initial ball lightning grace period has expired and we should proceed to exterminate with extreme prejudice - bool ballLightningEnabled; // switch that is flipped to true if we try to evade due to no eligible targets in melee range - bool shockingEligibility; - }; - + private: + bool stalaggAlive; + bool feugenAlive; + bool ballLightningUnlocked; // whether the initial ball lightning grace period has expired and we should proceed to exterminate with extreme prejudice + bool ballLightningEnabled; // switch that is flipped to true if we try to evade due to no eligible targets in melee range + bool shockingEligibility; }; class npc_stalagg : public CreatureScript @@ -527,7 +494,7 @@ public: { if (GameObject* coil = myCoilGO()) coil->SetGoState(GO_STATE_READY); - me->DespawnOrUnsummon(); + me->DespawnOrUnsummon(0, Hours(24*7)); // will be force respawned by thaddius me->setActive(false); } @@ -544,9 +511,6 @@ public: case ACTION_BEGIN_RESET_ENCOUNTER: BeginResetEncounter(); break; - case ACTION_RESET_ENCOUNTER: - ResetEncounter(); - break; case ACTION_STALAGG_REVIVING_FX: break; case ACTION_STALAGG_REVIVED: @@ -571,7 +535,7 @@ public: break; case ACTION_TRANSITION: me->KillSelf(); // true death - me->DespawnOrUnsummon(); + me->DespawnOrUnsummon(0, Hours(24*7)); if (Creature* coil = myCoil()) { @@ -793,16 +757,10 @@ public: { if (GameObject* coil = myCoilGO()) coil->SetGoState(GO_STATE_READY); - me->DespawnOrUnsummon(); + me->DespawnOrUnsummon(0, Hours(24*7)); // will be force respawned by thaddius me->setActive(false); } - void ResetEncounter() - { - me->Respawn(true); - Initialize(); - } - void DoAction(int32 action) override { switch (action) @@ -810,9 +768,6 @@ public: case ACTION_BEGIN_RESET_ENCOUNTER: BeginResetEncounter(); break; - case ACTION_RESET_ENCOUNTER: - ResetEncounter(); - break; case ACTION_FEUGEN_REVIVING_FX: break; case ACTION_FEUGEN_REVIVED: @@ -839,7 +794,7 @@ public: break; case ACTION_TRANSITION: me->KillSelf(); // true death this time around - me->DespawnOrUnsummon(); + me->DespawnOrUnsummon(0, Hours(24*7)); if (Creature* coil = myCoil()) { @@ -1270,20 +1225,19 @@ class spell_thaddius_magnetic_pull : public SpellScriptLoader } }; -class at_thaddius_entrance : public AreaTriggerScript +class at_thaddius_entrance : public OnlyOnceAreaTriggerScript { public: - at_thaddius_entrance() : AreaTriggerScript("at_thaddius_entrance") { } + at_thaddius_entrance() : OnlyOnceAreaTriggerScript("at_thaddius_entrance") { } - bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + bool _OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override { InstanceScript* instance = player->GetInstanceScript(); - if (!instance || instance->GetData(DATA_HAD_THADDIUS_GREET) || instance->GetBossState(BOSS_THADDIUS) == DONE) + if (!instance || instance->GetBossState(BOSS_THADDIUS) == DONE) return true; if (Creature* thaddius = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THADDIUS))) thaddius->AI()->Talk(SAY_GREET); - instance->SetData(DATA_HAD_THADDIUS_GREET, 1u); return true; } @@ -1303,7 +1257,7 @@ class achievement_thaddius_shocking : public AchievementCriteriaScript void AddSC_boss_thaddius() { - new boss_thaddius(); + RegisterNaxxramasCreatureAI(boss_thaddius); new npc_stalagg(); new npc_feugen(); new npc_tesla(); diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp index f8638a33fa0..3e06319a379 100644 --- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp +++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp @@ -121,9 +121,6 @@ class instance_naxxramas : public InstanceMapScript LoadDoorData(doorData); LoadObjectData(nullptr, objectData); - hadAnubRekhanGreet = false; - hadFaerlinaGreet = false; - hadThaddiusGreet = false; hadSapphironBirth = false; CurrentWingTaunt = SAY_KELTHUZAD_FIRST_WING_TAUNT; @@ -279,15 +276,6 @@ class instance_naxxramas : public InstanceMapScript if (GameObject* gate = instance->GetGameObject(GothikGateGUID)) gate->SetGoState(GOState(value)); break; - case DATA_HAD_ANUBREKHAN_GREET: - hadAnubRekhanGreet = (value == 1u); - break; - case DATA_HAD_FAERLINA_GREET: - hadFaerlinaGreet = (value == 1u); - break; - case DATA_HAD_THADDIUS_GREET: - hadThaddiusGreet = (value == 1u); - break; case DATA_HAD_SAPPHIRON_BIRTH: hadSapphironBirth = (value == 1u); break; @@ -300,12 +288,6 @@ class instance_naxxramas : public InstanceMapScript { switch (id) { - case DATA_HAD_ANUBREKHAN_GREET: - return hadAnubRekhanGreet ? 1u : 0u; - case DATA_HAD_FAERLINA_GREET: - return hadFaerlinaGreet ? 1u : 0u; - case DATA_HAD_THADDIUS_GREET: - return hadThaddiusGreet ? 1u : 0u; case DATA_HAD_SAPPHIRON_BIRTH: return hadSapphironBirth ? 1u : 0u; default: @@ -625,9 +607,6 @@ class instance_naxxramas : public InstanceMapScript ObjectGuid PortalsGUID[4]; ObjectGuid KelthuzadDoorGUID; ObjectGuid LichKingGUID; - bool hadAnubRekhanGreet; - bool hadFaerlinaGreet; - bool hadThaddiusGreet; bool hadSapphironBirth; uint8 CurrentWingTaunt; diff --git a/src/server/scripts/Northrend/Naxxramas/naxxramas.h b/src/server/scripts/Northrend/Naxxramas/naxxramas.h index 2813e338245..64e313fea59 100644 --- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h +++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h @@ -47,9 +47,6 @@ enum NAXEncounter enum NAXData { DATA_GOTHIK_GATE, - DATA_HAD_ANUBREKHAN_GREET, - DATA_HAD_FAERLINA_GREET, - DATA_HAD_THADDIUS_GREET, DATA_HAD_SAPPHIRON_BIRTH, DATA_HORSEMEN_CHECK_ACHIEVEMENT_CREDIT, @@ -224,5 +221,6 @@ inline AI* GetNaxxramasAI(T* obj) { return GetInstanceAI<AI>(obj, NaxxramasScriptName); } +#define RegisterNaxxramasCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetNaxxramasAI) #endif diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 8e51d79a87b..a7ea5b57fb0 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -2142,7 +2142,7 @@ class spell_alexstrasza_bunny_destroy_platform_event : public SpellScriptLoader void HandleScript(SpellEffIndex /*effIndex*/) { - GetCaster()->CastSpell((Unit*)nullptr, SPELL_SUMMON_RED_DRAGON_BUDDY_F_CAST); + GetCaster()->CastSpell(nullptr, SPELL_SUMMON_RED_DRAGON_BUDDY_F_CAST); } void Register() override @@ -2271,7 +2271,7 @@ class spell_malygos_surge_of_power_warning_selector_25 : public SpellScriptLoade void ExecuteMainSpell() { - GetCaster()->ToCreature()->CastSpell((Unit*)nullptr, SPELL_SURGE_OF_POWER_PHASE_3_25); + GetCaster()->ToCreature()->CastSpell(nullptr, SPELL_SURGE_OF_POWER_PHASE_3_25); } void Register() override diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_krystallus.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_krystallus.cpp index 338b273808f..a98fadd56d9 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_krystallus.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_krystallus.cpp @@ -156,7 +156,7 @@ class spell_krystallus_shatter : public SpellScriptLoader if (Unit* target = GetHitUnit()) { target->RemoveAurasDueToSpell(SPELL_STONED); - target->CastSpell((Unit*)nullptr, SPELL_SHATTER_EFFECT, true); + target->CastSpell(nullptr, SPELL_SHATTER_EFFECT, true); } } diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp index fc54e6fec54..80d59691fb5 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/halls_of_stone.cpp @@ -271,9 +271,9 @@ class npc_brann_hos : public CreatureScript public: npc_brann_hos() : CreatureScript("npc_brann_hos") { } - struct npc_brann_hosAI : public npc_escortAI + struct npc_brann_hosAI : public EscortAI { - npc_brann_hosAI(Creature* creature) : npc_escortAI(creature) + npc_brann_hosAI(Creature* creature) : EscortAI(creature) { Initialize(); instance = creature->GetInstanceScript(); @@ -326,7 +326,7 @@ public: lDwarfGUIDList.clear(); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index 69d5fa4d2fb..868e337933d 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -254,7 +254,7 @@ class ActivateLivingConstellation : public BasicEvent if (!_instance || _instance->GetBossState(BOSS_ALGALON) != IN_PROGRESS) return true; // delete event - _owner->CastSpell((Unit*)nullptr, SPELL_TRIGGER_3_ADDS, TRIGGERED_FULL_MASK); + _owner->CastSpell(nullptr, SPELL_TRIGGER_3_ADDS, TRIGGERED_FULL_MASK); _owner->m_Events.AddEvent(this, execTime + urand(45000, 50000)); return false; } @@ -273,7 +273,7 @@ class CosmicSmashDamageEvent : public BasicEvent bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override { - _caster->CastSpell((Unit*)nullptr, SPELL_COSMIC_SMASH_TRIGGERED, TRIGGERED_FULL_MASK); + _caster->CastSpell(nullptr, SPELL_COSMIC_SMASH_TRIGGERED, TRIGGERED_FULL_MASK); return true; } @@ -290,7 +290,7 @@ class SummonUnleashedDarkMatter : public BasicEvent bool Execute(uint64 execTime, uint32 /*diff*/) override { - _caster->CastSpell((Unit*)nullptr, SPELL_SUMMON_UNLEASHED_DARK_MATTER, TRIGGERED_FULL_MASK); + _caster->CastSpell(nullptr, SPELL_SUMMON_UNLEASHED_DARK_MATTER, TRIGGERED_FULL_MASK); _caster->m_Events.AddEvent(this, execTime + 30000); return false; } @@ -474,9 +474,9 @@ class boss_algalon_the_observer : public CreatureScript break; case NPC_BLACK_HOLE: summon->SetReactState(REACT_PASSIVE); - summon->CastSpell((Unit*)nullptr, SPELL_BLACK_HOLE_TRIGGER, TRIGGERED_FULL_MASK); + summon->CastSpell(nullptr, SPELL_BLACK_HOLE_TRIGGER, TRIGGERED_FULL_MASK); summon->CastSpell(summon, SPELL_CONSTELLATION_PHASE_TRIGGER, TRIGGERED_FULL_MASK); - summon->CastSpell((Unit*)nullptr, SPELL_BLACK_HOLE_EXPLOSION); + summon->CastSpell(nullptr, SPELL_BLACK_HOLE_EXPLOSION); summon->CastSpell(summon, SPELL_SUMMON_VOID_ZONE_VISUAL, TRIGGERED_FULL_MASK); break; case NPC_ALGALON_VOID_ZONE_VISUAL_STALKER: @@ -787,7 +787,7 @@ class npc_living_constellation : public CreatureScript me->DespawnOrUnsummon(1); if (InstanceScript* instance = me->GetInstanceScript()) instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, EVENT_ID_SUPERMASSIVE_START); - caster->CastSpell((Unit*)nullptr, SPELL_BLACK_HOLE_CREDIT, TRIGGERED_FULL_MASK); + caster->CastSpell(nullptr, SPELL_BLACK_HOLE_CREDIT, TRIGGERED_FULL_MASK); caster->ToCreature()->DespawnOrUnsummon(1); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_auriaya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_auriaya.cpp index aa6e8e955b4..d2e8549a110 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_auriaya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_auriaya.cpp @@ -17,556 +17,646 @@ #include "ScriptMgr.h" #include "InstanceScript.h" +#include "MotionMaster.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" +#include "SpellAuras.h" #include "SpellScript.h" #include "ulduar.h" enum AuriayaSpells { // Auriaya - SPELL_SENTINEL_BLAST = 64389, - SPELL_SONIC_SCREECH = 64422, - SPELL_TERRIFYING_SCREECH = 64386, - SPELL_SUMMON_SWARMING_GUARDIAN = 64396, - SPELL_ACTIVATE_DEFENDER = 64449, - SPELL_DEFENDER_TRIGGER = 64448, - SPELL_SUMMON_DEFENDER = 64447, - SPELL_BERSERK = 47008, + SPELL_SENTINEL_BLAST = 64389, + SPELL_SONIC_SCREECH = 64422, + SPELL_TERRIFYING_SCREECH = 64386, + SPELL_SUMMON_SWARMING_GUARDIAN = 64396, + SPELL_ACTIVATE_DEFENDER = 64449, + SPELL_DEFENDER_TRIGGER = 64448, + SPELL_BERSERK = 47008, + SPELL_INSTAKILL_ARACHNOPOD = 64900, // Feral Defender - SPELL_FERAL_RUSH = 64496, - SPELL_FERAL_POUNCE = 64478, - SPELL_SEEPING_ESSENCE = 64458, - SPELL_SUMMON_ESSENCE = 64457, - SPELL_FERAL_ESSENCE = 64455, + SPELL_FERAL_RUSH = 64496, + SPELL_FERAL_RUSH_2 = 64489, + SPELL_FERAL_POUNCE = 64478, + SPELL_SUMMON_ESSENCE = 64457, + SPELL_FERAL_ESSENCE = 64455, + SPELL_SHADOW_PAWS = 64479, + SPELL_REDUCE_CRITCAL = 64481, + SPELL_RANDOM_AGRO_PERIODIC = 61906, + SPELL_PERMANENT_FEIGN_DEATH = 58951, + SPELL_FERAL_ESSENCE_APPLICATION_REMOVAL = 64456, + SPELL_CLEAR_ALL_DEBUFFS = 34098, + SPELL_DROWNED_STATE = 64462, + SPELL_FULL_HEAL = 64460, + + // Seeping Essence Stalker + SPELL_SEEPING_ESSENCE = 64458, // Sanctum Sentry - SPELL_SAVAGE_POUNCE = 64666, - SPELL_RIP_FLESH = 64375, - SPELL_STRENGHT_PACK = 64369, -}; + SPELL_SAVAGE_POUNCE = 64666, + SPELL_RIP_FLESH = 64375, + SPELL_STRENGHT_OF_THE_PACK = 64369, -enum AuriayaNPCs -{ - NPC_SANCTUM_SENTRY = 34014, - NPC_FERAL_DEFENDER = 34035, - NPC_FERAL_DEFENDER_TRIGGER = 34096, - NPC_SEEPING_TRIGGER = 34098, + // Swarming Guardian + SPELL_AGRO_CREATOR = 63709, + SPELL_POUNCE = 64399 }; enum AuriayaEvents { // Auriaya - EVENT_SCREECH = 1, - EVENT_BLAST = 2, - EVENT_TERRIFYING = 3, - EVENT_SUMMON = 4, - EVENT_DEFENDER = 5, - EVENT_ACTIVATE_DEFENDER = 6, - EVENT_RESPAWN_DEFENDER = 7, - EVENT_BERSERK = 8, + EVENT_SONIC_SCREECH = 1, + EVENT_BLAST, + EVENT_TERRIFYING_SCREECH, + EVENT_SWARNING_GUARDIAN, + EVENT_SUMMON_DEFENDER, + EVENT_ACTIVATE_DEFENDER, + EVENT_BERSERK, // Sanctum Sentry - EVENT_RIP = 9, - EVENT_POUNCE = 10, + EVENT_RIP, + EVENT_SAVAGE_POUNCE, // Feral Defender - EVENT_FERAL_POUNCE = 11, - EVENT_RUSH = 12, + EVENT_FERAL_POUNCE, + EVENT_RUSH, + EVENT_START_COMBAT, + EVENT_RESPAWN_DEFENDER, + EVENT_RESPAWN_DEFENDER_2, + EVENT_RESPAWN_DEFENDER_3 }; enum AuriayaYells { - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_DEATH = 2, - SAY_BERSERK = 3, - EMOTE_FEAR = 4, - EMOTE_DEFENDER = 5 + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_BERSERK = 2, + EMOTE_FEAR = 3, + EMOTE_DEFENDER = 4 }; enum AuriayaActions { - ACTION_CRAZY_CAT_LADY = 0, - ACTION_RESPAWN_DEFENDER + ACTION_CRAZY_CAT_LADY = 0, + ACTION_DEFENDER_DIED }; -#define SENTRY_NUMBER RAID_MODE<uint8>(2, 4) - -enum Mis +enum Misc { - DATA_NINE_LIVES = 30763077, - DATA_CRAZY_CAT_LADY = 30063007 + DATA_NINE_LIVES = 30763077, + DATA_CRAZY_CAT_LADY = 30063007, + PHASE_NONE = 1, + PHASE_COMBAT = 2, + SUMMON_GROUP_10_MAN = 1, + SUMMON_GROUP_25_MAN = 2, + AURIAYA_DEATH_SOUNDID = 15476, + NPC_SANCTUM_SENTRY = 34014 }; -class boss_auriaya : public CreatureScript +class CatsTargetSelector { - public: - boss_auriaya() : CreatureScript("boss_auriaya") { } +public: + CatsTargetSelector(Unit const* unit, float minDist, float maxDist) : _me(unit), _minDist(minDist), _maxDist(maxDist) { } + + bool operator()(Unit* unit) const + { + float dist = _me->GetDistance(unit); + return unit->GetTypeId() == TYPEID_PLAYER && dist >= _minDist && dist < _maxDist && _me->IsWithinLOSInMap(unit); + } + +private: + Unit const* _me; + float _minDist; + float _maxDist; +}; - struct boss_auriayaAI : public BossAI +struct boss_auriaya : public BossAI +{ + boss_auriaya(Creature* creature) : BossAI(creature, BOSS_AURIAYA), _crazyCatLady(true), _nineLives(false) { } + + void Reset() override + { + _Reset(); + _crazyCatLady = true; + _nineLives = false; + HandleCats(true); + } + + void HandleCats(bool isResetting) + { + std::list<Creature*> catList; + me->GetCreatureListWithEntryInGrid(catList, NPC_SANCTUM_SENTRY, 500.0f); + for (std::list<Creature*>::const_iterator itr = catList.begin(); itr != catList.end(); ++itr) { - boss_auriayaAI(Creature* creature) : BossAI(creature, BOSS_AURIAYA) - { - Initialize(); - } - - void Initialize() - { - DefenderGUID.Clear(); - defenderLives = 8; - crazyCatLady = true; - nineLives = false; - } - - void Reset() override - { - _Reset(); - Initialize(); - } - - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - Talk(SAY_AGGRO); - - events.ScheduleEvent(EVENT_SCREECH, urand(45000, 65000)); - events.ScheduleEvent(EVENT_BLAST, urand(20000, 25000)); - events.ScheduleEvent(EVENT_TERRIFYING, urand(20000, 30000)); - events.ScheduleEvent(EVENT_DEFENDER, urand(40000, 55000)); - events.ScheduleEvent(EVENT_SUMMON, urand(45000, 55000)); - events.ScheduleEvent(EVENT_BERSERK, 600000); - } - - void KilledUnit(Unit* who) override - { - if (who->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } - - void JustSummoned(Creature* summoned) override - { - summons.Summon(summoned); - - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) - { - summoned->AI()->AttackStart(target); - AddThreat(target, 250.0f, summoned); - DoZoneInCombat(summoned); - } - - if (summoned->GetEntry() == NPC_FERAL_DEFENDER) - { - if (!summoned->IsInCombat() && me->GetVictim()) - summoned->AI()->AttackStart(me->GetVictim()); - summoned->SetAuraStack(SPELL_FERAL_ESSENCE, summoned, 9); - DefenderGUID = summoned->GetGUID(); - DoZoneInCombat(summoned); - } - } - - void DoAction(int32 action) override - { - switch (action) - { - case ACTION_CRAZY_CAT_LADY: - SetData(DATA_CRAZY_CAT_LADY, 0); - break; - case ACTION_RESPAWN_DEFENDER: - --defenderLives; - if (!defenderLives) - { - SetData(DATA_NINE_LIVES, 1); - break; - } - events.ScheduleEvent(EVENT_RESPAWN_DEFENDER, 30000); - break; - default: - break; - } - } - - uint32 GetData(uint32 type) const override - { - switch (type) - { - case DATA_NINE_LIVES: - return nineLives ? 1 : 0; - case DATA_CRAZY_CAT_LADY: - return crazyCatLady ? 1 : 0; - } + if (isResetting) + (*itr)->Respawn(); + else + (*itr)->DespawnOrUnsummon(); + } + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + events.ScheduleEvent(EVENT_SONIC_SCREECH, Seconds(48)); + events.ScheduleEvent(EVENT_TERRIFYING_SCREECH, Seconds(38)); + events.ScheduleEvent(EVENT_SUMMON_DEFENDER, Seconds(60)); + events.ScheduleEvent(EVENT_SWARNING_GUARDIAN, Seconds(51)); + events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); + } + + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() == TYPEID_PLAYER && roll_chance_i(50)) + Talk(SAY_SLAY); + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_CRAZY_CAT_LADY: + _crazyCatLady = false; + break; + case ACTION_DEFENDER_DIED: + _nineLives = true; + break; + default: + break; + } + } + uint32 GetData(uint32 type) const override + { + switch (type) + { + case DATA_NINE_LIVES: + return _nineLives ? 1 : 0; + case DATA_CRAZY_CAT_LADY: + return _crazyCatLady ? 1 : 0; + default: return 0; - } - - void SetData(uint32 id, uint32 data) override - { - switch (id) - { - case DATA_NINE_LIVES: - nineLives = data ? true : false; - break; - case DATA_CRAZY_CAT_LADY: - crazyCatLady = data ? true : false; - break; - } - } - - void JustDied(Unit* /*killer*/) override + } + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + DoPlaySoundToSet(me, AURIAYA_DEATH_SOUNDID); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + HandleCats(false); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + summons.DespawnAll(); + _DespawnAtEvade(Seconds(5)); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - _JustDied(); - Talk(SAY_DEATH); + case EVENT_SONIC_SCREECH: + DoCastVictim(SPELL_SONIC_SCREECH); + events.Repeat(Seconds(22), Seconds(30)); + break; + case EVENT_TERRIFYING_SCREECH: + Talk(EMOTE_FEAR); + DoCastSelf(SPELL_TERRIFYING_SCREECH); + events.ScheduleEvent(EVENT_BLAST, Milliseconds(1)); + events.Repeat(Seconds(36), Seconds(45)); + break; + case EVENT_BLAST: + DoCastAOE(SPELL_SENTINEL_BLAST); + break; + case EVENT_SUMMON_DEFENDER: + Talk(EMOTE_DEFENDER); + DoCastSelf(SPELL_DEFENDER_TRIGGER); + events.ScheduleEvent(EVENT_ACTIVATE_DEFENDER, Seconds(2)); + break; + case EVENT_ACTIVATE_DEFENDER: + DoCastSelf(SPELL_ACTIVATE_DEFENDER); + break; + case EVENT_SWARNING_GUARDIAN: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true)) + DoCast(target, SPELL_SUMMON_SWARMING_GUARDIAN); + events.Repeat(Seconds(25), Seconds(45)); + break; + case EVENT_BERSERK: + DoCastSelf(SPELL_BERSERK, true); + Talk(SAY_BERSERK); + break; + default: + break; } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SCREECH: - DoCast(SPELL_SONIC_SCREECH); - events.ScheduleEvent(EVENT_SCREECH, urand(40000, 60000)); - break; - case EVENT_TERRIFYING: - Talk(EMOTE_FEAR); - DoCast(SPELL_TERRIFYING_SCREECH); - events.ScheduleEvent(EVENT_TERRIFYING, urand(20000, 30000)); - break; - case EVENT_BLAST: - DoCastAOE(SPELL_SENTINEL_BLAST); - events.ScheduleEvent(EVENT_BLAST, urand(25000, 35000)); - break; - case EVENT_DEFENDER: - Talk(EMOTE_DEFENDER); - DoCast(SPELL_DEFENDER_TRIGGER); - if (Creature* trigger = me->FindNearestCreature(NPC_FERAL_DEFENDER_TRIGGER, 15.0f, true)) - DoCast(trigger, SPELL_ACTIVATE_DEFENDER, true); - break; - case EVENT_RESPAWN_DEFENDER: - if (Creature* Defender = ObjectAccessor::GetCreature(*me, DefenderGUID)) - { - Defender->Respawn(); - if (defenderLives) - Defender->SetAuraStack(SPELL_FERAL_ESSENCE, Defender, defenderLives); - Defender->SetInCombatWithZone(); - if (!Defender->IsInCombat()) - Defender->AI()->AttackStart(me->GetVictim()); - events.CancelEvent(EVENT_RESPAWN_DEFENDER); - } - break; - case EVENT_SUMMON: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) - DoCast(target, SPELL_SUMMON_SWARMING_GUARDIAN); - events.ScheduleEvent(EVENT_SUMMON, urand(30000, 45000)); - break; - case EVENT_BERSERK: - DoCast(me, SPELL_BERSERK, true); - Talk(SAY_BERSERK); - events.CancelEvent(EVENT_BERSERK); - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - private: - ObjectGuid DefenderGUID; - uint8 defenderLives; - bool crazyCatLady; - bool nineLives; - }; + DoMeleeAttackIfReady(); + } - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI<boss_auriayaAI>(creature); - } +private: + bool _crazyCatLady; + bool _nineLives; }; -class npc_auriaya_seeping_trigger : public CreatureScript +struct npc_sanctum_sentry : public ScriptedAI { - public: - npc_auriaya_seeping_trigger() : CreatureScript("npc_auriaya_seeping_trigger") { } - - struct npc_auriaya_seeping_triggerAI : public ScriptedAI + npc_sanctum_sentry(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + + void Reset() override + { + DoCastSelf(SPELL_STRENGHT_OF_THE_PACK, true); + me->SetWalk(true); + } + + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_RIP, Seconds(6)); + _events.ScheduleEvent(EVENT_SAVAGE_POUNCE, Milliseconds(1)); + me->SetWalk(false); + } + + void JustDied(Unit* /*killer*/) override + { + if (Creature* auriaya = _instance->GetCreature(BOSS_AURIAYA)) + auriaya->AI()->DoAction(ACTION_CRAZY_CAT_LADY); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) { - npc_auriaya_seeping_triggerAI(Creature* creature) : ScriptedAI(creature) + switch (eventId) { - instance = me->GetInstanceScript(); - } - - void Reset() override - { - me->DespawnOrUnsummon(600000); - DoCast(me, SPELL_SEEPING_ESSENCE); + case EVENT_RIP: + DoCastVictim(SPELL_RIP_FLESH); + _events.Repeat(Seconds(10), Seconds(12)); + break; + case EVENT_SAVAGE_POUNCE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, CatsTargetSelector(me, 10.0f, 15.0f))) + { + DoCast(target, SPELL_SAVAGE_POUNCE); + _events.Repeat(Seconds(10)); + break; + } + _events.Repeat(Seconds(1)); + break; + default: + break; } - void UpdateAI(uint32 /*diff*/) override - { - if (instance->GetBossState(BOSS_AURIAYA) != IN_PROGRESS) - me->DespawnOrUnsummon(); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - private: - InstanceScript* instance; - }; + DoMeleeAttackIfReady(); + } - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI<npc_auriaya_seeping_triggerAI>(creature); - } +private: + InstanceScript* _instance; + EventMap _events; }; -class npc_sanctum_sentry : public CreatureScript +struct npc_feral_defender : public ScriptedAI { - public: - npc_sanctum_sentry() : CreatureScript("npc_sanctum_sentry") { } - - struct npc_sanctum_sentryAI : public ScriptedAI - { - npc_sanctum_sentryAI(Creature* creature) : ScriptedAI(creature) - { - instance = me->GetInstanceScript(); - } + npc_feral_defender(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - void Reset() override - { - events.ScheduleEvent(EVENT_RIP, urand(4000, 8000)); - events.ScheduleEvent(EVENT_POUNCE, urand(12000, 15000)); - } + void Reset() override + { + me->SetReactState(REACT_PASSIVE); + DoCastSelf(SPELL_SHADOW_PAWS, true); + DoCastSelf(SPELL_REDUCE_CRITCAL, true); + me->SetAuraStack(SPELL_FERAL_ESSENCE, me, 8); + DoCastSelf(SPELL_RANDOM_AGRO_PERIODIC, true); + _events.SetPhase(PHASE_NONE); + _events.ScheduleEvent(EVENT_START_COMBAT, Seconds(1)); - void EnterCombat(Unit* /*who*/) override - { - DoCast(me, SPELL_STRENGHT_PACK, true); - } + if (Creature* auriaya = _instance->GetCreature(BOSS_AURIAYA)) + auriaya->AI()->JustSummoned(me); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !_events.IsInPhase(PHASE_NONE)) + return; - events.Update(diff); + _events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_START_COMBAT: + _events.SetPhase(PHASE_COMBAT); + me->SetReactState(REACT_AGGRESSIVE); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) + AttackStart(target); + _events.ScheduleEvent(EVENT_RUSH, Seconds(1)); + break; + case EVENT_RUSH: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, CatsTargetSelector(me, 10.0f, 11.0f))) { - case EVENT_RIP: - DoCastVictim(SPELL_RIP_FLESH); - events.ScheduleEvent(EVENT_RIP, urand(12000, 15000)); - break; - case EVENT_POUNCE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) - { - AddThreat(target, 100.0f); - AttackStart(target); - DoCast(target, SPELL_SAVAGE_POUNCE); - } - events.ScheduleEvent(EVENT_POUNCE, urand(12000, 17000)); - break; - default: - break; + DoCast(target, SPELL_FERAL_RUSH, true); + _events.Repeat(Seconds(5)); + break; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); + _events.Repeat(Seconds(1)); + break; + case EVENT_RESPAWN_DEFENDER: + me->SetDisableGravity(true); + me->SetHover(true); + DoCastSelf(SPELL_DROWNED_STATE, true); + _events.ScheduleEvent(EVENT_RESPAWN_DEFENDER_2, Seconds(3)); + _events.ScheduleEvent(EVENT_RESPAWN_DEFENDER_3, Seconds(5)); + break; + case EVENT_RESPAWN_DEFENDER_2: + me->RemoveAurasDueToSpell(SPELL_DROWNED_STATE); + break; + case EVENT_RESPAWN_DEFENDER_3: + me->RemoveAurasDueToSpell(SPELL_PERMANENT_FEIGN_DEATH); + DoCastSelf(SPELL_FULL_HEAL, true); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + me->SetDisableGravity(false); + me->SetHover(false); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) + AttackStart(target); + _events.ScheduleEvent(EVENT_RUSH, Seconds(1)); + break; + default: + break; } - void JustDied(Unit* /*killer*/) override - { - if (Creature* auriaya = instance->GetCreature(BOSS_AURIAYA)) - auriaya->AI()->DoAction(ACTION_CRAZY_CAT_LADY); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - private: - InstanceScript* instance; - EventMap events; - }; + DoMeleeAttackIfReady(); + } - CreatureAI* GetAI(Creature* creature) const override + void DamageTaken(Unit* /*done_by*/, uint32 &damage) override + { + if (damage >= me->GetHealth() && me->HasAura(SPELL_FERAL_ESSENCE)) { - return GetUlduarAI<npc_sanctum_sentryAI>(creature); + damage = 0; + if (!me->HasAura(SPELL_PERMANENT_FEIGN_DEATH)) + { + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH, true); + DoCastSelf(SPELL_FERAL_ESSENCE_APPLICATION_REMOVAL, true); + DoCastSelf(SPELL_SUMMON_ESSENCE, true); + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS, true); + ResetThreatList(); + _events.ScheduleEvent(EVENT_RESPAWN_DEFENDER, Seconds(30)); + _events.CancelEvent(EVENT_RUSH); + } } + } + + void JustDied(Unit* /*killer*/) override + { + DoCastSelf(SPELL_SUMMON_ESSENCE, true); + if (Creature* auriaya = _instance->GetCreature(BOSS_AURIAYA)) + auriaya->AI()->DoAction(ACTION_DEFENDER_DIED); + } + +private: + InstanceScript* _instance; + EventMap _events; }; -class npc_feral_defender : public CreatureScript +struct npc_swarming_guardian : public ScriptedAI { - public: - npc_feral_defender() : CreatureScript("npc_feral_defender") { } + npc_swarming_guardian(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - struct npc_feral_defenderAI : public ScriptedAI + void Reset() override + { + me->SetReactState(REACT_PASSIVE); + _scheduler.Schedule(Seconds(1), [this](TaskContext /*context*/) { - npc_feral_defenderAI(Creature* creature) : ScriptedAI(creature) - { - instance = me->GetInstanceScript(); - } - - void Reset() override - { - events.ScheduleEvent(EVENT_FERAL_POUNCE, 5000); - events.ScheduleEvent(EVENT_RUSH, 10000); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); + me->SetReactState(REACT_AGGRESSIVE); + DoCastSelf(SPELL_AGRO_CREATOR); + }); + if (Creature* auriaya = _instance->GetCreature(BOSS_AURIAYA)) + auriaya->AI()->JustSummoned(me); + } + + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + +private: + InstanceScript* _instance; + TaskScheduler _scheduler; +}; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; +struct npc_seeping_essence_stalker : public ScriptedAI +{ + npc_seeping_essence_stalker(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_FERAL_POUNCE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) - { - AddThreat(target, 100.0f); - AttackStart(target); - DoCast(target, SPELL_FERAL_POUNCE); - } - events.ScheduleEvent(EVENT_FERAL_POUNCE, urand(10000, 12000)); - break; - case EVENT_RUSH: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) - { - AddThreat(target, 100.0f); - AttackStart(target); - DoCast(target, SPELL_FERAL_RUSH); - } - events.ScheduleEvent(EVENT_RUSH, urand(10000, 12000)); - break; - default: - break; - } + void Reset() override + { + DoCastSelf(SPELL_SEEPING_ESSENCE); + if (Creature* auriaya = _instance->GetCreature(BOSS_AURIAYA)) + auriaya->AI()->JustSummoned(me); + } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } + void UpdateAI(uint32 /*diff*/) override { } - DoMeleeAttackIfReady(); - } +private: + InstanceScript* _instance; +}; - void JustDied(Unit* /*killer*/) override - { - DoCast(me, SPELL_SUMMON_ESSENCE); - if (Creature* auriaya = instance->GetCreature(BOSS_AURIAYA)) - auriaya->AI()->DoAction(ACTION_RESPAWN_DEFENDER); - } +// 64381 - Strength of the Pack +class spell_auriaya_strenght_of_the_pack : public SpellScript +{ + PrepareSpellScript(spell_auriaya_strenght_of_the_pack); - private: - InstanceScript* instance; - EventMap events; - }; + void FilterTargets(std::list<WorldObject*>& unitList) + { + unitList.remove_if([](WorldObject* obj) { return obj->GetEntry() != NPC_SANCTUM_SENTRY; }); + } - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI<npc_feral_defenderAI>(creature); - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_auriaya_strenght_of_the_pack::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); + } }; -class SanctumSentryCheck +// 64392, 64679 - Sentinel Blast +class spell_auriaya_sentinel_blast : public SpellScript { - public: - bool operator()(WorldObject* object) const - { - if (object->GetEntry() == NPC_SANCTUM_SENTRY) - return false; - - return true; - } + PrepareSpellScript(spell_auriaya_sentinel_blast); + + void FilterTargets(std::list<WorldObject*>& unitList) + { + unitList.remove_if(PlayerOrPetCheck()); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_auriaya_sentinel_blast::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_auriaya_sentinel_blast::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY); + } }; -class spell_auriaya_strenght_of_the_pack : public SpellScriptLoader +// 63709 - Aggro Creator +class spell_auriaya_agro_creator : public SpellScript { - public: - spell_auriaya_strenght_of_the_pack() : SpellScriptLoader("spell_auriaya_strenght_of_the_pack") { } + PrepareSpellScript(spell_auriaya_agro_creator); - class spell_auriaya_strenght_of_the_pack_SpellScript : public SpellScript - { - PrepareSpellScript(spell_auriaya_strenght_of_the_pack_SpellScript); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_POUNCE }); + } - void FilterTargets(std::list<WorldObject*>& unitList) - { - unitList.remove_if(SanctumSentryCheck()); - } + void HandleDummyEffect(SpellEffIndex /*effIndex*/) + { + Creature* caster = GetCaster()->ToCreature(); + if (!caster || !caster->IsAIEnabled || caster->HasReactState(REACT_PASSIVE)) + return; - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_auriaya_strenght_of_the_pack_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); - } - }; - - SpellScript* GetSpellScript() const override + if (Unit* target = caster->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, CatsTargetSelector(caster, 5.0f, 10.0f))) { - return new spell_auriaya_strenght_of_the_pack_SpellScript(); + caster->CastSpell(target, SPELL_POUNCE, true); + caster->GetThreatManager().AddThreat(target, 50000000.0f, nullptr, true); + caster->AI()->AttackStart(target); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_auriaya_agro_creator::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_auriaya_sentinel_blast : public SpellScriptLoader +// 61906 - Random Aggro Periodic (5 sec) +class spell_auriaya_random_agro_periodic : public AuraScript { - public: - spell_auriaya_sentinel_blast() : SpellScriptLoader("spell_auriaya_sentinel_blast") { } + PrepareAuraScript(spell_auriaya_random_agro_periodic); - class spell_auriaya_sentinel_blast_SpellScript : public SpellScript - { - PrepareSpellScript(spell_auriaya_sentinel_blast_SpellScript); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FERAL_POUNCE }); + } - void FilterTargets(std::list<WorldObject*>& unitList) - { - unitList.remove_if(PlayerOrPetCheck()); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_auriaya_sentinel_blast_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_auriaya_sentinel_blast_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY); - } - }; + void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) + { + Creature* owner = GetUnitOwner()->ToCreature(); + if (!owner || !owner->IsAIEnabled || owner->HasReactState(REACT_PASSIVE)) + return; - SpellScript* GetSpellScript() const override + if (Unit* target = owner->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, CatsTargetSelector(owner, 15.0f, 25.0f))) { - return new spell_auriaya_sentinel_blast_SpellScript(); + owner->GetThreatManager().AddThreat(target, 3000000.0f, nullptr, true); + owner->CastSpell(target, SPELL_FERAL_POUNCE, true); + owner->AI()->AttackStart(target); } + else if (Unit* target = owner->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0)) + { + owner->GetThreatManager().AddThreat(target, 3000000.0f); + owner->AI()->AttackStart(target); + } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_auriaya_random_agro_periodic::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } }; +// 64456 - Feral Essence Application Removal +class spell_auriaya_feral_essence_removal : public SpellScript +{ + PrepareSpellScript(spell_auriaya_feral_essence_removal); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FERAL_ESSENCE }); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + if (Aura* essence = GetCaster()->GetAura(SPELL_FERAL_ESSENCE)) + essence->ModStackAmount(-1); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_auriaya_feral_essence_removal::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + +// 64496, 64674 - Feral Rush +class spell_auriaya_feral_rush : public SpellScript +{ + PrepareSpellScript(spell_auriaya_feral_rush); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FERAL_RUSH_2 }); + } + + void HandleOnHit(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetCaster(), SPELL_FERAL_RUSH_2, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_auriaya_feral_rush::HandleOnHit, EFFECT_1, SPELL_EFFECT_SCHOOL_DAMAGE); + } +}; class achievement_nine_lives : public AchievementCriteriaScript { public: - achievement_nine_lives() : AchievementCriteriaScript("achievement_nine_lives") - { - } + achievement_nine_lives() : AchievementCriteriaScript("achievement_nine_lives") { } bool OnCheck(Player* /*player*/, Unit* target) override { if (!target) return false; - if (Creature* Auriaya = target->ToCreature()) - if (Auriaya->AI()->GetData(DATA_NINE_LIVES)) + if (Creature* auriaya = target->ToCreature()) + if (auriaya->AI()->GetData(DATA_NINE_LIVES)) return true; return false; @@ -576,17 +666,15 @@ class achievement_nine_lives : public AchievementCriteriaScript class achievement_crazy_cat_lady : public AchievementCriteriaScript { public: - achievement_crazy_cat_lady() : AchievementCriteriaScript("achievement_crazy_cat_lady") - { - } + achievement_crazy_cat_lady() : AchievementCriteriaScript("achievement_crazy_cat_lady") { } bool OnCheck(Player* /*player*/, Unit* target) override { if (!target) return false; - if (Creature* Auriaya = target->ToCreature()) - if (Auriaya->AI()->GetData(DATA_CRAZY_CAT_LADY)) + if (Creature* auriaya = target->ToCreature()) + if (auriaya->AI()->GetData(DATA_CRAZY_CAT_LADY)) return true; return false; @@ -595,12 +683,17 @@ class achievement_crazy_cat_lady : public AchievementCriteriaScript void AddSC_boss_auriaya() { - new boss_auriaya(); - new npc_auriaya_seeping_trigger(); - new npc_feral_defender(); - new npc_sanctum_sentry(); - new spell_auriaya_strenght_of_the_pack(); - new spell_auriaya_sentinel_blast(); + RegisterUlduarCreatureAI(boss_auriaya); + RegisterUlduarCreatureAI(npc_feral_defender); + RegisterUlduarCreatureAI(npc_sanctum_sentry); + RegisterUlduarCreatureAI(npc_swarming_guardian); + RegisterUlduarCreatureAI(npc_seeping_essence_stalker); + RegisterSpellScript(spell_auriaya_strenght_of_the_pack); + RegisterSpellScript(spell_auriaya_sentinel_blast); + RegisterSpellScript(spell_auriaya_agro_creator); + RegisterAuraScript(spell_auriaya_random_agro_periodic); + RegisterSpellScript(spell_auriaya_feral_essence_removal); + RegisterSpellScript(spell_auriaya_feral_rush); new achievement_nine_lives(); new achievement_crazy_cat_lady(); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index dac2a8707d9..3925fd3b042 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -973,9 +973,9 @@ class npc_mimirons_inferno : public CreatureScript public: npc_mimirons_inferno() : CreatureScript("npc_mimirons_inferno") { } - struct npc_mimirons_infernoAI : public npc_escortAI + struct npc_mimirons_infernoAI : public EscortAI { - npc_mimirons_infernoAI(Creature* creature) : npc_escortAI(creature) + npc_mimirons_infernoAI(Creature* creature) : EscortAI(creature) { Initialize(); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); @@ -988,11 +988,6 @@ public: infernoTimer = 2000; } - void WaypointReached(uint32 /*waypointId*/) override - { - - } - void Reset() override { Initialize(); @@ -1002,7 +997,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!HasEscortState(STATE_ESCORT_ESCORTING)) Start(false, true, ObjectGuid::Empty, nullptr, false, true); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp index fe0539fc407..8b5039b8326 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp @@ -610,7 +610,7 @@ class boss_freya : public CreatureScript /* 25N */ {62955, 62956, 62957, 62958} }; - me->CastSpell((Unit*)nullptr, summonSpell[me->GetMap()->GetDifficulty()][elderCount], true); + me->CastSpell(nullptr, summonSpell[me->GetMap()->GetDifficulty()][elderCount], true); Talk(SAY_DEATH); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index 3b95b36c375..8200ea8ca06 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -2129,7 +2129,7 @@ class spell_mimiron_proximity_trigger : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { - GetCaster()->CastSpell((Unit*)nullptr, SPELL_PROXIMITY_MINE_EXPLOSION, true); + GetCaster()->CastSpell(nullptr, SPELL_PROXIMITY_MINE_EXPLOSION, true); } void Register() override @@ -2215,7 +2215,7 @@ class spell_mimiron_rocket_strike : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { - GetHitUnit()->CastSpell((Unit*)nullptr, SPELL_SCRIPT_EFFECT_ROCKET_STRIKE, true, nullptr, nullptr, GetCaster()->GetGUID()); + GetHitUnit()->CastSpell(nullptr, SPELL_SCRIPT_EFFECT_ROCKET_STRIKE, true, nullptr, nullptr, GetCaster()->GetGUID()); } void Register() override @@ -2262,7 +2262,7 @@ class spell_mimiron_rocket_strike_damage : public SpellScriptLoader void HandleFriendlyFire(SpellEffIndex /*effIndex*/) { - GetHitUnit()->CastSpell((Unit*)nullptr, SPELL_NOT_SO_FRIENDLY_FIRE, true); + GetHitUnit()->CastSpell(nullptr, SPELL_NOT_SO_FRIENDLY_FIRE, true); } void Register() override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp index d571bd541df..41985059aae 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp @@ -15,15 +15,15 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/// @todo Harpoon chain from 62505 should not get removed when other chain is applied - #include "ScriptMgr.h" #include "GameObject.h" #include "GameObjectAI.h" #include "InstanceScript.h" #include "Map.h" #include "MotionMaster.h" +#include "MoveSplineInit.h" #include "ObjectAccessor.h" +#include "PassiveAI.h" #include "Player.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" @@ -31,1133 +31,1673 @@ #include "SpellScript.h" #include "TemporarySummon.h" #include "ulduar.h" +#include <G3D/Vector3.h> enum Says { // Expedition Commander - SAY_INTRO = 0, - SAY_GROUND_PHASE = 1, - SAY_AGGRO_2 = 2, + SAY_COMMANDER_AGGRO = 0, + SAY_COMMANDER_GROUND_PHASE = 1, + SAY_COMMANDER_ENGINEERS_DEAD = 2, // Expedition Engineer - SAY_AGGRO_1 = 0, - SAY_AGGRO_3 = 1, - SAY_TURRETS = 2, // unused + SAY_AGGRO = 0, + SAY_START_REPAIR = 1, + SAY_REBUILD_TURRETS = 2, // Razorscale Controller - EMOTE_HARPOON = 0, + EMOTE_HARPOON = 0, // Razorscale - EMOTE_PERMA = 0, - EMOTE_BREATH = 1 + EMOTE_PERMA_GROUND = 0, + EMOTE_BREATH = 1, + EMOTE_BERSERK = 2 }; enum Spells { - SPELL_FLAMEBUFFET = 64016, - SPELL_FIREBALL = 62796, - SPELL_FLAME_GROUND = 64734, - SPELL_WINGBUFFET = 62666, - SPELL_FLAMEBREATH = 63317, - SPELL_FUSEARMOR = 64771, - SPELL_FLAMED = 62696, - SPELL_STUN = 9032, - SPELL_BERSERK = 47008, - // Additonal Spells - // Devouring Flame Spells - SPELL_DEVOURING_FLAME = 63308, - SPELL_DEVOURING_FLAME_DAMAGE = 64704, - SPELL_DEVOURING_FLAME_TRIGGER = 64709, - // HarpoonSpells - SPELL_HARPOON_TRIGGER = 62505, - SPELL_HARPOON_SHOT_1 = 63658, - SPELL_HARPOON_SHOT_2 = 63657, - SPELL_HARPOON_SHOT_3 = 63659, - SPELL_HARPOON_SHOT_4 = 63524, - // MoleMachine Spells - SPELL_SUMMON_MOLE_MACHINE = 62899, - SPELL_SUMMON_IRON_DWARVES = 63116, - SPELL_SUMMON_IRON_DWARVES_2 = 63114, - SPELL_SUMMON_IRON_DWARVE_GUARDIAN = 62926, - SPELL_SUMMON_IRON_DWARVE_WATCHER = 63135, + SPELL_FIREBALL = 63815, + SPELL_DEVOURING_FLAME = 63236, + SPELL_WING_BUFFET = 62666, + SPELL_FIREBOLT = 62669, + SPELL_FUSE_ARMOR = 64821, + SPELL_FUSED_ARMOR = 64774, + SPELL_STUN_SELF = 62794, + SPELL_BERSERK = 47008, + + // Razorscale Harpoon Fire State + SPELL_HARPOON_FIRE_STATE = 62696, + + // Harpoon + SPELL_HARPOON_TRIGGER = 62505, + SPELL_HARPOON_SHOT_1 = 63658, + SPELL_HARPOON_SHOT_2 = 63657, + SPELL_HARPOON_SHOT_3 = 63659, + SPELL_HARPOON_SHOT_4 = 63524, + + // Razorscale Spawner + SPELL_SUMMON_MOLE_MACHINE = 62899, + SPELL_SUMMON_IRON_DWARF_GUARDIAN = 62926, + SPELL_TRIGGER_SUMMON_IRON_DWARVES = 63968, + SPELL_TRIGGER_SUMMON_IRON_DWARVES_2 = 63970, + SPELL_TRIGGER_SUMMON_IRON_DWARVES_3 = 63969, + SPELL_TRIGGER_SUMMON_IRON_VRYKUL = 63798, + SPELL_SUMMON_IRON_DWARF_WATCHER = 63135, + + // Dark Rune Guardian + SPELL_STORMSTRIKE = 64757, + + // Dark Rune Sentinel + SPELL_BATTLE_SHOUT = 46763, + SPELL_HEROIC_STRIKE = 45026, + SPELL_WHIRLWIND = 63808, + + // Expedition Defender + SPELL_THREAT = 65146, + + // Expedition Trapper + SPELL_SHACKLE = 62646 }; -enum NPC +#define DEVOURING_FLAME_GROUND RAID_MODE<uint32>(64709, 64734) +#define FLAME_BREATH RAID_MODE<uint32>(63317, 64021) +#define CHAIN_LIGHTNING RAID_MODE<uint32>(64758, 64759) +#define LIGHTNING_BOLT RAID_MODE<uint32>(63809, 64696) + +enum Actions { - NPC_DARK_RUNE_GUARDIAN = 33388, - NPC_DARK_RUNE_SENTINEL = 33846, - NPC_DARK_RUNE_WATCHER = 33453, - MOLE_MACHINE_TRIGGER = 33245, - NPC_COMMANDER = 33210, - NPC_ENGINEER = 33287, - NPC_DEFENDER = 33816, + ACTION_START_FIGHT = 1, + ACTION_FIX_HARPOONS, + ACTION_GROUND_PHASE, + ACTION_ENGINEER_DEAD, + ACTION_SHACKLE_RAZORSCALE, + ACTION_START_PERMA_GROUND, + ACTION_RETURN_TO_BASE, + ACTION_BUILD_HARPOON_1, + ACTION_BUILD_HARPOON_2, + ACTION_BUILD_HARPOON_3, + ACTION_BUILD_HARPOON_4, + ACTION_DESTROY_HARPOONS, + ACTION_STOP_CONTROLLERS, + ACTION_STOP_CAST }; -enum DarkRuneSpells +enum Events { + EVENT_BERSERK = 1, + EVENT_FIREBALL, + EVENT_DEVOURING_FLAME, + EVENT_SUMMON_MINIONS, + EVENT_SUMMON_MINIONS_2, + EVENT_FLAME_BREATH, + EVENT_FLAME_BREATH_GROUND, + EVENT_WING_BUFFET, + EVENT_RESUME_AIR_PHASE, + EVENT_FIREBOLT, + EVENT_FUSE_ARMOR, + EVENT_RESUME_MOVE_CHASE, + + // Expedition Commander + EVENT_BUILD_HARPOON_1, + EVENT_BUILD_HARPOON_2, + EVENT_BUILD_HARPOON_3, + EVENT_BUILD_HARPOON_4, + EVENT_HANDLE_DESTROY_HARPOON, + + // Dark Rune Sentinel + EVENT_START_COMBAT, + EVENT_HEROIC_STRIKE, + EVENT_BATTLE_SHOUT, + EVENT_WHIRLWIND, + // Dark Rune Watcher - SPELL_CHAIN_LIGHTNING = 64758, - SPELL_LIGHTNING_BOLT = 63809, + EVENT_LIGHTNING_BOLT, + EVENT_CHAIN_LIGHTNING, + // Dark Rune Guardian - SPELL_STORMSTRIKE = 64757, - // Dark Rune Sentinel - SPELL_BATTLE_SHOUT = 46763, - SPELL_HEROIC_STRIKE = 45026, - SPELL_WHIRLWIND = 63807, + EVENT_STORMSTRIKE }; -enum Actions +enum Misc { - ACTION_EVENT_START = 1, - ACTION_GROUND_PHASE = 2, - ACTION_HARPOON_BUILD = 3, - ACTION_PLACE_BROKEN_HARPOON = 4, - ACTION_COMMANDER_RESET = 7, + DATA_QUICK_SHAVE = 29192921, // 2919, 2921 are achievement IDs + DATA_IRON_DWARF_MEDIUM_RARE = 29232924, + GOSSIP_START_ENCOUNTER = 0, + DATA_EXPEDITION_NUMBER = 1, + RAZORSCALE_EXPEDITION_GROUP = 1, + RAZORSCALE_FIRE_STATE_10_GROUP = 2, + RAZORSCALE_FIRE_STATE_25_GROUP = 3, + ENGINEER_NORTH = 0, + ENGINEER_EAST = 1, + ENGINEER_WEST = 2, + HARPOON_1 = 0, + HARPOON_2 = 1, + HARPOON_3 = 2, + HARPOON_4 = 3, + WORLD_STATE_RAZORSCALE_MUSIC = 4162 }; -enum Phases +enum MovePoints { - PHASE_PERMAGROUND = 1, - PHASE_GROUND = 2, - PHASE_FLIGHT = 3, + POINT_DEFENDER_ATTACK = 1, + POINT_SHACKLE_RAZORSCALE, + POINT_BASE, + POINT_HARPOON_1, + POINT_HARPOON_1_25, + POINT_HARPOON_2, + POINT_HARPOON_2_25, + POINT_HARPOON_3, + POINT_HARPOON_4, + POINT_RAZORSCALE_FLIGHT, + POINT_RAZORSCALE_TAKEOFF, + POINT_RAZORSCALE_FLIGHT_2, + POINT_RAZORSCALE_LAND, + POINT_RAZORSCALE_GROUND, + POINT_START_WAYPOINT }; -enum Events +enum EngineersSplineMovements { - EVENT_BERSERK = 1, - EVENT_BREATH = 2, - EVENT_BUFFET = 3, - EVENT_FIREBALL = 5, - EVENT_FLIGHT = 6, - EVENT_DEVOURING = 7, - EVENT_FLAME = 8, - EVENT_LAND = 9, - EVENT_GROUND = 10, - EVENT_FUSE = 11, - EVENT_SUMMON = 12, - // Razorscale Controller - EVENT_BUILD_HARPOON_1 = 13, - EVENT_BUILD_HARPOON_2 = 14, - EVENT_BUILD_HARPOON_3 = 15, - EVENT_BUILD_HARPOON_4 = 16, + SPLINE_ENGINEER_NORTH_10_HARPOON_1 = 1, + SPLINE_ENGINEER_NORTH_10_HARPOON_2 = 2, + SPLINE_ENGINEER_NORTH_10_BASE = 3, + SPLINE_ENGINEER_NORTH_25_HARPOON_1 = 4, + SPLINE_ENGINEER_NORTH_25_HARPOON_2 = 5, + SPLINE_ENGINEER_NORTH_25_HARPOON_3 = 6, + SPLINE_ENGINEER_NORTH_25_HARPOON_4 = 7, + SPLINE_ENGINEER_NORTH_25_BASE = 8, + SPLINE_ENGINEER_EAST_10_HARPOON_1 = 9, + SPLINE_ENGINEER_EAST_10_HARPOON_2 = 10, + SPLINE_ENGINEER_EAST_10_BASE = 11, + SPLINE_ENGINEER_EAST_25_HARPOON_1 = 12, + SPLINE_ENGINEER_EAST_25_HARPOON_2 = 13, + SPLINE_ENGINEER_EAST_25_HARPOON_3 = 14, + SPLINE_ENGINEER_EAST_25_HARPOON_4 = 15, + SPLINE_ENGINEER_WEST_10_HARPOON_1 = 16, + SPLINE_ENGINEER_WEST_10_HARPOON_2 = 17, + SPLINE_ENGINEER_WEST_10_BASE = 18, + SPLINE_ENGINEER_WEST_25_HARPOON_1 = 19, + SPLINE_ENGINEER_WEST_25_HARPOON_2 = 20, + SPLINE_ENGINEER_WEST_25_HARPOON_3 = 21, + SPLINE_ENGINEER_WEST_25_HARPOON_4 = 22, + SPLINE_ENGINEER_WEST_25_BASE = 23 }; -#define GROUND_Z 391.517f -#define GOSSIP_ITEM_1 "Activate Harpoons!" - -enum Misc +enum RazorscalePhases { - DATA_QUICK_SHAVE = 29192921, // 2919, 2921 are achievement IDs - DATA_IRON_DWARF_MEDIUM_RARE = 29232924 + PHASE_NONE = 0, + PHASE_COMBAT, + PHASE_GROUND, + PHASE_AIR, + PHASE_PERMA_GROUND }; -const Position PosEngRepair[4] = +Position const PosBrokenHarpoon[4] = { - { 590.442f, -130.550f, GROUND_Z, 4.789f }, - { 574.850f, -133.687f, GROUND_Z, 4.252f }, - { 606.567f, -143.369f, GROUND_Z, 4.434f }, - { 560.609f, -142.967f, GROUND_Z, 5.074f }, + { 571.9465f, -136.0118f, 391.5171f, 2.286379f }, // 1 + { 589.9233f, -133.6223f, 391.8968f, 3.298687f }, // 2 + { 559.1199f, -140.5058f, 391.1803f, 4.049168f }, // 0 + { 606.2297f, -136.7212f, 391.1803f, 5.131269f } // 3 }; -const Position PosDefSpawn[4] = +Position const PosHarpoon[4] = { - { 600.75f, -104.850f, GROUND_Z, 0 }, - { 596.38f, -110.262f, GROUND_Z, 0 }, - { 566.47f, -103.633f, GROUND_Z, 0 }, - { 570.41f, -108.791f, GROUND_Z, 0 }, + { 571.9012f, -136.5541f, 391.5171f, 4.921829f }, // GO_RAZOR_HARPOON_1 + { 589.9233f, -133.6223f, 391.8968f, 4.81711f }, // GO_RAZOR_HARPOON_2 + { 559.1199f, -140.5058f, 391.1803f, 5.061456f }, // GO_RAZOR_HARPOON_3 + { 606.2297f, -136.7212f, 391.1803f, 4.537859f } // GO_RAZOR_HARPOON_4 }; -const Position PosDefCombat[4] = +Position const DefendersPosition[6] = { - { 614.975f, -155.138f, GROUND_Z, 4.154f }, - { 609.814f, -204.968f, GROUND_Z, 5.385f }, - { 563.531f, -201.557f, GROUND_Z, 4.108f }, - { 560.231f, -153.677f, GROUND_Z, 5.403f }, + { 624.3065f, -154.4163f, 391.6442f }, + { 611.6274f, -170.9375f, 391.8087f }, + { 572.1548f, -167.4471f, 391.8087f }, + { 558.4640f, -165.0114f, 391.8087f }, + { 603.3345f, -164.4297f, 391.8087f }, + { 549.1727f, -159.1180f, 391.8087f } }; -const Position PosHarpoon[4] = +Position const TrapperPosition[3] = { - { 571.901f, -136.554f, GROUND_Z, 0 }, - { 589.450f, -134.888f, GROUND_Z, 0 }, - { 559.119f, -140.505f, GROUND_Z, 0 }, - { 606.229f, -136.721f, GROUND_Z, 0 }, + { 574.9293f, -184.5150f, 391.8921f }, + { 539.7838f, -178.5337f, 391.3053f }, + { 627.1754f, -177.9638f, 391.5553f } }; -const Position RazorFlight = { 588.050f, -251.191f, 470.536f, 1.498f }; -const Position RazorGround = { 586.966f, -175.534f, GROUND_Z, 4.682f }; -const Position PosEngSpawn = { 591.951f, -95.9680f, GROUND_Z, 0.000f }; - -class boss_razorscale_controller : public CreatureScript +uint32 const SummonMinionsSpells[4] = { - public: - boss_razorscale_controller() : CreatureScript("boss_razorscale_controller") { } - - struct boss_razorscale_controllerAI : public ScriptedAI - { - boss_razorscale_controllerAI(Creature* creature) : ScriptedAI(creature), summons(me) - { - instance = creature->GetInstanceScript(); - me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); - } - - InstanceScript* instance; - EventMap events; - SummonList summons; + SPELL_TRIGGER_SUMMON_IRON_DWARVES, + SPELL_TRIGGER_SUMMON_IRON_DWARVES_2, + SPELL_TRIGGER_SUMMON_IRON_DWARVES_3, + SPELL_TRIGGER_SUMMON_IRON_VRYKUL +}; - void Reset() override - { - events.Reset(); - summons.DespawnAll(); - me->SetReactState(REACT_PASSIVE); - } +uint32 const pathSize = 11; +G3D::Vector3 const RazorscalePath[pathSize] = +{ + { 657.0227f, -361.1278f, 519.5406f }, + { 698.9319f, -340.9654f, 520.4857f }, + { 713.8673f, -290.2219f, 518.4573f }, + { 711.1782f, -259.6798f, 524.6802f }, + { 695.5101f, -234.6734f, 529.1528f }, + { 666.9619f, -220.7599f, 531.4860f }, + { 629.2765f, -219.7951f, 528.9301f }, + { 597.4018f, -233.7745f, 526.6508f }, + { 577.5307f, -275.4489f, 528.1241f }, + { 583.1092f, -319.5873f, 527.9302f }, + { 611.5800f, -353.1930f, 526.2653f } +}; - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override - { - switch (spell->Id) - { - case SPELL_FLAMED: - if (GameObject* harpoon = instance->GetGameObject(GO_RAZOR_HARPOON_1)) - harpoon->RemoveFromWorld(); - if (GameObject* harpoon = instance->GetGameObject(GO_RAZOR_HARPOON_2)) - harpoon->RemoveFromWorld(); - if (GameObject* harpoon = instance->GetGameObject(GO_RAZOR_HARPOON_3)) - harpoon->RemoveFromWorld(); - if (GameObject* harpoon = instance->GetGameObject(GO_RAZOR_HARPOON_4)) - harpoon->RemoveFromWorld(); - DoAction(ACTION_HARPOON_BUILD); - DoAction(ACTION_PLACE_BROKEN_HARPOON); - break; - case SPELL_HARPOON_SHOT_1: - case SPELL_HARPOON_SHOT_2: - case SPELL_HARPOON_SHOT_3: - case SPELL_HARPOON_SHOT_4: - DoCast(SPELL_HARPOON_TRIGGER); - break; - } - } +Position const RazorFlightPosition = { 585.3610f, -173.5592f, 456.8430f, 1.526665f }; +Position const RazorFlightPositionPhase2 = { 619.1450f, -238.0780f, 475.1800f, 1.423917f }; +Position const RazorscaleLand = { 585.4010f, -173.5430f, 408.5080f, 1.570796f }; +Position const RazorscaleGroundPosition = { 585.4010f, -173.5430f, 391.6421f, 1.570796f }; +Position const RazorscaleFirstPoint = { 657.0227f, -361.1278f, 519.5406f }; - void JustDied(Unit* /*killer*/) override +struct boss_razorscale : public BossAI +{ + boss_razorscale(Creature* creature) : BossAI(creature, BOSS_RAZORSCALE) + { + Initialize(); + } + + void Initialize() + { + _engineersCount = 3; + _defendersCount = 0; + _engineersSummonCount = 0; + _harpoonHitCount = 0; + _trappersCount = 0; + _permaGround = false; + _flyCount = 0; + me->SetDisableGravity(true); + me->SetByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + } + + void Reset() override + { + _Reset(); + Initialize(); + events.SetPhase(PHASE_NONE); + me->SummonCreatureGroup(RAZORSCALE_EXPEDITION_GROUP); + me->SummonCreatureGroup(RAZORSCALE_FIRE_STATE_10_GROUP); + if (Is25ManRaid()) + me->SummonCreatureGroup(RAZORSCALE_FIRE_STATE_25_GROUP); + // @Developer remove this comment when someone create a way to change view distance for objects + // me->GetMotionMaster()->MovePoint(POINT_START_WAYPOINT, RazorscaleFirstPoint); + // And apply it on DB: UPDATE `creature` SET `position_x`=699.7847, `position_y`=-424.8246, `position_z`=589.2745, `orientation`=1.972222 WHERE `guid`=137611; -- Razorscale + SetCombatMovement(false); + } + + void HandleInitialMovement() + { + Movement::PointsArray path(RazorscalePath, RazorscalePath + pathSize); + Movement::MoveSplineInit init(me); + init.MovebyPath(path, 0); + init.SetCyclic(); + init.SetFly(); + init.Launch(); + } + + bool CanAIAttack(Unit const* target) const override + { + switch (target->GetEntry()) + { + case NPC_EXPEDITION_DEFENDER: + case NPC_EXPEDITION_TRAPPER: + case NPC_EXPEDITION_COMMANDER: + case NPC_EXPEDITION_ENGINEER: + return false; + default: + return BossAI::CanAIAttack(target); + } + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me); + ScheduleAirPhaseEvents(); + summons.DoAction(ACTION_START_FIGHT, DummyEntryCheckPredicate()); + events.ScheduleEvent(EVENT_BERSERK, Minutes(15)); + HandleMusic(true); + me->SetByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + } + + void ScheduleAirPhaseEvents() + { + events.ScheduleEvent(EVENT_FIREBALL, Seconds(3), 0, PHASE_AIR); + events.ScheduleEvent(EVENT_DEVOURING_FLAME, Seconds(9), 0, PHASE_AIR); + events.ScheduleEvent(EVENT_SUMMON_MINIONS, Seconds(1), 0, PHASE_AIR); + } + + void ScheduleGroundPhaseEvents() + { + events.ScheduleEvent(EVENT_FIREBOLT, Seconds(3), 0, PHASE_PERMA_GROUND); + events.ScheduleEvent(EVENT_FUSE_ARMOR, Seconds(15), 0, PHASE_PERMA_GROUND); + events.ScheduleEvent(EVENT_FLAME_BREATH_GROUND, Seconds(18), 0, PHASE_PERMA_GROUND); + events.ScheduleEvent(EVENT_DEVOURING_FLAME, Seconds(22), 0, PHASE_PERMA_GROUND); + } + + void DoAction(int32 actionId) override + { + switch (actionId) + { + case ACTION_START_FIGHT: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + me->SetSpeedRate(MOVE_RUN, 3.0f); + me->StopMoving(); + me->GetMotionMaster()->MovePoint(POINT_RAZORSCALE_FLIGHT, RazorFlightPosition); + break; + case ACTION_GROUND_PHASE: + me->InterruptNonMeleeSpells(false); + events.SetPhase(PHASE_GROUND); + _harpoonHitCount = 0; + me->SetSpeedRate(MOVE_RUN, 3.0f); + me->GetMotionMaster()->MovePoint(POINT_RAZORSCALE_LAND, RazorscaleLand); + break; + case ACTION_START_PERMA_GROUND: { - events.Reset(); - summons.DespawnAll(); + me->SetDisableGravity(false); + me->RemoveByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + me->RemoveAurasDueToSpell(SPELL_STUN_SELF); + Talk(EMOTE_PERMA_GROUND); + DoCastSelf(SPELL_WING_BUFFET); + EntryCheckPredicate pred(NPC_EXPEDITION_TRAPPER); + summons.DoAction(ACTION_STOP_CAST, pred); + events.ScheduleEvent(EVENT_RESUME_MOVE_CHASE, Milliseconds(1)); + ScheduleGroundPhaseEvents(); + break; } + default: + break; + } + } - void DoAction(int32 action) override - { - if (instance->GetBossState(BOSS_RAZORSCALE) != IN_PROGRESS) - return; + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE) + return; - switch (action) + switch (pointId) + { + case POINT_START_WAYPOINT: + HandleInitialMovement(); + break; + case POINT_RAZORSCALE_FLIGHT: + me->UpdateSpeed(MOVE_RUN); + me->SetFacingTo(RazorFlightPosition.GetOrientation()); + DoZoneInCombat(); + break; + case POINT_RAZORSCALE_GROUND: + me->SetDisableGravity(false); + me->RemoveByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + if (!_permaGround) { - case ACTION_HARPOON_BUILD: - events.ScheduleEvent(EVENT_BUILD_HARPOON_1, 50000); - if (me->GetMap()->GetSpawnMode() == RAID_DIFFICULTY_25MAN_NORMAL) - events.ScheduleEvent(EVENT_BUILD_HARPOON_3, 90000); - break; - case ACTION_PLACE_BROKEN_HARPOON: - for (uint8 n = 0; n < RAID_MODE(2, 4); n++) - me->SummonGameObject(GO_RAZOR_BROKEN_HARPOON, PosHarpoon[n].GetPositionX(), PosHarpoon[n].GetPositionY(), PosHarpoon[n].GetPositionZ(), 2.286f, QuaternionData(), 180); - break; + DoCastSelf(SPELL_STUN_SELF, true); + EntryCheckPredicate pred(NPC_EXPEDITION_TRAPPER); + summons.DoAction(ACTION_SHACKLE_RAZORSCALE, pred); + if (Creature* commander = instance->GetCreature(DATA_EXPEDITION_COMMANDER)) + commander->AI()->DoAction(ACTION_GROUND_PHASE); + events.ScheduleEvent(EVENT_FLAME_BREATH, Seconds(30), 0, PHASE_GROUND); } - } + break; + case POINT_RAZORSCALE_TAKEOFF: + me->SetSpeedRate(MOVE_RUN, 3.0f); + me->GetMotionMaster()->MovePoint(POINT_RAZORSCALE_FLIGHT_2, RazorFlightPositionPhase2); + break; + case POINT_RAZORSCALE_FLIGHT_2: + me->SetFacingTo(RazorFlightPositionPhase2.GetOrientation()); + me->SetReactState(REACT_AGGRESSIVE); + ScheduleAirPhaseEvents(); + ++_flyCount; + me->UpdateSpeed(MOVE_RUN); + break; + case POINT_RAZORSCALE_LAND: + me->UpdateSpeed(MOVE_RUN); + me->SetFacingTo(RazorscaleLand.GetOrientation()); + me->GetMotionMaster()->MoveLand(POINT_RAZORSCALE_GROUND, RazorscaleGroundPosition); + break; + default: + break; + } + } - void UpdateAI(uint32 Diff) override - { - events.Update(Diff); + void JustSummoned(Creature* summon) override + { + BossAI::JustSummoned(summon); - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_BUILD_HARPOON_1: - Talk(EMOTE_HARPOON); - if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_1, PosHarpoon[0].GetPositionX(), PosHarpoon[0].GetPositionY(), PosHarpoon[0].GetPositionZ(), 4.790f, QuaternionData(), uint32(me->GetRespawnTime()))) - { - if (GameObject* brokenHarpoon = harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) //only nearest broken harpoon - brokenHarpoon->RemoveFromWorld(); - events.ScheduleEvent(EVENT_BUILD_HARPOON_2, 20000); - events.CancelEvent(EVENT_BUILD_HARPOON_1); - } - return; - case EVENT_BUILD_HARPOON_2: - Talk(EMOTE_HARPOON); - if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_2, PosHarpoon[1].GetPositionX(), PosHarpoon[1].GetPositionY(), PosHarpoon[1].GetPositionZ(), 4.659f, QuaternionData(), uint32(me->GetRespawnTime()))) - { - if (GameObject* brokenHarpoon = harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) - brokenHarpoon->RemoveFromWorld(); - events.CancelEvent(EVENT_BUILD_HARPOON_2); - } - return; - case EVENT_BUILD_HARPOON_3: - Talk(EMOTE_HARPOON); - if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_3, PosHarpoon[2].GetPositionX(), PosHarpoon[2].GetPositionY(), PosHarpoon[2].GetPositionZ(), 5.382f, QuaternionData(), uint32(me->GetRespawnTime()))) - { - if (GameObject* brokenHarpoon = harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) - brokenHarpoon->RemoveFromWorld(); - events.ScheduleEvent(EVENT_BUILD_HARPOON_4, 20000); - events.CancelEvent(EVENT_BUILD_HARPOON_3); - } - return; - case EVENT_BUILD_HARPOON_4: - Talk(EMOTE_HARPOON); - if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_4, PosHarpoon[3].GetPositionX(), PosHarpoon[3].GetPositionY(), PosHarpoon[3].GetPositionZ(), 4.266f, QuaternionData(), uint32(me->GetRespawnTime()))) - { - if (GameObject* brokenHarpoon = harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) - brokenHarpoon->RemoveFromWorld(); - events.CancelEvent(EVENT_BUILD_HARPOON_4); - } - return; - } - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override + switch (summon->GetEntry()) { - return GetUlduarAI<boss_razorscale_controllerAI>(creature); + case NPC_EXPEDITION_DEFENDER: + summon->AI()->SetData(DATA_EXPEDITION_NUMBER, _defendersCount); + ++_defendersCount; + break; + case NPC_EXPEDITION_ENGINEER: + summon->AI()->SetData(DATA_EXPEDITION_NUMBER, _engineersSummonCount); + ++_engineersSummonCount; + break; + case NPC_EXPEDITION_TRAPPER: + summon->AI()->SetData(DATA_EXPEDITION_NUMBER, _trappersCount); + ++_trappersCount; + break; + default: + break; } -}; + } -class go_razorscale_harpoon : public GameObjectScript -{ - public: - go_razorscale_harpoon() : GameObjectScript("go_razorscale_harpoon") { } - - struct go_razorscale_harpoonAI : public GameObjectAI + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + if (summon->GetEntry() == NPC_EXPEDITION_ENGINEER) { - go_razorscale_harpoonAI(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { } - - InstanceScript* instance; - - bool GossipHello(Player* /*player*/) override - { - if (instance->GetCreature(BOSS_RAZORSCALE)) - me->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - return false; - } - }; + _engineersCount--; + if (_engineersCount == 0) + if (Creature* commander = instance->GetCreature(DATA_EXPEDITION_COMMANDER)) + commander->AI()->DoAction(ACTION_ENGINEER_DEAD); + } + } - GameObjectAI* GetAI(GameObject* go) const override + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_HARPOON_TRIGGER) { - return GetUlduarAI<go_razorscale_harpoonAI>(go); + _harpoonHitCount++; + if (_harpoonHitCount == RAID_MODE(2, 4)) + DoAction(ACTION_GROUND_PHASE); } -}; - -class boss_razorscale : public CreatureScript -{ - public: - boss_razorscale() : CreatureScript("boss_razorscale") { } - - struct boss_razorscaleAI : public BossAI + } + + uint32 GetData(uint32 type) const override + { + if (type == DATA_QUICK_SHAVE && _flyCount <= 1) + return 1; + return 0; + } + + void EnterEvadeMode(EvadeReason why) override + { + if (why == EVADE_REASON_BOUNDARY && !events.IsInPhase(PHASE_PERMA_GROUND)) + return; + + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + summons.DespawnAll(); + HandleMusic(false); + _EnterEvadeMode(); + _DespawnAtEvade(); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + HandleMusic(false); + } + + void HandleMusic(bool active) + { + uint32 enabled = active ? 1 : 0; + instance->DoUpdateWorldState(WORLD_STATE_RAZORSCALE_MUSIC, enabled); + } + + void SummonMinions() + { + float x = frand(540.0f, 640.0f); // Safe range is between 500 and 650 + float y = frand(-230.0f, -195.0f); // Safe range is between -235 and -145 + float z = 391.517f; // Ground level + me->SummonCreature(NPC_RAZORSCALE_SPAWNER, x, y, z, 0, TEMPSUMMON_TIMED_DESPAWN, 15000); + } + + void DamageTaken(Unit* /*done_by*/, uint32 &damage) override + { + if (!_permaGround && me->HealthBelowPctDamaged(50, damage) && events.IsInPhase(PHASE_GROUND)) { - boss_razorscaleAI(Creature* creature) : BossAI(creature, BOSS_RAZORSCALE) - { - Initialize(); - // Do not let Razorscale be affected by Battle Shout buff - me->ApplySpellImmune(0, IMMUNITY_ID, (SPELL_BATTLE_SHOUT), true); - FlyCount = 0; - EnrageTimer = 0; - Enraged = false; - phase = PHASE_GROUND; - } - - void Initialize() - { - PermaGround = false; - HarpoonCounter = 0; - } - - Phases phase; - - uint32 EnrageTimer; - uint8 FlyCount; - uint8 HarpoonCounter; - bool PermaGround; - bool Enraged; - - void Reset() override - { - _Reset(); - me->SetCanFly(true); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_PASSIVE); - Initialize(); - if (Creature* commander = instance->GetCreature(DATA_EXPEDITION_COMMANDER)) - commander->AI()->DoAction(ACTION_COMMANDER_RESET); - } - - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - if (Creature* controller = instance->GetCreature(DATA_RAZORSCALE_CONTROL)) - controller->AI()->DoAction(ACTION_HARPOON_BUILD); - me->SetSpeedRate(MOVE_FLIGHT, 3.0f); - me->SetReactState(REACT_PASSIVE); - phase = PHASE_GROUND; - events.SetPhase(PHASE_GROUND); - FlyCount = 0; - EnrageTimer = 600000; - Enraged = false; - events.ScheduleEvent(EVENT_FLIGHT, 0, 0, PHASE_GROUND); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - if (Creature* controller = instance->GetCreature(DATA_RAZORSCALE_CONTROL)) - controller->AI()->Reset(); - } + _permaGround = true; + me->SetReactState(REACT_AGGRESSIVE); + events.SetPhase(PHASE_PERMA_GROUND); + DoAction(ACTION_START_PERMA_GROUND); + } + } - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override - { - if (spell->Id == SPELL_HARPOON_TRIGGER) - ++HarpoonCounter; - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void MovementInform(uint32 type, uint32 id) override - { - if (type == EFFECT_MOTION_TYPE && id == 1) - { - phase = PHASE_GROUND; - events.SetPhase(PHASE_GROUND); - events.ScheduleEvent(EVENT_LAND, 0, 0, PHASE_GROUND); - } - } + events.Update(diff); - uint32 GetData(uint32 type) const override - { - if (type == DATA_QUICK_SHAVE) - if (FlyCount <= 2) - return 1; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - return 0; - } - - void UpdateAI(uint32 Diff) override + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - if (!UpdateVictim()) - return; - - events.Update(Diff); - - if (HealthBelowPct(50) && !PermaGround) - EnterPermaGround(); - - if (EnrageTimer <= Diff && !Enraged) - { - DoCast(me, SPELL_BERSERK); - Enraged = true; - } - else - EnrageTimer -= Diff; - - if (HarpoonCounter == RAID_MODE(2, 4)) + case EVENT_BERSERK: + DoCastSelf(SPELL_BERSERK, true); + Talk(EMOTE_BERSERK, me); + break; + case EVENT_FIREBALL: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) + DoCast(target, SPELL_FIREBALL); + events.Repeat(Seconds(2), Seconds(3)); + break; + case EVENT_DEVOURING_FLAME: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM)) + DoCast(target, SPELL_DEVOURING_FLAME); + if (_permaGround) + events.Repeat(Seconds(10), Seconds(12)); + else + events.Repeat(Seconds(6), Seconds(12)); + break; + case EVENT_SUMMON_MINIONS: { - HarpoonCounter = 0; - me->GetMotionMaster()->MoveLand(1, RazorGround); - } - - if (phase == PHASE_GROUND) - { - while (uint32 eventId = events.ExecuteEvent()) + uint8 random = urand(2, 4); + uint8 time = 5; + for (uint8 n = 0; n < random; ++n) { - switch (eventId) - { - case EVENT_FLIGHT: - phase = PHASE_FLIGHT; - events.SetPhase(PHASE_FLIGHT); - me->SetCanFly(true); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_PASSIVE); - me->AttackStop(); - me->GetMotionMaster()->MoveTakeoff(0, RazorFlight); - events.ScheduleEvent(EVENT_FIREBALL, 7000, 0, PHASE_FLIGHT); - events.ScheduleEvent(EVENT_DEVOURING, 10000, 0, PHASE_FLIGHT); - events.ScheduleEvent(EVENT_SUMMON, 5000, 0, PHASE_FLIGHT); - ++FlyCount; - return; - case EVENT_LAND: - me->SetCanFly(false); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); - if (Creature* commander = instance->GetCreature(DATA_EXPEDITION_COMMANDER)) - commander->AI()->DoAction(ACTION_GROUND_PHASE); - events.ScheduleEvent(EVENT_BREATH, 30000, 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_BUFFET, 33000, 0, PHASE_GROUND); - events.ScheduleEvent(EVENT_FLIGHT, 35000, 0, PHASE_GROUND); - return; - case EVENT_BREATH: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); - me->RemoveAllAuras(); - me->SetReactState(REACT_AGGRESSIVE); - Talk(EMOTE_BREATH); - DoCastAOE(SPELL_FLAMEBREATH); - events.CancelEvent(EVENT_BREATH); - return; - case EVENT_BUFFET: - DoCastAOE(SPELL_WINGBUFFET); - if (Creature* controller = instance->GetCreature(DATA_RAZORSCALE_CONTROL)) - controller->CastSpell(controller, SPELL_FLAMED, true); - events.CancelEvent(EVENT_BUFFET); - return; - } + events.ScheduleEvent(EVENT_SUMMON_MINIONS_2, Seconds(time), 0, PHASE_AIR); + time += 5; } + events.Repeat(Seconds(40)); + break; } - if (phase == PHASE_PERMAGROUND) + case EVENT_SUMMON_MINIONS_2: + SummonMinions(); + break; + case EVENT_FLAME_BREATH: + me->RemoveAurasDueToSpell(SPELL_STUN_SELF); + Talk(EMOTE_BREATH, me); + DoCastVictim(FLAME_BREATH); + events.ScheduleEvent(EVENT_WING_BUFFET, Seconds(2), 0, PHASE_GROUND); + break; + case EVENT_FLAME_BREATH_GROUND: + Talk(EMOTE_BREATH, me); + DoCastVictim(FLAME_BREATH); + events.Repeat(Seconds(15), Seconds(18)); + break; + case EVENT_WING_BUFFET: { - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_FLAME: - DoCastAOE(SPELL_FLAMEBUFFET); - events.ScheduleEvent(EVENT_FLAME, 10000, 0, PHASE_PERMAGROUND); - return; - case EVENT_BREATH: - Talk(EMOTE_BREATH); - DoCastVictim(SPELL_FLAMEBREATH); - events.ScheduleEvent(EVENT_BREATH, 20000, 0, PHASE_PERMAGROUND); - return; - case EVENT_FIREBALL: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true)) - DoCast(target, SPELL_FIREBALL); - events.ScheduleEvent(EVENT_FIREBALL, 3000, 0, PHASE_PERMAGROUND); - return; - case EVENT_DEVOURING: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true)) - DoCast(target, SPELL_DEVOURING_FLAME); - events.ScheduleEvent(EVENT_DEVOURING, 10000, 0, PHASE_PERMAGROUND); - return; - case EVENT_BUFFET: - DoCastAOE(SPELL_WINGBUFFET); - events.CancelEvent(EVENT_BUFFET); - return; - case EVENT_FUSE: - DoCastVictim(SPELL_FUSEARMOR); - events.ScheduleEvent(EVENT_FUSE, 10000, 0, PHASE_PERMAGROUND); - return; - } - } - - DoMeleeAttackIfReady(); + DoCastSelf(SPELL_WING_BUFFET); + events.ScheduleEvent(EVENT_FIREBOLT, Seconds(2), 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_RESUME_AIR_PHASE, Seconds(4), 0, PHASE_GROUND); + EntryCheckPredicate pred(NPC_EXPEDITION_TRAPPER); + summons.DoAction(ACTION_STOP_CAST, pred); + break; } - else + case EVENT_RESUME_AIR_PHASE: { - if (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_FIREBALL: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true)) - DoCast(target, SPELL_FIREBALL); - events.ScheduleEvent(EVENT_FIREBALL, 3000, 0, PHASE_FLIGHT); - return; - case EVENT_DEVOURING: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 200.0f, true)) - me->CastSpell(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), SPELL_DEVOURING_FLAME, true); - events.ScheduleEvent(EVENT_DEVOURING, 10000, 0, PHASE_FLIGHT); - return; - case EVENT_SUMMON: - SummonMoleMachines(); - events.ScheduleEvent(EVENT_SUMMON, 45000, 0, PHASE_FLIGHT); - return; - } - } + me->SetDisableGravity(true); + me->SetByteFlag(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_ANIM_TIER, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + events.SetPhase(PHASE_AIR); + me->SetReactState(REACT_PASSIVE); + Position pos = me->GetPosition(); + pos.m_positionZ += 10.0f; + me->GetMotionMaster()->MoveTakeoff(POINT_RAZORSCALE_TAKEOFF, pos); + EntryCheckPredicate pred(NPC_EXPEDITION_ENGINEER); + summons.DoAction(ACTION_FIX_HARPOONS, pred); + break; } + case EVENT_FIREBOLT: + DoCastSelf(SPELL_FIREBOLT); + break; + case EVENT_FUSE_ARMOR: + DoCastVictim(SPELL_FUSE_ARMOR); + events.Repeat(Seconds(10), Seconds(15)); + break; + case EVENT_RESUME_MOVE_CHASE: + SetCombatMovement(true); + if (Unit* victim = me->GetVictim()) + me->GetMotionMaster()->MoveChase(victim); + break; + default: + break; } - void EnterPermaGround() - { - Talk(EMOTE_PERMA); - phase = PHASE_PERMAGROUND; - events.SetPhase(PHASE_PERMAGROUND); - me->SetCanFly(false); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveAurasDueToSpell(SPELL_HARPOON_TRIGGER); - me->SetSpeedRate(MOVE_FLIGHT, 1.0f); - PermaGround = true; - DoCastAOE(SPELL_FLAMEBREATH); - events.ScheduleEvent(EVENT_FLAME, 15000, 0, PHASE_PERMAGROUND); - events.RescheduleEvent(EVENT_DEVOURING, 15000, 0, PHASE_PERMAGROUND); - events.RescheduleEvent(EVENT_BREATH, 20000, 0, PHASE_PERMAGROUND); - events.RescheduleEvent(EVENT_FIREBALL, 3000, 0, PHASE_PERMAGROUND); - events.RescheduleEvent(EVENT_DEVOURING, 6000, 0, PHASE_PERMAGROUND); - events.RescheduleEvent(EVENT_BUFFET, 2500, 0, PHASE_PERMAGROUND); - events.RescheduleEvent(EVENT_FUSE, 5000, 0, PHASE_PERMAGROUND); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + if (events.IsInPhase(PHASE_PERMA_GROUND)) + DoMeleeAttackIfReady(); + } + +private: + uint8 _engineersCount; + uint8 _engineersSummonCount; + uint8 _defendersCount; + uint8 _harpoonHitCount; + uint8 _trappersCount; + bool _permaGround; + uint32 _flyCount; +}; - void SummonMoleMachines() +struct npc_expedition_commander : public ScriptedAI +{ + npc_expedition_commander(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), + _is25Man(Is25ManRaid()), _building(false), _destroy(false), _stopControllers(false) { } + + void Reset() override + { + _events.Reset(); + _events.SetPhase(PHASE_NONE); + BuildBrokenHarpoons(); + } + + bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + { + if (gossipListId == GOSSIP_START_ENCOUNTER) + { + CloseGossipMenuFor(player); + _events.SetPhase(PHASE_COMBAT); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + if (Creature* razorscale = _instance->GetCreature(BOSS_RAZORSCALE)) + razorscale->AI()->DoAction(ACTION_START_FIGHT); + return true; + } + return false; + } + + void BuildBrokenHarpoons() + { + uint8 harpoonNumber = _is25Man ? 4 : 2; + for (uint8 i = 0; i < harpoonNumber; ++i) + me->SummonGameObject(GO_RAZOR_BROKEN_HARPOON, PosBrokenHarpoon[i], QuaternionData(0.0f, 0.0f, -0.8987932f, 0.4383728f), WEEK); + } + + void DestroyHarpoons() + { + for (ObjectGuid harpoonGuid : _harpoons) + if (GameObject* harpoon = ObjectAccessor::GetGameObject(*me, harpoonGuid)) + harpoon->RemoveFromWorld(); + + _harpoons.clear(); + BuildBrokenHarpoons(); + _events.ScheduleEvent(EVENT_HANDLE_DESTROY_HARPOON, Seconds(10)); + } + + void HandleControllersStopCast() + { + std::list<Creature*> Controllers; + me->GetCreatureListWithEntryInGrid(Controllers, NPC_RAZORSCALE_CONTROLLER, 100.0f); + + for (Creature* controller : Controllers) + controller->InterruptNonMeleeSpells(false); + + _stopControllers = false; + } + + void BuildHarpoon(uint8 harpoonNumber) + { + + if (_is25Man) + { + switch (harpoonNumber) { - // Adds will come in waves from mole machines. One mole can spawn a Dark Rune Watcher - // with 1-2 Guardians, or a lone Sentinel. Up to 4 mole machines can spawn adds at any given time. - uint8 random = urand(2, 4); - for (uint8 n = 0; n < random; n++) - { - float x = float(irand(540, 640)); // Safe range is between 500 and 650 - float y = float(irand(-230, -195)); // Safe range is between -235 and -145 - float z = GROUND_Z; // Ground level - me->SummonCreature(MOLE_MACHINE_TRIGGER, x, y, z, 0, TEMPSUMMON_TIMED_DESPAWN, 15000); - } + case HARPOON_1: + if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_3, PosHarpoon[2], QuaternionData(0.0f, 0.0f, -0.573576f, 0.8191524f), WEEK)) + _harpoons.emplace_back(harpoon->GetGUID()); + break; + case HARPOON_2: + if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_1, PosHarpoon[0], QuaternionData(0.0f, 0.0f, -0.6293201f, 0.7771462f), WEEK)) + _harpoons.emplace_back(harpoon->GetGUID()); + break; + case HARPOON_3: + if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_2, PosHarpoon[1], QuaternionData(0.0f, 0.0f, -0.6691303f, 0.743145f), WEEK)) + _harpoons.emplace_back(harpoon->GetGUID()); + break; + case HARPOON_4: + if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_4, PosHarpoon[3], QuaternionData(0.0f, 0.0f, -0.7660437f, 0.6427886f), WEEK)) + _harpoons.emplace_back(harpoon->GetGUID()); + break; + default: + break; } - - void DoAction(int32 action) override + } + else + { + switch (harpoonNumber) { - switch (action) - { - case ACTION_EVENT_START: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_AGGRESSIVE); - DoZoneInCombat(me, 150.0f); - break; - } + case HARPOON_1: + if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_1, PosHarpoon[harpoonNumber], QuaternionData(0.0f, 0.0f, -0.6293201f, 0.7771462f), 0)) + _harpoons.emplace_back(harpoon->GetGUID()); + break; + case HARPOON_2: + if (GameObject* harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_2, PosHarpoon[harpoonNumber], QuaternionData(0.0f, 0.0f, -0.6691303f, 0.743145f), 0)) + _harpoons.emplace_back(harpoon->GetGUID()); + break; + default: + break; } - }; + } + } + + void DoAction(int32 actionId) override + { + if (_building && actionId != ACTION_ENGINEER_DEAD) + return; - CreatureAI* GetAI(Creature* creature) const override + switch (actionId) { - return GetUlduarAI<boss_razorscaleAI>(creature); + case ACTION_START_FIGHT: + Talk(SAY_COMMANDER_AGGRO); + break; + case ACTION_GROUND_PHASE: + Talk(SAY_COMMANDER_GROUND_PHASE); + break; + case ACTION_ENGINEER_DEAD: + Talk(SAY_COMMANDER_ENGINEERS_DEAD); + _events.Reset(); + _building = false; + break; + case ACTION_BUILD_HARPOON_1: + _building = true; + _events.ScheduleEvent(EVENT_BUILD_HARPOON_1, Seconds(18)); + break; + case ACTION_BUILD_HARPOON_2: + _building = true; + _events.ScheduleEvent(EVENT_BUILD_HARPOON_2, Seconds(18)); + break; + case ACTION_BUILD_HARPOON_3: + _building = true; + _events.ScheduleEvent(EVENT_BUILD_HARPOON_3, Seconds(18)); + break; + case ACTION_BUILD_HARPOON_4: + _building = true; + _events.ScheduleEvent(EVENT_BUILD_HARPOON_4, Seconds(18)); + break; + case ACTION_DESTROY_HARPOONS: + if (_destroy) + return; + _destroy = true; + DestroyHarpoons(); + break; + case ACTION_STOP_CONTROLLERS: + if (_stopControllers) + return; + _stopControllers = true; + HandleControllersStopCast(); + break; + default: + break; } -}; + } -class npc_expedition_commander : public CreatureScript -{ - public: - npc_expedition_commander() : CreatureScript("npc_expedition_commander") { } + void UpdateAI(uint32 diff) override + { + if (!_events.IsInPhase(PHASE_COMBAT)) + return; - struct npc_expedition_commanderAI : public ScriptedAI - { - npc_expedition_commanderAI(Creature* creature) : ScriptedAI(creature), summons(creature) - { - Initialize(); - instance = me->GetInstanceScript(); - } + _events.Update(diff); - void Initialize() + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - AttackStartTimer = 0; - Phase = 0; - Greet = false; + case EVENT_BUILD_HARPOON_1: + BuildHarpoon(HARPOON_1); + _building = false; + break; + case EVENT_BUILD_HARPOON_2: + BuildHarpoon(HARPOON_2); + _building = false; + break; + case EVENT_BUILD_HARPOON_3: + BuildHarpoon(HARPOON_3); + _building = false; + break; + case EVENT_BUILD_HARPOON_4: + BuildHarpoon(HARPOON_4); + _building = false; + break; + case EVENT_HANDLE_DESTROY_HARPOON: + _destroy = false; + break; + default: + break; } + } + } + +private: + InstanceScript* _instance; + GuidVector _harpoons; + bool _is25Man; + bool _building; + bool _destroy; + bool _stopControllers; + EventMap _events; +}; - InstanceScript* instance; - SummonList summons; +struct npc_expedition_defender : public ScriptedAI +{ + npc_expedition_defender(Creature* creature) : ScriptedAI(creature), _myPositionNumber(0), _instance(creature->GetInstanceScript()) + { + me->SetRegenerateHealth(false); + } + + void Reset() override + { + DoCastSelf(SPELL_THREAT); + } + + bool CanAIAttack(Unit const* target) const override + { + if (target->GetEntry() == NPC_RAZORSCALE || target->GetEntry() == NPC_RAZORSCALE_SPAWNER) + return false; - bool Greet; - uint32 AttackStartTimer; - uint8 Phase; - ObjectGuid Engineer[4]; - ObjectGuid Defender[4]; + return ScriptedAI::CanAIAttack(target); + } + + void SetData(uint32 type, uint32 value) override + { + if (type == DATA_EXPEDITION_NUMBER) + _myPositionNumber = value; + } + + void DoAction(int32 actionId) override + { + if (actionId == ACTION_START_FIGHT) + me->GetMotionMaster()->MovePoint(POINT_DEFENDER_ATTACK, DefendersPosition[_myPositionNumber]); + } + + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != POINT_MOTION_TYPE && pointId != POINT_DEFENDER_ATTACK) + return; + + me->SetHomePosition(DefendersPosition[_myPositionNumber]); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + } + +private: + uint8 _myPositionNumber; + InstanceScript* _instance; +}; - void Reset() override - { - Initialize(); - summons.DespawnAll(); - } +struct npc_expedition_trapper : public ScriptedAI +{ + npc_expedition_trapper(Creature* creature) : ScriptedAI(creature), _myPositionNumber(0), _instance(creature->GetInstanceScript()) + { + SetCombatMovement(false); + me->SetReactState(REACT_PASSIVE); + } + + void DoAction(int32 actionId) override + { + if (!me->IsAlive()) + return; + + switch (actionId) + { + case ACTION_SHACKLE_RAZORSCALE: + me->GetMotionMaster()->MovePoint(POINT_SHACKLE_RAZORSCALE, TrapperPosition[_myPositionNumber]); + break; + case ACTION_RETURN_TO_BASE: + me->GetMotionMaster()->MoveTargetedHome(); + break; + case ACTION_START_FIGHT: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + break; + case ACTION_STOP_CAST: + me->InterruptNonMeleeSpells(false); + _scheduler.Schedule(Seconds(2), [this](TaskContext /*context*/) + { + me->GetMotionMaster()->MoveTargetedHome(); + }); + if (Creature* commander = _instance->GetCreature(DATA_EXPEDITION_COMMANDER)) + commander->AI()->DoAction(ACTION_STOP_CONTROLLERS); + break; + default: + break; + } + } + + void SetData(uint32 type, uint32 value) override + { + if (type == DATA_EXPEDITION_NUMBER) + _myPositionNumber = value; + } + + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != POINT_MOTION_TYPE && pointId != POINT_SHACKLE_RAZORSCALE) + return; + + DoCastSelf(SPELL_SHACKLE); + } + + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } + +private: + uint8 _myPositionNumber; + InstanceScript* _instance; + TaskScheduler _scheduler; +}; - void MoveInLineOfSight(Unit* who) override +struct npc_expedition_engineer : public ScriptedAI +{ + npc_expedition_engineer(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _myPositionNumber(0), _canUpdateAI(false) { } - { - if (!Greet && me->IsWithinDistInMap(who, 10.0f) && who->GetTypeId() == TYPEID_PLAYER) - { - Talk(SAY_INTRO); - Greet = true; - } - } + void Reset() override + { + me->SetReactState(REACT_PASSIVE); + _scheduler.CancelAll(); + } - void JustSummoned(Creature* summoned) override - { - summons.Summon(summoned); - } + void DoAction(int32 actionId) override + { + if (!me->IsAlive()) + return; - void DoAction(int32 action) override + if (actionId == ACTION_START_FIGHT) + { + _canUpdateAI = true; + if (_myPositionNumber == ENGINEER_EAST) + Talk(SAY_AGGRO); + _scheduler.Schedule(Seconds(28), [this](TaskContext /*context*/) { - switch (action) - { - case ACTION_GROUND_PHASE: - Talk(SAY_GROUND_PHASE); - break; - case ACTION_COMMANDER_RESET: - summons.DespawnAll(); - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - break; - } - } - - void UpdateAI(uint32 Diff) override + HandleHarpoonMovement(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + }); + } + else if (actionId == ACTION_FIX_HARPOONS) + { + if (_myPositionNumber == ENGINEER_EAST) + Talk(SAY_AGGRO); + _scheduler.Schedule(Seconds(28), [this](TaskContext /*context*/) { - if (AttackStartTimer <= Diff) - { - switch (Phase) - { - case 1: - instance->SetBossState(BOSS_RAZORSCALE, IN_PROGRESS); - summons.DespawnAll(); - AttackStartTimer = 1000; - Phase = 2; - break; - case 2: - for (uint8 n = 0; n < RAID_MODE(2, 4); n++) - { - if (Creature* summonedEngineer = me->SummonCreature(NPC_ENGINEER, PosEngSpawn, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000)) - { - summonedEngineer->SetWalk(false); - summonedEngineer->SetSpeedRate(MOVE_RUN, 0.5f); - summonedEngineer->SetHomePosition(PosEngRepair[n]); - summonedEngineer->GetMotionMaster()->MoveTargetedHome(); - Engineer[n] = summonedEngineer->GetGUID(); - } - } - if (Creature* firstSummon = ObjectAccessor::GetCreature(*me, Engineer[0])) - firstSummon->AI()->Talk(SAY_AGGRO_3); - Phase = 3; - AttackStartTimer = 14000; - break; - case 3: - for (uint8 n = 0; n < 4; n++) - { - if (Creature* summonedDefender = me->SummonCreature(NPC_DEFENDER, PosDefSpawn[n], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000)) - { - summonedDefender->SetWalk(false); - summonedDefender->SetHomePosition(PosDefCombat[n]); - summonedDefender->GetMotionMaster()->MoveTargetedHome(); - Defender[n] = summonedDefender->GetGUID(); - } - } - Phase = 4; - break; - case 4: - for (uint8 n = 0; n < RAID_MODE(2, 4); n++) - if (Creature* summonedEngineer = ObjectAccessor::GetCreature(*me, Engineer[n])) - summonedEngineer->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_USE_STANDING); - for (uint8 n = 0; n < 4; ++n) - if (Creature* summonedDefender = ObjectAccessor::GetCreature(*me, Defender[n])) - summonedDefender->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H); - Talk(SAY_AGGRO_2); - AttackStartTimer = 16000; - Phase = 5; - break; - case 5: - if (Creature* razorscale = instance->GetCreature(BOSS_RAZORSCALE)) - { - razorscale->AI()->DoAction(ACTION_EVENT_START); - me->SetInCombatWith(razorscale); - } - if (Creature* firstEngineer = ObjectAccessor::GetCreature(*me, Engineer[0])) - firstEngineer->AI()->Talk(SAY_AGGRO_1); - Phase = 6; - break; - } - } - else - AttackStartTimer -= Diff; - } + HandleHarpoonMovement(); + }); + } + } - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF: - CloseGossipMenuFor(player); - Phase = 1; - break; - } - return true; - } + void ChangeOrientation(float orientation) + { + _scheduler.Schedule(Milliseconds(1), [this, orientation](TaskContext /*context*/) + { + me->SetFacingTo(orientation); + }); + } - bool GossipHello(Player* player) override - { - if (instance->GetBossState(BOSS_RAZORSCALE) == NOT_STARTED) - { - player->PrepareGossipMenu(me); + void HandleHarpoonMovement() + { + switch (_myPositionNumber) + { + case ENGINEER_NORTH: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_1_25, SPLINE_ENGINEER_NORTH_25_HARPOON_1, false); + else + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_1, SPLINE_ENGINEER_NORTH_10_HARPOON_1, false); + break; + case ENGINEER_EAST: + Talk(SAY_START_REPAIR); + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_1_25, SPLINE_ENGINEER_EAST_25_HARPOON_1, false); + else + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_1, SPLINE_ENGINEER_EAST_10_HARPOON_1, false); + break; + case ENGINEER_WEST: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_1_25, SPLINE_ENGINEER_WEST_25_HARPOON_1, false); + else + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_1, SPLINE_ENGINEER_WEST_10_HARPOON_1, false); + break; + default: + break; + } + } - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - SendGossipMenuFor(player, 13853, me->GetGUID()); - } + void HandleSecondHarpoonMovement() + { + switch (_myPositionNumber) + { + case ENGINEER_NORTH: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_2_25, SPLINE_ENGINEER_NORTH_25_HARPOON_2, false); + else + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_2, SPLINE_ENGINEER_NORTH_10_HARPOON_2, false); + break; + case ENGINEER_EAST: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_2_25, SPLINE_ENGINEER_EAST_25_HARPOON_2, false); else - SendGossipMenuFor(player, 13910, me->GetGUID()); + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_2, SPLINE_ENGINEER_EAST_10_HARPOON_2, false); + break; + case ENGINEER_WEST: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_2_25, SPLINE_ENGINEER_WEST_25_HARPOON_2, false); + else + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_2, SPLINE_ENGINEER_WEST_10_HARPOON_2, false); + break; + default: + break; + } + } - return true; - } + void HandleThirdHarpoonMovement() + { + switch (_myPositionNumber) + { + case ENGINEER_NORTH: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_3, SPLINE_ENGINEER_NORTH_25_HARPOON_3, false); + else + me->GetMotionMaster()->MoveAlongSplineChain(POINT_BASE, SPLINE_ENGINEER_NORTH_10_BASE, false); + break; + case ENGINEER_EAST: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_3, SPLINE_ENGINEER_EAST_25_HARPOON_3, false); + else + me->GetMotionMaster()->MoveAlongSplineChain(POINT_BASE, SPLINE_ENGINEER_EAST_10_BASE, false); + break; + case ENGINEER_WEST: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_3, SPLINE_ENGINEER_WEST_25_HARPOON_3, false); + else + me->GetMotionMaster()->MoveAlongSplineChain(POINT_BASE, SPLINE_ENGINEER_WEST_10_BASE, false); + break; + default: + break; + } + } - }; + void HandleFourthHarpoonMovement() + { + switch (_myPositionNumber) + { + case ENGINEER_NORTH: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_4, SPLINE_ENGINEER_NORTH_25_HARPOON_4, false); + break; + case ENGINEER_EAST: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_4, SPLINE_ENGINEER_EAST_25_HARPOON_4, false); + break; + case ENGINEER_WEST: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_HARPOON_4, SPLINE_ENGINEER_WEST_25_HARPOON_4, false); + break; + default: + break; + } + } - CreatureAI* GetAI(Creature* creature) const override + void HandleBaseMovement() + { + switch (_myPositionNumber) { - return GetUlduarAI<npc_expedition_commanderAI>(creature); + case ENGINEER_NORTH: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_BASE, SPLINE_ENGINEER_NORTH_25_BASE, false); + break; + case ENGINEER_EAST: + if (Is25ManRaid()) + me->GetMotionMaster()->MovePoint(POINT_BASE, me->GetHomePosition()); + break; + case ENGINEER_WEST: + if (Is25ManRaid()) + me->GetMotionMaster()->MoveAlongSplineChain(POINT_BASE, SPLINE_ENGINEER_WEST_25_BASE, false); + break; + default: + break; } -}; + } -class npc_mole_machine_trigger : public CreatureScript -{ - public: - npc_mole_machine_trigger() : CreatureScript("npc_mole_machine_trigger") { } + void UpdateAI(uint32 diff) override + { + if (!_canUpdateAI) + return; - struct npc_mole_machine_triggerAI : public ScriptedAI - { - npc_mole_machine_triggerAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - SetCombatMovement(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED); - } + _scheduler.Update(diff); + } - void Initialize() - { - SummonGobTimer = 2000; - SummonNpcTimer = 6000; - DissapearTimer = 10000; - GobSummoned = false; - NpcSummoned = false; - } + void SetData(uint32 type, uint32 value) override + { + if (type == DATA_EXPEDITION_NUMBER) + _myPositionNumber = value; + } - uint32 SummonGobTimer; - uint32 SummonNpcTimer; - uint32 DissapearTimer; - bool GobSummoned; - bool NpcSummoned; + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != POINT_MOTION_TYPE && type != SPLINE_CHAIN_MOTION_TYPE) + return; - void Reset() override - { - Initialize(); - } + switch (pointId) + { + case POINT_HARPOON_1: + case POINT_HARPOON_1_25: + if (Creature* commander = _instance->GetCreature(DATA_EXPEDITION_COMMANDER)) + commander->AI()->DoAction(ACTION_BUILD_HARPOON_1); - void UpdateAI(uint32 Diff) override - { - if (!GobSummoned && SummonGobTimer <= Diff) + _scheduler. + Schedule(Seconds(3), [this](TaskContext /*context*/) { - DoCast(SPELL_SUMMON_MOLE_MACHINE); - GobSummoned = true; - } - else - SummonGobTimer -= Diff; - - if (!NpcSummoned && SummonNpcTimer <= Diff) + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_USE_STANDING); + }) + .Schedule(Seconds(18), [this](TaskContext /*context*/) { - switch (urand(0, 1 )) - { - case 0: - DoCast(SPELL_SUMMON_IRON_DWARVES); - break; - case 1: - DoCast(SPELL_SUMMON_IRON_DWARVES_2); - break; - } - - DoCast(SPELL_SUMMON_IRON_DWARVE_GUARDIAN); - DoCast(SPELL_SUMMON_IRON_DWARVE_WATCHER); - NpcSummoned = true; - } - else - SummonNpcTimer -= Diff; - - if (DissapearTimer <= Diff) + HandleSecondHarpoonMovement(); + }); + break; + case POINT_HARPOON_2: + case POINT_HARPOON_2_25: + if (Creature* commander = _instance->GetCreature(DATA_EXPEDITION_COMMANDER)) + commander->AI()->DoAction(ACTION_BUILD_HARPOON_2); + _scheduler.Schedule(Seconds(18), [this](TaskContext /*context*/) { - if (GameObject* molemachine = me->FindNearestGameObject(GO_MOLE_MACHINE, 1)) - molemachine->Delete(); - - me->DisappearAndDie(); - } - else - DissapearTimer -= Diff; - } + HandleThirdHarpoonMovement(); + }); + break; + case POINT_HARPOON_3: + if (Creature* commander = _instance->GetCreature(DATA_EXPEDITION_COMMANDER)) + commander->AI()->DoAction(ACTION_BUILD_HARPOON_3); + _scheduler.Schedule(Seconds(18), [this](TaskContext /*context*/) + { + HandleFourthHarpoonMovement(); + }); + break; + case POINT_HARPOON_4: + if (Creature* commander = _instance->GetCreature(DATA_EXPEDITION_COMMANDER)) + commander->AI()->DoAction(ACTION_BUILD_HARPOON_4); + _scheduler.Schedule(Seconds(18), [this](TaskContext /*context*/) + { + HandleBaseMovement(); + }); + break; + case POINT_BASE: + ChangeOrientation(4.61684f); + break; + default: + break; + } + } - void JustSummoned(Creature* summoned) override - { - summoned->AI()->DoZoneInCombat(); - } - }; +private: + InstanceScript* _instance; + TaskScheduler _scheduler; + uint8 _myPositionNumber; + bool _canUpdateAI; +}; - CreatureAI* GetAI(Creature* creature) const override +struct npc_razorscale_spawner : public ScriptedAI +{ + npc_razorscale_spawner(Creature* creature) : ScriptedAI(creature) { } + + void Reset() override + { + me->setActive(true); + me->SetReactState(REACT_PASSIVE); + _scheduler. + Schedule(Seconds(1), [this](TaskContext /*context*/) { - return GetUlduarAI<npc_mole_machine_triggerAI>(creature); - } + DoCastSelf(SPELL_SUMMON_MOLE_MACHINE); + }).Schedule(Seconds(6), [this](TaskContext /*context*/) + { + DoCastSelf(SummonMinionsSpells[urand(0, 3)]); + }); + } + + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } + +private: + TaskScheduler _scheduler; }; -class npc_devouring_flame : public CreatureScript +struct npc_darkrune_watcher : public ScriptedAI { - public: - npc_devouring_flame() : CreatureScript("npc_devouring_flame") { } - - struct npc_devouring_flameAI : public ScriptedAI + npc_darkrune_watcher(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + + void Reset() override + { + _events.Reset(); + me->SetReactState(REACT_PASSIVE); + _events.ScheduleEvent(EVENT_START_COMBAT, Seconds(2)); + if (Creature* razorscale = _instance->GetCreature(BOSS_RAZORSCALE)) + razorscale->AI()->JustSummoned(me); + } + + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_LIGHTNING_BOLT, Seconds(5)); + _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, Seconds(34)); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) { - npc_devouring_flameAI(Creature* creature) : ScriptedAI(creature) + switch (eventId) { - SetCombatMovement(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED); + case EVENT_START_COMBAT: + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + break; + case EVENT_LIGHTNING_BOLT: + DoCastVictim(LIGHTNING_BOLT); + _events.Repeat(Seconds(3)); + break; + case EVENT_CHAIN_LIGHTNING: + DoCastVictim(CHAIN_LIGHTNING); + _events.Repeat(Seconds(9), Seconds(15)); + break; + default: + break; } - void Reset() override - { - DoCast(SPELL_FLAME_GROUND); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetUlduarAI<npc_devouring_flameAI>(creature); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } + + DoMeleeAttackIfReady(); + } + +private: + InstanceScript* _instance; + EventMap _events; }; -class npc_darkrune_watcher : public CreatureScript +struct npc_darkrune_guardian : public ScriptedAI { - public: - npc_darkrune_watcher() : CreatureScript("npc_darkrune_watcher") { } - - struct npc_darkrune_watcherAI : public ScriptedAI + npc_darkrune_guardian(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _killedByBreath(false) { } + + void Reset() override + { + _events.Reset(); + me->SetReactState(REACT_PASSIVE); + _events.ScheduleEvent(EVENT_START_COMBAT, Seconds(2)); + if (Creature* razorscale = _instance->GetCreature(BOSS_RAZORSCALE)) + razorscale->AI()->JustSummoned(me); + } + + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_STORMSTRIKE, Seconds(23)); + } + + uint32 GetData(uint32 type) const override + { + return type == DATA_IRON_DWARF_MEDIUM_RARE ? _killedByBreath : 0; + } + + void SetData(uint32 type, uint32 value) override + { + if (type == DATA_IRON_DWARF_MEDIUM_RARE) + _killedByBreath = value != 0; + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) { - npc_darkrune_watcherAI(Creature* creature) : ScriptedAI(creature) + switch (eventId) { - Initialize(); + case EVENT_START_COMBAT: + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + break; + case EVENT_STORMSTRIKE: + DoCastVictim(SPELL_STORMSTRIKE); + _events.Repeat(Seconds(13), Seconds(25)); + break; + default: + break; } - void Initialize() - { - ChainTimer = urand(10000, 15000); - LightTimer = urand(1000, 3000); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); + } - uint32 ChainTimer; - uint32 LightTimer; +private: + InstanceScript* _instance; + EventMap _events; + bool _killedByBreath; +}; - void Reset() override +struct npc_darkrune_sentinel : public ScriptedAI +{ + npc_darkrune_sentinel(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + + void Reset() override + { + _events.Reset(); + me->SetReactState(REACT_PASSIVE); + _events.ScheduleEvent(EVENT_START_COMBAT, Seconds(2)); + if (Creature* razorscale = _instance->GetCreature(BOSS_RAZORSCALE)) + razorscale->AI()->JustSummoned(me); + } + + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_HEROIC_STRIKE, Seconds(9)); + _events.ScheduleEvent(EVENT_BATTLE_SHOUT, Seconds(15)); + _events.ScheduleEvent(EVENT_WHIRLWIND, Seconds(17)); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - Initialize(); + case EVENT_START_COMBAT: + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + break; + case EVENT_HEROIC_STRIKE: + DoCastVictim(SPELL_HEROIC_STRIKE); + _events.Repeat(Seconds(5), Seconds(9)); + break; + case EVENT_BATTLE_SHOUT: + DoCastSelf(SPELL_BATTLE_SHOUT); + _events.Repeat(Seconds(25)); + break; + case EVENT_WHIRLWIND: + DoCastSelf(SPELL_WHIRLWIND); + _events.Repeat(Seconds(10), Seconds(13)); + break; + default: + break; } - void UpdateAI(uint32 Diff) override - { - if (!UpdateVictim()) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - if (ChainTimer <= Diff) - { - DoCastVictim(SPELL_CHAIN_LIGHTNING); - ChainTimer = urand(10000, 15000); - } - else - ChainTimer -= Diff; + DoMeleeAttackIfReady(); + } - if (LightTimer <= Diff) - { - DoCastVictim(SPELL_LIGHTNING_BOLT); - LightTimer = urand(5000, 7000); - } - else - LightTimer -= Diff; +private: + InstanceScript* _instance; + EventMap _events; +}; - DoMeleeAttackIfReady(); - } - }; +struct npc_razorscale_harpoon_fire_state : public ScriptedAI +{ + npc_razorscale_harpoon_fire_state(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - CreatureAI* GetAI(Creature* creature) const override + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_FIREBOLT) { - return GetUlduarAI<npc_darkrune_watcherAI>(creature); + DoCastSelf(SPELL_HARPOON_FIRE_STATE); + if (Creature* commander = _instance->GetCreature(DATA_EXPEDITION_COMMANDER)) + commander->AI()->DoAction(ACTION_DESTROY_HARPOONS); } + } + +private: + InstanceScript* _instance; }; -class npc_darkrune_guardian : public CreatureScript +struct npc_razorscale_devouring_flame : public ScriptedAI { - public: - npc_darkrune_guardian() : CreatureScript("npc_darkrune_guardian") { } + npc_razorscale_devouring_flame(Creature* creature) : ScriptedAI(creature) { } - struct npc_darkrune_guardianAI : public ScriptedAI - { - npc_darkrune_guardianAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } + void Reset() override + { + DoCastSelf(DEVOURING_FLAME_GROUND); + } +}; - void Initialize() - { - StormTimer = urand(3000, 6000); - killedByBreath = false; - } +class go_razorscale_harpoon : public GameObjectScript +{ +public: + go_razorscale_harpoon() : GameObjectScript("go_razorscale_harpoon") { } - uint32 StormTimer; + struct go_razorscale_harpoonAI : public GameObjectAI + { + go_razorscale_harpoonAI(GameObject* go) : GameObjectAI(go) { } - void Reset() override + void Reset() override + { + _scheduler.Schedule(Seconds(1), [this](TaskContext /*context*/) { - Initialize(); - } + if (Creature* controller = me->FindNearestCreature(NPC_RAZORSCALE_CONTROLLER, 5.0f)) + controller->AI()->Talk(EMOTE_HARPOON); - uint32 GetData(uint32 type) const override - { - return type == DATA_IRON_DWARF_MEDIUM_RARE ? killedByBreath : 0; - } + if (GameObject* brokenHarpoon = me->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) + brokenHarpoon->RemoveFromWorld(); + }); + } - void SetData(uint32 type, uint32 value) override + uint32 SelectRightSpell() + { + switch (me->GetEntry()) { - if (type == DATA_IRON_DWARF_MEDIUM_RARE) - killedByBreath = value != 0; + case GO_RAZOR_HARPOON_1: + return SPELL_HARPOON_SHOT_1; + case GO_RAZOR_HARPOON_2: + return SPELL_HARPOON_SHOT_2; + case GO_RAZOR_HARPOON_3: + return SPELL_HARPOON_SHOT_3; + case GO_RAZOR_HARPOON_4: + return SPELL_HARPOON_SHOT_4; + default: + return 0; } + } - - void UpdateAI(uint32 Diff) override + bool GossipHello(Player* /*player*/) override + { + me->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + if (Creature* controller = me->FindNearestCreature(NPC_RAZORSCALE_CONTROLLER, 5.0f)) { - if (!UpdateVictim()) - return; + // Prevent 2 players clicking at "same time" + if (controller->HasUnitState(UNIT_STATE_CASTING)) + return true; - if (StormTimer <= Diff) - { - DoCastVictim(SPELL_STORMSTRIKE); - StormTimer = urand(4000, 8000); - } - else - StormTimer -= Diff; - - DoMeleeAttackIfReady(); + uint32 spellId = SelectRightSpell(); + controller->CastSpell(nullptr, spellId, true); } - private: - bool killedByBreath; - }; + return true; + } - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetUlduarAI<npc_darkrune_guardianAI>(creature); + _scheduler.Update(diff); } + + private: + TaskScheduler _scheduler; + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return GetUlduarAI<go_razorscale_harpoonAI>(go); + } }; -class npc_darkrune_sentinel : public CreatureScript +class go_razorscale_mole_machine : public GameObjectScript { - public: - npc_darkrune_sentinel() : CreatureScript("npc_darkrune_sentinel") { } +public: + go_razorscale_mole_machine() : GameObjectScript("go_razorscale_mole_machine") { } - struct npc_darkrune_sentinelAI : public ScriptedAI + struct go_razorscale_mole_machineAI : public GameObjectAI + { + go_razorscale_mole_machineAI(GameObject* go) : GameObjectAI(go) { } + + void Reset() override { - npc_darkrune_sentinelAI(Creature* creature) : ScriptedAI(creature) + me->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + _scheduler.Schedule(Seconds(1), [this](TaskContext /*context*/) { - Initialize(); - } - - void Initialize() + me->UseDoorOrButton(); + }); + _scheduler.Schedule(Seconds(10), [this](TaskContext /*context*/) { - HeroicTimer = urand(4000, 8000); - WhirlTimer = urand(20000, 25000); - ShoutTimer = urand(15000, 30000); - } - - uint32 HeroicTimer; - uint32 WhirlTimer; - uint32 ShoutTimer; - - void Reset() override - { - Initialize(); - } + me->Delete(); + }); + } - void UpdateAI(uint32 Diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } - if (HeroicTimer <= Diff) - { - DoCastVictim(SPELL_HEROIC_STRIKE); - HeroicTimer = urand(4000, 6000); - } - else - HeroicTimer -= Diff; + private: + TaskScheduler _scheduler; + }; + GameObjectAI* GetAI(GameObject* go) const override + { + return GetUlduarAI<go_razorscale_mole_machineAI>(go); + } +}; - if (WhirlTimer <= Diff) - { - DoCastVictim(SPELL_WHIRLWIND); - WhirlTimer = urand(20000, 25000); - } - else - WhirlTimer -= Diff; +/* 63317 - Flame Breath + 64021 - Flame Breath */ +class spell_razorscale_flame_breath : public SpellScript +{ + PrepareSpellScript(spell_razorscale_flame_breath); - if (ShoutTimer <= Diff) - { - DoCast(me, SPELL_BATTLE_SHOUT); - ShoutTimer = urand(30000, 40000); - } - else - ShoutTimer -= Diff; + void CheckDamage() + { + Creature* target = GetHitCreature(); + if (!target || target->GetEntry() != NPC_DARK_RUNE_GUARDIAN || !target->IsAlive()) + return; - DoMeleeAttackIfReady(); - } - }; + if (GetHitDamage() >= int32(target->GetHealth())) + target->AI()->SetData(DATA_IRON_DWARF_MEDIUM_RARE, 1); + } - CreatureAI* GetAI(Creature* creature) const override + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if([](WorldObject* obj) { - return GetUlduarAI<npc_darkrune_sentinelAI>(creature); - } + if (Creature* target = obj->ToCreature()) + if (target->IsTrigger()) + return true; + + return false; + }); + } + + void Register() override + { + OnHit += SpellHitFn(spell_razorscale_flame_breath::CheckDamage); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_razorscale_flame_breath::FilterTargets, EFFECT_1, TARGET_UNIT_CONE_ENTRY); + } }; -class spell_razorscale_devouring_flame : public SpellScriptLoader +/* 63968 - Summon Iron Dwarves + 63970 - Summon Iron Dwarves + 63969 - Summon Iron Dwarves */ +class spell_razorscale_summon_iron_dwarves : public SpellScript { - public: - spell_razorscale_devouring_flame() : SpellScriptLoader("spell_razorscale_devouring_flame") { } + PrepareSpellScript(spell_razorscale_summon_iron_dwarves); - class spell_razorscale_devouring_flame_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_razorscale_devouring_flame_SpellScript); - - void HandleSummon(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - Unit* caster = GetCaster(); - uint32 entry = uint32(GetSpellInfo()->Effects[effIndex].MiscValue); - WorldLocation const* summonLocation = GetExplTargetDest(); - if (!caster || !summonLocation) - return; - - caster->SummonCreature(entry, summonLocation->GetPositionX(), summonLocation->GetPositionY(), GROUND_Z, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 20000); - } - - void Register() override - { - OnEffectHit += SpellEffectFn(spell_razorscale_devouring_flame_SpellScript::HandleSummon, EFFECT_0, SPELL_EFFECT_SUMMON); - } - }; - - SpellScript* GetSpellScript() const override + SPELL_SUMMON_IRON_DWARF_GUARDIAN, + SPELL_SUMMON_IRON_DWARF_WATCHER + }); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + switch (GetSpellInfo()->Id) { - return new spell_razorscale_devouring_flame_SpellScript(); + case SPELL_TRIGGER_SUMMON_IRON_DWARVES: + caster->CastSpell(caster, SPELL_SUMMON_IRON_DWARF_GUARDIAN, true); + caster->CastSpell(caster, SPELL_SUMMON_IRON_DWARF_GUARDIAN, true); + caster->CastSpell(caster, SPELL_SUMMON_IRON_DWARF_WATCHER, true); + break; + case SPELL_TRIGGER_SUMMON_IRON_DWARVES_2: + case SPELL_TRIGGER_SUMMON_IRON_DWARVES_3: + caster->CastSpell(caster, SPELL_SUMMON_IRON_DWARF_GUARDIAN, true); + caster->CastSpell(caster, SPELL_SUMMON_IRON_DWARF_WATCHER, true); + caster->CastSpell(caster, SPELL_SUMMON_IRON_DWARF_WATCHER, true); + break; + default: + break; } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_razorscale_summon_iron_dwarves::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; -class spell_razorscale_flame_breath : public SpellScriptLoader +// 64771 - Fuse Armor +class spell_razorscale_fuse_armor : public AuraScript { - public: - spell_razorscale_flame_breath() : SpellScriptLoader("spell_razorscale_flame_breath") { } - - class spell_razorscale_flame_breath_SpellScript : public SpellScript - { - PrepareSpellScript(spell_razorscale_flame_breath_SpellScript); - - void CheckDamage() - { - Creature* target = GetHitCreature(); - if (!target || target->GetEntry() != NPC_DARK_RUNE_GUARDIAN) - return; + PrepareAuraScript(spell_razorscale_fuse_armor); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FUSED_ARMOR }); + } + + void HandleFused(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetStackAmount() == 5) + GetTarget()->CastSpell(GetTarget(), SPELL_FUSED_ARMOR, true); + } + + void Register() override + { + AfterEffectApply += AuraEffectRemoveFn(spell_razorscale_fuse_armor::HandleFused, EFFECT_1, SPELL_AURA_MOD_MELEE_HASTE, AURA_EFFECT_HANDLE_REAL); + } +}; - if (GetHitDamage() >= int32(target->GetHealth())) - target->AI()->SetData(DATA_IRON_DWARF_MEDIUM_RARE, 1); - } +// 62669 - Firebolt +class spell_razorscale_firebolt : public SpellScript +{ + PrepareSpellScript(spell_razorscale_firebolt); - void Register() override - { - OnHit += SpellHitFn(spell_razorscale_flame_breath_SpellScript::CheckDamage); - } - }; + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if([](WorldObject* obj) { return obj->GetEntry() != NPC_RAZORSCALE_HARPOON_FIRE_STATE; }); + } - SpellScript* GetSpellScript() const override - { - return new spell_razorscale_flame_breath_SpellScript(); - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_razorscale_firebolt::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); + } }; class achievement_iron_dwarf_medium_rare : public AchievementCriteriaScript { public: - achievement_iron_dwarf_medium_rare() : AchievementCriteriaScript("achievement_iron_dwarf_medium_rare") - { - } + achievement_iron_dwarf_medium_rare() : AchievementCriteriaScript("achievement_iron_dwarf_medium_rare") { } bool OnCheck(Player* /*player*/, Unit* target) override { @@ -1183,17 +1723,23 @@ class achievement_quick_shave : public AchievementCriteriaScript void AddSC_boss_razorscale() { - new boss_razorscale_controller(); + RegisterUlduarCreatureAI(boss_razorscale); + RegisterUlduarCreatureAI(npc_expedition_defender); + RegisterUlduarCreatureAI(npc_expedition_trapper); + RegisterUlduarCreatureAI(npc_expedition_engineer); + RegisterUlduarCreatureAI(npc_expedition_commander); + RegisterUlduarCreatureAI(npc_razorscale_spawner); + RegisterUlduarCreatureAI(npc_darkrune_watcher); + RegisterUlduarCreatureAI(npc_darkrune_guardian); + RegisterUlduarCreatureAI(npc_darkrune_sentinel); + RegisterUlduarCreatureAI(npc_razorscale_harpoon_fire_state); + RegisterUlduarCreatureAI(npc_razorscale_devouring_flame); new go_razorscale_harpoon(); - new boss_razorscale(); - new npc_expedition_commander(); - new npc_mole_machine_trigger(); - new npc_devouring_flame(); - new npc_darkrune_watcher(); - new npc_darkrune_guardian(); - new npc_darkrune_sentinel(); - new spell_razorscale_devouring_flame(); - new spell_razorscale_flame_breath(); + new go_razorscale_mole_machine(); + RegisterSpellScript(spell_razorscale_flame_breath); + RegisterSpellScript(spell_razorscale_summon_iron_dwarves); + RegisterAuraScript(spell_razorscale_fuse_armor); + RegisterSpellScript(spell_razorscale_firebolt); new achievement_iron_dwarf_medium_rare(); new achievement_quick_shave(); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp index 9c6cffcba13..8b618c8b271 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp @@ -375,7 +375,7 @@ class RunicSmashExplosionEvent : public BasicEvent bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override { - _owner->CastSpell((Unit*)nullptr, SPELL_RUNIC_SMASH); + _owner->CastSpell(nullptr, SPELL_RUNIC_SMASH); return true; } @@ -393,7 +393,7 @@ class TrashJumpEvent : public BasicEvent switch (_stage) { case 0: - _owner->CastSpell((Unit*)nullptr, SPELL_LEAP); + _owner->CastSpell(nullptr, SPELL_LEAP); ++_stage; _owner->m_Events.AddEvent(this, eventTime + 2000); return false; @@ -425,7 +425,7 @@ class LightningFieldEvent : public BasicEvent { if (instance->GetBossState(BOSS_THORIM) == IN_PROGRESS) { - _owner->CastSpell((Unit*)nullptr, SPELL_LIGHTNING_FIELD); + _owner->CastSpell(nullptr, SPELL_LIGHTNING_FIELD); _owner->m_Events.AddEvent(this, eventTime + 1000); return false; } @@ -515,7 +515,7 @@ class boss_thorim : public CreatureScript if (Creature* pillar = ObjectAccessor::GetCreature(*me, _activePillarGUID)) { pillar->CastSpell(pillar, SPELL_LIGHTNING_ORB_CHARGED, true); - pillar->CastSpell((Unit*)nullptr, SPELL_LIGHTNING_PILLAR_2); + pillar->CastSpell(nullptr, SPELL_LIGHTNING_PILLAR_2); events.ScheduleEvent(EVENT_LIGHTNING_CHARGE, 8000, 0, PHASE_2); } } @@ -1778,7 +1778,7 @@ class spell_thorim_charge_orb : public SpellScriptLoader void HandleScript() { if (Unit* target = GetHitUnit()) - target->CastSpell((Unit*)nullptr, SPELL_LIGHTNING_PILLAR_1, true); + target->CastSpell(nullptr, SPELL_LIGHTNING_PILLAR_1, true); } void Register() override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp index c3817b0525b..2dbc0d0dfbb 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp @@ -558,7 +558,7 @@ class boss_voice_of_yogg_saron : public CreatureScript if (Creature* yogg = instance->GetCreature(BOSS_YOGG_SARON)) { yogg->AI()->Talk(EMOTE_YOGG_SARON_EXTINGUISH_ALL_LIFE, me); - yogg->CastSpell((Unit*)nullptr, SPELL_EXTINGUISH_ALL_LIFE, true); + yogg->CastSpell(nullptr, SPELL_EXTINGUISH_ALL_LIFE, true); } events.ScheduleEvent(EVENT_EXTINGUISH_ALL_LIFE, 10000); // cast it again after a short while, players can survive break; @@ -2340,7 +2340,7 @@ class spell_yogg_saron_empowering_shadows_missile : public SpellScriptLoader void HandleScript(SpellEffIndex /*effIndex*/) { if (Unit* target = GetHitUnit()) - target->CastSpell((Unit*)nullptr, SPELL_EMPOWERING_SHADOWS, true); + target->CastSpell(nullptr, SPELL_EMPOWERING_SHADOWS, true); } void Register() override @@ -2600,7 +2600,7 @@ class spell_yogg_saron_death_ray_warning_visual : public SpellScriptLoader / if (Unit* caster = GetCaster()) { caster->CastSpell(caster, SPELL_DEATH_RAY_PERIODIC, true); - caster->CastSpell((Unit*)nullptr, SPELL_DEATH_RAY_DAMAGE_VISUAL, true); + caster->CastSpell(nullptr, SPELL_DEATH_RAY_DAMAGE_VISUAL, true); // TODO: set better movement caster->GetMotionMaster()->MoveConfused(); } @@ -2778,7 +2778,7 @@ class spell_yogg_saron_induce_madness : public SpellScriptLoader // 64059 void ClearShatteredIllusion() { - GetCaster()->CastSpell((Unit*)nullptr, SPELL_SHATTERED_ILLUSION_REMOVE); + GetCaster()->CastSpell(nullptr, SPELL_SHATTERED_ILLUSION_REMOVE); if (InstanceScript* instance = GetCaster()->GetInstanceScript()) if (Creature* voice = instance->GetCreature(DATA_VOICE_OF_YOGG_SARON)) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp index bd1d2b9c3b6..9c202f2066b 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp @@ -496,10 +496,6 @@ class instance_ulduar : public InstanceMapScript if (GetBossState(BOSS_LEVIATHAN) == DONE) gameObject->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); break; - case GO_MOLE_MACHINE: - if (GetBossState(BOSS_RAZORSCALE) == IN_PROGRESS) - gameObject->SetGoState(GO_STATE_ACTIVE); - break; case GO_BRAIN_ROOM_DOOR_1: BrainRoomDoorGUIDs[0] = gameObject->GetGUID(); break; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h index 4548eac4b97..1a0d57ab691 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h @@ -59,9 +59,7 @@ enum UlduarNPCs NPC_SALVAGED_CHOPPER = 33062, NPC_IGNIS = 33118, NPC_RAZORSCALE = 33186, - NPC_RAZORSCALE_CONTROLLER = 33233, NPC_STEELFORGED_DEFFENDER = 33236, - NPC_EXPEDITION_COMMANDER = 33210, NPC_XT002 = 33293, NPC_XT_TOY_PILE = 33337, NPC_STEELBREAKER = 32867, @@ -82,6 +80,18 @@ enum UlduarNPCs NPC_YOGG_SARON = 33288, NPC_ALGALON = 32871, + // Razorscale + NPC_DARK_RUNE_GUARDIAN = 33388, + NPC_DARK_RUNE_SENTINEL = 33846, + NPC_DARK_RUNE_WATCHER = 33453, + NPC_RAZORSCALE_SPAWNER = 33245, + NPC_EXPEDITION_COMMANDER = 33210, + NPC_EXPEDITION_ENGINEER = 33287, + NPC_EXPEDITION_DEFENDER = 33816, + NPC_EXPEDITION_TRAPPER = 33259, + NPC_RAZORSCALE_CONTROLLER = 33233, + NPC_RAZORSCALE_HARPOON_FIRE_STATE = 33282, + //XT002 NPC_XS013_SCRAPBOT = 33343, @@ -498,6 +508,8 @@ inline AI* GetUlduarAI(T* obj) return GetInstanceAI<AI, T>(obj, UlduarScriptName); } +#define RegisterUlduarCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetUlduarAI) + class KeeperDespawnEvent : public BasicEvent { public: diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp index 968210df209..3ead66593a6 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp @@ -342,7 +342,7 @@ public: void Reset() override { me->SetReactState(REACT_PASSIVE); - me->setRegeneratingHealth(false); + me->SetRegenerateHealth(false); me->SetSpeedRate(MOVE_RUN, 2.5f); } @@ -855,7 +855,7 @@ class spell_summon_gauntlet_mobs_periodic : public SpellScriptLoader for (uint8 i = 0; i < 2; ++i) { uint32 spellId = SummonSpellsList.front(); - GetTarget()->CastSpell((Unit*)nullptr, spellId, true); + GetTarget()->CastSpell(nullptr, spellId, true); SummonSpellsList.push_back(spellId); SummonSpellsList.pop_front(); } diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp index 03ee2289818..ded80918b1c 100644 --- a/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp +++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp @@ -243,12 +243,12 @@ class spell_archavon_rock_shards : public SpellScriptLoader for (uint8 i = 0; i < 3; ++i) { - caster->CastSpell((Unit*)nullptr, SPELL_ROCK_SHARDS_VISUAL_L, true); - caster->CastSpell((Unit*)nullptr, SPELL_ROCK_SHARDS_VISUAL_R, true); + caster->CastSpell(nullptr, SPELL_ROCK_SHARDS_VISUAL_L, true); + caster->CastSpell(nullptr, SPELL_ROCK_SHARDS_VISUAL_R, true); } - caster->CastSpell((Unit*)nullptr, SPELL_ROCK_SHARDS_DAMAGE_L, true); - caster->CastSpell((Unit*)nullptr, SPELL_ROCK_SHARDS_DAMAGE_R, true); + caster->CastSpell(nullptr, SPELL_ROCK_SHARDS_DAMAGE_L, true); + caster->CastSpell(nullptr, SPELL_ROCK_SHARDS_DAMAGE_R, true); } void Register() override diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp index 0f785817e9b..d093bb65fa8 100644 --- a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp +++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp @@ -17,28 +17,26 @@ #include "ScriptMgr.h" #include "InstanceScript.h" -#include "ObjectAccessor.h" #include "ScriptedCreature.h" +#include "SpellScript.h" #include "vault_of_archavon.h" enum Spells { // Toravon - SPELL_FREEZING_GROUND = 72090, // don't know cd... using 20 secs. + SPELL_FREEZING_GROUND = 72090, SPELL_FROZEN_ORB = 72091, - SPELL_WHITEOUT = 72034, // Every 38 sec. cast. (after SPELL_FROZEN_ORB) + SPELL_WHITEOUT = 72034, SPELL_FROZEN_MALLET = 71993, - // Frost Warder - SPELL_FROST_BLAST = 72123, // don't know cd... using 20 secs. - SPELL_FROZEN_MALLET_2 = 72122, - // Frozen Orb - SPELL_FROZEN_ORB_DMG = 72081, // priodic dmg aura - SPELL_FROZEN_ORB_AURA = 72067, // make visible + SPELL_FROZEN_ORB_DMG = 72081, + SPELL_FROZEN_ORB_AURA = 72067, + SPELL_RANDOM_AGGRO = 72084, - // Frozen Orb Stalker - SPELL_FROZEN_ORB_SUMMON = 72093, // summon orb + // Frost Warder + SPELL_FROST_BLAST = 72123, // don't know cd... using 20 secs. + SPELL_FROZEN_MALLET_2 = 72122 }; enum Events @@ -47,256 +45,159 @@ enum Events EVENT_FROZEN_ORB = 2, EVENT_WHITEOUT = 3, - EVENT_FROST_BLAST = 4, + EVENT_FROST_BLAST = 4 }; -enum Creatures +struct boss_toravon : public BossAI { - NPC_FROZEN_ORB = 38456 // 1 in 10 mode and 3 in 25 mode + boss_toravon(Creature* creature) : BossAI(creature, DATA_TORAVON) { } -}; + void EnterCombat(Unit* /*who*/) override + { + DoCastSelf(SPELL_FROZEN_MALLET); -class boss_toravon : public CreatureScript -{ - public: - boss_toravon() : CreatureScript("boss_toravon") { } + events.ScheduleEvent(EVENT_FROZEN_ORB, Seconds(12)); + events.ScheduleEvent(EVENT_WHITEOUT, Seconds(25)); + events.ScheduleEvent(EVENT_FREEZING_GROUND, Seconds(7)); - struct boss_toravonAI : public BossAI - { - boss_toravonAI(Creature* creature) : BossAI(creature, DATA_TORAVON) - { - } + _EnterCombat(); + } - void EnterCombat(Unit* /*who*/) override - { - DoCast(me, SPELL_FROZEN_MALLET); + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - events.ScheduleEvent(EVENT_FROZEN_ORB, 11000); - events.ScheduleEvent(EVENT_WHITEOUT, 13000); - events.ScheduleEvent(EVENT_FREEZING_GROUND, 15000); + events.Update(diff); - _EnterCombat(); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void UpdateAI(uint32 diff) override + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - if (!UpdateVictim()) - return; - - events.Update(diff); - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_FROZEN_ORB: - me->CastCustomSpell(SPELL_FROZEN_ORB, SPELLVALUE_MAX_TARGETS, 1, me); - events.ScheduleEvent(EVENT_FROZEN_ORB, 38000); - break; - case EVENT_WHITEOUT: - DoCast(me, SPELL_WHITEOUT); - events.ScheduleEvent(EVENT_WHITEOUT, 38000); - break; - case EVENT_FREEZING_GROUND: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) - DoCast(target, SPELL_FREEZING_GROUND); - events.ScheduleEvent(EVENT_FREEZING_GROUND, 20000); - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); + case EVENT_FROZEN_ORB: + me->CastCustomSpell(SPELL_FROZEN_ORB, SPELLVALUE_MAX_TARGETS, RAID_MODE(1, 3), me); + events.Repeat(Seconds(32)); + break; + case EVENT_WHITEOUT: + DoCastSelf(SPELL_WHITEOUT); + events.Repeat(Seconds(38)); + break; + case EVENT_FREEZING_GROUND: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) + DoCast(target, SPELL_FREEZING_GROUND); + events.Repeat(Seconds(38)); + break; + default: + break; } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetVaultOfArchavonAI<boss_toravonAI>(creature); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } + + DoMeleeAttackIfReady(); + } }; -/*###### -## Mob Frost Warder -######*/ -class npc_frost_warder : public CreatureScript +struct npc_frost_warder : public ScriptedAI { - public: - npc_frost_warder() : CreatureScript("npc_frost_warder") { } - - struct npc_frost_warderAI : public ScriptedAI - { - npc_frost_warderAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override - { - events.Reset(); - } - - void EnterCombat(Unit* /*who*/) override - { - DoZoneInCombat(); - - DoCast(me, SPELL_FROZEN_MALLET_2); + npc_frost_warder(Creature* creature) : ScriptedAI(creature) { } - events.ScheduleEvent(EVENT_FROST_BLAST, 5000); - } + void Reset() override + { + _events.Reset(); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void EnterCombat(Unit* /*who*/) override + { + DoZoneInCombat(); - events.Update(diff); + DoCastSelf(SPELL_FROZEN_MALLET_2); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + _events.ScheduleEvent(EVENT_FROST_BLAST, 5000); + } - if (events.ExecuteEvent() == EVENT_FROST_BLAST) - { - DoCastVictim(SPELL_FROST_BLAST); - events.ScheduleEvent(EVENT_FROST_BLAST, 20000); - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - DoMeleeAttackIfReady(); - } + _events.Update(diff); - private: - EventMap events; - }; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - CreatureAI* GetAI(Creature* creature) const override + if (_events.ExecuteEvent() == EVENT_FROST_BLAST) { - return GetVaultOfArchavonAI<npc_frost_warderAI>(creature); + DoCastVictim(SPELL_FROST_BLAST); + _events.ScheduleEvent(EVENT_FROST_BLAST, 20000); } + + DoMeleeAttackIfReady(); + } + +private: + EventMap _events; }; -/*###### -## Mob Frozen Orb -######*/ -class npc_frozen_orb : public CreatureScript +struct npc_frozen_orb : public ScriptedAI { -public: - npc_frozen_orb() : CreatureScript("npc_frozen_orb") { } + npc_frozen_orb(Creature* creature) : ScriptedAI(creature) { } - struct npc_frozen_orbAI : public ScriptedAI + void IsSummonedBy(Unit* /*summoner*/) override { - npc_frozen_orbAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } + DoCastSelf(SPELL_FROZEN_ORB_AURA, true); + DoCastSelf(SPELL_FROZEN_ORB_DMG, true); + DoCastSelf(SPELL_RANDOM_AGGRO, true); - void Initialize() + if (Creature* toravon = me->GetInstanceScript()->GetCreature(DATA_TORAVON)) { - done = false; - killTimer = 60000; // if after this time there is no victim -> destroy! - } - - void Reset() override - { - Initialize(); - } - - void EnterCombat(Unit* /*who*/) override - { - DoZoneInCombat(); - } - - void UpdateAI(uint32 diff) override - { - if (!done) - { - DoCast(me, SPELL_FROZEN_ORB_AURA, true); - DoCast(me, SPELL_FROZEN_ORB_DMG, true); - done = true; - } - - if (killTimer <= diff) + if (toravon->IsInCombat()) { - if (!UpdateVictim()) - me->DespawnOrUnsummon(); - killTimer = 10000; + toravon->AI()->JustSummoned(me); + me->SetInCombatWithZone(); } else - killTimer -= diff; + me->DespawnOrUnsummon(); } - - private: - uint32 killTimer; - bool done; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetVaultOfArchavonAI<npc_frozen_orbAI>(creature); } }; -/*###### -## Mob Frozen Orb Stalker -######*/ -class npc_frozen_orb_stalker : public CreatureScript +// 46523 - Random Aggro +class spell_toravon_random_aggro : public SpellScript { - public: - npc_frozen_orb_stalker() : CreatureScript("npc_frozen_orb_stalker") { } + PrepareSpellScript(spell_toravon_random_aggro); - struct npc_frozen_orb_stalkerAI : public ScriptedAI - { - npc_frozen_orb_stalkerAI(Creature* creature) : ScriptedAI(creature) - { - creature->SetVisible(false); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - me->SetControlled(true, UNIT_STATE_ROOT); - creature->SetReactState(REACT_PASSIVE); - - instance = creature->GetInstanceScript(); - spawned = false; + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } - SetCombatMovement(false); - } + void HandleScript(SpellEffIndex /*effIndex*/) + { + Creature* caster = GetCaster()->ToCreature(); + if (!caster->IsAIEnabled) + return; - void UpdateAI(uint32 /*diff*/) override - { - if (spawned) - return; - - spawned = true; - Unit* toravon = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_TORAVON)); - if (!toravon) - return; - - uint8 num_orbs = RAID_MODE(1, 3); - for (uint8 i = 0; i < num_orbs; ++i) - { - Position pos; - me->GetNearPoint(toravon, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, 10.0f, 0.0f); - me->UpdatePosition(pos); - DoCast(me, SPELL_FROZEN_ORB_SUMMON); - } - } + caster->GetThreatManager().resetAllAggro(); - private: - InstanceScript* instance; - bool spawned; - }; + if (Unit* target = caster->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1)) + caster->GetThreatManager().AddThreat(target, 1000000); + } - CreatureAI* GetAI(Creature* creature) const override - { - return GetVaultOfArchavonAI<npc_frozen_orb_stalkerAI>(creature); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_toravon_random_aggro::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; void AddSC_boss_toravon() { - new boss_toravon(); - new npc_frost_warder(); - new npc_frozen_orb(); - new npc_frozen_orb_stalker(); + RegisterVaultOfArchavonCreatureAI(boss_toravon); + RegisterVaultOfArchavonCreatureAI(npc_frost_warder); + RegisterVaultOfArchavonCreatureAI(npc_frozen_orb); + RegisterSpellScript(spell_toravon_random_aggro); } diff --git a/src/server/scripts/Northrend/VaultOfArchavon/instance_vault_of_archavon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/instance_vault_of_archavon.cpp index f399449932f..df562d81546 100644 --- a/src/server/scripts/Northrend/VaultOfArchavon/instance_vault_of_archavon.cpp +++ b/src/server/scripts/Northrend/VaultOfArchavon/instance_vault_of_archavon.cpp @@ -16,7 +16,6 @@ */ #include "ScriptMgr.h" -#include "Creature.h" #include "InstanceScript.h" #include "Map.h" #include "vault_of_archavon.h" @@ -28,6 +27,15 @@ 4 - Toravon the Ice Watcher event */ +ObjectData const creatureData[] = +{ + { NPC_ARCHAVON, DATA_ARCHAVON }, + { NPC_EMALON, DATA_EMALON }, + { NPC_KORALON, DATA_KORALON }, + { NPC_TORAVON, DATA_TORAVON }, + { 0, 0, } +}; + class instance_vault_of_archavon : public InstanceMapScript { public: @@ -39,42 +47,13 @@ class instance_vault_of_archavon : public InstanceMapScript { SetHeaders(DataHeader); SetBossNumber(EncounterCount); + LoadObjectData(creatureData, nullptr); ArchavonDeath = 0; EmalonDeath = 0; KoralonDeath = 0; } - void OnCreatureCreate(Creature* creature) override - { - switch (creature->GetEntry()) - { - case NPC_EMALON: - EmalonGUID = creature->GetGUID(); - break; - case NPC_TORAVON: - ToravonGUID = creature->GetGUID(); - break; - default: - break; - } - } - - ObjectGuid GetGuidData(uint32 identifier) const override - { - switch (identifier) - { - case DATA_EMALON: - return EmalonGUID; - case DATA_TORAVON: - return ToravonGUID; - default: - break; - } - - return ObjectGuid::Empty; - } - bool SetBossState(uint32 type, EncounterState state) override { if (!InstanceScript::SetBossState(type, state)) @@ -127,8 +106,6 @@ class instance_vault_of_archavon : public InstanceMapScript } private: - ObjectGuid EmalonGUID; - ObjectGuid ToravonGUID; time_t ArchavonDeath; time_t EmalonDeath; time_t KoralonDeath; diff --git a/src/server/scripts/Northrend/VaultOfArchavon/vault_of_archavon.h b/src/server/scripts/Northrend/VaultOfArchavon/vault_of_archavon.h index d4f252d3715..491ef5dd82d 100644 --- a/src/server/scripts/Northrend/VaultOfArchavon/vault_of_archavon.h +++ b/src/server/scripts/Northrend/VaultOfArchavon/vault_of_archavon.h @@ -30,7 +30,7 @@ enum VAData DATA_ARCHAVON = 0, DATA_EMALON = 1, DATA_KORALON = 2, - DATA_TORAVON = 3, + DATA_TORAVON = 3 }; enum VACreatureIds @@ -44,12 +44,12 @@ enum VACreatureIds enum VAAchievementCriteriaIds { CRITERIA_EARTH_WIND_FIRE_10 = 12018, - CRITERIA_EARTH_WIND_FIRE_25 = 12019, + CRITERIA_EARTH_WIND_FIRE_25 = 12019 }; enum VAAchievementSpells { - SPELL_EARTH_WIND_FIRE_ACHIEVEMENT_CHECK = 68308, + SPELL_EARTH_WIND_FIRE_ACHIEVEMENT_CHECK = 68308 }; template <class AI, class T> @@ -58,4 +58,6 @@ inline AI* GetVaultOfArchavonAI(T* obj) return GetInstanceAI<AI>(obj, VoAScriptName); } +#define RegisterVaultOfArchavonCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetVaultOfArchavonAI) + #endif diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp index e06087a738b..3675973c6e9 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp @@ -820,9 +820,9 @@ class npc_violet_hold_teleportation_portal_intro : public CreatureScript } }; -struct violet_hold_trashAI : public npc_escortAI +struct violet_hold_trashAI : public EscortAI { - violet_hold_trashAI(Creature* creature) : npc_escortAI(creature) + violet_hold_trashAI(Creature* creature) : EscortAI(creature) { _instance = creature->GetInstanceScript(); @@ -898,7 +898,7 @@ struct violet_hold_trashAI : public npc_escortAI } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId == _lastWaypointId) CreatureStartAttackDoor(); @@ -906,7 +906,7 @@ struct violet_hold_trashAI : public npc_escortAI void EnterCombat(Unit* who) override { - npc_escortAI::EnterCombat(who); + EscortAI::EnterCombat(who); ScheduledTasks(); } @@ -919,7 +919,7 @@ struct violet_hold_trashAI : public npc_escortAI return; _scheduler.Update(diff, - std::bind(&npc_escortAI::DoMeleeAttackIfReady, this)); + std::bind(&EscortAI::DoMeleeAttackIfReady, this)); } virtual void ScheduledTasks() { } diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index fccc0a8f437..4b32348ab54 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -571,9 +571,9 @@ class npc_lurgglbr : public CreatureScript public: npc_lurgglbr() : CreatureScript("npc_lurgglbr") { } - struct npc_lurgglbrAI : public npc_escortAI + struct npc_lurgglbrAI : public EscortAI { - npc_lurgglbrAI(Creature* creature) : npc_escortAI(creature) + npc_lurgglbrAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -593,7 +593,7 @@ public: Initialize(); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -602,6 +602,7 @@ public: IntroTimer = 2000; break; case 41: + SetEscortPaused(true); IntroPhase = 4; IntroTimer = 2000; break; @@ -628,7 +629,7 @@ public: IntroTimer = 7500; break; case 3: - me->SetReactState(REACT_AGGRESSIVE); + me->SetReactState(REACT_DEFENSIVE); IntroPhase = 0; IntroTimer = 0; break; @@ -643,14 +644,12 @@ public: IntroPhase = 6; IntroTimer = 2500; break; - case 6: if (Player* player = GetPlayerForEscort()) player->AreaExploredOrEventHappens(QUEST_ESCAPE_WINTERFIN_CAVERNS); IntroPhase = 7; IntroTimer = 2500; break; - case 7: me->DespawnOrUnsummon(); IntroPhase = 0; @@ -659,7 +658,7 @@ public: } } else IntroTimer -= diff; } - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!UpdateVictim()) return; @@ -672,8 +671,7 @@ public: if (GameObject* go = me->FindNearestGameObject(GO_CAGE, 5.0f)) { go->SetRespawnTime(0); - go->SetGoType(GAMEOBJECT_TYPE_BUTTON); - go->UseDoorOrButton(20); + go->UseDoorOrButton(20000); } Start(true, false, player->GetGUID()); @@ -794,9 +792,9 @@ class npc_thassarian : public CreatureScript public: npc_thassarian() : CreatureScript("npc_thassarian") { } - struct npc_thassarianAI : public npc_escortAI + struct npc_thassarianAI : public EscortAI { - npc_thassarianAI(Creature* creature) : npc_escortAI(creature) + npc_thassarianAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -838,7 +836,7 @@ public: Initialize(); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -873,7 +871,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (arthasInPosition && talbotInPosition) { @@ -1638,9 +1636,9 @@ class npc_mootoo_the_younger : public CreatureScript public: npc_mootoo_the_younger() : CreatureScript("npc_mootoo_the_younger") { } - struct npc_mootoo_the_youngerAI : public npc_escortAI + struct npc_mootoo_the_youngerAI : public EscortAI { - npc_mootoo_the_youngerAI(Creature* creature) : npc_escortAI(creature) { } + npc_mootoo_the_youngerAI(Creature* creature) : EscortAI(creature) { } void Reset() override { @@ -1653,7 +1651,7 @@ public: player->FailQuest(QUEST_ESCAPING_THE_MIST); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -1727,9 +1725,9 @@ class npc_bonker_togglevolt : public CreatureScript public: npc_bonker_togglevolt() : CreatureScript("npc_bonker_togglevolt") { } - struct npc_bonker_togglevoltAI : public npc_escortAI + struct npc_bonker_togglevoltAI : public EscortAI { - npc_bonker_togglevoltAI(Creature* creature) : npc_escortAI(creature) + npc_bonker_togglevoltAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -1767,7 +1765,7 @@ public: else Bonker_agro=0; } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -2384,6 +2382,108 @@ public: } }; +enum BloodsporeRuination +{ + NPC_BLOODMAGE_LAURITH = 25381, + SAY_BLOODMAGE_LAURITH = 0, + EVENT_TALK = 1, + EVENT_RESET_ORIENTATION +}; + +class spell_q11719_bloodspore_ruination_45997 : public SpellScriptLoader +{ +public: + spell_q11719_bloodspore_ruination_45997() : SpellScriptLoader("spell_q11719_bloodspore_ruination_45997") { } + + class spell_q11719_bloodspore_ruination_45997_SpellScript : public SpellScript + { + PrepareSpellScript(spell_q11719_bloodspore_ruination_45997_SpellScript); + + void HandleEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + if (Creature* laurith = caster->FindNearestCreature(NPC_BLOODMAGE_LAURITH, 100.0f)) + laurith->AI()->SetGUID(caster->GetGUID()); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_q11719_bloodspore_ruination_45997_SpellScript::HandleEffect, EFFECT_1, SPELL_EFFECT_SEND_EVENT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_q11719_bloodspore_ruination_45997_SpellScript(); + } +}; + +class npc_bloodmage_laurith : public CreatureScript +{ +public: + npc_bloodmage_laurith() : CreatureScript("npc_bloodmage_laurith") { } + + struct npc_bloodmage_laurithAI : public ScriptedAI + { + npc_bloodmage_laurithAI(Creature* creature) : ScriptedAI(creature) { } + + void Reset() override + { + _events.Reset(); + _playerGUID.Clear(); + } + + void SetGUID(ObjectGuid guid, int32 /*action*/) override + { + if (!_playerGUID.IsEmpty()) + return; + + _playerGUID = guid; + + if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) + me->SetFacingToObject(player); + + _events.ScheduleEvent(EVENT_TALK, Seconds(1)); + } + + void UpdateAI(uint32 diff) override + { + if (UpdateVictim()) + { + DoMeleeAttackIfReady(); + return; + } + + _events.Update(diff); + + if (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_TALK: + if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) + Talk(SAY_BLOODMAGE_LAURITH, player); + _playerGUID.Clear(); + _events.ScheduleEvent(EVENT_RESET_ORIENTATION, Seconds(5)); + break; + case EVENT_RESET_ORIENTATION: + me->SetFacingTo(me->GetHomePosition().GetOrientation()); + break; + } + } + } + + private: + EventMap _events; + ObjectGuid _playerGUID; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_bloodmage_laurithAI(creature); + } +}; + void AddSC_borean_tundra() { new npc_sinkhole_kill_credit(); @@ -2409,4 +2509,6 @@ void AddSC_borean_tundra() new npc_warmage_coldarra(); new npc_hidden_cultist(); new spell_windsoul_totem_aura(); + new spell_q11719_bloodspore_ruination_45997(); + new npc_bloodmage_laurith(); } diff --git a/src/server/scripts/Northrend/zone_dalaran.cpp b/src/server/scripts/Northrend/zone_dalaran.cpp index a8920ff3a07..4702d10ed4b 100644 --- a/src/server/scripts/Northrend/zone_dalaran.cpp +++ b/src/server/scripts/Northrend/zone_dalaran.cpp @@ -135,14 +135,18 @@ enum MinigobData { ZONE_DALARAN = 4395, - SPELL_MANABONKED = 61834, + SPELL_MANABONKED = 61839, SPELL_TELEPORT_VISUAL = 51347, SPELL_IMPROVED_BLINK = 61995, EVENT_SELECT_TARGET = 1, - EVENT_BLINK = 2, - EVENT_DESPAWN_VISUAL = 3, - EVENT_DESPAWN = 4, + EVENT_LAUGH_1 = 2, + EVENT_WANDER = 3, + EVENT_PAUSE = 4, + EVENT_CAST = 5, + EVENT_LAUGH_2 = 6, + EVENT_BLINK = 7, + EVENT_DESPAWN = 8, MAIL_MINIGOB_ENTRY = 264, MAIL_DELIVER_DELAY_MIN = 5*MINUTE, @@ -163,26 +167,29 @@ class npc_minigob_manabonk : public CreatureScript void Reset() override { + playerGuid = ObjectGuid(); me->SetVisible(false); - events.ScheduleEvent(EVENT_SELECT_TARGET, IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SELECT_TARGET, Seconds(1)); } - Player* SelectTargetInDalaran() + void GetPlayersInDalaran(std::vector<Player*>& playerList) const { - std::vector<Player*> PlayerInDalaranList; - Map::PlayerList const& players = me->GetMap()->GetPlayers(); for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if (Player* player = itr->GetSource()->ToPlayer()) if (player->GetZoneId() == ZONE_DALARAN && !player->IsFlying() && !player->IsMounted() && !player->IsGameMaster()) - PlayerInDalaranList.push_back(player); + playerList.push_back(player); + } + static Player* SelectTargetInDalaran(std::vector<Player*>& PlayerInDalaranList) + { if (PlayerInDalaranList.empty()) return nullptr; + return Trinity::Containers::SelectRandomContainerElement(PlayerInDalaranList); } - void SendMailToPlayer(Player* player) + void SendMailToPlayer(Player* player) const { SQLTransaction trans = CharacterDatabase.BeginTransaction(); int16 deliverDelay = irand(MAIL_DELIVER_DELAY_MIN, MAIL_DELIVER_DELAY_MAX); @@ -199,30 +206,61 @@ class npc_minigob_manabonk : public CreatureScript switch (eventId) { case EVENT_SELECT_TARGET: + { + std::vector<Player*> PlayerInDalaranList; + GetPlayersInDalaran(PlayerInDalaranList); + + // Increases chance of event based on player count in Dalaran (100 players or more = 100% else player count%) + if (PlayerInDalaranList.empty() || urand(1, 100) > PlayerInDalaranList.size()) + me->AddObjectToRemoveList(); + me->SetVisible(true); - DoCast(me, SPELL_TELEPORT_VISUAL); - if (Player* player = SelectTargetInDalaran()) + DoCastSelf(SPELL_TELEPORT_VISUAL); + if (Player* player = SelectTargetInDalaran(PlayerInDalaranList)) + { + playerGuid = player->GetGUID(); + Position pos = player->GetPosition(); + float dist = frand(10.0f, 30.0f); + float angle = frand(0.0f, 1.0f) * M_PI * 2.0f; + player->MovePositionToFirstCollision(pos, dist, angle); + me->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); + } + events.ScheduleEvent(EVENT_LAUGH_1, Seconds(2)); + break; + } + case EVENT_LAUGH_1: + me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH_NO_SHEATHE); + events.ScheduleEvent(EVENT_WANDER, Seconds(3)); + break; + case EVENT_WANDER: + me->GetMotionMaster()->MoveRandom(8); + events.ScheduleEvent(EVENT_PAUSE, Minutes(1)); + break; + case EVENT_PAUSE: + me->GetMotionMaster()->MoveIdle(); + events.ScheduleEvent(EVENT_CAST, Seconds(2)); + break; + case EVENT_CAST: + if (Player* player = me->GetMap()->GetPlayer(playerGuid)) { - me->NearTeleportTo(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), 0.0f); DoCast(player, SPELL_MANABONKED); SendMailToPlayer(player); } - events.ScheduleEvent(EVENT_BLINK, 3*IN_MILLISECONDS); + else + me->AddObjectToRemoveList(); + + events.ScheduleEvent(EVENT_LAUGH_2, Seconds(8)); break; - case EVENT_BLINK: - { - DoCast(me, SPELL_IMPROVED_BLINK); - Position pos = me->GetRandomNearPosition(frand(15, 40)); - me->GetMotionMaster()->MovePoint(0, pos.m_positionX, pos.m_positionY, pos.m_positionZ); - events.ScheduleEvent(EVENT_DESPAWN, 3 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_DESPAWN_VISUAL, 2.5*IN_MILLISECONDS); + case EVENT_LAUGH_2: + me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH_NO_SHEATHE); + events.ScheduleEvent(EVENT_BLINK, Seconds(3)); break; - } - case EVENT_DESPAWN_VISUAL: - DoCast(me, SPELL_TELEPORT_VISUAL); + case EVENT_BLINK: + DoCastSelf(SPELL_IMPROVED_BLINK); + events.ScheduleEvent(EVENT_DESPAWN, Seconds(4)); break; case EVENT_DESPAWN: - me->DespawnOrUnsummon(); + me->AddObjectToRemoveList(); break; default: break; @@ -231,6 +269,8 @@ class npc_minigob_manabonk : public CreatureScript } private: + + ObjectGuid playerGuid; EventMap events; }; diff --git a/src/server/scripts/Northrend/zone_dragonblight.cpp b/src/server/scripts/Northrend/zone_dragonblight.cpp index 43983271833..6af5973eab0 100644 --- a/src/server/scripts/Northrend/zone_dragonblight.cpp +++ b/src/server/scripts/Northrend/zone_dragonblight.cpp @@ -411,7 +411,7 @@ public: tree->AI()->Talk(SAY_WALKER_FRIENDLY, player); tree->DespawnOrUnsummon(1000); } - else if (roll == 0) // enemy version + else // enemy version { tree->AI()->Talk(SAY_WALKER_ENEMY, player); tree->SetFaction(FACTION_MONSTER); diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp index f4749ef74f4..6809dce26c2 100644 --- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp +++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp @@ -64,9 +64,9 @@ class npc_emily : public CreatureScript public: npc_emily() : CreatureScript("npc_emily") { } - struct npc_emilyAI : public npc_escortAI + struct npc_emilyAI : public EscortAI { - npc_emilyAI(Creature* creature) : npc_escortAI(creature) { } + npc_emilyAI(Creature* creature) : EscortAI(creature) { } void JustSummoned(Creature* summoned) override { @@ -76,7 +76,7 @@ public: summoned->AI()->AttackStart(me->GetVictim()); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -564,6 +564,11 @@ public: { npc_venture_co_stragglerAI(Creature* creature) : ScriptedAI(creature) { } + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_CHOP, Seconds(3), Seconds(6)); + } + void Reset() override { _playerGUID.Clear(); @@ -602,7 +607,7 @@ public: case EVENT_CHOP: if (UpdateVictim()) DoCastVictim(SPELL_CHOP); - _events.ScheduleEvent(EVENT_CHOP, 10000, 12000); + _events.Repeat(Seconds(10), Seconds(12)); break; default: break; @@ -611,7 +616,6 @@ public: if (!UpdateVictim()) return; - DoMeleeAttackIfReady(); } diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index aeb2fa3e59a..e2df8f6ca96 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -80,9 +80,9 @@ class npc_apothecary_hanes : public CreatureScript public: npc_apothecary_hanes() : CreatureScript("npc_apothecary_hanes") { } - struct npc_Apothecary_HanesAI : public npc_escortAI + struct npc_Apothecary_HanesAI : public EscortAI { - npc_Apothecary_HanesAI(Creature* creature) : npc_escortAI(creature) + npc_Apothecary_HanesAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -133,7 +133,7 @@ public: if (GetAttack() && UpdateVictim()) DoMeleeAttackIfReady(); - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (me->IsInCombat()) return; @@ -156,7 +156,7 @@ public: events.Reset(); me->SetFaction(FACTION_ESCORTEE_H_PASSIVE); me->SetReactState(REACT_AGGRESSIVE); - ENSURE_AI(npc_escortAI, (me->AI()))->Start(true, true, _player); + ENSURE_AI(EscortAI, (me->AI()))->Start(true, true, _player); break; case EVENT_TALK_1: if (Player* player = ObjectAccessor::GetPlayer(*me, _player)) @@ -202,7 +202,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -272,9 +272,9 @@ class npc_plaguehound_tracker : public CreatureScript public: npc_plaguehound_tracker() : CreatureScript("npc_plaguehound_tracker") { } - struct npc_plaguehound_trackerAI : public npc_escortAI + struct npc_plaguehound_trackerAI : public EscortAI { - npc_plaguehound_trackerAI(Creature* creature) : npc_escortAI(creature) { } + npc_plaguehound_trackerAI(Creature* creature) : EscortAI(creature) { } void Reset() override { @@ -292,7 +292,7 @@ public: Start(false, false, summonerGUID); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { if (waypointId != 26) return; diff --git a/src/server/scripts/Northrend/zone_icecrown.cpp b/src/server/scripts/Northrend/zone_icecrown.cpp index b7f60c8e9d2..ab3b83857d8 100644 --- a/src/server/scripts/Northrend/zone_icecrown.cpp +++ b/src/server/scripts/Northrend/zone_icecrown.cpp @@ -433,7 +433,7 @@ public: void Reset() override { - me->setRegeneratingHealth(false); + me->SetRegenerateHealth(false); DoCast(SPELL_THREAT_PULSE); Talk(BANNER_SAY); events.ScheduleEvent(EVENT_SPAWN, 3000); diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index 118aae0da83..e96c8d2f17d 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -18,14 +18,18 @@ /* ScriptData SDName: Sholazar_Basin SD%Complete: 100 -SDComment: Quest support: 12573, 12621, 12726 +SDComment: Quest support: 12550, 12645, 12688, 12726, 13957 SDCategory: Sholazar_Basin EndScriptData */ /* ContentData -npc_vekjik -avatar_of_freya +npc_engineer_helice +npc_jungle_punch_target +spell_q12620_the_lifewarden_wrath +spell_q12589_shoot_rjr npc_haiphoon (Quest: "Song of Wind and Water") +npc_vics_flying_machine +spell_shango_tracks EndContentData */ #include "ScriptMgr.h" @@ -36,196 +40,11 @@ EndContentData */ #include "ObjectMgr.h" #include "Player.h" #include "ScriptedEscortAI.h" -#include "ScriptedGossip.h" #include "SpellAuras.h" #include "SpellScript.h" -#include "TemporarySummon.h" #include "Vehicle.h" /*###### -## npc_vekjik -######*/ - -#define GOSSIP_VEKJIK_ITEM1 "Shaman Vekjik, I have spoken with the big-tongues and they desire peace. I have brought this offering on their behalf." -#define GOSSIP_VEKJIK_ITEM2 "No no... I had no intentions of betraying your people. I was only defending myself. it was all a misunderstanding." - -enum Vekjik -{ - GOSSIP_TEXTID_VEKJIK1 = 13137, - GOSSIP_TEXTID_VEKJIK2 = 13138, - - SAY_TEXTID_VEKJIK1 = 0, - - SPELL_FREANZYHEARTS_FURY = 51469, - - QUEST_MAKING_PEACE = 12573 -}; - -class npc_vekjik : public CreatureScript -{ -public: - npc_vekjik() : CreatureScript("npc_vekjik") { } - - struct npc_vekjikAI : public ScriptedAI - { - npc_vekjikAI(Creature* creature) : ScriptedAI(creature) { } - - bool GossipHello(Player* player) override - { - if (me->IsQuestGiver()) - player->PrepareQuestMenu(me->GetGUID()); - - if (player->GetQuestStatus(QUEST_MAKING_PEACE) == QUEST_STATUS_INCOMPLETE) - { - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_VEKJIK_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, GOSSIP_TEXTID_VEKJIK1, me->GetGUID()); - return true; - } - - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - return true; - } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF + 1: - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_VEKJIK_ITEM2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, GOSSIP_TEXTID_VEKJIK2, me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - CloseGossipMenuFor(player); - Talk(SAY_TEXTID_VEKJIK1, player); - player->AreaExploredOrEventHappens(QUEST_MAKING_PEACE); - DoCast(player, SPELL_FREANZYHEARTS_FURY, false); - break; - } - - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_vekjikAI(creature); - } -}; - -/*###### -## avatar_of_freya -######*/ - -#define GOSSIP_ITEM_AOF1 "I want to stop the Scourge as much as you do. How can I help?" -#define GOSSIP_ITEM_AOF2 "You can trust me. I am no friend of the Lich King." -#define GOSSIP_ITEM_AOF3 "I will not fail." - -enum Freya -{ - QUEST_FREYA_PACT = 12621, - - SPELL_FREYA_CONVERSATION = 52045, - - GOSSIP_TEXTID_AVATAR1 = 13303, - GOSSIP_TEXTID_AVATAR2 = 13304, - GOSSIP_TEXTID_AVATAR3 = 13305 -}; - -class npc_avatar_of_freya : public CreatureScript -{ -public: - npc_avatar_of_freya() : CreatureScript("npc_avatar_of_freya") { } - - struct npc_avatar_of_freyaAI : public ScriptedAI - { - npc_avatar_of_freyaAI(Creature* creature) : ScriptedAI(creature) { } - - bool GossipHello(Player* player) override - { - if (me->IsQuestGiver()) - player->PrepareQuestMenu(me->GetGUID()); - - if (player->GetQuestStatus(QUEST_FREYA_PACT) == QUEST_STATUS_INCOMPLETE) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_AOF1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - player->PlayerTalkClass->SendGossipMenu(GOSSIP_TEXTID_AVATAR1, me->GetGUID()); - return true; - } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF + 1: - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_AOF2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - player->PlayerTalkClass->SendGossipMenu(GOSSIP_TEXTID_AVATAR2, me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_AOF3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - player->PlayerTalkClass->SendGossipMenu(GOSSIP_TEXTID_AVATAR3, me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - player->CastSpell(player, SPELL_FREYA_CONVERSATION, true); - CloseGossipMenuFor(player); - break; - } - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_avatar_of_freyaAI(creature); - } -}; - -/*###### -## npc_bushwhacker -######*/ - -class npc_bushwhacker : public CreatureScript -{ -public: - npc_bushwhacker() : CreatureScript("npc_bushwhacker") { } - - struct npc_bushwhackerAI : public ScriptedAI - { - npc_bushwhackerAI(Creature* creature) : ScriptedAI(creature) - { - } - - void InitializeAI() override - { - if (me->isDead()) - return; - - if (TempSummon* summ = me->ToTempSummon()) - if (Unit* summoner = summ->GetSummoner()) - me->GetMotionMaster()->MovePoint(0, summoner->GetPositionX(), summoner->GetPositionY(), summoner->GetPositionZ()); - - Reset(); - } - - void UpdateAI(uint32 /*uiDiff*/) override - { - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_bushwhackerAI(creature); - } -}; - -/*###### ## npc_engineer_helice ######*/ @@ -253,9 +72,9 @@ class npc_engineer_helice : public CreatureScript public: npc_engineer_helice() : CreatureScript("npc_engineer_helice") { } - struct npc_engineer_heliceAI : public npc_escortAI + struct npc_engineer_heliceAI : public EscortAI { - npc_engineer_heliceAI(Creature* creature) : npc_escortAI(creature) + npc_engineer_heliceAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -267,7 +86,7 @@ public: uint32 m_uiChatTimer; - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); @@ -322,7 +141,7 @@ public: void UpdateAI(uint32 uiDiff) override { - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); if (HasEscortState(STATE_ESCORT_ESCORTING)) { @@ -550,96 +369,6 @@ public: }; /*###### -## npc_adventurous_dwarf -######*/ - -#define GOSSIP_OPTION_ORANGE "Can you spare an orange?" -#define GOSSIP_OPTION_BANANAS "Have a spare bunch of bananas?" -#define GOSSIP_OPTION_PAPAYA "I could really use a papaya." - -enum AdventurousDwarf -{ - QUEST_12634 = 12634, - - ITEM_BANANAS = 38653, - ITEM_PAPAYA = 38655, - ITEM_ORANGE = 38656, - - SPELL_ADD_ORANGE = 52073, - SPELL_ADD_BANANAS = 52074, - SPELL_ADD_PAPAYA = 52076, - - GOSSIP_MENU_DWARF = 13307, - - SAY_DWARF_OUCH = 0, - SAY_DWARF_HELP = 1 -}; - -class npc_adventurous_dwarf : public CreatureScript -{ -public: - npc_adventurous_dwarf() : CreatureScript("npc_adventurous_dwarf") { } - - struct npc_adventurous_dwarfAI : public ScriptedAI - { - npc_adventurous_dwarfAI(Creature* creature) : ScriptedAI(creature) - { - Talk(SAY_DWARF_OUCH); - } - - bool GossipHello(Player* player) override - { - if (player->GetQuestStatus(QUEST_12634) != QUEST_STATUS_INCOMPLETE) - return false; - - if (player->GetItemCount(ITEM_ORANGE) < 1) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_OPTION_ORANGE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - if (player->GetItemCount(ITEM_BANANAS) < 2) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_OPTION_BANANAS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - - if (player->GetItemCount(ITEM_PAPAYA) < 1) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_OPTION_PAPAYA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - - player->PlayerTalkClass->SendGossipMenu(GOSSIP_MENU_DWARF, me->GetGUID()); - return true; - } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - uint32 spellId = 0; - - switch (action) - { - case GOSSIP_ACTION_INFO_DEF + 1: - spellId = SPELL_ADD_ORANGE; - break; - case GOSSIP_ACTION_INFO_DEF + 2: - spellId = SPELL_ADD_BANANAS; - break; - case GOSSIP_ACTION_INFO_DEF + 3: - spellId = SPELL_ADD_PAPAYA; - break; - } - - if (spellId) - player->CastSpell(player, spellId, true); - - Talk(SAY_DWARF_HELP); - me->DespawnOrUnsummon(); - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_adventurous_dwarfAI(creature); - } -}; - -/*###### ## Quest The Lifewarden's Wrath ######*/ @@ -1044,11 +773,7 @@ public: void AddSC_sholazar_basin() { - new npc_vekjik(); - new npc_avatar_of_freya(); - new npc_bushwhacker(); new npc_engineer_helice(); - new npc_adventurous_dwarf(); new npc_jungle_punch_target(); new spell_q12620_the_lifewarden_wrath(); new spell_q12589_shoot_rjr(); diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index efe1691d479..c0c22a43696 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -48,11 +48,11 @@ class npc_injured_goblin : public CreatureScript public: npc_injured_goblin() : CreatureScript("npc_injured_goblin") { } - struct npc_injured_goblinAI : public npc_escortAI + struct npc_injured_goblinAI : public EscortAI { - npc_injured_goblinAI(Creature* creature) : npc_escortAI(creature) { } + npc_injured_goblinAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -82,7 +82,7 @@ public: void UpdateAI(uint32 uiDiff) override { - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); if (!UpdateVictim()) return; DoMeleeAttackIfReady(); @@ -211,7 +211,7 @@ public: me->CastSpell(me, SPELL_ICE_PRISON, true); } - void JustRespawned() override + void JustAppeared() override { Reset(); } @@ -346,9 +346,9 @@ class npc_icefang : public CreatureScript public: npc_icefang() : CreatureScript("npc_icefang") { } - struct npc_icefangAI : public npc_escortAI + struct npc_icefangAI : public EscortAI { - npc_icefangAI(Creature* creature) : npc_escortAI(creature) { } + npc_icefangAI(Creature* creature) : EscortAI(creature) { } void AttackStart(Unit* /*who*/) override { } void EnterCombat(Unit* /*who*/) override { } @@ -363,13 +363,12 @@ public: } } - void WaypointReached(uint32 /*waypointId*/) override { } void JustDied(Unit* /*killer*/) override { } void OnCharmed(bool /*apply*/) override { } void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!UpdateVictim()) return; @@ -1045,7 +1044,7 @@ class spell_close_rift : public SpellScriptLoader void HandlePeriodic(AuraEffect const* /* aurEff */) { if (++_counter == 5) - GetTarget()->CastSpell((Unit*)nullptr, SPELL_DESPAWN_RIFT, true); + GetTarget()->CastSpell(nullptr, SPELL_DESPAWN_RIFT, true); } void Register() override @@ -1241,7 +1240,7 @@ class spell_low_health_trigger : public SpellScriptLoader void HandleScript(SpellEffIndex /*effIndex*/) { - GetHitUnit()->CastSpell((Unit*)nullptr, GetEffectValue(), true); + GetHitUnit()->CastSpell(nullptr, GetEffectValue(), true); } void Register() override @@ -1312,7 +1311,7 @@ class spell_claw_swipe_check : public SpellScriptLoader } } - GetTarget()->CastSpell((Unit*)nullptr, aurEff->GetAmount(), false); + GetTarget()->CastSpell(nullptr, aurEff->GetAmount(), false); } void Register() override @@ -1355,7 +1354,7 @@ class spell_fatal_strike : public SpellScriptLoader return; } - GetCaster()->CastSpell((Unit*)nullptr, SPELL_FATAL_STRIKE_DAMAGE, true); + GetCaster()->CastSpell(nullptr, SPELL_FATAL_STRIKE_DAMAGE, true); } void Register() override @@ -1422,7 +1421,7 @@ class spell_player_mount_wyrm : public SpellScriptLoader void HandleDummy(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - GetTarget()->CastSpell((Unit*)nullptr, SPELL_FIGHT_WYRM, true); + GetTarget()->CastSpell(nullptr, SPELL_FIGHT_WYRM, true); } void Register() override diff --git a/src/server/scripts/Northrend/zone_zuldrak.cpp b/src/server/scripts/Northrend/zone_zuldrak.cpp index 607e6845157..30ae441d7ce 100644 --- a/src/server/scripts/Northrend/zone_zuldrak.cpp +++ b/src/server/scripts/Northrend/zone_zuldrak.cpp @@ -924,7 +924,7 @@ public: me->CastSpell(me, STORM_VISUAL, true); } - void JustRespawned() override + void JustAppeared() override { Reset(); } diff --git a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp index a6340227270..1573fb3bcf7 100644 --- a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp +++ b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp @@ -178,6 +178,9 @@ class spell_talon_king_ikiss_blink : public SpellScriptLoader void FilterTargets(std::list<WorldObject*>& targets) { + if (targets.empty()) + return; + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); targets.clear(); targets.push_back(target); diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp index c12dd178729..945edfe3772 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_ambassador_hellmaw.cpp @@ -57,9 +57,9 @@ class boss_ambassador_hellmaw : public CreatureScript public: boss_ambassador_hellmaw() : CreatureScript("boss_ambassador_hellmaw") { } - struct boss_ambassador_hellmawAI : public npc_escortAI + struct boss_ambassador_hellmawAI : public EscortAI { - boss_ambassador_hellmawAI(Creature* creature) : npc_escortAI(creature) + boss_ambassador_hellmawAI(Creature* creature) : EscortAI(creature) { _instance = creature->GetInstanceScript(); _intro = false; @@ -86,11 +86,7 @@ class boss_ambassador_hellmaw : public CreatureScript if (me->HasAura(SPELL_BANISH)) return; - npc_escortAI::MoveInLineOfSight(who); - } - - void WaypointReached(uint32 /*waypointId*/) override - { + EscortAI::MoveInLineOfSight(who); } void DoAction(int32 actionId) override diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp index e5458ac1b10..04cc23136d9 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp @@ -16,16 +16,15 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* -Name: Boss_Blackheart_the_Inciter -%Complete: 75 -Comment: Incite Chaos not functional since core lacks Mind Control support -Category: Auchindoun, Shadow Labyrinth -*/ - -#include "ScriptMgr.h" +#include "InstanceScript.h" +#include "Map.h" #include "ObjectAccessor.h" +#include "PassiveAI.h" +#include "Player.h" +#include "PlayerAI.h" +#include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" #include "shadow_labyrinth.h" enum BlackheartTheInciter @@ -56,94 +55,178 @@ enum Events EVENT_WAR_STOMP = 3 }; -class boss_blackheart_the_inciter : public CreatureScript +class BlackheartCharmedPlayerAI : public SimpleCharmedPlayerAI { - public: - boss_blackheart_the_inciter() : CreatureScript("boss_blackheart_the_inciter") { } - - struct boss_blackheart_the_inciterAI : public BossAI + using SimpleCharmedPlayerAI::SimpleCharmedPlayerAI; + void OnCharmed(bool apply) override + { + SimpleCharmedPlayerAI::OnCharmed(apply); + if (!me->GetMap()->IsDungeon()) + return; + if (Creature* blackheart = ObjectAccessor::GetCreature(*me, me->GetInstanceScript()->GetGuidData(DATA_BLACKHEART_THE_INCITER))) { - boss_blackheart_the_inciterAI(Creature* creature) : BossAI(creature, DATA_BLACKHEART_THE_INCITER) { } - - void Reset() override - { - _Reset(); - } - - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - events.ScheduleEvent(EVENT_INCITE_CHAOS, 20000); - events.ScheduleEvent(EVENT_CHARGE_ATTACK, 5000); - events.ScheduleEvent(EVENT_WAR_STOMP, 15000); - - Talk(SAY_AGGRO); - } - - void KilledUnit(Unit* who) override - { - if (who->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } + blackheart->AI()->SetData(0, apply); + blackheart->GetThreatManager().AddThreat(me, 0.0f); + } + } +}; - void JustDied(Unit* /*killer*/) override +struct boss_blackheart_the_inciter : public BossAI +{ + boss_blackheart_the_inciter(Creature* creature) : BossAI(creature, DATA_BLACKHEART_THE_INCITER) { } + + void Reset() override + { + me->SetReactState(REACT_AGGRESSIVE); + _Reset(); + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + events.ScheduleEvent(EVENT_INCITE_CHAOS, 20000); + events.ScheduleEvent(EVENT_CHARGE_ATTACK, 5000); + events.ScheduleEvent(EVENT_WAR_STOMP, 15000); + + Talk(SAY_AGGRO); + } + + void KilledUnit(Unit* who) override + { + if (who->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } + + uint8 charmCount = 0; + void SetData(uint32 /*index*/, uint32 data) override + { + if (data) + ++charmCount; + else + { + if (!charmCount) + EnterEvadeMode(EVADE_REASON_OTHER); // sanity check + --charmCount; + } + if (charmCount) + me->SetReactState(REACT_PASSIVE); + else + me->SetReactState(REACT_AGGRESSIVE); + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + if (me->HasReactState(REACT_PASSIVE) || !UpdateVictim()) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - _JustDied(); - Talk(SAY_DEATH); + case EVENT_INCITE_CHAOS: + { + if (me->GetThreatManager().GetThreatListSize() > 1) + { + ResetThreatList(); + DoCast(me, SPELL_INCITE_CHAOS); + } + events.ScheduleEvent(EVENT_INCITE_CHAOS, 40000); + break; + } + case EVENT_CHARGE_ATTACK: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_CHARGE); + events.ScheduleEvent(EVENT_CHARGE, urand(15000, 25000)); + break; + case EVENT_WAR_STOMP: + DoCast(me, SPELL_WAR_STOMP); + events.ScheduleEvent(EVENT_WAR_STOMP, urand(18000, 24000)); + break; } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - events.Update(diff); + if (me->HasReactState(REACT_PASSIVE) || me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + DoMeleeAttackIfReady(); + } +}; - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) +struct boss_blackheart_the_inciter_mc_dummy : public NullCreatureAI +{ + using NullCreatureAI::NullCreatureAI; + void InitializeAI() override { me->SetReactState(REACT_PASSIVE); } + static const uint32 FIRST_DUMMY = NPC_BLACKHEART_DUMMY1, LAST_DUMMY = NPC_BLACKHEART_DUMMY5; + void IsSummonedBy(Unit* who) override + { + me->CastSpell(who, SPELL_INCITE_CHAOS_B, true); + + // ensure everyone is in combat with everyone + if (auto* dummies = GetBlackheartDummies(me->GetInstanceScript())) + for (ObjectGuid const& guid : *dummies) + if (Creature* trigger = ObjectAccessor::GetCreature(*me, guid)) + if (me->GetEntry() != trigger->GetEntry()) { - case EVENT_INCITE_CHAOS: + me->GetThreatManager().AddThreat(trigger, 0.0f); + trigger->GetThreatManager().AddThreat(who, 0.0f); + for (Unit* other : trigger->m_Controlled) { - DoCast(me, SPELL_INCITE_CHAOS); - - for (ThreatReference const* ref : me->GetThreatManager().GetUnsortedThreatList()) - if (ref->GetVictim()->GetTypeId() == TYPEID_PLAYER) - me->CastSpell(ref->GetVictim(), SPELL_INCITE_CHAOS_B, true); - - ResetThreatList(); - events.ScheduleEvent(EVENT_INCITE_CHAOS, 40000); - break; + me->GetThreatManager().AddThreat(other, 0.0f); + other->GetThreatManager().AddThreat(who, 0.0f); } - case EVENT_CHARGE_ATTACK: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_CHARGE); - events.ScheduleEvent(EVENT_CHARGE, urand(15000, 25000)); - break; - case EVENT_WAR_STOMP: - DoCast(me, SPELL_WAR_STOMP); - events.ScheduleEvent(EVENT_WAR_STOMP, urand(18000, 24000)); - break; } + } + void UpdateAI(uint32 /*diff*/) override + { + if (me->m_Controlled.empty()) + me->DespawnOrUnsummon(); + } + PlayerAI* GetAIForCharmedPlayer(Player* player) override + { + return new BlackheartCharmedPlayerAI(player); + } +}; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override +class spell_blackheart_incite_chaos : public SpellScript +{ + PrepareSpellScript(spell_blackheart_incite_chaos); + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_INCITE_CHAOS_B }); + } + + static const uint8 NUM_INCITE_SPELLS = 5; + static const uint32 INCITE_SPELLS[NUM_INCITE_SPELLS]; + uint8 i=0; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) { - return GetShadowLabyrinthAI<boss_blackheart_the_inciterAI>(creature); + target->CastSpell(nullptr, INCITE_SPELLS[i], true); + i = (i + 1) % NUM_INCITE_SPELLS; } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_blackheart_incite_chaos::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; +const uint32 spell_blackheart_incite_chaos::INCITE_SPELLS[spell_blackheart_incite_chaos::NUM_INCITE_SPELLS] = { 33677,33680,33681,33682,33683 }; void AddSC_boss_blackheart_the_inciter() { - new boss_blackheart_the_inciter(); + RegisterCreatureAIWithFactory(boss_blackheart_the_inciter, GetShadowLabyrinthAI); + RegisterCreatureAIWithFactory(boss_blackheart_the_inciter_mc_dummy, GetShadowLabyrinthAI); + RegisterSpellScript(spell_blackheart_incite_chaos); } diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp index edaab7cd9cb..1d415bed072 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp @@ -185,7 +185,7 @@ class spell_murmur_sonic_boom : public SpellScriptLoader void HandleEffect(SpellEffIndex /*effIndex*/) { - GetCaster()->CastSpell((Unit*)nullptr, SPELL_SONIC_BOOM_EFFECT, true); + GetCaster()->CastSpell(nullptr, SPELL_SONIC_BOOM_EFFECT, true); } void Register() override diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/instance_shadow_labyrinth.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/instance_shadow_labyrinth.cpp index c5ddb8e6b50..709b3569134 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/instance_shadow_labyrinth.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/instance_shadow_labyrinth.cpp @@ -53,6 +53,16 @@ class instance_shadow_labyrinth : public InstanceMapScript case NPC_AMBASSADOR_HELLMAW: AmbassadorHellmawGUID = creature->GetGUID(); break; + case NPC_BLACKHEART: + BlackheartGUID = creature->GetGUID(); + break; + case NPC_BLACKHEART_DUMMY1: + case NPC_BLACKHEART_DUMMY2: + case NPC_BLACKHEART_DUMMY3: + case NPC_BLACKHEART_DUMMY4: + case NPC_BLACKHEART_DUMMY5: + BlackheartDummyGUIDs.insert(creature->GetGUID()); + break; case NPC_GRANDMASTER_VORPIL: GrandmasterVorpilGUID = creature->GetGUID(); break; @@ -69,6 +79,22 @@ class instance_shadow_labyrinth : public InstanceMapScript } } + void OnCreatureRemove(Creature* creature) override + { + switch (creature->GetEntry()) + { + case NPC_BLACKHEART_DUMMY1: + case NPC_BLACKHEART_DUMMY2: + case NPC_BLACKHEART_DUMMY3: + case NPC_BLACKHEART_DUMMY4: + case NPC_BLACKHEART_DUMMY5: + BlackheartDummyGUIDs.erase(creature->GetGUID()); + break; + default: + break; + } + } + void OnGameObjectCreate(GameObject* go) override { switch (go->GetEntry()) @@ -128,6 +154,8 @@ class instance_shadow_labyrinth : public InstanceMapScript { switch (type) { + case DATA_BLACKHEART_THE_INCITER: + return BlackheartGUID; case DATA_GRANDMASTER_VORPIL: return GrandmasterVorpilGUID; default: @@ -136,8 +164,12 @@ class instance_shadow_labyrinth : public InstanceMapScript return ObjectGuid::Empty; } + GuidUnorderedSet const& GetBlackheartDummies() const { return BlackheartDummyGUIDs; } + protected: ObjectGuid AmbassadorHellmawGUID; + ObjectGuid BlackheartGUID; + GuidUnorderedSet BlackheartDummyGUIDs; ObjectGuid GrandmasterVorpilGUID; uint32 FelOverseerCount; }; @@ -148,6 +180,14 @@ class instance_shadow_labyrinth : public InstanceMapScript } }; +GuidUnorderedSet const* GetBlackheartDummies(InstanceScript const* s) +{ + if (auto* script = dynamic_cast<instance_shadow_labyrinth::instance_shadow_labyrinth_InstanceMapScript const*>(s)) + return &script->GetBlackheartDummies(); + return nullptr; + +} + void AddSC_instance_shadow_labyrinth() { new instance_shadow_labyrinth(); diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/shadow_labyrinth.h b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/shadow_labyrinth.h index 0c7e7b5afca..262a7b9c494 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/shadow_labyrinth.h +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/shadow_labyrinth.h @@ -19,6 +19,7 @@ #define SHADOW_LABYRINTH_H_ #include "CreatureAIImpl.h" +#include "ObjectGuid.h" #define SLScriptName "instance_shadow_labyrinth" #define DataHeader "SL" @@ -40,6 +41,12 @@ enum SLDataTypes enum SLCreatureIds { NPC_AMBASSADOR_HELLMAW = 18731, + NPC_BLACKHEART = 18667, + NPC_BLACKHEART_DUMMY1 = 19300, + NPC_BLACKHEART_DUMMY2 = 19301, + NPC_BLACKHEART_DUMMY3 = 19302, + NPC_BLACKHEART_DUMMY4 = 19303, + NPC_BLACKHEART_DUMMY5 = 19304, NPC_GRANDMASTER_VORPIL = 18732, NPC_FEL_OVERSEER = 18796 }; @@ -56,6 +63,8 @@ enum SLMisc ACTION_AMBASSADOR_HELLMAW_BANISH = 2, }; +GuidUnorderedSet const* GetBlackheartDummies(InstanceScript const* s); + template <class AI, class T> inline AI* GetShadowLabyrinthAI(T* obj) { diff --git a/src/server/scripts/Outland/BlackTemple/black_temple.cpp b/src/server/scripts/Outland/BlackTemple/black_temple.cpp index 08b00caa519..693d2be970d 100644 --- a/src/server/scripts/Outland/BlackTemple/black_temple.cpp +++ b/src/server/scripts/Outland/BlackTemple/black_temple.cpp @@ -57,274 +57,226 @@ enum Misc GROUP_OUT_OF_COMBAT = 1 }; -// ######################################################## -// Wrathbone Flayer -// ######################################################## - -class npc_wrathbone_flayer : public CreatureScript +struct npc_wrathbone_flayer : public ScriptedAI { -public: - npc_wrathbone_flayer() : CreatureScript("npc_wrathbone_flayer") { } + npc_wrathbone_flayer(Creature* creature) : ScriptedAI(creature) + { + Initialize(); + _instance = creature->GetInstanceScript(); + } - struct npc_wrathbone_flayerAI : public ScriptedAI + void Initialize() { - npc_wrathbone_flayerAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - _instance = creature->GetInstanceScript(); - } + _enteredCombat = false; + } - void Initialize() - { - _enteredCombat = false; - } + void Reset() override + { + _events.ScheduleEvent(EVENT_GET_CHANNELERS, 3000); + Initialize(); + _bloodmageList.clear(); + _deathshaperList.clear(); + } - void Reset() override - { - _events.ScheduleEvent(EVENT_GET_CHANNELERS, 3000); - Initialize(); - _bloodmageList.clear(); - _deathshaperList.clear(); - } + void JustDied(Unit* /*killer*/) override { } - void JustDied(Unit* /*killer*/) override { } + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_CLEAVE, 5000); + _events.ScheduleEvent(EVENT_IGNORED, 7000); + _enteredCombat = true; + } - void EnterCombat(Unit* /*who*/) override + void UpdateAI(uint32 diff) override + { + if (!_enteredCombat) { - _events.ScheduleEvent(EVENT_CLEAVE, 5000); - _events.ScheduleEvent(EVENT_IGNORED, 7000); - _enteredCombat = true; - } + _events.Update(diff); - void UpdateAI(uint32 diff) override - { - if (!_enteredCombat) + while (uint32 eventId = _events.ExecuteEvent()) { - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) + switch (eventId) { - switch (eventId) + case EVENT_GET_CHANNELERS: { - case EVENT_GET_CHANNELERS: - { - std::list<Creature*> BloodMageList; - me->GetCreatureListWithEntryInGrid(BloodMageList, NPC_BLOOD_MAGE, 15.0f); - - if (!BloodMageList.empty()) - for (std::list<Creature*>::const_iterator itr = BloodMageList.begin(); itr != BloodMageList.end(); ++itr) - { - _bloodmageList.push_back((*itr)->GetGUID()); - if ((*itr)->isDead()) - (*itr)->Respawn(); - } - - std::list<Creature*> DeathShaperList; - me->GetCreatureListWithEntryInGrid(DeathShaperList, NPC_DEATHSHAPER, 15.0f); - - if (!DeathShaperList.empty()) - for (std::list<Creature*>::const_iterator itr = DeathShaperList.begin(); itr != DeathShaperList.end(); ++itr) - { - _deathshaperList.push_back((*itr)->GetGUID()); - if ((*itr)->isDead()) - (*itr)->Respawn(); - } - - _events.ScheduleEvent(EVENT_SET_CHANNELERS, 3000); - - break; - } - case EVENT_SET_CHANNELERS: - { - for (ObjectGuid guid : _bloodmageList) - if (Creature* bloodmage = ObjectAccessor::GetCreature(*me, guid)) - bloodmage->CastSpell((Unit*)nullptr, SPELL_SUMMON_CHANNEL); - - for (ObjectGuid guid : _deathshaperList) - if (Creature* deathshaper = ObjectAccessor::GetCreature(*me, guid)) - deathshaper->CastSpell((Unit*)nullptr, SPELL_SUMMON_CHANNEL); - - _events.ScheduleEvent(EVENT_SET_CHANNELERS, 12000); - - break; - } - default: - break; + std::list<Creature*> BloodMageList; + me->GetCreatureListWithEntryInGrid(BloodMageList, NPC_BLOOD_MAGE, 15.0f); + + if (!BloodMageList.empty()) + for (std::list<Creature*>::const_iterator itr = BloodMageList.begin(); itr != BloodMageList.end(); ++itr) + { + _bloodmageList.push_back((*itr)->GetGUID()); + if ((*itr)->isDead()) + (*itr)->Respawn(); + } + + std::list<Creature*> DeathShaperList; + me->GetCreatureListWithEntryInGrid(DeathShaperList, NPC_DEATHSHAPER, 15.0f); + + if (!DeathShaperList.empty()) + for (std::list<Creature*>::const_iterator itr = DeathShaperList.begin(); itr != DeathShaperList.end(); ++itr) + { + _deathshaperList.push_back((*itr)->GetGUID()); + if ((*itr)->isDead()) + (*itr)->Respawn(); + } + + _events.ScheduleEvent(EVENT_SET_CHANNELERS, 3000); + + break; } - } - } + case EVENT_SET_CHANNELERS: + { + for (ObjectGuid guid : _bloodmageList) + if (Creature* bloodmage = ObjectAccessor::GetCreature(*me, guid)) + bloodmage->CastSpell(nullptr, SPELL_SUMMON_CHANNEL); - if (!UpdateVictim()) - return; + for (ObjectGuid guid : _deathshaperList) + if (Creature* deathshaper = ObjectAccessor::GetCreature(*me, guid)) + deathshaper->CastSpell(nullptr, SPELL_SUMMON_CHANNEL); - _events.Update(diff); + _events.ScheduleEvent(EVENT_SET_CHANNELERS, 12000); - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_CLEAVE: - DoCastVictim(SPELL_CLEAVE); - _events.ScheduleEvent(EVENT_CLEAVE, urand (1000, 2000)); - break; - case EVENT_IGNORED: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_IGNORED); - _events.ScheduleEvent(EVENT_IGNORED, 10000); break; + } default: break; } } - DoMeleeAttackIfReady(); } - private: - InstanceScript* _instance; - EventMap _events; - GuidList _bloodmageList; - GuidList _deathshaperList; - bool _enteredCombat; - }; + if (!UpdateVictim()) + return; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_wrathbone_flayerAI>(creature); + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + _events.ScheduleEvent(EVENT_CLEAVE, urand(1000, 2000)); + break; + case EVENT_IGNORED: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_IGNORED); + _events.ScheduleEvent(EVENT_IGNORED, 10000); + break; + default: + break; + } + } + DoMeleeAttackIfReady(); } + +private: + InstanceScript* _instance; + EventMap _events; + GuidList _bloodmageList; + GuidList _deathshaperList; + bool _enteredCombat; }; -class npc_angered_soul_fragment : public CreatureScript +struct npc_angered_soul_fragment : public ScriptedAI { -public: - npc_angered_soul_fragment() : CreatureScript("npc_angered_soul_fragment") { } + npc_angered_soul_fragment(Creature* creature) : ScriptedAI(creature) { } - struct npc_angered_soul_fragmentAI : public ScriptedAI + void Reset() override { - npc_angered_soul_fragmentAI(Creature* creature) : ScriptedAI(creature) { } + _scheduler.CancelAll(); - void Reset() override + _scheduler.Schedule(Seconds(1), GROUP_OUT_OF_COMBAT, [this](TaskContext invi) { - _scheduler.CancelAll(); + DoCastSelf(SPELL_GREATER_INVISIBILITY); - _scheduler.Schedule(Seconds(1), GROUP_OUT_OF_COMBAT, [this](TaskContext invi) + /* Workaround - On Retail creature appear and "vanish" again periodically, but i cant find packets + with UPDATE_AURA on sniffs about it */ + _scheduler.Schedule(Seconds(5), Seconds(10), GROUP_OUT_OF_COMBAT, [this](TaskContext /*context*/) { - DoCastSelf(SPELL_GREATER_INVISIBILITY); - - /* Workaround - On Retail creature appear and "vanish" again periodically, but i cant find packets - with UPDATE_AURA on sniffs about it */ - _scheduler.Schedule(Seconds(5), Seconds(10), GROUP_OUT_OF_COMBAT, [this](TaskContext /*context*/) - { - me->RemoveAurasDueToSpell(SPELL_GREATER_INVISIBILITY); - }); - - invi.Repeat(Seconds(15), Seconds(25)); + me->RemoveAurasDueToSpell(SPELL_GREATER_INVISIBILITY); }); - } - void EnterCombat(Unit* /*who*/) override - { - me->RemoveAurasDueToSpell(SPELL_GREATER_INVISIBILITY); + invi.Repeat(Seconds(15), Seconds(25)); + }); + } - _scheduler.CancelGroup(GROUP_OUT_OF_COMBAT); - _scheduler.Schedule(Seconds(1), [this](TaskContext anger) - { - Unit* target = me->GetVictim(); - if (target && me->IsWithinMeleeRange(target)) - DoCastSelf(SPELL_ANGER); - else - anger.Repeat(Seconds(1)); - }); - } + void EnterCombat(Unit* /*who*/) override + { + me->RemoveAurasDueToSpell(SPELL_GREATER_INVISIBILITY); - void UpdateAI(uint32 diff) override + _scheduler.CancelGroup(GROUP_OUT_OF_COMBAT); + _scheduler.Schedule(Seconds(1), [this](TaskContext anger) { - _scheduler.Update(diff); - - if (!UpdateVictim()) - return; + Unit* target = me->GetVictim(); + if (target && me->IsWithinMeleeRange(target)) + DoCastSelf(SPELL_ANGER); + else + anger.Repeat(Seconds(1)); + }); + } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); - DoMeleeAttackIfReady(); - } + if (!UpdateVictim()) + return; - private: - TaskScheduler _scheduler; - }; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_angered_soul_fragmentAI>(creature); + DoMeleeAttackIfReady(); } + +private: + TaskScheduler _scheduler; }; // 41986 - Anger -class spell_soul_fragment_anger : public SpellScriptLoader +class spell_soul_fragment_anger : public SpellScript { - public: - spell_soul_fragment_anger() : SpellScriptLoader("spell_soul_fragment_anger") { } - - class spell_soul_fragment_anger_SpellScript : public SpellScript - { - PrepareSpellScript(spell_soul_fragment_anger_SpellScript); + PrepareSpellScript(spell_soul_fragment_anger); - void HandleKill() - { - if (Creature* caster = GetCaster()->ToCreature()) - caster->DespawnOrUnsummon(Milliseconds(200)); - } - - void Register() override - { - AfterCast += SpellCastFn(spell_soul_fragment_anger_SpellScript::HandleKill); - } - }; + void HandleKill() + { + if (Creature* caster = GetCaster()->ToCreature()) + caster->DespawnOrUnsummon(Milliseconds(200)); + } - SpellScript* GetSpellScript() const override - { - return new spell_soul_fragment_anger_SpellScript(); - } + void Register() override + { + AfterCast += SpellCastFn(spell_soul_fragment_anger::HandleKill); + } }; // 39645 - Shadow Inferno -class spell_illidari_nightlord_shadow_inferno : public SpellScriptLoader +class spell_illidari_nightlord_shadow_inferno : public AuraScript { - public: - spell_illidari_nightlord_shadow_inferno() : SpellScriptLoader("spell_illidari_nightlord_shadow_inferno") { } - - class spell_illidari_nightlord_shadow_inferno_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidari_nightlord_shadow_inferno_AuraScript); + PrepareAuraScript(spell_illidari_nightlord_shadow_inferno); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SHADOW_INFERNO_DAMAGE }); - } - - void OnPeriodic(AuraEffect const* aurEffect) - { - PreventDefaultAction(); - int32 bp = aurEffect->GetTickNumber() * aurEffect->GetAmount(); - GetUnitOwner()->CastCustomSpell(SPELL_SHADOW_INFERNO_DAMAGE, SPELLVALUE_BASE_POINT0, bp, GetUnitOwner(), true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SHADOW_INFERNO_DAMAGE }); + } - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidari_nightlord_shadow_inferno_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; + void OnPeriodic(AuraEffect const* aurEffect) + { + PreventDefaultAction(); + int32 bp = aurEffect->GetTickNumber() * aurEffect->GetAmount(); + GetUnitOwner()->CastCustomSpell(SPELL_SHADOW_INFERNO_DAMAGE, SPELLVALUE_BASE_POINT0, bp, GetUnitOwner(), true); + } - AuraScript* GetAuraScript() const override - { - return new spell_illidari_nightlord_shadow_inferno_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidari_nightlord_shadow_inferno::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; void AddSC_black_temple() { - new npc_wrathbone_flayer(); - new npc_angered_soul_fragment(); - new spell_soul_fragment_anger(); - new spell_illidari_nightlord_shadow_inferno(); + RegisterBlackTempleCreatureAI(npc_wrathbone_flayer); + RegisterBlackTempleCreatureAI(npc_angered_soul_fragment); + RegisterSpellScript(spell_soul_fragment_anger); + RegisterAuraScript(spell_illidari_nightlord_shadow_inferno); } diff --git a/src/server/scripts/Outland/BlackTemple/black_temple.h b/src/server/scripts/Outland/BlackTemple/black_temple.h index bdade01ab33..02b587d374f 100644 --- a/src/server/scripts/Outland/BlackTemple/black_temple.h +++ b/src/server/scripts/Outland/BlackTemple/black_temple.h @@ -40,25 +40,22 @@ enum BTDataTypes // Additional Data DATA_AKAMA_SHADE = 9, - DATA_AKAMA = 10, DATA_MAIEV = 11, DATA_GO_ILLIDAN_GATE = 12, DATA_BLACK_TEMPLE_TRIGGER = 13, - DATA_GATHIOS_THE_SHATTERER = 14, DATA_HIGH_NETHERMANCER_ZEREVOR = 15, DATA_LADY_MALANDE = 16, DATA_VERAS_DARKSHADOW = 17, DATA_BLOOD_ELF_COUNCIL_VOICE = 18, - DATA_GO_DEN_OF_MORTAL_DOOR = 19, - DATA_ESSENCE_OF_SUFFERING = 20, DATA_ESSENCE_OF_DESIRE = 21, DATA_ESSENCE_OF_ANGER = 22, - DATA_ILLIDAN_MUSIC_CONTROLLER = 23, + DATA_TERON_GOREFIEND_INTRO = 24, + DATA_AKAMA_ILLIDAN_INTRO = 25 }; enum TriggerEmotes @@ -108,7 +105,8 @@ enum BTCreatureIds NPC_ILLIDARI_ELITE = 23226, NPC_GLAIVE_TARGET = 23448, NPC_GLAIVE_WORLD_TRIGGER = 22515, - NPC_DEMON_FIRE = 23069 + NPC_DEMON_FIRE = 23069, + NPC_PARASITIC_SHADOWFIEND = 23498 }; enum BTGameObjectIds @@ -146,4 +144,6 @@ inline AI* GetBlackTempleAI(T* obj) return GetInstanceAI<AI>(obj, BTScriptName); } +#define RegisterBlackTempleCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetBlackTempleAI) + #endif // BLACK_TEMPLE_H_ diff --git a/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp b/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp index 037eaa82d99..7e743b1c62e 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp @@ -89,322 +89,279 @@ enum Events EVENT_CHARGE_PLAYER }; -class boss_gurtogg_bloodboil : public CreatureScript + +struct boss_gurtogg_bloodboil : public BossAI { -public: - boss_gurtogg_bloodboil() : CreatureScript("boss_gurtogg_bloodboil") { } + boss_gurtogg_bloodboil(Creature* creature) : BossAI(creature, DATA_GURTOGG_BLOODBOIL) + { + Initialize(); + } - struct boss_gurtogg_bloodboilAI : public BossAI + void Reset() override { - boss_gurtogg_bloodboilAI(Creature* creature) : BossAI(creature, DATA_GURTOGG_BLOODBOIL) - { - Initialize(); - } + _Reset(); + Initialize(); + events.SetPhase(PHASE_1); + me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); + me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false); + } - void Reset() override - { - _Reset(); - Initialize(); - events.SetPhase(PHASE_1); - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false); - } + void Initialize() + { + _oldThreat = 0.0f; + _oldTargetGUID.Clear(); + _targetGUID.Clear(); + } - void Initialize() - { - _oldThreat = 0.0f; - _oldTargetGUID.Clear(); - _targetGUID.Clear(); - } + bool CanAIAttack(Unit const* who) const override + { + return BossAI::CanAIAttack(who) && !who->HasAura(SPELL_BEWILDERING_STRIKE); + } - bool CanAIAttack(Unit const* who) const override - { - return BossAI::CanAIAttack(who) && !who->HasAura(SPELL_BEWILDERING_STRIKE); - } + void AttackStart(Unit* who) override + { + if (!CanAIAttack(who)) + return; - void AttackStart(Unit* who) override - { - if (!CanAIAttack(who)) - return; + BossAI::AttackStart(who); + } - BossAI::AttackStart(who); - } + void EnterCombat(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _EnterCombat(); + events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); + events.ScheduleEvent(EVENT_CHANGE_PHASE, Seconds(60)); + ScheduleEvents(); + } - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - _EnterCombat(); - events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); - events.ScheduleEvent(EVENT_CHANGE_PHASE, Seconds(60)); - ScheduleEvents(); - } + void EnterEvadeMode(EvadeReason /*why*/) override + { + _DespawnAtEvade(); + } - void EnterEvadeMode(EvadeReason /*why*/) override + void ScheduleEvents() + { + if (events.IsInPhase(PHASE_1)) { - _DespawnAtEvade(); + events.ScheduleEvent(EVENT_BLOODBOIL, Seconds(10), GROUP_PHASE_1, PHASE_1); + events.ScheduleEvent(EVENT_ARCING_SMASH, Seconds(10), GROUP_PHASE_1, PHASE_1); + events.ScheduleEvent(EVENT_FEL_ACID_BREATH, Seconds(25), GROUP_PHASE_1, PHASE_1); + events.ScheduleEvent(EVENT_EJECT, Seconds(35), GROUP_PHASE_1, PHASE_1); + events.ScheduleEvent(EVENT_BEWILDERING_STRIKE, Seconds(47), GROUP_PHASE_1, PHASE_1); } - - void ScheduleEvents() + else if (events.IsInPhase(PHASE_2)) { - if (events.IsInPhase(PHASE_1)) - { - events.ScheduleEvent(EVENT_BLOODBOIL, Seconds(10), GROUP_PHASE_1, PHASE_1); - events.ScheduleEvent(EVENT_ARCING_SMASH, Seconds(10), GROUP_PHASE_1, PHASE_1); - events.ScheduleEvent(EVENT_FEL_ACID_BREATH, Seconds(25), GROUP_PHASE_1, PHASE_1); - events.ScheduleEvent(EVENT_EJECT, Seconds(35), GROUP_PHASE_1, PHASE_1); - events.ScheduleEvent(EVENT_BEWILDERING_STRIKE, Seconds(47), GROUP_PHASE_1, PHASE_1); - } - else if (events.IsInPhase(PHASE_2)) - { - events.ScheduleEvent(EVENT_START_PHASE_2, Milliseconds(100), GROUP_PHASE_2, PHASE_2); - events.ScheduleEvent(EVENT_EJECT_2, Seconds(14), GROUP_PHASE_2, PHASE_2); - events.ScheduleEvent(EVENT_FEL_ACID_BREATH_2, Seconds(16), GROUP_PHASE_2, PHASE_2); - events.ScheduleEvent(EVENT_ARCING_SMASH_2, Seconds(8), GROUP_PHASE_2, PHASE_2); - } + events.ScheduleEvent(EVENT_START_PHASE_2, Milliseconds(100), GROUP_PHASE_2, PHASE_2); + events.ScheduleEvent(EVENT_EJECT_2, Seconds(14), GROUP_PHASE_2, PHASE_2); + events.ScheduleEvent(EVENT_FEL_ACID_BREATH_2, Seconds(16), GROUP_PHASE_2, PHASE_2); + events.ScheduleEvent(EVENT_ARCING_SMASH_2, Seconds(8), GROUP_PHASE_2, PHASE_2); } + } - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - DoPlaySoundToSet(me, SOUND_ID_DEATH); - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + DoPlaySoundToSet(me, SOUND_ID_DEATH); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - events.Update(diff); + events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_BLOODBOIL: - DoCast(SPELL_BLOODBOIL); - events.Repeat(Seconds(10)); - break; - case EVENT_ARCING_SMASH: - DoCastVictim(SPELL_ARCING_SMASH); - events.Repeat(Seconds(10)); - break; - case EVENT_FEL_ACID_BREATH: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, me->GetCombatReach())) - DoCast(target, SPELL_FEL_ACID_BREATH); - events.Repeat(Seconds(25), Seconds(30)); - break; - case EVENT_EJECT: - Talk(SAY_SPECIAL); - DoCastVictim(SPELL_EJECT); - break; - case EVENT_BEWILDERING_STRIKE: - DoCastVictim(SPELL_BEWILDERING_STRIKE); - break; - case EVENT_CHANGE_PHASE: - ChangePhase(); - break; - case EVENT_START_PHASE_2: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) - { - if (Unit* oldTarget = me->GetVictim()) - { - _oldTargetGUID = oldTarget->GetGUID(); - _oldThreat = GetThreat(oldTarget); - } - _targetGUID = target->GetGUID(); - DoCastSelf(SPELL_FEL_RAGE_SELF, true); - DoCast(target, SPELL_FEL_RAGE_TARGET, true); - DoCast(target, SPELL_FEL_RAGE_2, true); - DoCast(target, SPELL_FEL_RAGE_3, true); - DoCast(target, SPELL_FEL_GEYSER, true); - DoCast(target, SPELL_FEL_RAGE_TARGET_2, true); - target->CastSpell(target, SPELL_FEL_RAGE_P, true); - target->CastSpell(target, SPELL_TAUNT_GURTOGG, true); - DoCastAOE(SPELL_INSIGNIFIGANCE, true); - - events.ScheduleEvent(EVENT_CHARGE_PLAYER, Seconds(2), GROUP_PHASE_2, PHASE_2); - - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); - } - else // If no other targets are found, reset phase 1 + case EVENT_BLOODBOIL: + DoCast(SPELL_BLOODBOIL); + events.Repeat(Seconds(10)); + break; + case EVENT_ARCING_SMASH: + DoCastVictim(SPELL_ARCING_SMASH); + events.Repeat(Seconds(10)); + break; + case EVENT_FEL_ACID_BREATH: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, me->GetCombatReach())) + DoCast(target, SPELL_FEL_ACID_BREATH); + events.Repeat(Seconds(25), Seconds(30)); + break; + case EVENT_EJECT: + Talk(SAY_SPECIAL); + DoCastVictim(SPELL_EJECT); + break; + case EVENT_BEWILDERING_STRIKE: + DoCastVictim(SPELL_BEWILDERING_STRIKE); + break; + case EVENT_CHANGE_PHASE: + ChangePhase(); + break; + case EVENT_START_PHASE_2: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) + { + if (Unit* oldTarget = me->GetVictim()) { - events.SetPhase(PHASE_1); - events.CancelEventGroup(GROUP_PHASE_2); - ScheduleEvents(); - events.RescheduleEvent(EVENT_CHANGE_PHASE, Seconds(60)); + _oldTargetGUID = oldTarget->GetGUID(); + _oldThreat = GetThreat(oldTarget); } - break; - case EVENT_CHARGE_PLAYER: - if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) - DoCast(target, SPELL_CHARGE); - break; - case EVENT_EJECT_2: - DoCastVictim(SPELL_EJECT_2); - break; - case EVENT_FEL_ACID_BREATH_2: - DoCastVictim(SPELL_FEL_ACID_BREATH_2); - break; - case EVENT_ARCING_SMASH_2: - DoCastVictim(SPELL_ARCING_SMASH_2); - events.Repeat(Seconds(13)); - break; - case EVENT_BERSERK: - DoCast(SPELL_BERSERK); - roll_chance_i(50) ? Talk(SAY_ENRAGE) : DoPlaySoundToSet(me, SOUND_ID_ENRAGE); - break; - default: - break; - } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); - } - - void ChangePhase() - { - if (events.IsInPhase(PHASE_1)) - { - events.SetPhase(PHASE_2); - events.CancelEventGroup(GROUP_PHASE_1); - events.ScheduleEvent(EVENT_CHANGE_PHASE, Seconds(30)); - ScheduleEvents(); - } - else if (events.IsInPhase(PHASE_2)) - { - events.SetPhase(PHASE_1); - events.CancelEventGroup(GROUP_PHASE_2); - events.ScheduleEvent(EVENT_CHANGE_PHASE, Seconds(60)); - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false); - ScheduleEvents(); - - // Attack the stored target - if (Unit* oldTarget = ObjectAccessor::GetUnit(*me, _oldTargetGUID)) - if (Unit* currentTarget = ObjectAccessor::GetUnit(*me, _targetGUID)) + _targetGUID = target->GetGUID(); + DoCastSelf(SPELL_FEL_RAGE_SELF, true); + DoCast(target, SPELL_FEL_RAGE_TARGET, true); + DoCast(target, SPELL_FEL_RAGE_2, true); + DoCast(target, SPELL_FEL_RAGE_3, true); + DoCast(target, SPELL_FEL_GEYSER, true); + DoCast(target, SPELL_FEL_RAGE_TARGET_2, true); + target->CastSpell(target, SPELL_FEL_RAGE_P, true); + target->CastSpell(target, SPELL_TAUNT_GURTOGG, true); + DoCastAOE(SPELL_INSIGNIFIGANCE, true); + + events.ScheduleEvent(EVENT_CHARGE_PLAYER, Seconds(2), GROUP_PHASE_2, PHASE_2); + + me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); + } + else // If no other targets are found, reset phase 1 { - ModifyThreatByPercent(currentTarget, -100); - AttackStart(oldTarget); - AddThreat(oldTarget, _oldThreat); - Initialize(); + events.SetPhase(PHASE_1); + events.CancelEventGroup(GROUP_PHASE_2); + ScheduleEvents(); + events.RescheduleEvent(EVENT_CHANGE_PHASE, Seconds(60)); } + break; + case EVENT_CHARGE_PLAYER: + if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) + DoCast(target, SPELL_CHARGE); + break; + case EVENT_EJECT_2: + DoCastVictim(SPELL_EJECT_2); + break; + case EVENT_FEL_ACID_BREATH_2: + DoCastVictim(SPELL_FEL_ACID_BREATH_2); + break; + case EVENT_ARCING_SMASH_2: + DoCastVictim(SPELL_ARCING_SMASH_2); + events.Repeat(Seconds(13)); + break; + case EVENT_BERSERK: + DoCast(SPELL_BERSERK); + roll_chance_i(50) ? Talk(SAY_ENRAGE) : DoPlaySoundToSet(me, SOUND_ID_ENRAGE); + break; + default: + break; } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - private: - ObjectGuid _targetGUID; - ObjectGuid _oldTargetGUID; - float _oldThreat; - }; + DoMeleeAttackIfReady(); + } - CreatureAI* GetAI(Creature* creature) const override + void ChangePhase() { - return GetBlackTempleAI<boss_gurtogg_bloodboilAI>(creature); + if (events.IsInPhase(PHASE_1)) + { + events.SetPhase(PHASE_2); + events.CancelEventGroup(GROUP_PHASE_1); + events.ScheduleEvent(EVENT_CHANGE_PHASE, Seconds(30)); + ScheduleEvents(); + } + else if (events.IsInPhase(PHASE_2)) + { + events.SetPhase(PHASE_1); + events.CancelEventGroup(GROUP_PHASE_2); + events.ScheduleEvent(EVENT_CHANGE_PHASE, Seconds(60)); + me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); + me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false); + ScheduleEvents(); + + // Attack the stored target + if (Unit* oldTarget = ObjectAccessor::GetUnit(*me, _oldTargetGUID)) + if (Unit* currentTarget = ObjectAccessor::GetUnit(*me, _targetGUID)) + { + ModifyThreatByPercent(currentTarget, -100); + AttackStart(oldTarget); + AddThreat(oldTarget, _oldThreat); + Initialize(); + } + } } + +private: + ObjectGuid _targetGUID; + ObjectGuid _oldTargetGUID; + float _oldThreat; }; -class npc_fel_geyser : public CreatureScript +struct npc_fel_geyser : public PassiveAI { -public: - npc_fel_geyser() : CreatureScript("npc_fel_geyser") { } - - struct npc_fel_geyserAI : public PassiveAI - { - npc_fel_geyserAI(Creature* creature) : PassiveAI(creature) { } - - void Reset() override - { - DoCastSelf(SPELL_FEL_GEYSER_2, true); - DoCastSelf(SPELL_BIRTH, true); - } - }; + npc_fel_geyser(Creature* creature) : PassiveAI(creature) { } - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetBlackTempleAI<npc_fel_geyserAI>(creature); + DoCastSelf(SPELL_FEL_GEYSER_2, true); + DoCastSelf(SPELL_BIRTH, true); } }; // 42005 - Bloodboil -class spell_gurtogg_bloodboil_bloodboil : public SpellScriptLoader +class spell_gurtogg_bloodboil_bloodboil : public SpellScript { - public: - spell_gurtogg_bloodboil_bloodboil() : SpellScriptLoader("spell_gurtogg_bloodboil_bloodboil") { } - - class spell_gurtogg_bloodboil_bloodboil_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gurtogg_bloodboil_bloodboil_SpellScript); - - void FilterTargets(std::list<WorldObject*>& targets) - { - if (targets.size() <= 5) - return; + PrepareSpellScript(spell_gurtogg_bloodboil_bloodboil); - // Sort the list of players - targets.sort(Trinity::ObjectDistanceOrderPred(GetCaster(), false)); - // Resize so we only get top 5 - targets.resize(5); - } + void FilterTargets(std::list<WorldObject*>& targets) + { + if (targets.size() <= 5) + return; - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gurtogg_bloodboil_bloodboil_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - } - }; + // Sort the list of players + targets.sort(Trinity::ObjectDistanceOrderPred(GetCaster(), false)); + // Resize so we only get top 5 + targets.resize(5); + } - SpellScript* GetSpellScript() const override - { - return new spell_gurtogg_bloodboil_bloodboil_SpellScript(); - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gurtogg_bloodboil_bloodboil::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } }; // 40618 - Insignificance -class spell_gurtogg_bloodboil_insignificance : public SpellScriptLoader +class spell_gurtogg_bloodboil_insignificance : public SpellScript { -public: - spell_gurtogg_bloodboil_insignificance() : SpellScriptLoader("spell_gurtogg_bloodboil_insignificance") { } + PrepareSpellScript(spell_gurtogg_bloodboil_insignificance); - class spell_gurtogg_bloodboil_insignificance_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spell*/) override { - PrepareSpellScript(spell_gurtogg_bloodboil_insignificance_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_FEL_RAGE_TARGET }); - } - - void FilterTargets(std::list<WorldObject*>& targets) - { - targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_FEL_RAGE_TARGET)); - } + return ValidateSpellInfo({ SPELL_FEL_RAGE_TARGET }); + } - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gurtogg_bloodboil_insignificance_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - } - }; + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_FEL_RAGE_TARGET)); + } - SpellScript* GetSpellScript() const override + void Register() override { - return new spell_gurtogg_bloodboil_insignificance_SpellScript(); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gurtogg_bloodboil_insignificance::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); } }; void AddSC_boss_gurtogg_bloodboil() { - new boss_gurtogg_bloodboil(); - new npc_fel_geyser(); - new spell_gurtogg_bloodboil_bloodboil(); - new spell_gurtogg_bloodboil_insignificance(); + RegisterBlackTempleCreatureAI(boss_gurtogg_bloodboil); + RegisterBlackTempleCreatureAI(npc_fel_geyser); + RegisterSpellScript(spell_gurtogg_bloodboil_bloodboil); + RegisterSpellScript(spell_gurtogg_bloodboil_insignificance); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index fc6a92e203f..181a01f6745 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -159,6 +159,8 @@ enum IllidanSpells SPELL_DRAW_SOUL = 40904, SPELL_DRAW_SOUL_HEAL = 40903, SPELL_PARASITIC_SHADOWFIEND = 41917, + SPELL_PARASITIC_SHADOWFIEND_2 = 41914, + SPELL_REMOVE_PARASITIC_SHADOWFIEND = 41923, SPELL_AGONIZING_FLAMES = 40932, SPELL_AGONIZING_FLAMES_SELECTOR = 40834, SPELL_FRENZY = 40683, @@ -211,6 +213,7 @@ enum IllidanActions ACTION_FLAME_DEAD, ACTION_FINALIZE_AIR_PHASE, ACTION_START_PHASE_4, + ACTION_RESUME_COMBAT, ACTION_ILLIDAN_CAGED, ACTION_START_OUTRO, ACTION_MAIEV_DOWN_FADE @@ -404,6 +407,12 @@ Position const IllidanDBTargetPoints[4] = { 660.3492f, 345.5749f, 353.2961f } }; +Position const BladesPositions[2] = +{ + { 676.226013f, 325.230988f }, + { 678.059998f, 285.220001f } +}; + uint32 const SummonCageTrapSpells[8] = { SPELL_SUMMON_CAGE_TRAP_1, @@ -435,2118 +444,1880 @@ private: class ChargeTargetSelector : public std::unary_function<Unit*, bool> { public: - ChargeTargetSelector(Unit const* unit) : _me(unit) { } + ChargeTargetSelector() { } bool operator()(Unit* unit) const { - return unit->GetTypeId() == TYPEID_PLAYER && _me->GetDistance2d(unit) > 25.0f; + return unit->GetTypeId() == TYPEID_PLAYER + && unit->GetDistance2d(BladesPositions[0].GetPositionX(), BladesPositions[0].GetPositionY()) > 25.0f + && unit->GetDistance2d(BladesPositions[1].GetPositionX(), BladesPositions[1].GetPositionY()) > 25.0f; } - -private: - Unit const* _me; }; -class boss_illidan_stormrage : public CreatureScript +struct boss_illidan_stormrage : public BossAI { -public: - boss_illidan_stormrage() : CreatureScript("boss_illidan_stormrage") { } + boss_illidan_stormrage(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE), + _minionsCount(0), _flameCount(0), _orientation(0.0f), _pillarIndex(0), _phase(0), _dead(false), _isDemon(false) { } + + void Reset() override + { + _Reset(); + specialEvents.Reset(); + me->SummonCreatureGroup(SUMMON_GROUP); + me->LoadEquipment(1, true); + me->SetSheath(SHEATH_STATE_UNARMED); + me->SetControlled(false, UNIT_STATE_ROOT); + me->SetDisableGravity(false); + _dead = false; + _minionsCount = 0; + _flameCount = 0; + _phase = PHASE_1; + _isDemon = false; + if (instance->GetData(DATA_AKAMA_ILLIDAN_INTRO) && instance->GetBossState(DATA_ILLIDARI_COUNCIL) == DONE) + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_ACTIVE_AKAMA_INTRO); + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + me->SetCanDualWield(true); + if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) + musicController->PlayDirectMusic(EVENT_BT_SUMMIT_WALK_3_SOUND_ID); + specialEvents.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(10)); + specialEvents.ScheduleEvent(EVENT_BERSERK, Minutes(25)); + ScheduleEvents(GROUP_PHASE_1, GROUP_PHASE_1); + events.ScheduleEvent(EVENT_TAUNT, Seconds(30), Seconds(60), GROUP_PHASE_ALL); + } + + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_PARASITIC_SHADOWFIEND) + summons.Summon(summon); + else + BossAI::JustSummoned(summon); + } - struct boss_illidan_stormrageAI : public BossAI + void ChangeOrientation(float orientation) { - boss_illidan_stormrageAI(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE), - _intro(true), _minionsCount(0), _flameCount(0), _orientation(0.0f), _pillarIndex(0), _phase(0), _dead(false), _isDemon(false) { } + _orientation = orientation; + events.ScheduleEvent(EVENT_CHANGE_ORIENTATION, Milliseconds(1), GROUP_PHASE_ALL); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_ILLIDAN_KILL); + } + + void ScheduleEvents(uint8 phase, uint8 group) + { + switch (phase) + { + case GROUP_PHASE_1: + events.ScheduleEvent(EVENT_FLAME_CRASH, Seconds(30), group); + events.ScheduleEvent(EVENT_DRAW_SOUL, Seconds(34), group); + events.ScheduleEvent(EVENT_SHEAR, Seconds(10), group); + events.ScheduleEvent(EVENT_PARASITIC_SHADOWFIEND, Seconds(26), group); + break; + case GROUP_PHASE_2: + events.ScheduleEvent(EVENT_FIREBALL, Seconds(1), Seconds(8), group); + events.ScheduleEvent(EVENT_EYE_BLAST, Seconds(1), Seconds(30), group); + if (roll_chance_i(50)) + events.ScheduleEvent(EVENT_DARK_BARRAGE, Seconds(1), Seconds(20), group); + break; + case GROUP_PHASE_3: + ScheduleEvents(GROUP_PHASE_1, group); + events.ScheduleEvent(EVENT_AGONIZING_FLAMES, Seconds(21), group); + events.ScheduleEvent(EVENT_DEMON, Seconds(60), group); + break; + case GROUP_PHASE_DEMON: + events.ScheduleEvent(EVENT_SHADOW_BLAST, Seconds(1), group); + events.ScheduleEvent(EVENT_FLAME_BURST, Seconds(6), group); + events.ScheduleEvent(EVENT_SHADOW_DEMON, Seconds(18), Seconds(30), group); + break; + case GROUP_PHASE_4: + ScheduleEvents(GROUP_PHASE_3, group); + events.ScheduleEvent(EVENT_FRENZY, Seconds(40), group); + break; + default: + break; + } + } - void Reset() override + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override + { + if (summon->GetEntry() == NPC_ILLIDARI_ELITE) + _minionsCount--; + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + summons.DespawnAll(); + specialEvents.Reset(); + _DespawnAtEvade(); + } + + void DoAction(int32 actionId) override + { + switch (actionId) { - _Reset(); - specialEvents.Reset(); - me->SummonCreatureGroup(SUMMON_GROUP); - me->LoadEquipment(1, true); - me->SetSheath(SHEATH_STATE_UNARMED); - me->SetControlled(false, UNIT_STATE_ROOT); - me->SetDisableGravity(false); - _dead = false; - _minionsCount = 0; - _flameCount = 0; - _phase = PHASE_1; - _isDemon = false; - if (_intro && instance->GetBossState(DATA_ILLIDARI_COUNCIL) == DONE) + case ACTION_START_ENCOUNTER: + events.SetPhase(PHASE_INTRO); + me->RemoveAurasDueToSpell(SPELL_KNEEL); + events.ScheduleEvent(EVENT_START_INTRO, Seconds(2), GROUP_PHASE_ALL); + events.ScheduleEvent(EVENT_UNCONVINCED, Seconds(24), GROUP_PHASE_ALL); + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_FREE); + break; + case ACTION_INTRO_DONE: + instance->SetData(DATA_AKAMA_ILLIDAN_INTRO, 0); + break; + case ACTION_START_MINIONS: + Talk(SAY_ILLIDAN_MINION); if (Creature* akama = instance->GetCreature(DATA_AKAMA)) - akama->AI()->DoAction(ACTION_ACTIVE_AKAMA_INTRO); + akama->AI()->DoAction(ACTION_START_MINIONS); + break; + case ACTION_START_MINIONS_WEAVE: + events.ScheduleEvent(EVENT_MINIONS_WEAVE, Milliseconds(1), GROUP_PHASE_ALL); + break; + case ACTION_START_PHASE_2: + { + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + me->SetDisableGravity(true); + DoPlaySoundToSet(me, ILLIDAN_TAKEOFF_SOUND_ID); + events.ScheduleEvent(EVENT_FLY, Seconds(1), GROUP_PHASE_ALL); + events.CancelEventGroup(GROUP_PHASE_1); + break; + } + case ACTION_FLAME_DEAD: + _flameCount++; + if (_flameCount == 2) + { + _flameCount = 0; + DoAction(ACTION_FINALIZE_AIR_PHASE); + } + break; + case ACTION_FINALIZE_AIR_PHASE: + me->InterruptNonMeleeSpells(false); + me->GetMotionMaster()->Clear(); + events.CancelEventGroup(GROUP_PHASE_2); + _phase = PHASE_3; + events.CancelEvent(EVENT_FLY_TO_RANDOM_PILLAR); + me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_MIDDLE, IllidanMiddlePoint); + break; + case ACTION_START_PHASE_4: + events.CancelEventGroup(GROUP_PHASE_3); + DoCastSelf(SPELL_SHADOW_PRISON, true); + summons.DoAction(ACTION_START_PHASE_4, EntryCheckPredicate(NPC_PARASITIC_SHADOWFIEND)); + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + events.ScheduleEvent(EVENT_SHADOW_PRISON_TEXT, Milliseconds(500), GROUP_PHASE_ALL); + break; + case ACTION_ILLIDAN_CAGED: + for (uint32 summonSpell : SummonCageTrapSpells) + DoCastSelf(summonSpell, true); + DoCastSelf(SPELL_CAGE_TRAP, true); + break; + case ACTION_START_OUTRO: + me->AttackStop(); + events.Reset(); + specialEvents.Reset(); + DoCastSelf(SPELL_DEATH, true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + events.ScheduleEvent(EVENT_DEFEATED_TEXT, Seconds(4)); + break; + default: + break; } + } + + void JustDied(Unit* /*killer*/) override + { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + instance->SetBossState(DATA_ILLIDAN_STORMRAGE, DONE); + events.Reset(); + } - void EnterCombat(Unit* /*who*/) override + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE) + return; + + switch (pointId) { - _EnterCombat(); - me->SetCanDualWield(true); - if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) - musicController->PlayDirectMusic(EVENT_BT_SUMMIT_WALK_3_SOUND_ID); - specialEvents.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(10)); - specialEvents.ScheduleEvent(EVENT_BERSERK, Minutes(25)); - ScheduleEvents(GROUP_PHASE_1, GROUP_PHASE_1); - events.ScheduleEvent(EVENT_TAUNT, Seconds(30), Seconds(60), GROUP_PHASE_ALL); + case POINT_THROW_GLAIVE: + DoPlaySoundToSet(me, ILLIDAN_WARGLAIVE_SOUND_ID); + events.ScheduleEvent(EVENT_THROW_WARGLAIVE, Seconds(2), GROUP_PHASE_ALL); + events.ScheduleEvent(EVENT_FACE_MIDDLE, Milliseconds(1), GROUP_PHASE_ALL); + break; + case POINT_RANDOM_PILLAR: + { + float orientation = IllidanPhase2Positions[_pillarIndex].GetOrientation(); + ChangeOrientation(orientation); + ScheduleEvents(GROUP_PHASE_2, GROUP_PHASE_2); + break; + } + case POINT_ILLIDAN_MIDDLE: + { + float orientation = IllidanMiddlePoint.GetOrientation(); + ChangeOrientation(orientation); + + std::list<Creature*> triggers; + GetCreatureListWithEntryInGrid(triggers, me, NPC_BLADE_OF_AZZINOTH, 150.0f); + for (Creature* trigger : triggers) + trigger->CastSpell(trigger, SPELL_GLAIVE_RETURNS, true); + + events.ScheduleEvent(EVENT_GLAIVE_EMOTE, Seconds(3), GROUP_PHASE_ALL); + break; + } + default: + break; } + } + + void EnterEvadeModeIfNeeded() + { + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + if (Player* player = i->GetSource()) + if (player->IsAlive() && !player->IsGameMaster() && CheckBoundary(player)) + return; - void ChangeOrientation(float orientation) + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } + + void SummonMinions() + { + uint8 needSummon = MAX_MINIONS_NUMBER - _minionsCount; + for (uint8 i = 0; i < needSummon; ++i) { - _orientation = orientation; - events.ScheduleEvent(EVENT_CHANGE_ORIENTATION, Milliseconds(1), GROUP_PHASE_ALL); + _minionsCount++; + me->SummonCreature(NPC_ILLIDARI_ELITE, MinionsSpawnPositions[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); } + } - void KilledUnit(Unit* victim) override + void DamageTaken(Unit* who, uint32 &damage) override + { + + if (damage >= me->GetHealth() && who->GetGUID() != me->GetGUID()) { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_ILLIDAN_KILL); + damage = me->GetHealth() - 1; + if (!_dead) + { + if (_isDemon) + { + events.Reset(); + specialEvents.Reset(); + DoCastSelf(SPELL_DEMON_TRANSFORM_1); + return; + } + _dead = true; + summons.DespawnEntry(NPC_PARASITIC_SHADOWFIEND); + DoCastSelf(SPELL_REMOVE_PARASITIC_SHADOWFIEND, true); + DoAction(ACTION_START_OUTRO); + if (Creature* maiev = instance->GetCreature(DATA_MAIEV)) + maiev->AI()->DoAction(ACTION_START_OUTRO); + } + } + else if (me->HealthBelowPct(90) && _phase < PHASE_MINIONS) + { + _phase = PHASE_MINIONS; + DoAction(ACTION_START_MINIONS); + } + else if (me->HealthBelowPct(65) && _phase < PHASE_2) + { + _phase = PHASE_2; + DoAction(ACTION_START_PHASE_2); } + else if (me->HealthBelowPct(30) && _phase < PHASE_4) + { + _phase = PHASE_4; - void ScheduleEvents(uint8 phase, uint8 group) + if (_isDemon) + { + _isDemon = false; + me->SetControlled(false, UNIT_STATE_ROOT); + events.CancelEventGroup(GROUP_PHASE_DEMON); + me->InterruptNonMeleeSpells(false); + DoCastSelf(SPELL_DEMON_TRANSFORM_1, true); + events.ScheduleEvent(EVENT_PHASE_4_DELAYED, Seconds(12), GROUP_PHASE_ALL); + } + else + DoAction(ACTION_START_PHASE_4); + } + } + + void ExecuteSpecialEvents() + { + while (uint32 eventId = specialEvents.ExecuteEvent()) { - switch (phase) + switch (eventId) { - case GROUP_PHASE_1: - events.ScheduleEvent(EVENT_FLAME_CRASH, Seconds(30), group); - events.ScheduleEvent(EVENT_DRAW_SOUL, Seconds(34), group); - events.ScheduleEvent(EVENT_SHEAR, Seconds(10), group); - events.ScheduleEvent(EVENT_PARASITIC_SHADOWFIEND, Seconds(26), group); - break; - case GROUP_PHASE_2: - events.ScheduleEvent(EVENT_FIREBALL, Seconds(1), Seconds(8), group); - events.ScheduleEvent(EVENT_EYE_BLAST, Seconds(1), Seconds(30), group); - if (roll_chance_i(50)) - events.ScheduleEvent(EVENT_DARK_BARRAGE, Seconds(1), Seconds(20), group); - break; - case GROUP_PHASE_3: - ScheduleEvents(GROUP_PHASE_1, group); - events.ScheduleEvent(EVENT_AGONIZING_FLAMES, Seconds(21), group); - events.ScheduleEvent(EVENT_DEMON, Seconds(60), group); - break; - case GROUP_PHASE_DEMON: - events.ScheduleEvent(EVENT_SHADOW_BLAST, Seconds(1), group); - events.ScheduleEvent(EVENT_FLAME_BURST, Seconds(6), group); - events.ScheduleEvent(EVENT_SHADOW_DEMON, Seconds(18), Seconds(30), group); - break; - case GROUP_PHASE_4: - ScheduleEvents(GROUP_PHASE_3, group); - events.ScheduleEvent(EVENT_FRENZY, Seconds(40), group); + case EVENT_BERSERK: + Talk(SAY_ILLIDAN_ENRAGE); + DoCastSelf(SPELL_BERSERK, true); + break; + case EVENT_CANCEL_DEMON_FORM: + me->InterruptNonMeleeSpells(false); + me->SetControlled(false, UNIT_STATE_ROOT); + events.CancelEventGroup(GROUP_PHASE_DEMON); + DoCastSelf(SPELL_DEMON_TRANSFORM_1, true); + events.ScheduleEvent(EVENT_RESUME_COMBAT_DEMON, Seconds(12), GROUP_PHASE_ALL); + _isDemon = false; + break; + case EVENT_EVADE_CHECK: + EnterEvadeModeIfNeeded(); + specialEvents.Repeat(Seconds(10)); break; default: break; } } + } - void JustSummoned(Creature* summon) override - { - BossAI::JustSummoned(summon); - if (summon->GetEntry() == NPC_ILLIDARI_ELITE) - if (Creature* akama = instance->GetCreature(DATA_AKAMA)) - AddThreat(akama, 1000.0f, summon); - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) + return; - void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override - { - if (summon->GetEntry() == NPC_ILLIDARI_ELITE) - _minionsCount--; - } + specialEvents.Update(diff); - void EnterEvadeMode(EvadeReason /*why*/) override - { - summons.DespawnAll(); - specialEvents.Reset(); - _DespawnAtEvade(); - } + ExecuteSpecialEvents(); - void DoAction(int32 actionId) override + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - switch (actionId) + switch (eventId) { - case ACTION_START_ENCOUNTER: - events.SetPhase(PHASE_INTRO); - me->RemoveAurasDueToSpell(SPELL_KNEEL); - events.ScheduleEvent(EVENT_START_INTRO, Seconds(2), GROUP_PHASE_ALL); - events.ScheduleEvent(EVENT_UNCONVINCED, Seconds(24), GROUP_PHASE_ALL); - if (Creature* akama = instance->GetCreature(DATA_AKAMA)) - akama->AI()->DoAction(ACTION_FREE); + case EVENT_START_INTRO: + Talk(SAY_ILLIDAN_DUPLICITY); + break; + case EVENT_UNCONVINCED: + Talk(SAY_ILLIDAN_UNCONVINCED); + events.ScheduleEvent(EVENT_PREPARED, Seconds(14), GROUP_PHASE_ALL); break; - case ACTION_INTRO_DONE: - _intro = false; + case EVENT_PREPARED: + Talk(SAY_ILLIDAN_PREPARED); + me->SetSheath(SHEATH_STATE_MELEE); + events.ScheduleEvent(EVENT_ENCOUNTER_START, Seconds(3), GROUP_PHASE_ALL); break; - case ACTION_START_MINIONS: - Talk(SAY_ILLIDAN_MINION); + case EVENT_ENCOUNTER_START: + me->SetImmuneToAll(false); + DoZoneInCombat(); if (Creature* akama = instance->GetCreature(DATA_AKAMA)) - akama->AI()->DoAction(ACTION_START_MINIONS); + akama->AI()->DoAction(ACTION_START_ENCOUNTER); break; - case ACTION_START_MINIONS_WEAVE: - events.ScheduleEvent(EVENT_MINIONS_WEAVE, Milliseconds(1), GROUP_PHASE_ALL); + case EVENT_FLAME_CRASH: + DoCastVictim(SPELL_FLAME_CRASH); + events.Repeat(Seconds(30)); break; - case ACTION_START_PHASE_2: - { - me->SetReactState(REACT_PASSIVE); - me->AttackStop(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); - me->SetDisableGravity(true); - DoPlaySoundToSet(me, ILLIDAN_TAKEOFF_SOUND_ID); - events.ScheduleEvent(EVENT_FLY, Seconds(1), GROUP_PHASE_ALL); - events.CancelEventGroup(GROUP_PHASE_1); + case EVENT_DRAW_SOUL: + DoCastAOE(SPELL_DRAW_SOUL); + events.Repeat(Seconds(34)); break; - } - case ACTION_FLAME_DEAD: - _flameCount++; - if (_flameCount == 2) - { - _flameCount = 0; - DoAction(ACTION_FINALIZE_AIR_PHASE); - } + case EVENT_SHEAR: + DoCastVictim(SPELL_SHEAR); + events.Repeat(Seconds(12)); break; - case ACTION_FINALIZE_AIR_PHASE: - me->InterruptNonMeleeSpells(false); - me->GetMotionMaster()->Clear(); - events.CancelEventGroup(GROUP_PHASE_2); - _phase = PHASE_3; - events.CancelEvent(EVENT_FLY_TO_RANDOM_PILLAR); - me->GetMotionMaster()->MovePoint(POINT_ILLIDAN_MIDDLE, IllidanMiddlePoint); + case EVENT_PARASITIC_SHADOWFIEND: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) + DoCast(target, SPELL_PARASITIC_SHADOWFIEND); + events.Repeat(Seconds(30)); break; - case ACTION_START_PHASE_4: - events.CancelEventGroup(GROUP_PHASE_3); - DoCastSelf(SPELL_SHADOW_PRISON, true); - me->SetReactState(REACT_PASSIVE); - me->AttackStop(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_SHADOW_PRISON_TEXT, Milliseconds(500), GROUP_PHASE_ALL); + case EVENT_MINIONS_WEAVE: + if (_dead) + return; + SummonMinions(); + events.Repeat(Seconds(30)); break; - case ACTION_ILLIDAN_CAGED: - for (uint32 summonSpell : SummonCageTrapSpells) - DoCastSelf(summonSpell, true); - DoCastSelf(SPELL_CAGE_TRAP, true); + case EVENT_MOVE_TO_WARGLAIVE_POINT: + { + Position pos; + std::list<Creature*> triggers; + GetCreatureListWithEntryInGrid(triggers, me, NPC_GLAIVE_WORLD_TRIGGER, 150.0f); + triggers.remove_if([](WorldObject* unit) + { + return unit->GetPositionZ() < 355.0f || unit->GetPositionZ() > 365.0f; + }); + + if (triggers.empty()) + break; + + triggers.sort(Trinity::ObjectDistanceOrderPred(me)); + pos.Relocate(triggers.front()); + pos.SetOrientation(0.0f); + me->GetMotionMaster()->MovePoint(POINT_THROW_GLAIVE, pos); + if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) + musicController->PlayDirectMusic(EVENT_BT_STORM_WALK_HERO_2_SOUND_ID); break; - case ACTION_START_OUTRO: - me->AttackStop(); - events.Reset(); - specialEvents.Reset(); - DoCastSelf(SPELL_DEATH, true); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_DEFEATED_TEXT, Seconds(4)); + } + case EVENT_THROW_WARGLAIVE: + DoCastAOE(SPELL_THROW_GLAIVE); + events.ScheduleEvent(EVENT_THROW_WARGLAIVE_2, Seconds(1), GROUP_PHASE_ALL); break; - default: + case EVENT_THROW_WARGLAIVE_2: + DoCastAOE(SPELL_THROW_GLAIVE2); + me->SetSheath(SHEATH_STATE_UNARMED); + events.ScheduleEvent(EVENT_FLY_TO_RANDOM_PILLAR, Seconds(2), GROUP_PHASE_ALL); break; - } - } - - void JustDied(Unit* /*killer*/) override - { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - instance->SetBossState(DATA_ILLIDAN_STORMRAGE, DONE); - events.Reset(); - } - - void MovementInform(uint32 type, uint32 pointId) override - { - if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE) - return; - - switch (pointId) - { - case POINT_THROW_GLAIVE: - DoPlaySoundToSet(me, ILLIDAN_WARGLAIVE_SOUND_ID); - events.ScheduleEvent(EVENT_THROW_WARGLAIVE, Seconds(2), GROUP_PHASE_ALL); - events.ScheduleEvent(EVENT_FACE_MIDDLE, Milliseconds(1), GROUP_PHASE_ALL); + case EVENT_CHANGE_ORIENTATION: + me->SetFacingTo(_orientation); break; - case POINT_RANDOM_PILLAR: + case EVENT_FLY: + ChangeOrientation(3.137039f); + events.ScheduleEvent(EVENT_MOVE_TO_WARGLAIVE_POINT, Seconds(6), GROUP_PHASE_ALL); + break; + case EVENT_FLY_TO_RANDOM_PILLAR: { - float orientation = IllidanPhase2Positions[_pillarIndex].GetOrientation(); - ChangeOrientation(orientation); - ScheduleEvents(GROUP_PHASE_2, GROUP_PHASE_2); + events.CancelEventGroup(GROUP_PHASE_2); + _pillarIndex = urand(0, 3); + me->GetMotionMaster()->MovePoint(POINT_RANDOM_PILLAR, IllidanPhase2Positions[_pillarIndex]); + events.Repeat(Seconds(30)); break; } - case POINT_ILLIDAN_MIDDLE: + case EVENT_FACE_MIDDLE: { - float orientation = IllidanMiddlePoint.GetOrientation(); - ChangeOrientation(orientation); - - std::list<Creature*> triggers; - GetCreatureListWithEntryInGrid(triggers, me, NPC_BLADE_OF_AZZINOTH, 150.0f); - for (Creature* trigger : triggers) - trigger->CastSpell(trigger, SPELL_GLAIVE_RETURNS, true); - - events.ScheduleEvent(EVENT_GLAIVE_EMOTE, Seconds(3), GROUP_PHASE_ALL); + float angle = me->GetAngle(IllidanMiddlePoint); + me->SetFacingTo(angle); break; } - default: - break; - } - } - - void EnterEvadeModeIfNeeded() - { - Map::PlayerList const& players = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) - if (Player* player = i->GetSource()) - if (player->IsAlive() && !player->IsGameMaster() && CheckBoundary(player)) - return; - - EnterEvadeMode(EVADE_REASON_NO_HOSTILES); - } - - void SummonMinions() - { - uint8 needSummon = MAX_MINIONS_NUMBER - _minionsCount; - for (uint8 i = 0; i < needSummon; ++i) - { - _minionsCount++; - me->SummonCreature(NPC_ILLIDARI_ELITE, MinionsSpawnPositions[i], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - } - } - - void DamageTaken(Unit* who, uint32 &damage) override - { - - if (damage >= me->GetHealth() && who->GetGUID() != me->GetGUID()) - { - damage = me->GetHealth() - 1; - if (!_dead) + case EVENT_EYE_BLAST: { - if (_isDemon) + events.CancelEvent(EVENT_DARK_BARRAGE); + Position pos = IllidanDBTargetSpawnPositions[_pillarIndex]; + if (TempSummon* dbTarget = me->SummonCreature(NPC_ILLIDAN_DB_TARGET, pos, TEMPSUMMON_MANUAL_DESPAWN)) { - events.Reset(); - specialEvents.Reset(); - DoCastSelf(SPELL_DEMON_TRANSFORM_1); - return; + Talk(SAY_ILLIDAN_EYE_BLAST); + DoCast(dbTarget, SPELL_EYE_BLAST); + dbTarget->GetMotionMaster()->MovePoint(POINT_DB_TARGET, IllidanDBTargetPoints[_pillarIndex]); } - _dead = true; - DoAction(ACTION_START_OUTRO); - if (Creature* maiev = instance->GetCreature(DATA_MAIEV)) - maiev->AI()->DoAction(ACTION_START_OUTRO); + break; } - } - else if (me->HealthBelowPct(90) && _phase < PHASE_MINIONS) - { - _phase = PHASE_MINIONS; - DoAction(ACTION_START_MINIONS); - } - else if (me->HealthBelowPct(65) && _phase < PHASE_2) - { - _phase = PHASE_2; - DoAction(ACTION_START_PHASE_2); - } - else if (me->HealthBelowPct(30) && _phase < PHASE_4) - { - _phase = PHASE_4; - - if (_isDemon) + case EVENT_DARK_BARRAGE: { - _isDemon = false; - me->SetControlled(false, UNIT_STATE_ROOT); - events.CancelEventGroup(GROUP_PHASE_DEMON); - me->InterruptNonMeleeSpells(false); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 150.0f, true)) + DoCast(target, SPELL_DARK_BARRAGE); + events.RescheduleEvent(EVENT_EYE_BLAST, Seconds(5), GROUP_PHASE_2); + uint32 currentTime = events.GetNextEventTime(EVENT_FLY_TO_RANDOM_PILLAR); + events.RescheduleEvent(EVENT_FLY_TO_RANDOM_PILLAR, Seconds(currentTime) + Seconds(30), GROUP_PHASE_2); + break; + } + case EVENT_FIREBALL: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 150.0f, true)) + DoCast(target, SPELL_FIREBALL); + events.Repeat(Seconds(2), Seconds(4)); + break; + case EVENT_GLAIVE_EMOTE: + me->SetDisableGravity(false); + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + me->SetSheath(SHEATH_STATE_MELEE); + events.ScheduleEvent(EVENT_RESUME_COMBAT, Seconds(3), GROUP_PHASE_ALL); + break; + case EVENT_RESUME_COMBAT: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + ScheduleEvents(GROUP_PHASE_3, GROUP_PHASE_3); + if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) + musicController->PlayDirectMusic(EVENT_BT_STORM_WALK_UNI_3_SOUND_ID); + break; + case EVENT_AGONIZING_FLAMES: + DoCastSelf(SPELL_AGONIZING_FLAMES_SELECTOR); + events.Repeat(Seconds(53)); + break; + case EVENT_DEMON: + me->SetControlled(true, UNIT_STATE_ROOT); + _isDemon = true; + events.CancelEventGroup(_phase == PHASE_3 ? GROUP_PHASE_3 : GROUP_PHASE_4); + me->LoadEquipment(0, true); DoCastSelf(SPELL_DEMON_TRANSFORM_1, true); - events.ScheduleEvent(EVENT_PHASE_4_DELAYED, Seconds(12), GROUP_PHASE_ALL); + events.ScheduleEvent(EVENT_DEMON_TEXT, Seconds(2), GROUP_PHASE_ALL); + specialEvents.ScheduleEvent(EVENT_CANCEL_DEMON_FORM, Minutes(1) + Seconds(12)); + events.ScheduleEvent(EVENT_SCHEDULE_DEMON_SPELLS, Seconds(15)); + break; + case EVENT_SCHEDULE_DEMON_SPELLS: + ResetThreatList(); + ScheduleEvents(GROUP_PHASE_DEMON, GROUP_PHASE_DEMON); + break; + case EVENT_DEMON_TEXT: + Talk(SAY_ILLIDAN_MORPH); + break; + case EVENT_RESUME_COMBAT_DEMON: + { + uint8 group = _phase == PHASE_3 ? GROUP_PHASE_3 : GROUP_PHASE_4; + ResetThreatList(); + ScheduleEvents(group, group); + me->LoadEquipment(1, true); + break; } - else + case EVENT_FLAME_BURST: + DoCastSelf(SPELL_FLAME_BURST); + events.Repeat(Seconds(22)); + break; + case EVENT_SHADOW_DEMON: + DoCastSelf(SPELL_SUMMON_SHADOWDEMON); + break; + case EVENT_SHADOW_BLAST: + DoCastVictim(SPELL_SHADOW_BLAST); + events.Repeat(Seconds(2)); + break; + case EVENT_PHASE_4_DELAYED: DoAction(ACTION_START_PHASE_4); - } - } - - void ExecuteSpecialEvents() - { - while (uint32 eventId = specialEvents.ExecuteEvent()) - { - switch (eventId) + break; + case EVENT_SHADOW_PRISON_TEXT: + Talk(SAY_ILLIDAN_SHADOW_PRISON); + events.ScheduleEvent(EVENT_SUMMON_MAIEV, Seconds(9), GROUP_PHASE_ALL); + break; + case EVENT_SUMMON_MAIEV: + DoCastSelf(SPELL_SUMMON_MAIEV); + if (Creature* maiev = instance->GetCreature(DATA_MAIEV)) + me->SetFacingToObject(maiev); + events.ScheduleEvent(EVENT_CONFRONT_MAIEV_TEXT, Seconds(9), GROUP_PHASE_ALL); + break; + case EVENT_CONFRONT_MAIEV_TEXT: + Talk(SAY_ILLIDAN_CONFRONT_MAIEV); + events.ScheduleEvent(EVENT_RESUME_COMBAT_PHASE_4, Seconds(13), GROUP_PHASE_ALL); + break; + case EVENT_RESUME_COMBAT_PHASE_4: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + ScheduleEvents(GROUP_PHASE_4, GROUP_PHASE_4); + summons.DoAction(ACTION_RESUME_COMBAT, EntryCheckPredicate(NPC_PARASITIC_SHADOWFIEND)); + break; + case EVENT_FRENZY: + DoCastSelf(SPELL_FRENZY); + Talk(SAY_ILLIDAN_FRENZY); + events.Repeat(Seconds(40)); + break; + case EVENT_TAUNT: + Talk(SAY_ILLIDAN_TAUNT); + events.Repeat(Seconds(30), Seconds(60)); + break; + case EVENT_DEFEATED_TEXT: + Talk(SAY_ILLIDAN_DEFEATED); + if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) + musicController->PlayDirectMusic(EVENT_BT_ARRIVAL_WALK_HERO_1_SOUND_ID); + events.ScheduleEvent(EVENT_QUIET_SUICIDE, Seconds(18)); + break; + case EVENT_QUIET_SUICIDE: { - case EVENT_BERSERK: - Talk(SAY_ILLIDAN_ENRAGE); - DoCastSelf(SPELL_BERSERK, true); - break; - case EVENT_CANCEL_DEMON_FORM: - me->InterruptNonMeleeSpells(false); - me->SetControlled(false, UNIT_STATE_ROOT); - events.CancelEventGroup(GROUP_PHASE_DEMON); - DoCastSelf(SPELL_DEMON_TRANSFORM_1, true); - events.ScheduleEvent(EVENT_RESUME_COMBAT_DEMON, Seconds(12), GROUP_PHASE_ALL); - _isDemon = false; - break; - case EVENT_EVADE_CHECK: - EnterEvadeModeIfNeeded(); - specialEvents.Repeat(Seconds(10)); - break; - default: - break; + DoCastSelf(SPELL_QUIET_SUICIDE, true); + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_START_OUTRO); + ObjectGuid _akamaGUID = instance->GetGuidData(DATA_AKAMA); + ObjectGuid _maievGUID = instance->GetGuidData(DATA_MAIEV); + summons.DespawnIf([_akamaGUID, _maievGUID](ObjectGuid unitGuid) + { + return unitGuid != _akamaGUID && unitGuid != _maievGUID; + }); + break; } + default: + break; } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) - return; - - specialEvents.Update(diff); - - ExecuteSpecialEvents(); if (me->HasUnitState(UNIT_STATE_CASTING)) return; + } - events.Update(diff); + DoMeleeAttackIfReady(); + } - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_START_INTRO: - Talk(SAY_ILLIDAN_DUPLICITY); - break; - case EVENT_UNCONVINCED: - Talk(SAY_ILLIDAN_UNCONVINCED); - events.ScheduleEvent(EVENT_PREPARED, Seconds(14), GROUP_PHASE_ALL); - break; - case EVENT_PREPARED: - Talk(SAY_ILLIDAN_PREPARED); - me->SetSheath(SHEATH_STATE_MELEE); - events.ScheduleEvent(EVENT_ENCOUNTER_START, Seconds(3), GROUP_PHASE_ALL); - break; - case EVENT_ENCOUNTER_START: - me->SetImmuneToAll(false); - DoZoneInCombat(); - if (Creature* akama = instance->GetCreature(DATA_AKAMA)) - akama->AI()->DoAction(ACTION_START_ENCOUNTER); - break; - case EVENT_FLAME_CRASH: - DoCastVictim(SPELL_FLAME_CRASH); - events.Repeat(Seconds(30)); - break; - case EVENT_DRAW_SOUL: - DoCastAOE(SPELL_DRAW_SOUL); - events.Repeat(Seconds(34)); - break; - case EVENT_SHEAR: - DoCastVictim(SPELL_SHEAR); - events.Repeat(Seconds(12)); - break; - case EVENT_PARASITIC_SHADOWFIEND: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) - DoCast(target, SPELL_PARASITIC_SHADOWFIEND); - events.Repeat(Seconds(30)); - break; - case EVENT_MINIONS_WEAVE: - SummonMinions(); - events.Repeat(Seconds(30)); - break; - case EVENT_MOVE_TO_WARGLAIVE_POINT: - { - Position pos; - std::list<Creature*> triggers; - GetCreatureListWithEntryInGrid(triggers, me, NPC_GLAIVE_WORLD_TRIGGER, 150.0f); - triggers.remove_if([](WorldObject* unit) - { - return unit->GetPositionZ() < 355.0f || unit->GetPositionZ() > 365.0f; - }); +private: + uint8 _minionsCount; + uint8 _flameCount; + float _orientation; + uint8 _pillarIndex; + uint8 _phase; + bool _dead; + bool _isDemon; + EventMap specialEvents; +}; - if (triggers.empty()) - break; +struct npc_akama_illidan : public ScriptedAI +{ + npc_akama_illidan(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), + _orientation(0.0f), _isTeleportToMinions(false) { } - triggers.sort(Trinity::ObjectDistanceOrderPred(me)); - pos.Relocate(triggers.front()); - pos.SetOrientation(0.0f); - me->GetMotionMaster()->MovePoint(POINT_THROW_GLAIVE, pos); - if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) - musicController->PlayDirectMusic(EVENT_BT_STORM_WALK_HERO_2_SOUND_ID); - break; - } - case EVENT_THROW_WARGLAIVE: - DoCastAOE(SPELL_THROW_GLAIVE); - events.ScheduleEvent(EVENT_THROW_WARGLAIVE_2, Seconds(1), GROUP_PHASE_ALL); - break; - case EVENT_THROW_WARGLAIVE_2: - DoCastAOE(SPELL_THROW_GLAIVE2); - me->SetSheath(SHEATH_STATE_UNARMED); - events.ScheduleEvent(EVENT_FLY_TO_RANDOM_PILLAR, Seconds(2), GROUP_PHASE_ALL); - break; - case EVENT_CHANGE_ORIENTATION: - me->SetFacingTo(_orientation); - break; - case EVENT_FLY: - ChangeOrientation(3.137039f); - events.ScheduleEvent(EVENT_MOVE_TO_WARGLAIVE_POINT, Seconds(6), GROUP_PHASE_ALL); - break; - case EVENT_FLY_TO_RANDOM_PILLAR: - { - events.CancelEventGroup(GROUP_PHASE_2); - _pillarIndex = urand(0, 3); - me->GetMotionMaster()->MovePoint(POINT_RANDOM_PILLAR, IllidanPhase2Positions[_pillarIndex]); - events.Repeat(Seconds(30)); - break; - } - case EVENT_FACE_MIDDLE: - { - float angle = me->GetAngle(IllidanMiddlePoint); - me->SetFacingTo(angle); - break; - } - case EVENT_EYE_BLAST: - { - events.CancelEvent(EVENT_DARK_BARRAGE); - Position pos = IllidanDBTargetSpawnPositions[_pillarIndex]; - if (TempSummon* dbTarget = me->SummonCreature(NPC_ILLIDAN_DB_TARGET, pos, TEMPSUMMON_MANUAL_DESPAWN)) - { - Talk(SAY_ILLIDAN_EYE_BLAST); - DoCast(dbTarget, SPELL_EYE_BLAST); - dbTarget->GetMotionMaster()->MovePoint(POINT_DB_TARGET, IllidanDBTargetPoints[_pillarIndex]); - } - break; - } - case EVENT_DARK_BARRAGE: - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 150.0f, true)) - DoCast(target, SPELL_DARK_BARRAGE); - events.RescheduleEvent(EVENT_EYE_BLAST, Seconds(5), GROUP_PHASE_2); - uint32 currentTime = events.GetNextEventTime(EVENT_FLY_TO_RANDOM_PILLAR); - events.RescheduleEvent(EVENT_FLY_TO_RANDOM_PILLAR, Seconds(currentTime) + Seconds(30), GROUP_PHASE_2); - break; - } - case EVENT_FIREBALL: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 150.0f, true)) - DoCast(target, SPELL_FIREBALL); - events.Repeat(Seconds(2), Seconds(4)); - break; - case EVENT_GLAIVE_EMOTE: - me->SetDisableGravity(false); - me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); - me->SetSheath(SHEATH_STATE_MELEE); - events.ScheduleEvent(EVENT_RESUME_COMBAT, Seconds(3), GROUP_PHASE_ALL); - break; - case EVENT_RESUME_COMBAT: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_AGGRESSIVE); - ScheduleEvents(GROUP_PHASE_3, GROUP_PHASE_3); - if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) - musicController->PlayDirectMusic(EVENT_BT_STORM_WALK_UNI_3_SOUND_ID); - break; - case EVENT_AGONIZING_FLAMES: - DoCastSelf(SPELL_AGONIZING_FLAMES_SELECTOR); - events.Repeat(Seconds(53)); - break; - case EVENT_DEMON: - me->SetControlled(true, UNIT_STATE_ROOT); - _isDemon = true; - events.CancelEventGroup(_phase == PHASE_3 ? GROUP_PHASE_3 : GROUP_PHASE_4); - me->LoadEquipment(0, true); - DoCastSelf(SPELL_DEMON_TRANSFORM_1, true); - events.ScheduleEvent(EVENT_DEMON_TEXT, Seconds(2), GROUP_PHASE_ALL); - specialEvents.ScheduleEvent(EVENT_CANCEL_DEMON_FORM, Minutes(1) + Seconds(12)); - events.ScheduleEvent(EVENT_SCHEDULE_DEMON_SPELLS, Seconds(15)); - break; - case EVENT_SCHEDULE_DEMON_SPELLS: - ResetThreatList(); - ScheduleEvents(GROUP_PHASE_DEMON, GROUP_PHASE_DEMON); - break; - case EVENT_DEMON_TEXT: - Talk(SAY_ILLIDAN_MORPH); - break; - case EVENT_RESUME_COMBAT_DEMON: - { - uint8 group = _phase == PHASE_3 ? GROUP_PHASE_3 : GROUP_PHASE_4; - ResetThreatList(); - ScheduleEvents(group, group); - me->LoadEquipment(1, true); - break; - } - case EVENT_FLAME_BURST: - DoCastSelf(SPELL_FLAME_BURST); - events.Repeat(Seconds(22)); - break; - case EVENT_SHADOW_DEMON: - DoCastSelf(SPELL_SUMMON_SHADOWDEMON); - break; - case EVENT_SHADOW_BLAST: - DoCastVictim(SPELL_SHADOW_BLAST); - events.Repeat(Seconds(2)); - break; - case EVENT_PHASE_4_DELAYED: - DoAction(ACTION_START_PHASE_4); - break; - case EVENT_SHADOW_PRISON_TEXT: - Talk(SAY_ILLIDAN_SHADOW_PRISON); - events.ScheduleEvent(EVENT_SUMMON_MAIEV, Seconds(9), GROUP_PHASE_ALL); - break; - case EVENT_SUMMON_MAIEV: - DoCastSelf(SPELL_SUMMON_MAIEV); - if (Creature* maiev = instance->GetCreature(DATA_MAIEV)) - me->SetFacingToObject(maiev); - events.ScheduleEvent(EVENT_CONFRONT_MAIEV_TEXT, Seconds(9), GROUP_PHASE_ALL); - break; - case EVENT_CONFRONT_MAIEV_TEXT: - Talk(SAY_ILLIDAN_CONFRONT_MAIEV); - events.ScheduleEvent(EVENT_RESUME_COMBAT_PHASE_4, Seconds(13), GROUP_PHASE_ALL); - break; - case EVENT_RESUME_COMBAT_PHASE_4: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_AGGRESSIVE); - ScheduleEvents(GROUP_PHASE_4, GROUP_PHASE_4); - break; - case EVENT_FRENZY: - DoCastSelf(SPELL_FRENZY); - Talk(SAY_ILLIDAN_FRENZY); - events.Repeat(Seconds(40)); - break; - case EVENT_TAUNT: - Talk(SAY_ILLIDAN_TAUNT); - events.Repeat(Seconds(30), Seconds(60)); - break; - case EVENT_DEFEATED_TEXT: - Talk(SAY_ILLIDAN_DEFEATED); - if (GameObject* musicController = instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) - musicController->PlayDirectMusic(EVENT_BT_ARRIVAL_WALK_HERO_1_SOUND_ID); - events.ScheduleEvent(EVENT_QUIET_SUICIDE, Seconds(18)); - break; - case EVENT_QUIET_SUICIDE: - { - DoCastSelf(SPELL_QUIET_SUICIDE, true); - if (Creature* akama = instance->GetCreature(DATA_AKAMA)) - akama->AI()->DoAction(ACTION_START_OUTRO); - ObjectGuid _akamaGUID = instance->GetGuidData(DATA_AKAMA); - ObjectGuid _maievGUID = instance->GetGuidData(DATA_MAIEV); - summons.DespawnIf([_akamaGUID, _maievGUID](ObjectGuid unitGuid) - { - return unitGuid != _akamaGUID && unitGuid != _maievGUID; - }); - break; - } - default: - break; - } + void Reset() override + { + _events.Reset(); + _spiritOfUdaloGUID.Clear(); + _spiritOfOlumGUID.Clear(); + _isTeleportToMinions = false; + } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } + bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + { + if (gossipListId == GOSSIP_START_INTRO) + { + _instance->SetData(DATA_AKAMA, AKAMA_FIGHT); + me->GetMotionMaster()->MoveAlongSplineChain(POINT_STAIRS, SPLINE_STAIRS, false); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->DoAction(ACTION_INTRO_DONE); + CloseGossipMenuFor(player); - DoMeleeAttackIfReady(); } + else if (gossipListId == GOSSIP_START_FIGHT) + { + _events.SetPhase(PHASE_INTRO); + me->GetMotionMaster()->MoveAlongSplineChain(POINT_FACE_ILLIDAN, SPLINE_FACE_ILLIDAN, false); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + CloseGossipMenuFor(player); + } + return false; + } - private: - bool _intro; - uint8 _minionsCount; - uint8 _flameCount; - float _orientation; - uint8 _pillarIndex; - uint8 _phase; - bool _dead; - bool _isDemon; - EventMap specialEvents; - }; + bool CanAIAttack(Unit const* who) const override + { + if (_events.IsInPhase(PHASE_MINIONS) && who->GetEntry() == NPC_ILLIDAN_STORMRAGE) + return false; + return ScriptedAI::CanAIAttack(who); + } - CreatureAI* GetAI(Creature* creature) const override + uint32 GetData(uint32 /*data*/) const override { - return GetBlackTempleAI<boss_illidan_stormrageAI>(creature); + return _isTeleportToMinions ? 1 : 0; } -}; -class npc_akama : public CreatureScript -{ -public: - npc_akama() : CreatureScript("npc_akama_illidan") { } + void EnterEvadeMode(EvadeReason /*why*/) override { } - struct npc_akamaAI : public ScriptedAI + void JustSummoned(Creature* summon) override { - npc_akamaAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _orientation(0.0f), _isTeleportToMinions(false) { } - - void Reset() override + if (summon->GetEntry() == NPC_SPIRIT_OF_UDALO) { - _events.Reset(); - _spiritOfUdaloGUID.Clear(); - _spiritOfOlumGUID.Clear(); - _isTeleportToMinions = false; + _spiritOfUdaloGUID = summon->GetGUID(); + summon->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + else if (summon->GetEntry() == NPC_SPIRIT_OF_OLUM) { - if (gossipListId == GOSSIP_START_INTRO) - { - _instance->SetData(DATA_AKAMA, AKAMA_FIGHT); - me->GetMotionMaster()->MoveAlongSplineChain(POINT_STAIRS, SPLINE_STAIRS, false); - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->DoAction(ACTION_INTRO_DONE); - CloseGossipMenuFor(player); + _spiritOfOlumGUID = summon->GetGUID(); + summon->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + } - } - else if (gossipListId == GOSSIP_START_FIGHT) - { + void DoAction(int32 actionId) override + { + switch (actionId) + { + case ACTION_ACTIVE_AKAMA_INTRO: _events.SetPhase(PHASE_INTRO); - me->GetMotionMaster()->MoveAlongSplineChain(POINT_FACE_ILLIDAN, SPLINE_FACE_ILLIDAN, false); me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - CloseGossipMenuFor(player); - } - return false; + _events.SetPhase(PHASE_INTRO); + _events.ScheduleEvent(EVENT_TELEPORT, Seconds(1)); + _events.ScheduleEvent(EVENT_MOVE_TO_ILLIDARI_ROOM, Seconds(1) + Milliseconds(500)); + break; + case ACTION_OPEN_DOOR: + _instance->SetData(ACTION_OPEN_DOOR, 0); + _events.ScheduleEvent(EVENT_AKAMA_THANKS, Seconds(2)); + break; + case ACTION_FREE: + _events.ScheduleEvent(EVENT_FREE, Seconds(14)); + break; + case ACTION_START_ENCOUNTER: + DoZoneInCombat(); + _events.ScheduleEvent(EVENT_HEALING_POTION, Seconds(1)); + break; + case ACTION_START_MINIONS: + _events.ScheduleEvent(EVENT_AKAMA_MINIONS, Seconds(8)); + break; + case ACTION_START_OUTRO: + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + _events.Reset(); + _events.ScheduleEvent(EVENT_AKAMA_MOVE_BACK, Seconds(2)); + break; + default: + break; } + } - bool CanAIAttack(Unit const* who) const override - { - if (_events.IsInPhase(PHASE_MINIONS) && who->GetEntry() == NPC_ILLIDAN_STORMRAGE) - return false; - return ScriptedAI::CanAIAttack(who); - } + void ChangeOrientation(float orientation) + { + _orientation = orientation; + _events.ScheduleEvent(EVENT_CHANGE_ORIENTATION, Milliseconds(1)); + } - uint32 GetData(uint32 /*data*/) const override - { - return _isTeleportToMinions ? 1 : 0; + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != POINT_MOTION_TYPE && type != SPLINE_CHAIN_MOTION_TYPE) + return; + + switch (pointId) + { + case POINT_ILLIDARI_COUNCIL: + Talk(SAY_AKAMA_FINISH); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + break; + case POINT_STAIRS: + ChangeOrientation(6.265732f); + _events.ScheduleEvent(EVENT_AKAMA_SAY_DOOR, Seconds(5)); + break; + case POINT_ILLIDAN_ROOM: + ChangeOrientation(2.129302f); + Talk(SAY_AKAMA_BETRAYER); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + break; + case POINT_FACE_ILLIDAN: + ChangeOrientation(3.140537f); + _events.ScheduleEvent(EVENT_START_ILLIDAN, Seconds(2)); + break; + case POINT_TELEPORT: + DoCastSelf(SPELL_AKAMA_TELEPORT); + _events.ScheduleEvent(EVENT_AKAMA_MINIONS_MOVE_2, Milliseconds(500)); + break; + case POINT_MINIONS: + _events.SetPhase(PHASE_MINIONS); + me->SetImmuneToNPC(false); + me->SetReactState(REACT_AGGRESSIVE); + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->DoAction(ACTION_START_MINIONS_WEAVE); + _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, Seconds(2)); + break; + case POINT_MOVE_BACK: + _events.ScheduleEvent(EVENT_AKAMA_MOVE_TO_ILLIDAN, Milliseconds(1)); + break; + case POINT_ILLIDAN: + _events.ScheduleEvent(EVENT_AKAMA_LIGHT_TEXT, Seconds(1)); + break; + default: + break; } + } - void EnterEvadeMode(EvadeReason /*why*/) override { } + void DamageTaken(Unit* /*who*/, uint32 &damage) override + { + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; + } - void JustSummoned(Creature* summon) override - { - if (summon->GetEntry() == NPC_SPIRIT_OF_UDALO) - { - _spiritOfUdaloGUID = summon->GetGUID(); - summon->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - } - else if (summon->GetEntry() == NPC_SPIRIT_OF_OLUM) - { - _spiritOfOlumGUID = summon->GetGUID(); - summon->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - } - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO)) + return; + + _events.Update(diff); - void DoAction(int32 actionId) override + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = _events.ExecuteEvent()) { - switch (actionId) + switch (eventId) { - case ACTION_ACTIVE_AKAMA_INTRO: - _events.SetPhase(PHASE_INTRO); - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - _events.SetPhase(PHASE_INTRO); - _events.ScheduleEvent(EVENT_TELEPORT, Seconds(1)); - _events.ScheduleEvent(EVENT_MOVE_TO_ILLIDARI_ROOM, Seconds(1) + Milliseconds(500)); + case EVENT_TELEPORT: + DoCastSelf(SPELL_AKAMA_TELEPORT, true); break; - case ACTION_OPEN_DOOR: - _instance->SetData(ACTION_OPEN_DOOR, 0); - _events.ScheduleEvent(EVENT_AKAMA_THANKS, Seconds(2)); + case EVENT_MOVE_TO_ILLIDARI_ROOM: + me->GetMotionMaster()->MoveAlongSplineChain(POINT_ILLIDARI_COUNCIL, SPLINE_ILLIDARI_COUNCIL, false); break; - case ACTION_FREE: - _events.ScheduleEvent(EVENT_FREE, Seconds(14)); + case EVENT_AKAMA_SAY_DOOR: + Talk(SAY_AKAMA_DOOR); + _events.ScheduleEvent(EVENT_AKAMA_DOOR_FAIL, Seconds(4)); break; - case ACTION_START_ENCOUNTER: - DoZoneInCombat(); - _events.ScheduleEvent(EVENT_HEALING_POTION, Seconds(1)); + case EVENT_AKAMA_DOOR_FAIL: + DoCastSelf(SPELL_AKAMA_DOOR_FAIL); + _events.ScheduleEvent(EVENT_AKAMA_SAY_ALONE, Seconds(10)); + break; + case EVENT_AKAMA_SAY_ALONE: + Talk(SAY_AKAMA_ALONE); + _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, Seconds(7)); + break; + case EVENT_SUMMON_SPIRITS: + me->SummonCreatureGroup(SUMMON_GROUP); + _events.ScheduleEvent(EVENT_SPIRIT_SAY_1, Seconds(1)); + break; + case EVENT_SPIRIT_SAY_1: + if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) + undalo->AI()->Talk(SAY_SPIRIT_ALONE); + _events.ScheduleEvent(EVENT_SPIRIT_SAY_2, Seconds(6)); break; - case ACTION_START_MINIONS: - _events.ScheduleEvent(EVENT_AKAMA_MINIONS, Seconds(8)); + case EVENT_SPIRIT_SAY_2: + if (Creature* olum = ObjectAccessor::GetCreature(*me, _spiritOfOlumGUID)) + olum->AI()->Talk(SAY_SPIRIT_ALONE); + _events.ScheduleEvent(EVENT_AKAMA_DOOR_SUCCESS, Seconds(6)); break; - case ACTION_START_OUTRO: + case EVENT_AKAMA_DOOR_SUCCESS: + DoCastSelf(SPELL_AKAMA_DOOR_CHANNEL); + if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) + undalo->CastSpell((Unit*) nullptr, SPELL_DEATHSWORN_DOOR_CHANNEL); + if (Creature* olum = ObjectAccessor::GetCreature(*me, _spiritOfOlumGUID)) + olum->CastSpell((Unit*) nullptr, SPELL_DEATHSWORN_DOOR_CHANNEL); + _events.ScheduleEvent(EVENT_AKAMA_START_SOUND, Seconds(5)); + break; + case EVENT_AKAMA_START_SOUND: + if (GameObject* musicController = _instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) + musicController->PlayDirectMusic(EVENT_BT_SUMMIT_WALK_SOUND_ID); + break; + case EVENT_AKAMA_THANKS: + Talk(SAY_AKAMA_SALUTE); + _events.ScheduleEvent(EVENT_SPIRIT_SALUTE, Seconds(3)); + _events.ScheduleEvent(EVENT_RUN_FROM_ILLIDAN_ROOM, Seconds(7)); + break; + case EVENT_SPIRIT_SALUTE: + if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) + { + undalo->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + undalo->DespawnOrUnsummon(Seconds(7)); + } + if (Creature* olum = ObjectAccessor::GetCreature(*me, _spiritOfOlumGUID)) + { + olum->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + olum->DespawnOrUnsummon(Seconds(7)); + } + break; + case EVENT_RUN_FROM_ILLIDAN_ROOM: + me->GetMotionMaster()->MoveAlongSplineChain(POINT_ILLIDAN_ROOM, SPLINE_ILLIDAN_ROOM, false); + break; + case EVENT_START_ILLIDAN: + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->DoAction(ACTION_START_ENCOUNTER); + break; + case EVENT_FREE: + Talk(SAY_AKAMA_FREE); + _events.ScheduleEvent(EVENT_TIME_HAS_COME, Seconds(18)); + break; + case EVENT_TIME_HAS_COME: + Talk(SAY_AKAMA_TIME_HAS_COME); + _events.ScheduleEvent(EVENT_ROAR, Seconds(2)); + break; + case EVENT_ROAR: + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H); + break; + case EVENT_CHANGE_ORIENTATION: + me->SetFacingTo(_orientation); + break; + case EVENT_HEALING_POTION: + if (me->HealthBelowPct(20)) + DoCastSelf(SPELL_HEALING_POTION); + _events.Repeat(Seconds(1)); + break; + case EVENT_AKAMA_MINIONS: + Talk(SAY_AKAMA_MINIONS); + _events.ScheduleEvent(EVENT_AKAMA_MINIONS_EMOTE, Seconds(2)); + break; + case EVENT_AKAMA_MINIONS_EMOTE: me->SetReactState(REACT_PASSIVE); me->AttackStop(); - _events.Reset(); - _events.ScheduleEvent(EVENT_AKAMA_MOVE_BACK, Seconds(2)); + me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + me->SetImmuneToNPC(true); + _events.ScheduleEvent(EVENT_AKAMA_MINIONS_MOVE, Seconds(4)); break; - default: + case EVENT_AKAMA_MINIONS_MOVE: + _isTeleportToMinions = true; + me->GetMotionMaster()->MoveAlongSplineChain(POINT_TELEPORT, SPLINE_TELEPORT, false); break; - } - } - - void ChangeOrientation(float orientation) - { - _orientation = orientation; - _events.ScheduleEvent(EVENT_CHANGE_ORIENTATION, Milliseconds(1)); - } - - void MovementInform(uint32 type, uint32 pointId) override - { - if (type != POINT_MOTION_TYPE && type != SPLINE_CHAIN_MOTION_TYPE) - return; - - switch (pointId) - { - case POINT_ILLIDARI_COUNCIL: - Talk(SAY_AKAMA_FINISH); - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - break; - case POINT_STAIRS: - ChangeOrientation(6.265732f); - _events.ScheduleEvent(EVENT_AKAMA_SAY_DOOR, Seconds(5)); - break; - case POINT_ILLIDAN_ROOM: - ChangeOrientation(2.129302f); - Talk(SAY_AKAMA_BETRAYER); - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - break; - case POINT_FACE_ILLIDAN: - ChangeOrientation(3.140537f); - _events.ScheduleEvent(EVENT_START_ILLIDAN, Seconds(2)); - break; - case POINT_TELEPORT: - DoCastSelf(SPELL_AKAMA_TELEPORT); - _events.ScheduleEvent(EVENT_AKAMA_MINIONS_MOVE_2, Milliseconds(500)); - break; - case POINT_MINIONS: - _events.SetPhase(PHASE_MINIONS); - me->SetImmuneToNPC(false); - me->SetReactState(REACT_AGGRESSIVE); + case EVENT_AKAMA_MINIONS_MOVE_2: + me->GetMotionMaster()->MoveAlongSplineChain(POINT_MINIONS, SPLINE_MINIONS, false); + break; + case EVENT_CHAIN_LIGHTNING: + DoCastVictim(SPELL_CHAIN_LIGHTNING); + _events.Repeat(Seconds(8) + Milliseconds(500)); + break; + case EVENT_AKAMA_MOVE_BACK: + me->GetMotionMaster()->MoveAlongSplineChain(POINT_MOVE_BACK, SPLINE_MOVE_BACK, false); + break; + case EVENT_AKAMA_MOVE_TO_ILLIDAN: if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->DoAction(ACTION_START_MINIONS_WEAVE); - _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, Seconds(2)); + me->GetMotionMaster()->MoveCloserAndStop(POINT_ILLIDAN, illidan, 5.0f); + break; + case EVENT_AKAMA_LIGHT_TEXT: + Talk(SAY_AKAMA_LIGHT); + _events.ScheduleEvent(EVENT_FINAL_SALUTE, Seconds(4)); break; - case POINT_MOVE_BACK: - _events.ScheduleEvent(EVENT_AKAMA_MOVE_TO_ILLIDAN, Milliseconds(1)); + case EVENT_FINAL_SALUTE: + me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + _events.ScheduleEvent(EVENT_AKAMA_DESPAWN, Seconds(5)); break; - case POINT_ILLIDAN: - _events.ScheduleEvent(EVENT_AKAMA_LIGHT_TEXT, Seconds(1)); + case EVENT_AKAMA_DESPAWN: + DoCastSelf(SPELL_AKAMA_DESPAWN, true); break; default: break; } - } - - void DamageTaken(Unit* /*who*/, uint32 &damage) override - { - if (damage >= me->GetHealth()) - damage = me->GetHealth() - 1; - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO)) - return; - - _events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_TELEPORT: - DoCastSelf(SPELL_AKAMA_TELEPORT, true); - break; - case EVENT_MOVE_TO_ILLIDARI_ROOM: - me->GetMotionMaster()->MoveAlongSplineChain(POINT_ILLIDARI_COUNCIL, SPLINE_ILLIDARI_COUNCIL, false); - break; - case EVENT_AKAMA_SAY_DOOR: - Talk(SAY_AKAMA_DOOR); - _events.ScheduleEvent(EVENT_AKAMA_DOOR_FAIL, Seconds(4)); - break; - case EVENT_AKAMA_DOOR_FAIL: - DoCastSelf(SPELL_AKAMA_DOOR_FAIL); - _events.ScheduleEvent(EVENT_AKAMA_SAY_ALONE, Seconds(10)); - break; - case EVENT_AKAMA_SAY_ALONE: - Talk(SAY_AKAMA_ALONE); - _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, Seconds(7)); - break; - case EVENT_SUMMON_SPIRITS: - me->SummonCreatureGroup(SUMMON_GROUP); - _events.ScheduleEvent(EVENT_SPIRIT_SAY_1, Seconds(1)); - break; - case EVENT_SPIRIT_SAY_1: - if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) - undalo->AI()->Talk(SAY_SPIRIT_ALONE); - _events.ScheduleEvent(EVENT_SPIRIT_SAY_2, Seconds(6)); - break; - case EVENT_SPIRIT_SAY_2: - if (Creature* olum = ObjectAccessor::GetCreature(*me, _spiritOfOlumGUID)) - olum->AI()->Talk(SAY_SPIRIT_ALONE); - _events.ScheduleEvent(EVENT_AKAMA_DOOR_SUCCESS, Seconds(6)); - break; - case EVENT_AKAMA_DOOR_SUCCESS: - DoCastSelf(SPELL_AKAMA_DOOR_CHANNEL); - if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) - undalo->CastSpell((Unit*) nullptr, SPELL_DEATHSWORN_DOOR_CHANNEL); - if (Creature* olum = ObjectAccessor::GetCreature(*me, _spiritOfOlumGUID)) - olum->CastSpell((Unit*) nullptr, SPELL_DEATHSWORN_DOOR_CHANNEL); - _events.ScheduleEvent(EVENT_AKAMA_START_SOUND, Seconds(5)); - break; - case EVENT_AKAMA_START_SOUND: - if (GameObject* musicController = _instance->GetGameObject(DATA_ILLIDAN_MUSIC_CONTROLLER)) - musicController->PlayDirectMusic(EVENT_BT_SUMMIT_WALK_SOUND_ID); - break; - case EVENT_AKAMA_THANKS: - Talk(SAY_AKAMA_SALUTE); - _events.ScheduleEvent(EVENT_SPIRIT_SALUTE, Seconds(3)); - _events.ScheduleEvent(EVENT_RUN_FROM_ILLIDAN_ROOM, Seconds(7)); - break; - case EVENT_SPIRIT_SALUTE: - if (Creature* undalo = ObjectAccessor::GetCreature(*me, _spiritOfUdaloGUID)) - { - undalo->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); - undalo->DespawnOrUnsummon(Seconds(7)); - } - if (Creature* olum = ObjectAccessor::GetCreature(*me, _spiritOfOlumGUID)) - { - olum->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); - olum->DespawnOrUnsummon(Seconds(7)); - } - break; - case EVENT_RUN_FROM_ILLIDAN_ROOM: - me->GetMotionMaster()->MoveAlongSplineChain(POINT_ILLIDAN_ROOM, SPLINE_ILLIDAN_ROOM, false); - break; - case EVENT_START_ILLIDAN: - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->DoAction(ACTION_START_ENCOUNTER); - break; - case EVENT_FREE: - Talk(SAY_AKAMA_FREE); - _events.ScheduleEvent(EVENT_TIME_HAS_COME, Seconds(18)); - break; - case EVENT_TIME_HAS_COME: - Talk(SAY_AKAMA_TIME_HAS_COME); - _events.ScheduleEvent(EVENT_ROAR, Seconds(2)); - break; - case EVENT_ROAR: - me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY1H); - break; - case EVENT_CHANGE_ORIENTATION: - me->SetFacingTo(_orientation); - break; - case EVENT_HEALING_POTION: - if (me->HealthBelowPct(20)) - DoCastSelf(SPELL_HEALING_POTION); - _events.Repeat(Seconds(1)); - break; - case EVENT_AKAMA_MINIONS: - Talk(SAY_AKAMA_MINIONS); - _events.ScheduleEvent(EVENT_AKAMA_MINIONS_EMOTE, Seconds(2)); - break; - case EVENT_AKAMA_MINIONS_EMOTE: - me->SetReactState(REACT_PASSIVE); - me->AttackStop(); - me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); - me->SetImmuneToNPC(true); - _events.ScheduleEvent(EVENT_AKAMA_MINIONS_MOVE, Seconds(4)); - break; - case EVENT_AKAMA_MINIONS_MOVE: - _isTeleportToMinions = true; - me->GetMotionMaster()->MoveAlongSplineChain(POINT_TELEPORT, SPLINE_TELEPORT, false); - break; - case EVENT_AKAMA_MINIONS_MOVE_2: - me->GetMotionMaster()->MoveAlongSplineChain(POINT_MINIONS, SPLINE_MINIONS, false); - break; - case EVENT_CHAIN_LIGHTNING: - DoCastVictim(SPELL_CHAIN_LIGHTNING); - _events.Repeat(Seconds(8) + Milliseconds(500)); - break; - case EVENT_AKAMA_MOVE_BACK: - me->GetMotionMaster()->MoveAlongSplineChain(POINT_MOVE_BACK, SPLINE_MOVE_BACK, false); - break; - case EVENT_AKAMA_MOVE_TO_ILLIDAN: - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - me->GetMotionMaster()->MoveCloserAndStop(POINT_ILLIDAN, illidan, 5.0f); - break; - case EVENT_AKAMA_LIGHT_TEXT: - Talk(SAY_AKAMA_LIGHT); - _events.ScheduleEvent(EVENT_FINAL_SALUTE, Seconds(4)); - break; - case EVENT_FINAL_SALUTE: - me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); - _events.ScheduleEvent(EVENT_AKAMA_DESPAWN, Seconds(5)); - break; - case EVENT_AKAMA_DESPAWN: - DoCastSelf(SPELL_AKAMA_DESPAWN, true); - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoMeleeAttackIfReady(); } - private: - InstanceScript* _instance; - EventMap _events; - ObjectGuid _spiritOfUdaloGUID; - ObjectGuid _spiritOfOlumGUID; - float _orientation; - bool _isTeleportToMinions; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_akamaAI>(creature); + DoMeleeAttackIfReady(); } + +private: + InstanceScript* _instance; + EventMap _events; + ObjectGuid _spiritOfUdaloGUID; + ObjectGuid _spiritOfOlumGUID; + float _orientation; + bool _isTeleportToMinions; }; -class npc_parasitic_shadowfiend : public CreatureScript +struct npc_parasitic_shadowfiend : public ScriptedAI { -public: - npc_parasitic_shadowfiend() : CreatureScript("npc_parasitic_shadowfiend") { } + npc_parasitic_shadowfiend(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - struct npc_parasitic_shadowfiendAI : public ScriptedAI + void Reset() override { - npc_parasitic_shadowfiendAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) + { + me->DespawnOrUnsummon(); + return; + } - void Reset() override + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->JustSummoned(me); + me->SetReactState(REACT_DEFENSIVE); + _scheduler.Schedule(Seconds(2), [this](TaskContext /*context*/) { - if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) - { - me->DespawnOrUnsummon(); - return; - } + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + }); + } - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->JustSummoned(me); - me->SetReactState(REACT_DEFENSIVE); + void DoAction(int32 action) override + { + if (action == ACTION_START_PHASE_4) + { + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + } + else if (action == ACTION_RESUME_COMBAT) _scheduler.Schedule(Seconds(2), [this](TaskContext /*context*/) { me->SetReactState(REACT_AGGRESSIVE); me->SetInCombatWithZone(); }); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + } - _scheduler.Update(diff, [this] - { - DoMeleeAttackIfReady(); - }); - } + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); - private: - InstanceScript* _instance; - TaskScheduler _scheduler; - }; + if (!UpdateVictim()) + return; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_parasitic_shadowfiendAI>(creature); + DoMeleeAttackIfReady(); } + +private: + InstanceScript* _instance; + TaskScheduler _scheduler; }; -class npc_blade_of_azzinoth : public CreatureScript +struct npc_blade_of_azzinoth : public NullCreatureAI { -public: - npc_blade_of_azzinoth() : CreatureScript("npc_blade_of_azzinoth") { } + npc_blade_of_azzinoth(Creature* creature) : NullCreatureAI(creature), _instance(creature->GetInstanceScript()) { } - struct npc_blade_of_azzinothAI : public NullCreatureAI + void Reset() override { - npc_blade_of_azzinothAI(Creature* creature) : NullCreatureAI(creature), _instance(creature->GetInstanceScript()) { } - - void Reset() override - { - if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) - { - me->DespawnOrUnsummon(); - return; - } - - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->JustSummoned(me); - _flameGuid.Clear(); - me->PlayDirectSound(WARGLAIVE_SPAWN_SOUND_ID); - DoCastSelf(SPELL_BIRTH, true); - _scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) - { - DoCastSelf(SPELL_SUMMON_TEAR_OF_AZZINOTH); - _scheduler.Schedule(Milliseconds(500), [this](TaskContext /*context*/) - { - if (Creature* flame = ObjectAccessor::GetCreature(*me, _flameGuid)) - DoCast(flame, SPELL_AZZINOTH_CHANNEL); - }); - }); - } - - void JustSummoned(Creature* summon) override + if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) { - if (summon->GetEntry() == NPC_FLAME_OF_AZZINOTH) - _flameGuid = summon->GetGUID(); + me->DespawnOrUnsummon(); + return; } - void UpdateAI(uint32 diff) override + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->JustSummoned(me); + _flameGuid.Clear(); + me->PlayDirectSound(WARGLAIVE_SPAWN_SOUND_ID); + DoCastSelf(SPELL_BIRTH, true); + _scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) { - _scheduler.Update(diff); - } + DoCastSelf(SPELL_SUMMON_TEAR_OF_AZZINOTH); + _scheduler.Schedule(Milliseconds(500), [this](TaskContext /*context*/) + { + if (Creature* flame = ObjectAccessor::GetCreature(*me, _flameGuid)) + DoCast(flame, SPELL_AZZINOTH_CHANNEL); + }); + }); + } - private: - InstanceScript* _instance; - TaskScheduler _scheduler; - ObjectGuid _flameGuid; - }; + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_FLAME_OF_AZZINOTH) + _flameGuid = summon->GetGUID(); + } - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetBlackTempleAI<npc_blade_of_azzinothAI>(creature); + _scheduler.Update(diff); } + +private: + InstanceScript* _instance; + TaskScheduler _scheduler; + ObjectGuid _flameGuid; }; -class npc_flame_of_azzinoth : public CreatureScript +struct npc_flame_of_azzinoth : public ScriptedAI { -public: - npc_flame_of_azzinoth() : CreatureScript("npc_flame_of_azzinoth") { } + npc_flame_of_azzinoth(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) + { + SetBoundary(_instance->GetBossBoundary(DATA_ILLIDAN_STORMRAGE)); + } - struct npc_flame_of_azzinothAI : public ScriptedAI + void Reset() override { - npc_flame_of_azzinothAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) + if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) { - SetBoundary(_instance->GetBossBoundary(DATA_ILLIDAN_STORMRAGE)); + me->DespawnOrUnsummon(); + return; } - void Reset() override - { - if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) - { - me->DespawnOrUnsummon(); - return; - } - - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->JustSummoned(me); - DoCastSelf(SPELL_FLAME_TEAR_OF_AZZINOTH, true); // Idk what this spell should do - me->SetReactState(REACT_PASSIVE); - _events.ScheduleEvent(EVENT_ENGAGE, Seconds(3)); - _events.ScheduleEvent(EVENT_FLAME_BLAST, Seconds(11)); - } + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->JustSummoned(me); + DoCastSelf(SPELL_FLAME_TEAR_OF_AZZINOTH, true); // Idk what this spell should do + me->SetReactState(REACT_PASSIVE); + _events.ScheduleEvent(EVENT_ENGAGE, Seconds(3)); + _events.ScheduleEvent(EVENT_FLAME_BLAST, Seconds(11)); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - _events.Update(diff); + _events.Update(diff); - while (uint32 eventId = _events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_ENGAGE: - me->SetReactState(REACT_AGGRESSIVE); - me->SetInCombatWithZone(); - _events.ScheduleEvent(EVENT_FLAME_CHARGE, Seconds(5)); - break; - case EVENT_FLAME_CHARGE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, ChargeTargetSelector(me))) - { - DoCast(target, SPELL_CHARGE); - _events.Repeat(Seconds(5)); - } - else - _events.Repeat(Seconds(1)); - break; - case EVENT_FLAME_BLAST: - DoCastAOE(SPELL_FLAME_BLAST); - _events.Repeat(Seconds(12)); - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + case EVENT_ENGAGE: + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + _events.ScheduleEvent(EVENT_FLAME_CHARGE, Seconds(5)); + break; + case EVENT_FLAME_CHARGE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, ChargeTargetSelector())) + { + DoCast(target, SPELL_CHARGE); + _events.Repeat(Seconds(5)); + } + else + _events.Repeat(Seconds(1)); + break; + case EVENT_FLAME_BLAST: + DoCastAOE(SPELL_FLAME_BLAST); + _events.Repeat(Seconds(12)); + break; + default: + break; } - DoMeleeAttackIfReady(); - } - - void JustDied(Unit* /*killer*/) override - { - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->AI()->DoAction(ACTION_FLAME_DEAD); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - private: - InstanceScript* _instance; - EventMap _events; - }; + DoMeleeAttackIfReady(); + } - CreatureAI* GetAI(Creature* creature) const override + void JustDied(Unit* /*killer*/) override { - return GetBlackTempleAI<npc_flame_of_azzinothAI>(creature); + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->AI()->DoAction(ACTION_FLAME_DEAD); } + +private: + InstanceScript* _instance; + EventMap _events; }; -class npc_illidan_db_target : public CreatureScript +struct npc_illidan_db_target : public NullCreatureAI { -public: - npc_illidan_db_target() : CreatureScript("npc_illidan_db_target") { } + npc_illidan_db_target(Creature* creature) : NullCreatureAI(creature) { } - struct npc_illidan_db_targetAI : public NullCreatureAI + void Reset() override { - npc_illidan_db_targetAI(Creature* creature) : NullCreatureAI(creature) { } - - void Reset() override - { - DoCastSelf(SPELL_EYE_BLAST_TRIGGER); - } + DoCastSelf(SPELL_EYE_BLAST_TRIGGER); + } - void JustSummoned(Creature* summon) override - { - if (summon->GetEntry() == NPC_DEMON_FIRE) - summon->SetReactState(REACT_PASSIVE); - } + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_DEMON_FIRE) + summon->SetReactState(REACT_PASSIVE); + } - void MovementInform(uint32 type, uint32 pointId) override + void MovementInform(uint32 type, uint32 pointId) override + { + if (type == POINT_MOTION_TYPE && pointId == POINT_DB_TARGET) { - if (type == POINT_MOTION_TYPE && pointId == POINT_DB_TARGET) - { - me->RemoveAurasDueToSpell(SPELL_EYE_BLAST_TRIGGER); - me->RemoveAurasDueToSpell(SPELL_EYE_BLAST); - } + me->RemoveAurasDueToSpell(SPELL_EYE_BLAST_TRIGGER); + me->RemoveAurasDueToSpell(SPELL_EYE_BLAST); } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_illidan_db_targetAI>(creature); } }; -class npc_illidan_shadow_demon : public CreatureScript +struct npc_shadow_demon : public PassiveAI { -public: - npc_illidan_shadow_demon() : CreatureScript("npc_shadow_demon") { } + npc_shadow_demon(Creature* creature) : PassiveAI(creature), _instance(creature->GetInstanceScript()) { } - struct npc_illidan_shadow_demonAI : public PassiveAI + void Reset() override { - npc_illidan_shadow_demonAI(Creature* creature) : PassiveAI(creature), _instance(creature->GetInstanceScript()) { } - - void Reset() override + if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) { - if (_instance->GetBossState(DATA_ILLIDAN_STORMRAGE) != IN_PROGRESS) - { - me->DespawnOrUnsummon(); - return; - } - - DoCastSelf(SPELL_SHADOW_DEMON_PASSIVE); - DoCastSelf(SPELL_FIND_TARGET); - _scheduler.Schedule(Seconds(1), [this](TaskContext checkTarget) - { - if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) - { - if (!target->IsAlive()) - DoCastSelf(SPELL_FIND_TARGET); - else if (me->IsWithinMeleeRange(target)) - { - me->InterruptNonMeleeSpells(false); - DoCast(target, SPELL_CONSUME_SOUL, true); - } - } - checkTarget.Repeat(); - }); + me->DespawnOrUnsummon(); + return; } - void SetGUID(ObjectGuid guid, int32 /*id*/) override + DoCastSelf(SPELL_SHADOW_DEMON_PASSIVE); + DoCastSelf(SPELL_FIND_TARGET); + _scheduler.Schedule(Seconds(1), [this](TaskContext checkTarget) { - _targetGUID = guid; if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) - me->GetMotionMaster()->MoveChase(target); - } - - void UpdateAI(uint32 diff) override - { - _scheduler.Update(diff); - } + { + if (!target->IsAlive()) + DoCastSelf(SPELL_FIND_TARGET); + else if (me->IsWithinMeleeRange(target)) + { + me->InterruptNonMeleeSpells(false); + DoCast(target, SPELL_CONSUME_SOUL, true); + } + } + checkTarget.Repeat(); + }); + } - private: - InstanceScript* _instance; - TaskScheduler _scheduler; - ObjectGuid _targetGUID; - }; + void SetGUID(ObjectGuid guid, int32 /*id*/) override + { + _targetGUID = guid; + if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) + me->GetMotionMaster()->MoveChase(target); + } - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetBlackTempleAI<npc_illidan_shadow_demonAI>(creature); + _scheduler.Update(diff); } + +private: + InstanceScript* _instance; + TaskScheduler _scheduler; + ObjectGuid _targetGUID; }; -class npc_maiev : public CreatureScript +struct npc_maiev : public ScriptedAI { -public: - npc_maiev() : CreatureScript("npc_maiev") { } + npc_maiev(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _canDown(true) { } + + void Reset() override + { + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + me->SetFacingToObject(illidan); + me->SetReactState(REACT_PASSIVE); + _events.SetPhase(PHASE_INTRO); + _events.ScheduleEvent(EVENT_MAIEV_APPEAR, Seconds(1)); + _events.ScheduleEvent(EVENT_MAIEV_EXCLAMATION, Seconds(2)); + _events.ScheduleEvent(EVENT_MAIEV_JUSTICE_TEXT, Seconds(14)); + _events.ScheduleEvent(EVENT_TAUNT, Seconds(20), Seconds(60)); + _canDown = true; + } - struct npc_maievAI : public ScriptedAI + void EnterCombat(Unit* /*who*/) override { - npc_maievAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _canDown(true) { } + _events.SetPhase(PHASE_1); + _events.ScheduleEvent(EVENT_CAGE_TRAP, Seconds(30)); + _events.ScheduleEvent(EVENT_SHADOW_STRIKE, Seconds(50)); + _events.ScheduleEvent(EVENT_THROW_DAGGER, Seconds(1)); + } - void Reset() override + void DoAction(int32 actionId) override + { + if (actionId == ACTION_START_OUTRO) { + _events.Reset(); + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) me->SetFacingToObject(illidan); - me->SetReactState(REACT_PASSIVE); - _events.SetPhase(PHASE_INTRO); - _events.ScheduleEvent(EVENT_MAIEV_APPEAR, Seconds(1)); - _events.ScheduleEvent(EVENT_MAIEV_EXCLAMATION, Seconds(2)); - _events.ScheduleEvent(EVENT_MAIEV_JUSTICE_TEXT, Seconds(14)); - _events.ScheduleEvent(EVENT_TAUNT, Seconds(20), Seconds(60)); - _canDown = true; + Talk(SAY_MAIEV_SHADOWSONG_FINISHED); + _events.ScheduleEvent(EVENT_MAIEV_OUTRO_TEXT, Seconds(28)); } + else if (actionId == ACTION_MAIEV_DOWN_FADE) + _canDown = true; + } - void EnterCombat(Unit* /*who*/) override + void DamageTaken(Unit* /*who*/, uint32 &damage) override + { + if (damage >= me->GetHealth() && _canDown) { - _events.SetPhase(PHASE_1); - _events.ScheduleEvent(EVENT_CAGE_TRAP, Seconds(30)); - _events.ScheduleEvent(EVENT_SHADOW_STRIKE, Seconds(50)); - _events.ScheduleEvent(EVENT_THROW_DAGGER, Seconds(1)); + damage = me->GetHealth() - 1; + _canDown = false; + DoCastSelf(SPELL_MAIEV_DOWN, true); + Talk(SAY_MAIEV_SHADOWSONG_DOWN, me); } + } - void DoAction(int32 actionId) override - { - if (actionId == ACTION_START_OUTRO) - { - _events.Reset(); - me->SetReactState(REACT_PASSIVE); - me->AttackStop(); - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - me->SetFacingToObject(illidan); - Talk(SAY_MAIEV_SHADOWSONG_FINISHED); - _events.ScheduleEvent(EVENT_MAIEV_OUTRO_TEXT, Seconds(28)); - } - else if (actionId == ACTION_MAIEV_DOWN_FADE) - _canDown = true; - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO)) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + _events.Update(diff); - void DamageTaken(Unit* /*who*/, uint32 &damage) override + while (uint32 eventId = _events.ExecuteEvent()) { - if (damage >= me->GetHealth() && _canDown) + switch (eventId) { - damage = me->GetHealth() - 1; - _canDown = false; - DoCastSelf(SPELL_MAIEV_DOWN, true); - Talk(SAY_MAIEV_SHADOWSONG_DOWN, me); + case EVENT_MAIEV_APPEAR: + Talk(SAY_MAIEV_SHADOWSONG_APPEAR); + break; + case EVENT_MAIEV_EXCLAMATION: + me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + break; + case EVENT_MAIEV_JUSTICE_TEXT: + Talk(SAY_MAIEV_SHADOWSONG_JUSTICE); + _events.ScheduleEvent(EVENT_MAIEV_YES, Seconds(2)); + break; + case EVENT_MAIEV_YES: + me->HandleEmoteCommand(EMOTE_ONESHOT_YES); + _events.ScheduleEvent(EVENT_MAIEV_ROAR, Seconds(3)); + break; + case EVENT_MAIEV_ROAR: + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + _events.ScheduleEvent(EVENT_MAIEV_COMBAT, Seconds(3)); + break; + case EVENT_MAIEV_COMBAT: + me->SetReactState(REACT_AGGRESSIVE); + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + AttackStart(illidan); + break; + case EVENT_CAGE_TRAP: + if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) + illidan->CastSpell(illidan, SPELL_CAGED_TRAP_TELEPORT, true); + DoCastSelf(SPELL_CAGE_TRAP_SUMMON); + Talk(SAY_MAIEV_SHADOWSONG_TRAP); + _events.Repeat(Seconds(30)); + break; + case EVENT_SHADOW_STRIKE: + DoCastVictim(SPELL_SHADOW_STRIKE); + _events.Repeat(Seconds(50)); + break; + case EVENT_THROW_DAGGER: + if (Unit* target = me->GetVictim()) + if (!me->IsWithinMeleeRange(target)) + { + DoCastVictim(SPELL_THROW_DAGGER); + _events.Repeat(Seconds(5)); + break; + } + _events.Repeat(Seconds(1)); + break; + case EVENT_TAUNT: + Talk(SAY_MAIEV_SHADOWSONG_TAUNT); + _events.Repeat(Seconds(30), Seconds(60)); + break; + case EVENT_MAIEV_OUTRO_TEXT: + Talk(SAY_MAIEV_SHADOWSONG_OUTRO); + _events.ScheduleEvent(EVENT_MAIEV_FAREWELL_TEXT, Seconds(11)); + break; + case EVENT_MAIEV_FAREWELL_TEXT: + Talk(SAY_MAIEV_SHADOWSONG_FAREWELL); + _events.ScheduleEvent(EVENT_MAIEV_TELEPORT_DESPAWN, Seconds(3)); + break; + case EVENT_MAIEV_TELEPORT_DESPAWN: + DoCastSelf(SPELL_TELEPORT_VISUAL); + me->DespawnOrUnsummon(Seconds(1)); + break; + default: + break; } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO)) - return; if (me->HasUnitState(UNIT_STATE_CASTING)) return; + } - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_MAIEV_APPEAR: - Talk(SAY_MAIEV_SHADOWSONG_APPEAR); - break; - case EVENT_MAIEV_EXCLAMATION: - me->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); - break; - case EVENT_MAIEV_JUSTICE_TEXT: - Talk(SAY_MAIEV_SHADOWSONG_JUSTICE); - _events.ScheduleEvent(EVENT_MAIEV_YES, Seconds(2)); - break; - case EVENT_MAIEV_YES: - me->HandleEmoteCommand(EMOTE_ONESHOT_YES); - _events.ScheduleEvent(EVENT_MAIEV_ROAR, Seconds(3)); - break; - case EVENT_MAIEV_ROAR: - me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); - _events.ScheduleEvent(EVENT_MAIEV_COMBAT, Seconds(3)); - break; - case EVENT_MAIEV_COMBAT: - me->SetReactState(REACT_AGGRESSIVE); - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - AttackStart(illidan); - break; - case EVENT_CAGE_TRAP: - if (Creature* illidan = _instance->GetCreature(DATA_ILLIDAN_STORMRAGE)) - illidan->CastSpell(illidan, SPELL_CAGED_TRAP_TELEPORT, true); - DoCastSelf(SPELL_CAGE_TRAP_SUMMON); - Talk(SAY_MAIEV_SHADOWSONG_TRAP); - _events.Repeat(Seconds(30)); - break; - case EVENT_SHADOW_STRIKE: - DoCastVictim(SPELL_SHADOW_STRIKE); - _events.Repeat(Seconds(50)); - break; - case EVENT_THROW_DAGGER: - if (Unit* target = me->GetVictim()) - if (!me->IsWithinMeleeRange(target)) - { - DoCastVictim(SPELL_THROW_DAGGER); - _events.Repeat(Seconds(5)); - break; - } - _events.Repeat(Seconds(1)); - break; - case EVENT_TAUNT: - Talk(SAY_MAIEV_SHADOWSONG_TAUNT); - _events.Repeat(Seconds(30), Seconds(60)); - break; - case EVENT_MAIEV_OUTRO_TEXT: - Talk(SAY_MAIEV_SHADOWSONG_OUTRO); - _events.ScheduleEvent(EVENT_MAIEV_FAREWELL_TEXT, Seconds(11)); - break; - case EVENT_MAIEV_FAREWELL_TEXT: - Talk(SAY_MAIEV_SHADOWSONG_FAREWELL); - _events.ScheduleEvent(EVENT_MAIEV_TELEPORT_DESPAWN, Seconds(3)); - break; - case EVENT_MAIEV_TELEPORT_DESPAWN: - DoCastSelf(SPELL_TELEPORT_VISUAL); - me->DespawnOrUnsummon(Seconds(1)); - break; - default: - break; - } + DoMeleeAttackIfReady(); + } - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } +private: + EventMap _events; + InstanceScript* _instance; + bool _canDown; +}; - DoMeleeAttackIfReady(); - } +struct npc_cage_trap_trigger : public PassiveAI +{ + npc_cage_trap_trigger(Creature* creature) : PassiveAI(creature) { } - private: - EventMap _events; - InstanceScript* _instance; - bool _canDown; - }; + void Reset() override + { + _scheduler.Schedule(Seconds(1), [this](TaskContext checkTarget) + { + DoCastSelf(SPELL_CAGE_TRAP_PERIODIC); + checkTarget.Repeat(); + }); + } - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetBlackTempleAI<npc_maievAI>(creature); + _scheduler.Update(diff); } + +private: + TaskScheduler _scheduler; }; -class npc_cage_trap_trigger : public CreatureScript +struct npc_illidari_elite : public ScriptedAI { -public: - npc_cage_trap_trigger() : CreatureScript("npc_cage_trap_trigger") { } + npc_illidari_elite(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - struct npc_cage_trap_triggerAI : public PassiveAI + void Reset() override { - npc_cage_trap_triggerAI(Creature* creature) : PassiveAI(creature) { } - - void Reset() override + if (Creature* akama = _instance->GetCreature(DATA_AKAMA)) { - _scheduler.Schedule(Seconds(1), [this](TaskContext checkTarget) - { - DoCastSelf(SPELL_CAGE_TRAP_PERIODIC); - checkTarget.Repeat(); - }); + AttackStart(akama); + AddThreat(akama, 1000.0f); } + } - void UpdateAI(uint32 diff) override - { - _scheduler.Update(diff); - } - - private: - TaskScheduler _scheduler; - }; - - CreatureAI* GetAI(Creature* creature) const override + bool CanAIAttack(Unit const* who) const override { - return GetBlackTempleAI<npc_cage_trap_triggerAI>(creature); + if (who->GetEntry() == NPC_AKAMA) + return true; + return false; } + +private: + InstanceScript* _instance; }; // 41077 - Akama Teleport -class spell_illidan_akama_teleport : public SpellScriptLoader +class spell_illidan_akama_teleport : public SpellScript { - public: - spell_illidan_akama_teleport() : SpellScriptLoader("spell_illidan_akama_teleport") { } - - class spell_illidan_akama_teleport_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_akama_teleport_SpellScript); - - void SetDest(SpellDestination& dest) - { - if (Creature* caster = GetCaster()->ToCreature()) - { - uint32 destination = caster->AI()->GetData(DATA_AKAMA_TELEPORT_POSITION); - dest.Relocate(AkamaTeleportPositions[destination]); - } - } - - void Register() override - { - OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_illidan_akama_teleport_SpellScript::SetDest, EFFECT_0, TARGET_DEST_NEARBY_ENTRY); - } - }; + PrepareSpellScript(spell_illidan_akama_teleport); - SpellScript* GetSpellScript() const override + void SetDest(SpellDestination& dest) + { + if (Creature* caster = GetCaster()->ToCreature()) { - return new spell_illidan_akama_teleport_SpellScript(); + uint32 destination = caster->AI()->GetData(DATA_AKAMA_TELEPORT_POSITION); + dest.Relocate(AkamaTeleportPositions[destination]); } + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_illidan_akama_teleport::SetDest, EFFECT_0, TARGET_DEST_NEARBY_ENTRY); + } }; // 41268 - Quest - Black Temple - Akama - Door Open -class spell_illidan_akama_door_channel : public SpellScriptLoader +class spell_illidan_akama_door_channel : public AuraScript { - public: - spell_illidan_akama_door_channel() : SpellScriptLoader("spell_illidan_akama_door_channel") { } - - class spell_illidan_akama_door_channel_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidan_akama_door_channel_AuraScript); + PrepareAuraScript(spell_illidan_akama_door_channel); - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_ARCANE_EXPLOSION }); - } - - void OnRemoveDummy(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - target->CastSpell(target, SPELL_ARCANE_EXPLOSION, true); + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_ARCANE_EXPLOSION }); + } - if (InstanceScript* instance = target->GetInstanceScript()) - if (Creature* akama = instance->GetCreature(DATA_AKAMA)) - akama->AI()->DoAction(ACTION_OPEN_DOOR); - } + void OnRemoveDummy(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_ARCANE_EXPLOSION, true); - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_akama_door_channel_AuraScript::OnRemoveDummy, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; + if (InstanceScript* instance = target->GetInstanceScript()) + if (Creature* akama = instance->GetCreature(DATA_AKAMA)) + akama->AI()->DoAction(ACTION_OPEN_DOOR); + } - AuraScript* GetAuraScript() const override - { - return new spell_illidan_akama_door_channel_AuraScript(); - } + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_akama_door_channel::OnRemoveDummy, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } }; // 40904 - Draw Soul -class spell_illidan_draw_soul : public SpellScriptLoader +class spell_illidan_draw_soul : public SpellScript { - public: - spell_illidan_draw_soul() : SpellScriptLoader("spell_illidan_draw_soul") { } + PrepareSpellScript(spell_illidan_draw_soul); - class spell_illidan_draw_soul_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_draw_soul_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DRAW_SOUL_HEAL }); - } - - void HandleScriptEffect(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - GetHitUnit()->CastSpell(GetCaster(), SPELL_DRAW_SOUL_HEAL, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DRAW_SOUL_HEAL }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidan_draw_soul_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScriptEffect(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetHitUnit()->CastSpell(GetCaster(), SPELL_DRAW_SOUL_HEAL, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidan_draw_soul_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_draw_soul::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } }; /* 41917 - Parasitic Shadowfiend 41914 - Parasitic Shadowfiend */ -class spell_illidan_parasitic_shadowfiend : public SpellScriptLoader +class spell_illidan_parasitic_shadowfiend : public AuraScript { - public: - spell_illidan_parasitic_shadowfiend() : SpellScriptLoader("spell_illidan_parasitic_shadowfiend") { } + PrepareAuraScript(spell_illidan_parasitic_shadowfiend); - class spell_illidan_parasitic_shadowfiend_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidan_parasitic_shadowfiend_AuraScript); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SUMMON_PARASITIC_SHADOWFIENDS }); + } - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SUMMON_PARASITIC_SHADOWFIENDS }); - } + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + AuraRemoveMode removeMode = GetTargetApplication()->GetRemoveMode(); + if (removeMode != AURA_REMOVE_BY_EXPIRE && removeMode != AURA_REMOVE_BY_DEATH) + return; - void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - target->CastSpell(target, SPELL_SUMMON_PARASITIC_SHADOWFIENDS, true); - } + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_SUMMON_PARASITIC_SHADOWFIENDS, true); + } - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_parasitic_shadowfiend_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); - } - }; + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_parasitic_shadowfiend::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } +}; - AuraScript* GetAuraScript() const override - { - return new spell_illidan_parasitic_shadowfiend_AuraScript(); - } +// 41923 - Remove Parasitic Shadowfiends (SERVERSIDE) +class spell_illidan_remove_parasitic_shadowfiend : public AuraScript +{ + PrepareAuraScript(spell_illidan_remove_parasitic_shadowfiend); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PARASITIC_SHADOWFIEND, SPELL_PARASITIC_SHADOWFIEND_2 }); + } + + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveAurasDueToSpell(SPELL_PARASITIC_SHADOWFIEND); + GetTarget()->RemoveAurasDueToSpell(SPELL_PARASITIC_SHADOWFIEND_2); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_illidan_remove_parasitic_shadowfiend::HandleApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } }; /* 39635 - Throw Glaive 39849 - Throw Glaive */ -class spell_illidan_throw_warglaive : public SpellScriptLoader +class spell_illidan_throw_warglaive : public SpellScript { - public: - spell_illidan_throw_warglaive() : SpellScriptLoader("spell_illidan_throw_warglaive") { } + PrepareSpellScript(spell_illidan_throw_warglaive); - class spell_illidan_throw_warglaive_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_throw_warglaive_SpellScript); - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* target = GetHitUnit(); - target->m_Events.AddEvent(new SummonWarglaiveEvent(target), target->m_Events.CalculateTime(1000)); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidan_throw_warglaive_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + target->m_Events.AddEvent(new SummonWarglaiveEvent(target), target->m_Events.CalculateTime(1000)); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidan_throw_warglaive_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_throw_warglaive::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 39857 - Tear of Azzinoth Summon Channel -class spell_illidan_tear_of_azzinoth_channel : public SpellScriptLoader +class spell_illidan_tear_of_azzinoth_channel : public AuraScript { - public: - spell_illidan_tear_of_azzinoth_channel() : SpellScriptLoader("spell_illidan_tear_of_azzinoth_channel") { } - - class spell_illidan_tear_of_azzinoth_channel_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidan_tear_of_azzinoth_channel_AuraScript); + PrepareAuraScript(spell_illidan_tear_of_azzinoth_channel); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_UNCAGED_WRATH }); - } - - void OnPeriodic(AuraEffect const* /*aurEff*/) - { - PreventDefaultAction(); - if (Unit* caster = GetCaster()) - { - Unit* target = GetTarget(); - if (caster->GetDistance2d(target) > 25.0f) - { - target->CastSpell(target, SPELL_UNCAGED_WRATH, true); - Remove(); - } - } - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_UNCAGED_WRATH }); + } - void Register() override + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + if (Unit* caster = GetCaster()) + { + Unit* target = GetTarget(); + if (caster->GetDistance2d(target) > 25.0f) { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_tear_of_azzinoth_channel_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + target->CastSpell(target, SPELL_UNCAGED_WRATH, true); + Remove(); } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_illidan_tear_of_azzinoth_channel_AuraScript(); } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_tear_of_azzinoth_channel::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; // 40631 - Flame Blast -class spell_illidan_flame_blast : public SpellScriptLoader +class spell_illidan_flame_blast : public SpellScript { - public: - spell_illidan_flame_blast() : SpellScriptLoader("spell_illidan_flame_blast") { } + PrepareSpellScript(spell_illidan_flame_blast); - class spell_illidan_flame_blast_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_flame_blast_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_BLAZE_SUMMON }); - } - - void HandleBlaze(SpellEffIndex /*effIndex*/) - { - Unit* target = GetHitUnit(); - if (target->GetTypeId() == TYPEID_PLAYER) - target->CastSpell(target, SPELL_BLAZE_SUMMON, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_BLAZE_SUMMON }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_blast_SpellScript::HandleBlaze, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); - } - }; + void HandleBlaze(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + if (target->GetTypeId() == TYPEID_PLAYER) + target->CastSpell(target, SPELL_BLAZE_SUMMON, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidan_flame_blast_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_blast::HandleBlaze, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } }; // 39873 - Glaive Returns -class spell_illidan_return_glaives : public SpellScriptLoader +class spell_illidan_return_glaives : public SpellScript { - public: spell_illidan_return_glaives() : SpellScriptLoader("spell_illidan_return_glaives") { } - - class spell_illidan_return_glaives_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_return_glaives_SpellScript); - - void HandleScriptEffect(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->SendPlaySpellVisual(SPELL_GLAIVE_VISUAL_KIT); - if (Creature* caster = GetCaster()->ToCreature()) - caster->DespawnOrUnsummon(); - } + PrepareSpellScript(spell_illidan_return_glaives); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidan_return_glaives_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->SendPlaySpellVisual(SPELL_GLAIVE_VISUAL_KIT); + if (Creature* caster = GetCaster()->ToCreature()) + caster->DespawnOrUnsummon(); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidan_return_glaives_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_return_glaives::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; // 40834 - Agonizing Flames -class spell_illidan_agonizing_flames : public SpellScriptLoader +class spell_illidan_agonizing_flames : public SpellScript { - public: - spell_illidan_agonizing_flames() : SpellScriptLoader("spell_illidan_agonizing_flames") { } - - class spell_illidan_agonizing_flames_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_agonizing_flames_SpellScript); + PrepareSpellScript(spell_illidan_agonizing_flames); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_AGONIZING_FLAMES }); - } - - void FilterTargets(std::list<WorldObject*>& targets) - { - if (targets.empty()) - return; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_AGONIZING_FLAMES }); + } - WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); - targets.clear(); - targets.push_back(target); - } + void FilterTargets(std::list<WorldObject*>& targets) + { + if (targets.empty()) + return; - void HandleScript(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell(GetHitUnit(), SPELL_AGONIZING_FLAMES, true); - } + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_agonizing_flames_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - OnEffectHitTarget += SpellEffectFn(spell_illidan_agonizing_flames_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_AGONIZING_FLAMES, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidan_agonizing_flames_SpellScript(); - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_agonizing_flames::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_illidan_agonizing_flames::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 40511 - Demon Transform 1 -class spell_illidan_demon_transform1 : public SpellScriptLoader +class spell_illidan_demon_transform1 : public AuraScript { - public: - spell_illidan_demon_transform1() : SpellScriptLoader("spell_illidan_demon_transform1") { } - - class spell_illidan_demon_transform1_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidan_demon_transform1_AuraScript); + PrepareAuraScript(spell_illidan_demon_transform1); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DEMON_TRANSFORM_2 }); - } - - void OnPeriodic(AuraEffect const* /*aurEff*/) - { - PreventDefaultAction(); - GetTarget()->CastSpell(GetTarget(), SPELL_DEMON_TRANSFORM_2, true); - Remove(); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DEMON_TRANSFORM_2 }); + } - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform1_AuraScript::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_DEMON_TRANSFORM_2, true); + Remove(); + } - AuraScript* GetAuraScript() const override - { - return new spell_illidan_demon_transform1_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform1::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; + // 40398 - Demon Transform 2 -class spell_illidan_demon_transform2 : public SpellScriptLoader +class spell_illidan_demon_transform2 : public AuraScript { - public: - spell_illidan_demon_transform2() : SpellScriptLoader("spell_illidan_demon_transform2") { } - - class spell_illidan_demon_transform2_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidan_demon_transform2_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DEMON_FORM, SPELL_DEMON_TRANSFORM_3 }); - } - - void OnPeriodic(AuraEffect const* aurEff) - { - PreventDefaultAction(); - Unit* target = GetTarget(); + PrepareAuraScript(spell_illidan_demon_transform2); - if (aurEff->GetTickNumber() == 1) - { - if (target->GetDisplayId() == target->GetNativeDisplayId()) - target->CastSpell(target, SPELL_DEMON_FORM, true); - else - target->RemoveAurasDueToSpell(SPELL_DEMON_FORM); - } - else if (aurEff->GetTickNumber() == 2) - { - target->CastSpell(target, SPELL_DEMON_TRANSFORM_3, true); - if (Aura* aura = GetUnitOwner()->GetAura(SPELL_DEMON_TRANSFORM_3)) - aura->SetDuration(4300); - Remove(); - } - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DEMON_FORM, SPELL_DEMON_TRANSFORM_3 }); + } - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform2_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; + void OnPeriodic(AuraEffect const* aurEff) + { + PreventDefaultAction(); + Unit* target = GetTarget(); - AuraScript* GetAuraScript() const override + if (aurEff->GetTickNumber() == 1) + { + if (target->GetDisplayId() == target->GetNativeDisplayId()) + target->CastSpell(target, SPELL_DEMON_FORM, true); + else + target->RemoveAurasDueToSpell(SPELL_DEMON_FORM); + } + else if (aurEff->GetTickNumber() == 2) { - return new spell_illidan_demon_transform2_AuraScript(); + target->CastSpell(target, SPELL_DEMON_TRANSFORM_3, true); + if (Aura* aura = GetUnitOwner()->GetAura(SPELL_DEMON_TRANSFORM_3)) + aura->SetDuration(4300); + Remove(); } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_demon_transform2::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; // 41126 - Flame Burst -class spell_illidan_flame_burst : public SpellScriptLoader +class spell_illidan_flame_burst : public SpellScript { - public: - spell_illidan_flame_burst() : SpellScriptLoader("spell_illidan_flame_burst") { } - - class spell_illidan_flame_burst_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_flame_burst_SpellScript); + PrepareSpellScript(spell_illidan_flame_burst); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_FLAME_BURST_EFFECT }); - } - - void HandleScriptEffect(SpellEffIndex /*effIndex*/) - { - GetHitUnit()->CastSpell(GetHitUnit(), SPELL_FLAME_BURST_EFFECT, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FLAME_BURST_EFFECT }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_burst_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_FLAME_BURST_EFFECT, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidan_flame_burst_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_flame_burst::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; // 41081 - Find Target -class spell_illidan_find_target : public SpellScriptLoader +class spell_illidan_find_target : public SpellScript { - public: - spell_illidan_find_target() : SpellScriptLoader("spell_illidan_find_target") { } - - class spell_illidan_find_target_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_find_target_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_PARALYZE }); - } + PrepareSpellScript(spell_illidan_find_target); - void FilterTargets(std::list<WorldObject*>& targets) - { - targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_PARALYZE)); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PARALYZE }); + } - if (targets.empty()) - return; + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_PARALYZE)); - WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); - targets.clear(); - targets.push_back(target); - } + if (targets.empty()) + return; - void HandleScript(SpellEffIndex /*effIndex*/) - { - Unit* target = GetHitUnit(); - if (Creature* caster = GetCaster()->ToCreature()) - { - caster->CastSpell(target, SPELL_PARALYZE, true); - caster->AI()->SetGUID(target->GetGUID(), 0); - } - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_find_target_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - OnEffectHitTarget += SpellEffectFn(spell_illidan_find_target_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } - SpellScript* GetSpellScript() const override + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + if (Creature* caster = GetCaster()->ToCreature()) { - return new spell_illidan_find_target_SpellScript(); + caster->CastSpell(target, SPELL_PARALYZE, true); + caster->AI()->SetGUID(target->GetGUID(), 0); } + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_illidan_find_target::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_illidan_find_target::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 39908 - Eye Blast -class spell_illidan_eye_blast : public SpellScriptLoader +class spell_illidan_eye_blast : public AuraScript { - public: - spell_illidan_eye_blast() : SpellScriptLoader("spell_illidan_eye_blast") { } - - class spell_illidan_eye_blast_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidan_eye_blast_AuraScript); - - void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Creature* target = GetTarget()->ToCreature()) - target->DespawnOrUnsummon(); - } + PrepareAuraScript(spell_illidan_eye_blast); - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_eye_blast_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + target->DespawnOrUnsummon(); + } - AuraScript* GetAuraScript() const override - { - return new spell_illidan_eye_blast_AuraScript(); - } + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_illidan_eye_blast::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } }; // 40761 - Cage Trap -class spell_illidan_cage_trap : public SpellScriptLoader +class spell_illidan_cage_trap : public SpellScript { - public: - spell_illidan_cage_trap() : SpellScriptLoader("spell_illidan_cage_trap") { } - - class spell_illidan_cage_trap_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_cage_trap_SpellScript); - - void HandleScriptEffect(SpellEffIndex /*effIndex*/) - { - Creature* target = GetHitCreature(); - Creature* caster = GetCaster()->ToCreature(); - - if (!target || !caster) - return; + PrepareSpellScript(spell_illidan_cage_trap); - if (caster->GetDistance2d(target) < 4.0f) - { - target->AI()->DoAction(ACTION_ILLIDAN_CAGED); - caster->DespawnOrUnsummon(); - if (GameObject* trap = target->FindNearestGameObject(GO_ILLIDAN_CAGE_TRAP, 10.0f)) - trap->UseDoorOrButton(); - } - } + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + Creature* target = GetHitCreature(); + Creature* caster = GetCaster()->ToCreature(); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidan_cage_trap_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + if (!target || !caster) + return; - SpellScript* GetSpellScript() const override + if (caster->GetDistance2d(target) < 4.0f) { - return new spell_illidan_cage_trap_SpellScript(); + target->AI()->DoAction(ACTION_ILLIDAN_CAGED); + caster->DespawnOrUnsummon(); + if (GameObject* trap = target->FindNearestGameObject(GO_ILLIDAN_CAGE_TRAP, 10.0f)) + trap->UseDoorOrButton(); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_cage_trap::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; // 40760 - Cage Trap -class spell_illidan_caged : public SpellScriptLoader +class spell_illidan_caged : public AuraScript { - public: - spell_illidan_caged() : SpellScriptLoader("spell_illidan_caged") { } - - class spell_illidan_caged_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidan_caged_AuraScript); + PrepareAuraScript(spell_illidan_caged); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_CAGED_DEBUFF }); - } - - void OnPeriodic(AuraEffect const* /*aurEff*/) - { - PreventDefaultAction(); - Unit* target = GetTarget(); - target->CastSpell(target, SPELL_CAGED_DEBUFF, true); - Remove(); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_CAGED_DEBUFF }); + } - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_caged_AuraScript::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_CAGED_DEBUFF, true); + Remove(); + } - AuraScript* GetAuraScript() const override - { - return new spell_illidan_caged_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidan_caged::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; // 40409 - Maiev Down -class spell_maiev_down : public SpellScriptLoader +class spell_maiev_down : public AuraScript { -public: - spell_maiev_down() : SpellScriptLoader("spell_maiev_down") { } + PrepareAuraScript(spell_maiev_down); - class spell_maiev_down_AuraScript : public AuraScript + bool Load() override { - PrepareAuraScript(spell_maiev_down_AuraScript); - - bool Load() override - { - return GetUnitOwner()->GetTypeId() == TYPEID_UNIT; - } - - void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - GetTarget()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - } + return GetUnitOwner()->GetTypeId() == TYPEID_UNIT; + } - void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - GetTarget()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - GetTarget()->GetAI()->DoAction(ACTION_MAIEV_DOWN_FADE); - } + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_maiev_down_AuraScript::HandleEffectApply, EFFECT_1, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_maiev_down_AuraScript::HandleEffectRemove, EFFECT_1, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); - } - }; + void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + GetTarget()->GetAI()->DoAction(ACTION_MAIEV_DOWN_FADE); + } - AuraScript* GetAuraScript() const override + void Register() override { - return new spell_maiev_down_AuraScript(); + OnEffectApply += AuraEffectApplyFn(spell_maiev_down::HandleEffectApply, EFFECT_1, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_maiev_down::HandleEffectRemove, EFFECT_1, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); } }; // 40693 - Cage Trap -class spell_illidan_cage_teleport : public SpellScriptLoader +class spell_illidan_cage_teleport : public SpellScript { - public: - spell_illidan_cage_teleport() : SpellScriptLoader("spell_illidan_cage_teleport") { } - - class spell_illidan_cage_teleport_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_cage_teleport_SpellScript); - - void SetDest(SpellDestination& dest) - { - Position offset = { 0.0f, 0.0f, GetCaster()->GetPositionZ(), 0.0f }; - dest.RelocateOffset(offset); - } + PrepareSpellScript(spell_illidan_cage_teleport); - void Register() override - { - OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_illidan_cage_teleport_SpellScript::SetDest, EFFECT_0, TARGET_DEST_CASTER_RADIUS); - } - }; + void SetDest(SpellDestination& dest) + { + Position offset = { 0.0f, 0.0f, GetCaster()->GetPositionZ(), 0.0f }; + dest.RelocateOffset(offset); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidan_cage_teleport_SpellScript(); - } + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_illidan_cage_teleport::SetDest, EFFECT_0, TARGET_DEST_CASTER_RADIUS); + } }; // 41242 - Akama Despawn -class spell_illidan_despawn_akama : public SpellScriptLoader +class spell_illidan_despawn_akama : public SpellScript { - public: - spell_illidan_despawn_akama() : SpellScriptLoader("spell_illidan_despawn_akama") { } - - class spell_illidan_despawn_akama_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidan_despawn_akama_SpellScript); + PrepareSpellScript(spell_illidan_despawn_akama); - void HandleDummy(SpellEffIndex /*effIndex*/) - { - if (Creature* target = GetHitCreature()) - target->DespawnOrUnsummon(Seconds(1)); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidan_despawn_akama_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Creature* target = GetHitCreature()) + target->DespawnOrUnsummon(Seconds(1)); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidan_despawn_akama_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidan_despawn_akama::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; void AddSC_boss_illidan() { - new boss_illidan_stormrage(); - new npc_akama(); - new npc_parasitic_shadowfiend(); - new npc_blade_of_azzinoth(); - new npc_flame_of_azzinoth(); - new npc_illidan_db_target(); - new npc_maiev(); - new npc_illidan_shadow_demon(); - new npc_cage_trap_trigger(); - new spell_illidan_akama_teleport(); - new spell_illidan_akama_door_channel(); - new spell_illidan_draw_soul(); - new spell_illidan_parasitic_shadowfiend(); - new spell_illidan_throw_warglaive(); - new spell_illidan_tear_of_azzinoth_channel(); - new spell_illidan_flame_blast(); - new spell_illidan_return_glaives(); - new spell_illidan_agonizing_flames(); - new spell_illidan_demon_transform1(); - new spell_illidan_demon_transform2(); - new spell_illidan_flame_burst(); - new spell_illidan_find_target(); - new spell_illidan_eye_blast(); - new spell_illidan_cage_trap(); - new spell_illidan_caged(); - new spell_maiev_down(); - new spell_illidan_cage_teleport(); - new spell_illidan_despawn_akama(); + RegisterBlackTempleCreatureAI(boss_illidan_stormrage); + RegisterBlackTempleCreatureAI(npc_akama_illidan); + RegisterBlackTempleCreatureAI(npc_parasitic_shadowfiend); + RegisterBlackTempleCreatureAI(npc_blade_of_azzinoth); + RegisterBlackTempleCreatureAI(npc_flame_of_azzinoth); + RegisterBlackTempleCreatureAI(npc_illidan_db_target); + RegisterBlackTempleCreatureAI(npc_maiev); + RegisterBlackTempleCreatureAI(npc_shadow_demon); + RegisterBlackTempleCreatureAI(npc_cage_trap_trigger); + RegisterBlackTempleCreatureAI(npc_illidari_elite); + RegisterSpellScript(spell_illidan_akama_teleport); + RegisterAuraScript(spell_illidan_akama_door_channel); + RegisterSpellScript(spell_illidan_draw_soul); + RegisterAuraScript(spell_illidan_parasitic_shadowfiend); + RegisterAuraScript(spell_illidan_remove_parasitic_shadowfiend); + RegisterSpellScript(spell_illidan_throw_warglaive); + RegisterAuraScript(spell_illidan_tear_of_azzinoth_channel); + RegisterSpellScript(spell_illidan_flame_blast); + RegisterSpellScript(spell_illidan_return_glaives); + RegisterSpellScript(spell_illidan_agonizing_flames); + RegisterAuraScript(spell_illidan_demon_transform1); + RegisterAuraScript(spell_illidan_demon_transform2); + RegisterSpellScript(spell_illidan_flame_burst); + RegisterSpellScript(spell_illidan_find_target); + RegisterAuraScript(spell_illidan_eye_blast); + RegisterSpellScript(spell_illidan_cage_trap); + RegisterAuraScript(spell_illidan_caged); + RegisterAuraScript(spell_maiev_down); + RegisterSpellScript(spell_illidan_cage_teleport); + RegisterSpellScript(spell_illidan_despawn_akama); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp index 245f7960628..72597d52d42 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp @@ -27,63 +27,63 @@ enum Says { - SAY_COUNCIL_AGRO = 0, - SAY_COUNCIL_ENRAGE = 1, - SAY_COUNCIL_SPECIAL = 2, - SAY_COUNCIL_SLAY = 3, - SAY_COUNCIL_COMNT = 4, - SAY_COUNCIL_DEATH = 5 + SAY_COUNCIL_AGRO = 0, + SAY_COUNCIL_ENRAGE = 1, + SAY_COUNCIL_SPECIAL = 2, + SAY_COUNCIL_SLAY = 3, + SAY_COUNCIL_COMNT = 4, + SAY_COUNCIL_DEATH = 5 }; enum Spells { // Illidari Council (Trigger) - SPELL_EMPYREAL_BALANCE = 41499, - SPELL_EMPYREAL_EQUIVALENCY = 41333, + SPELL_EMPYREAL_BALANCE = 41499, + SPELL_EMPYREAL_EQUIVALENCY = 41333, // Generic - SPELL_SHARED_RULE = 41342, - SPELL_BERSERK = 45078, - SPELL_BALANCE_OF_POWER = 41341, - SPELL_QUIET_SUICIDE = 3617, // Serverside spell + SPELL_SHARED_RULE = 41342, + SPELL_BERSERK = 45078, + SPELL_BALANCE_OF_POWER = 41341, + SPELL_QUIET_SUICIDE = 3617, // High Nethermancer Zerevor's - SPELL_FLAMESTRIKE = 41481, - SPELL_BLIZZARD = 41482, - SPELL_ARCANE_BOLT = 41483, - SPELL_ARCANE_EXPLOSION = 41524, - SPELL_DAMPEN_MAGIC = 41478, + SPELL_FLAMESTRIKE = 41481, + SPELL_BLIZZARD = 41482, + SPELL_ARCANE_BOLT = 41483, + SPELL_ARCANE_EXPLOSION = 41524, + SPELL_DAMPEN_MAGIC = 41478, // Lady Malande's - SPELL_EMPOWERED_SMITE = 41471, - SPELL_CIRCLE_OF_HEALING = 41455, - SPELL_REFLECTIVE_SHIELD = 41475, - SPELL_REFLECTIVE_SHIELD_DAMAGE = 33619, - SPELL_DIVINE_WRATH = 41472, + SPELL_EMPOWERED_SMITE = 41471, + SPELL_CIRCLE_OF_HEALING = 41455, + SPELL_REFLECTIVE_SHIELD = 41475, + SPELL_REFLECTIVE_SHIELD_DAMAGE = 33619, + SPELL_DIVINE_WRATH = 41472, // Gathios the Shatterer's - SPELL_BLESS_PROTECTION = 41450, - SPELL_BLESS_SPELL_WARDING = 41451, - SPELL_CONSECRATION = 41541, - SPELL_HAMMER_OF_JUSTICE = 41468, - SPELL_SEAL_OF_COMMAND = 41469, - SPELL_SEAL_OF_BLOOD = 41459, - SPELL_CHROMATIC_AURA = 41453, - SPELL_DEVOTION_AURA = 41452, - SPELL_JUDGEMENT_PRIMER = 41473, - SPELL_JUDGEMENT = 41467, - SPELL_JUDGEMENT_OF_COMMAND = 41470, - SPELL_JUDGEMENT_OF_BLOOD = 41461, + SPELL_BLESS_PROTECTION = 41450, + SPELL_BLESS_SPELL_WARDING = 41451, + SPELL_CONSECRATION = 41541, + SPELL_HAMMER_OF_JUSTICE = 41468, + SPELL_SEAL_OF_COMMAND = 41469, + SPELL_SEAL_OF_BLOOD = 41459, + SPELL_CHROMATIC_AURA = 41453, + SPELL_DEVOTION_AURA = 41452, + SPELL_JUDGEMENT_PRIMER = 41473, + SPELL_JUDGEMENT = 41467, + SPELL_JUDGEMENT_OF_COMMAND = 41470, + SPELL_JUDGEMENT_OF_BLOOD = 41461, // Veras Darkshadow's - SPELL_DEADLY_STRIKE = 41480, - SPELL_DEADLY_POISON = 41485, - SPELL_ENVENOM = 41487, - SPELL_VANISH = 41476, + SPELL_DEADLY_STRIKE = 41480, + SPELL_DEADLY_POISON = 41485, + SPELL_ENVENOM = 41487, + SPELL_VANISH = 41476, // Veras Vanish Effect - SPELL_BIRTH = 40031, - SPELL_ENVENOM_DUMMY = 41510 + SPELL_BIRTH = 40031, + SPELL_ENVENOM_DUMMY = 41510 }; enum IllidariEvents @@ -131,123 +131,113 @@ static uint32 GetRandomBossExcept(uint32 exception) return bossData[urand(0, 3)]; } -class boss_illidari_council : public CreatureScript +struct boss_illidari_council : public BossAI { -public: - boss_illidari_council() : CreatureScript("boss_illidari_council") { } + boss_illidari_council(Creature* creature) : BossAI(creature, DATA_ILLIDARI_COUNCIL), _inCombat(false) { } - struct boss_illidari_councilAI : public BossAI + void Reset() override { - boss_illidari_councilAI(Creature* creature) : BossAI(creature, DATA_ILLIDARI_COUNCIL), _inCombat(false) { } - - void Reset() override - { - _Reset(); - _inCombat = false; - me->SummonCreatureGroup(SUMMON_COUNCIL_GROUP); - DoCastSelf(SPELL_EMPYREAL_BALANCE, true); - } + _Reset(); + _inCombat = false; + me->SummonCreatureGroup(SUMMON_COUNCIL_GROUP); + DoCastSelf(SPELL_EMPYREAL_BALANCE, true); + } - void EnterCombat(Unit* /*who*/) override + void EnterCombat(Unit* /*who*/) override + { + if (!_inCombat) { - if (!_inCombat) + _inCombat = true; + _EnterCombat(); + for (uint32 bossData : CouncilData) { - _inCombat = true; - _EnterCombat(); - for (uint32 bossData : CouncilData) + if (Creature* council = instance->GetCreature(bossData)) { - if (Creature* council = instance->GetCreature(bossData)) - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, council); - DoZoneInCombat(council); - } + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, council); + DoZoneInCombat(council); } - events.ScheduleEvent(EVENT_EMPYREAL_EQUIVALENCY, Seconds(2)); - events.ScheduleEvent(EVENT_BERSERK, Minutes(15)); - if (Creature* council = instance->GetCreature(CouncilData[urand(0, 3)])) - council->AI()->Talk(SAY_COUNCIL_AGRO); } + events.ScheduleEvent(EVENT_EMPYREAL_EQUIVALENCY, Seconds(2)); + events.ScheduleEvent(EVENT_BERSERK, Minutes(15)); + if (Creature* council = instance->GetCreature(CouncilData[urand(0, 3)])) + council->AI()->Talk(SAY_COUNCIL_AGRO); } + } - void EnterEvadeMode(EvadeReason /*why*/) override + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (!me->IsInEvadeMode()) { - if (!me->IsInEvadeMode()) - { - _inCombat = false; - for (uint32 bossData : CouncilData) - if (Creature* council = instance->GetCreature(bossData)) - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, council); + _inCombat = false; + for (uint32 bossData : CouncilData) + if (Creature* council = instance->GetCreature(bossData)) + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, council); - summons.DespawnAll(); - _DespawnAtEvade(); - } + summons.DespawnAll(); + _DespawnAtEvade(); } + } - void JustDied(Unit* /*killer*/) override - { - _inCombat = false; - events.Reset(); - instance->SetBossState(DATA_ILLIDARI_COUNCIL, DONE); + void JustDied(Unit* /*killer*/) override + { + _inCombat = false; + events.Reset(); + instance->SetBossState(DATA_ILLIDARI_COUNCIL, DONE); - for (uint32 bossData : CouncilData) + for (uint32 bossData : CouncilData) + { + if (Creature* council = instance->GetCreature(bossData)) { - if (Creature* council = instance->GetCreature(bossData)) - { - // Allow loot - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, council); - council->LowerPlayerDamageReq(council->GetMaxHealth()); - council->CastSpell(council, SPELL_QUIET_SUICIDE, true); - } + // Allow loot + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, council); + council->LowerPlayerDamageReq(council->GetMaxHealth()); + council->CastSpell(council, SPELL_QUIET_SUICIDE, true); } } + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - events.Update(diff); + events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_EMPYREAL_EQUIVALENCY: - DoCastSelf(SPELL_EMPYREAL_EQUIVALENCY, true); - events.Repeat(Seconds(2)); - break; - case EVENT_BERSERK: - for (uint32 bossData : CouncilData) + case EVENT_EMPYREAL_EQUIVALENCY: + DoCastSelf(SPELL_EMPYREAL_EQUIVALENCY, true); + events.Repeat(Seconds(2)); + break; + case EVENT_BERSERK: + for (uint32 bossData : CouncilData) + { + if (Creature* council = instance->GetCreature(bossData)) { - if (Creature* council = instance->GetCreature(bossData)) - { - council->CastSpell(council, SPELL_BERSERK, true); - council->AI()->Talk(SAY_COUNCIL_ENRAGE); - } + council->CastSpell(council, SPELL_BERSERK, true); + council->AI()->Talk(SAY_COUNCIL_ENRAGE); } - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + } + break; + default: + break; } - } - private: - bool _inCombat; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<boss_illidari_councilAI>(creature); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } } + +private: + bool _inCombat; }; + struct IllidariCouncilBossAI : public BossAI { IllidariCouncilBossAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId), _bossId(bossId) @@ -317,648 +307,492 @@ private: Unit const* _me; }; -class boss_gathios_the_shatterer : public CreatureScript +struct boss_gathios_the_shatterer : public IllidariCouncilBossAI { -public: - boss_gathios_the_shatterer() : CreatureScript("boss_gathios_the_shatterer") { } + boss_gathios_the_shatterer(Creature* creature) : IllidariCouncilBossAI(creature, DATA_GATHIOS_THE_SHATTERER) { } - struct boss_gathios_the_shattererAI : public IllidariCouncilBossAI + void ScheduleEvents() override { - boss_gathios_the_shattererAI(Creature* creature) : IllidariCouncilBossAI(creature, DATA_GATHIOS_THE_SHATTERER) { } - - void ScheduleEvents() override - { - DoCastSelf(SPELL_SEAL_OF_BLOOD); - events.ScheduleEvent(EVENT_BLESS, Seconds(20)); - events.ScheduleEvent(EVENT_CONSECRATION, Seconds(10)); - events.ScheduleEvent(EVENT_HAMMER_OF_JUSTICE, Seconds(10)); - events.ScheduleEvent(EVENT_JUDGEMENT, Seconds(15)); - events.ScheduleEvent(EVENT_AURA, Seconds(6)); - } + DoCastSelf(SPELL_SEAL_OF_BLOOD); + events.ScheduleEvent(EVENT_BLESS, Seconds(20)); + events.ScheduleEvent(EVENT_CONSECRATION, Seconds(10)); + events.ScheduleEvent(EVENT_HAMMER_OF_JUSTICE, Seconds(10)); + events.ScheduleEvent(EVENT_JUDGEMENT, Seconds(15)); + events.ScheduleEvent(EVENT_AURA, Seconds(6)); + } - void ExecuteEvent(uint32 eventId) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - switch (eventId) + case EVENT_BLESS: { - case EVENT_BLESS: - { - std::list<Unit*> TargetList; - Trinity::AnyFriendlyUnitInObjectRangeCheck checker(me, me, 100.0f); - Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(me, TargetList, checker); - Cell::VisitAllObjects(me, searcher, 100.0f); + std::list<Unit*> TargetList; + Trinity::AnyFriendlyUnitInObjectRangeCheck checker(me, me, 100.0f); + Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(me, TargetList, checker); + Cell::VisitAllObjects(me, searcher, 100.0f); - if (!TargetList.empty()) - { - Unit* target = Trinity::Containers::SelectRandomContainerElement(TargetList); - DoCast(target, RAND(SPELL_BLESS_PROTECTION, SPELL_BLESS_SPELL_WARDING)); - } - events.Repeat(Seconds(30), Seconds(45)); - break; + if (!TargetList.empty()) + { + Unit* target = Trinity::Containers::SelectRandomContainerElement(TargetList); + DoCast(target, RAND(SPELL_BLESS_PROTECTION, SPELL_BLESS_SPELL_WARDING)); } - case EVENT_AURA: - DoCastSelf(RAND(SPELL_CHROMATIC_AURA, SPELL_DEVOTION_AURA)); - events.Repeat(Seconds(30)); - break; - case EVENT_HAMMER_OF_JUSTICE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, HammerTargetSelector(me))) - DoCast(target, SPELL_HAMMER_OF_JUSTICE); - events.Repeat(Seconds(20)); - break; - case EVENT_JUDGEMENT: - DoCastVictim(SPELL_JUDGEMENT); - events.Repeat(Seconds(15)); - break; - case EVENT_CONSECRATION: - DoCastSelf(SPELL_CONSECRATION); - events.Repeat(Seconds(30), Seconds(35)); - break; - default: - break; - } + events.Repeat(Seconds(30), Seconds(45)); + break; + } + case EVENT_AURA: + DoCastSelf(RAND(SPELL_CHROMATIC_AURA, SPELL_DEVOTION_AURA)); + events.Repeat(Seconds(30)); + break; + case EVENT_HAMMER_OF_JUSTICE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, HammerTargetSelector(me))) + DoCast(target, SPELL_HAMMER_OF_JUSTICE); + events.Repeat(Seconds(20)); + break; + case EVENT_JUDGEMENT: + DoCastVictim(SPELL_JUDGEMENT); + events.Repeat(Seconds(15)); + break; + case EVENT_CONSECRATION: + DoCastSelf(SPELL_CONSECRATION); + events.Repeat(Seconds(30), Seconds(35)); + break; + default: + break; } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<boss_gathios_the_shattererAI>(creature); } }; -class boss_high_nethermancer_zerevor : public CreatureScript +struct boss_high_nethermancer_zerevor : public IllidariCouncilBossAI { -public: - boss_high_nethermancer_zerevor() : CreatureScript("boss_high_nethermancer_zerevor") { } + boss_high_nethermancer_zerevor(Creature* creature) : IllidariCouncilBossAI(creature, DATA_HIGH_NETHERMANCER_ZEREVOR), _canUseArcaneExplosion(true) { } - struct boss_high_nethermancer_zerevorAI : public IllidariCouncilBossAI + void Reset() override { - boss_high_nethermancer_zerevorAI(Creature* creature) : IllidariCouncilBossAI(creature, DATA_HIGH_NETHERMANCER_ZEREVOR), _canUseArcaneExplosion(true) { } + IllidariCouncilBossAI::Reset(); + _canUseArcaneExplosion = true; + DoCastSelf(SPELL_DAMPEN_MAGIC); + } - void Reset() override - { - IllidariCouncilBossAI::Reset(); - _canUseArcaneExplosion = true; - DoCastSelf(SPELL_DAMPEN_MAGIC); - } + void ScheduleEvents() override + { + events.ScheduleEvent(EVENT_FLAMESTRIKE, Seconds(8)); + events.ScheduleEvent(EVENT_BLIZZARD, Seconds(25)); + events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, Seconds(5)); + DoCastSelf(SPELL_DAMPEN_MAGIC); + } - void ScheduleEvents() override - { - events.ScheduleEvent(EVENT_FLAMESTRIKE, Seconds(8)); - events.ScheduleEvent(EVENT_BLIZZARD, Seconds(25)); - events.ScheduleEvent(EVENT_ARCANE_EXPLOSION, Seconds(5)); - DoCastSelf(SPELL_DAMPEN_MAGIC); + void DoAction(int32 actionId) override + { + if (actionId == ACTION_REFRESH_DAMPEN) + events.ScheduleEvent(EVENT_DAMPEN_MAGIC, Seconds(50)); + } + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_FLAMESTRIKE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_FLAMESTRIKE); + Talk(SAY_COUNCIL_SPECIAL); + events.Repeat(Seconds(40)); + break; + case EVENT_BLIZZARD: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_BLIZZARD); + events.Repeat(Seconds(15), Seconds(40)); + break; + case EVENT_ARCANE_EXPLOSION_CHECK: + _canUseArcaneExplosion = true; + break; + case EVENT_ARCANE_EXPLOSION: + if (_canUseArcaneExplosion && SelectTarget(SELECT_TARGET_RANDOM, 0, 10.0f)) + { + DoCastSelf(SPELL_ARCANE_EXPLOSION); + _canUseArcaneExplosion = false; + events.ScheduleEvent(EVENT_ARCANE_EXPLOSION_CHECK, Seconds(5)); + } + events.Repeat(Seconds(1)); + break; + case EVENT_DAMPEN_MAGIC: + DoCastSelf(SPELL_DAMPEN_MAGIC); + break; + default: + break; } + } - void DoAction(int32 actionId) override - { - if (actionId == ACTION_REFRESH_DAMPEN) - events.ScheduleEvent(EVENT_DAMPEN_MAGIC, Seconds(50)); - } - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_FLAMESTRIKE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_FLAMESTRIKE); - Talk(SAY_COUNCIL_SPECIAL); - events.Repeat(Seconds(40)); - break; - case EVENT_BLIZZARD: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_BLIZZARD); - events.Repeat(Seconds(15), Seconds(40)); - break; - case EVENT_ARCANE_EXPLOSION_CHECK: - _canUseArcaneExplosion = true; - break; - case EVENT_ARCANE_EXPLOSION: - if (_canUseArcaneExplosion && SelectTarget(SELECT_TARGET_RANDOM, 0, 10.0f)) - { - DoCastSelf(SPELL_ARCANE_EXPLOSION); - _canUseArcaneExplosion = false; - events.ScheduleEvent(EVENT_ARCANE_EXPLOSION_CHECK, Seconds(5)); - } - events.Repeat(Seconds(1)); - break; - case EVENT_DAMPEN_MAGIC: - DoCastSelf(SPELL_DAMPEN_MAGIC); - break; - default: - break; - } - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + events.Update(diff); - events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + while (uint32 eventId = events.ExecuteEvent()) + { + ExecuteEvent(eventId); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - - while (uint32 eventId = events.ExecuteEvent()) - { - ExecuteEvent(eventId); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoSpellAttackIfReady(SPELL_ARCANE_BOLT); } - private: - bool _canUseArcaneExplosion; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<boss_high_nethermancer_zerevorAI>(creature); + DoSpellAttackIfReady(SPELL_ARCANE_BOLT); } +private: + bool _canUseArcaneExplosion; }; -class boss_lady_malande : public CreatureScript +struct boss_lady_malande : public IllidariCouncilBossAI { -public: - boss_lady_malande() : CreatureScript("boss_lady_malande") { } + boss_lady_malande(Creature* creature) : IllidariCouncilBossAI(creature, DATA_LADY_MALANDE) { } - struct boss_lady_malandeAI : public IllidariCouncilBossAI + void ScheduleEvents() override { - boss_lady_malandeAI(Creature* creature) : IllidariCouncilBossAI(creature, DATA_LADY_MALANDE) { } + events.ScheduleEvent(EVENT_CIRCLE_OF_HEALING, Seconds(20)); + events.ScheduleEvent(EVENT_REFLECTIVE_SHIELD, Seconds(25)); + events.ScheduleEvent(EVENT_DIVINE_WRATH, Seconds(32)); + } - void ScheduleEvents() override - { - events.ScheduleEvent(EVENT_CIRCLE_OF_HEALING, Seconds(20)); - events.ScheduleEvent(EVENT_REFLECTIVE_SHIELD, Seconds(25)); - events.ScheduleEvent(EVENT_DIVINE_WRATH, Seconds(32)); - } + void HealReceived(Unit* /*who*/, uint32& addhealth) override + { + // Need be negative to heal trigger + int32 bp = addhealth * (-1); + me->CastCustomSpell(SPELL_SHARED_RULE, SPELLVALUE_BASE_POINT0, bp, (Unit*) nullptr, true); + } - void HealReceived(Unit* /*who*/, uint32& addhealth) override - { - // Need be negative to heal trigger - int32 bp = addhealth * (-1); - me->CastCustomSpell(SPELL_SHARED_RULE, SPELLVALUE_BASE_POINT0, bp, (Unit*) nullptr, true); + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_CIRCLE_OF_HEALING: + DoCastSelf(SPELL_CIRCLE_OF_HEALING); + events.Repeat(Seconds(20), Seconds(35)); + break; + case EVENT_REFLECTIVE_SHIELD: + DoCastSelf(SPELL_REFLECTIVE_SHIELD); + Talk(SAY_COUNCIL_SPECIAL); + events.Repeat(Seconds(40)); + break; + case EVENT_DIVINE_WRATH: + DoCastVictim(SPELL_DIVINE_WRATH); + events.Repeat(Seconds(20)); + break; + default: + break; } + } - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_CIRCLE_OF_HEALING: - DoCastSelf(SPELL_CIRCLE_OF_HEALING); - events.Repeat(Seconds(20), Seconds(35)); - break; - case EVENT_REFLECTIVE_SHIELD: - DoCastSelf(SPELL_REFLECTIVE_SHIELD); - Talk(SAY_COUNCIL_SPECIAL); - events.Repeat(Seconds(40)); - break; - case EVENT_DIVINE_WRATH: - DoCastVictim(SPELL_DIVINE_WRATH); - events.Repeat(Seconds(20)); - break; - default: - break; - } - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + events.Update(diff); - events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + while (uint32 eventId = events.ExecuteEvent()) + { + ExecuteEvent(eventId); if (me->HasUnitState(UNIT_STATE_CASTING)) return; - - while (uint32 eventId = events.ExecuteEvent()) - { - ExecuteEvent(eventId); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - } - - DoSpellAttackIfReady(SPELL_EMPOWERED_SMITE); } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<boss_lady_malandeAI>(creature); + DoSpellAttackIfReady(SPELL_EMPOWERED_SMITE); } - }; -class boss_veras_darkshadow : public CreatureScript +struct boss_veras_darkshadow : public IllidariCouncilBossAI { -public: - boss_veras_darkshadow() : CreatureScript("boss_veras_darkshadow") { } - - struct boss_veras_darkshadowAI : public IllidariCouncilBossAI + boss_veras_darkshadow(Creature* creature) : IllidariCouncilBossAI(creature, DATA_VERAS_DARKSHADOW) { - boss_veras_darkshadowAI(Creature* creature) : IllidariCouncilBossAI(creature, DATA_VERAS_DARKSHADOW) - { - me->SetMaxHealth(1327900); - me->SetFullHealth(); - } - - void ScheduleEvents() override - { - events.ScheduleEvent(EVENT_DEADLY_STRIKE, Seconds(18)); - events.ScheduleEvent(EVENT_VANISH, Seconds(18)); - } + me->SetMaxHealth(1327900); + me->SetFullHealth(); + } - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_DEADLY_STRIKE: - DoCastSelf(SPELL_DEADLY_STRIKE); - events.Repeat(Seconds(60)); - break; - case EVENT_VANISH: - DoCastSelf(SPELL_VANISH); - Talk(SAY_COUNCIL_SPECIAL); - events.Repeat(Seconds(60)); - break; - default: - break; - } - } - }; + void ScheduleEvents() override + { + events.ScheduleEvent(EVENT_DEADLY_STRIKE, Seconds(18)); + events.ScheduleEvent(EVENT_VANISH, Seconds(18)); + } - CreatureAI* GetAI(Creature* creature) const override + void ExecuteEvent(uint32 eventId) override { - return GetBlackTempleAI<boss_veras_darkshadowAI>(creature); + switch (eventId) + { + case EVENT_DEADLY_STRIKE: + DoCastSelf(SPELL_DEADLY_STRIKE); + events.Repeat(Seconds(60)); + break; + case EVENT_VANISH: + DoCastSelf(SPELL_VANISH); + Talk(SAY_COUNCIL_SPECIAL); + events.Repeat(Seconds(60)); + break; + default: + break; + } } }; -class npc_veras_vanish_effect : public CreatureScript +struct npc_veras_vanish_effect : public PassiveAI { -public: - npc_veras_vanish_effect() : CreatureScript("npc_veras_vanish_effect") { } - - struct npc_veras_vanish_effectAI : public PassiveAI - { - npc_veras_vanish_effectAI(Creature* creature) : PassiveAI(creature) { } - - void Reset() override - { - DoCastSelf(SPELL_BIRTH, true); - DoCastSelf(SPELL_ENVENOM_DUMMY, true); - } - }; + npc_veras_vanish_effect(Creature* creature) : PassiveAI(creature) { } - CreatureAI* GetAI(Creature* creature) const override + void Reset() override { - return GetBlackTempleAI<npc_veras_vanish_effectAI>(creature); + DoCastSelf(SPELL_BIRTH, true); + DoCastSelf(SPELL_ENVENOM_DUMMY, true); } }; // 41499 - Empyreal Balance -class spell_illidari_council_empyreal_balance : public SpellScriptLoader +class spell_illidari_council_empyreal_balance : public SpellScript { - public: - spell_illidari_council_empyreal_balance() : SpellScriptLoader("spell_illidari_council_empyreal_balance") { } - - class spell_illidari_council_empyreal_balance_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidari_council_empyreal_balance_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_BALANCE_OF_POWER }); - } + PrepareSpellScript(spell_illidari_council_empyreal_balance); - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* target = GetHitUnit(); - target->CastSpell(target, SPELL_BALANCE_OF_POWER, true); - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_BALANCE_OF_POWER }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidari_council_empyreal_balance_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + target->CastSpell(target, SPELL_BALANCE_OF_POWER, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidari_council_empyreal_balance_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidari_council_empyreal_balance::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 41333 - Empyreal Equivalency -class spell_illidari_council_empyreal_equivalency : public SpellScriptLoader +class spell_illidari_council_empyreal_equivalency : public SpellScript { - public: - spell_illidari_council_empyreal_equivalency() : SpellScriptLoader("spell_illidari_council_empyreal_equivalency") { } + PrepareSpellScript(spell_illidari_council_empyreal_equivalency); - class spell_illidari_council_empyreal_equivalency_SpellScript : public SpellScript - { - PrepareSpellScript(spell_illidari_council_empyreal_equivalency_SpellScript); - - void HandleScript(SpellEffIndex /*effIndex*/) - { - Unit* target = GetHitUnit(); - int32 casterHpPct = (int32) GetCaster()->GetHealthPct(); - uint32 newHp = target->CountPctFromMaxHealth(casterHpPct); - if (newHp <= 0) - newHp = target->GetMaxHealth() - 1; - target->SetHealth(newHp); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidari_council_empyreal_equivalency_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + int32 casterHpPct = (int32)GetCaster()->GetHealthPct(); + uint32 newHp = target->CountPctFromMaxHealth(casterHpPct); + if (newHp <= 0) + newHp = target->GetMaxHealth() - 1; + target->SetHealth(newHp); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidari_council_empyreal_equivalency_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidari_council_empyreal_equivalency::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 41341 - Balance of Power -class spell_illidari_council_balance_of_power : public SpellScriptLoader +class spell_illidari_council_balance_of_power : public AuraScript { - public: - spell_illidari_council_balance_of_power() : SpellScriptLoader("spell_illidari_council_balance_of_power") { } + PrepareAuraScript(spell_illidari_council_balance_of_power); - class spell_illidari_council_balance_of_power_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidari_council_balance_of_power_AuraScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_SHARED_RULE }); - } - - void Absorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& /*absorbAmount*/) - { - PreventDefaultAction(); - int32 bp = dmgInfo.GetDamage(); - GetTarget()->CastCustomSpell(SPELL_SHARED_RULE, SPELLVALUE_BASE_POINT0, bp, (Unit*) nullptr, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_SHARED_RULE }); + } - void Register() override - { - OnEffectAbsorb += AuraEffectAbsorbFn(spell_illidari_council_balance_of_power_AuraScript::Absorb, EFFECT_0); - } - }; + void Absorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& /*absorbAmount*/) + { + PreventDefaultAction(); + int32 bp = dmgInfo.GetDamage(); + GetTarget()->CastCustomSpell(SPELL_SHARED_RULE, SPELLVALUE_BASE_POINT0, bp, (Unit*) nullptr, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_illidari_council_balance_of_power_AuraScript(); - } + void Register() override + { + OnEffectAbsorb += AuraEffectAbsorbFn(spell_illidari_council_balance_of_power::Absorb, EFFECT_0); + } }; // 41480 - Deadly Strike -class spell_illidari_council_deadly_strike : public SpellScriptLoader +class spell_illidari_council_deadly_strike : public AuraScript { - public: - spell_illidari_council_deadly_strike() : SpellScriptLoader("spell_illidari_council_deadly_strike") { } - - class spell_illidari_council_deadly_strike_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidari_council_deadly_strike_AuraScript); + PrepareAuraScript(spell_illidari_council_deadly_strike); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DEADLY_POISON }); - } - - void OnTrigger(AuraEffect const* aurEff) - { - PreventDefaultAction(); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DEADLY_POISON }); + } - if (Unit* victim = GetTarget()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0)) - GetTarget()->CastSpell(victim, SPELL_DEADLY_POISON, true, nullptr, aurEff); - } + void OnTrigger(AuraEffect const* aurEff) + { + PreventDefaultAction(); - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidari_council_deadly_strike_AuraScript::OnTrigger, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; + if (Unit* victim = GetTarget()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0)) + GetTarget()->CastSpell(victim, SPELL_DEADLY_POISON, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_illidari_council_deadly_strike_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_illidari_council_deadly_strike::OnTrigger, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; // 41485 - Deadly Poison -class spell_illidari_council_deadly_poison : public SpellScriptLoader +class spell_illidari_council_deadly_poison : public AuraScript { - public: - spell_illidari_council_deadly_poison() : SpellScriptLoader("spell_illidari_council_deadly_poison") { } - - class spell_illidari_council_deadly_poison_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidari_council_deadly_poison_AuraScript); + PrepareAuraScript(spell_illidari_council_deadly_poison); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_ENVENOM }); - } - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - caster->CastSpell(GetTarget(), SPELL_ENVENOM, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_ENVENOM }); + } - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(spell_illidari_council_deadly_poison_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); - } - }; + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + caster->CastSpell(GetTarget(), SPELL_ENVENOM, true); + } - AuraScript* GetAuraScript() const override - { - return new spell_illidari_council_deadly_poison_AuraScript(); - } + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_illidari_council_deadly_poison::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } }; // 41475 - Reflective Shield -class spell_illidari_council_reflective_shield : public SpellScriptLoader +class spell_illidari_council_reflective_shield : public AuraScript { - public: - spell_illidari_council_reflective_shield() : SpellScriptLoader("spell_illidari_council_reflective_shield") { } + PrepareAuraScript(spell_illidari_council_reflective_shield); - class spell_illidari_council_reflective_shield_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidari_council_reflective_shield_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_REFLECTIVE_SHIELD_DAMAGE }); - } - - void OnAbsorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount) - { - Unit* target = GetTarget(); - if (dmgInfo.GetAttacker() == target) - return; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_REFLECTIVE_SHIELD_DAMAGE }); + } - int32 bp = absorbAmount / 2; - target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_REFLECTIVE_SHIELD_DAMAGE, &bp, nullptr, nullptr, true, nullptr, aurEff); - } + void OnAbsorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount) + { + Unit* target = GetTarget(); + if (dmgInfo.GetAttacker() == target) + return; - void Register() override - { - AfterEffectAbsorb += AuraEffectAbsorbFn(spell_illidari_council_reflective_shield_AuraScript::OnAbsorb, EFFECT_0); - } - }; + int32 bp = absorbAmount / 2; + target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_REFLECTIVE_SHIELD_DAMAGE, &bp, nullptr, nullptr, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_illidari_council_reflective_shield_AuraScript(); - } + void Register() override + { + AfterEffectAbsorb += AuraEffectAbsorbFn(spell_illidari_council_reflective_shield::OnAbsorb, EFFECT_0); + } }; // 41467 - Judgement -class spell_illidari_council_judgement : public SpellScriptLoader +class spell_illidari_council_judgement : public SpellScript { - public: - spell_illidari_council_judgement() : SpellScriptLoader("spell_illidari_council_judgement") { } + PrepareSpellScript(spell_illidari_council_judgement); - class spell_illidari_council_judgement_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_illidari_council_judgement_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_JUDGEMENT_OF_BLOOD, - SPELL_JUDGEMENT_OF_COMMAND, - SPELL_JUDGEMENT_PRIMER - }); - } - - void HandleScript(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - Unit* target = GetHitUnit(); - uint32 judgementId = caster->HasAura(SPELL_SEAL_OF_BLOOD) ? SPELL_JUDGEMENT_OF_BLOOD : SPELL_JUDGEMENT_OF_COMMAND; - caster->CastSpell(target, SPELL_JUDGEMENT_PRIMER, true); - caster->CastSpell(target, judgementId, true); - } + SPELL_JUDGEMENT_OF_BLOOD, + SPELL_JUDGEMENT_OF_COMMAND, + SPELL_JUDGEMENT_PRIMER + }); + } - void OnFinishCast() - { - if (Creature* caster = GetCaster()->ToCreature()) - caster->AI()->Talk(SAY_COUNCIL_SPECIAL); - } + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + Unit* target = GetHitUnit(); + uint32 judgementId = caster->HasAura(SPELL_SEAL_OF_BLOOD) ? SPELL_JUDGEMENT_OF_BLOOD : SPELL_JUDGEMENT_OF_COMMAND; + caster->CastSpell(target, SPELL_JUDGEMENT_PRIMER, true); + caster->CastSpell(target, judgementId, true); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_illidari_council_judgement_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - AfterCast += SpellCastFn(spell_illidari_council_judgement_SpellScript::OnFinishCast); - } - }; + void OnFinishCast() + { + if (Creature* caster = GetCaster()->ToCreature()) + caster->AI()->Talk(SAY_COUNCIL_SPECIAL); + } - SpellScript* GetSpellScript() const override - { - return new spell_illidari_council_judgement_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_illidari_council_judgement::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + AfterCast += SpellCastFn(spell_illidari_council_judgement::OnFinishCast); + } }; /* 41469 - Seal of Command 41459 - Seal of Blood */ -class spell_illidari_council_seal : public SpellScriptLoader +class spell_illidari_council_seal : public AuraScript { - public: - spell_illidari_council_seal() : SpellScriptLoader("spell_illidari_council_seal") { } + PrepareAuraScript(spell_illidari_council_seal); - class spell_illidari_council_seal_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_illidari_council_seal_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_SEAL_OF_COMMAND, - SPELL_SEAL_OF_BLOOD - }); - } - - void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (target->IsInWorld() && target->IsAlive()) - { - uint32 spellId = aurEff->GetId() == SPELL_SEAL_OF_COMMAND ? SPELL_SEAL_OF_BLOOD : SPELL_SEAL_OF_COMMAND; - target->CastSpell(target, spellId, true); - } - } - - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(spell_illidari_council_seal_AuraScript::OnRemove, EFFECT_2, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; + SPELL_SEAL_OF_COMMAND, + SPELL_SEAL_OF_BLOOD + }); + } - AuraScript* GetAuraScript() const override + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + if (target->IsInWorld() && target->IsAlive()) { - return new spell_illidari_council_seal_AuraScript(); + uint32 spellId = aurEff->GetId() == SPELL_SEAL_OF_COMMAND ? SPELL_SEAL_OF_BLOOD : SPELL_SEAL_OF_COMMAND; + target->CastSpell(target, spellId, true); } + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_illidari_council_seal::OnRemove, EFFECT_2, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } }; // 41478 - Dampen Magic -class spell_illidari_dampen_magic : public SpellScriptLoader +class spell_illidari_dampen_magic : public AuraScript { - public: - spell_illidari_dampen_magic() : SpellScriptLoader("spell_illidari_dampen_magic") { } - - class spell_illidari_dampen_magic_AuraScript : public AuraScript - { - PrepareAuraScript(spell_illidari_dampen_magic_AuraScript); - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Creature* target = GetTarget()->ToCreature()) - { - AuraRemoveMode mode = GetTargetApplication()->GetRemoveMode(); - if (mode == AURA_REMOVE_BY_ENEMY_SPELL || mode == AURA_REMOVE_BY_EXPIRE) - target->AI()->DoAction(ACTION_REFRESH_DAMPEN); - } - } - - void Register() override - { - OnEffectRemove += AuraEffectRemoveFn(spell_illidari_dampen_magic_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); - } - }; + PrepareAuraScript(spell_illidari_dampen_magic); - AuraScript* GetAuraScript() const override + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) { - return new spell_illidari_dampen_magic_AuraScript(); + AuraRemoveMode mode = GetTargetApplication()->GetRemoveMode(); + if (mode == AURA_REMOVE_BY_ENEMY_SPELL || mode == AURA_REMOVE_BY_EXPIRE) + target->AI()->DoAction(ACTION_REFRESH_DAMPEN); } + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_illidari_dampen_magic::OnRemove, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); + } }; void AddSC_boss_illidari_council() { - new boss_illidari_council(); - new boss_gathios_the_shatterer(); - new boss_lady_malande(); - new boss_veras_darkshadow(); - new boss_high_nethermancer_zerevor(); - new npc_veras_vanish_effect(); - new spell_illidari_council_empyreal_balance(); - new spell_illidari_council_empyreal_equivalency(); - new spell_illidari_council_balance_of_power(); - new spell_illidari_council_deadly_strike(); - new spell_illidari_council_deadly_poison(); - new spell_illidari_council_reflective_shield(); - new spell_illidari_council_judgement(); - new spell_illidari_council_seal(); - new spell_illidari_dampen_magic(); + RegisterBlackTempleCreatureAI(boss_illidari_council); + RegisterBlackTempleCreatureAI(boss_gathios_the_shatterer); + RegisterBlackTempleCreatureAI(boss_lady_malande); + RegisterBlackTempleCreatureAI(boss_veras_darkshadow); + RegisterBlackTempleCreatureAI(boss_high_nethermancer_zerevor); + RegisterBlackTempleCreatureAI(npc_veras_vanish_effect); + RegisterSpellScript(spell_illidari_council_empyreal_balance); + RegisterSpellScript(spell_illidari_council_empyreal_equivalency); + RegisterAuraScript(spell_illidari_council_balance_of_power); + RegisterAuraScript(spell_illidari_council_deadly_strike); + RegisterAuraScript(spell_illidari_council_deadly_poison); + RegisterAuraScript(spell_illidari_council_reflective_shield); + RegisterSpellScript(spell_illidari_council_judgement); + RegisterAuraScript(spell_illidari_council_seal); + RegisterAuraScript(spell_illidari_dampen_magic); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp b/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp index 38fa695c5db..fdd79cf92ea 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp @@ -107,299 +107,233 @@ Position const TeleportPoint[7]= { 965.997f, 278.398f, 195.777f } }; -class boss_mother_shahraz : public CreatureScript +struct boss_mother_shahraz : public BossAI { -public: - boss_mother_shahraz() : CreatureScript("boss_mother_shahraz") { } + boss_mother_shahraz(Creature* creature) : BossAI(creature, DATA_MOTHER_SHAHRAZ), _enraged(false) { } - struct boss_shahrazAI : public BossAI + void Reset() override { - boss_shahrazAI(Creature* creature) : BossAI(creature, DATA_MOTHER_SHAHRAZ), _enraged(false) { } + _Reset(); + _enraged = false; + } - void Reset() override - { - _Reset(); - _enraged = false; - } + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_SILENCING_SHRIEK, Seconds(22)); + events.ScheduleEvent(EVENT_PRISMATIC_SHIELD, Seconds(15)); + events.ScheduleEvent(EVENT_FATAL_ATTRACTION, Seconds(35)); + events.ScheduleEvent(EVENT_RANDOM_BEAM, Seconds(6)); + events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); + events.ScheduleEvent(EVENT_TAUNT, Seconds(35)); + } - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_SILENCING_SHRIEK, Seconds(22)); - events.ScheduleEvent(EVENT_PRISMATIC_SHIELD, Seconds(15)); - events.ScheduleEvent(EVENT_FATAL_ATTRACTION, Seconds(35)); - events.ScheduleEvent(EVENT_RANDOM_BEAM, Seconds(6)); - events.ScheduleEvent(EVENT_BERSERK, Minutes(10)); - events.ScheduleEvent(EVENT_TAUNT, Seconds(35)); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - Talk(SAY_DEATH); - } + void EnterEvadeMode(EvadeReason /*why*/) override + { + _DespawnAtEvade(); + } - void EnterEvadeMode(EvadeReason /*why*/) override + void DamageTaken(Unit* /*attacker*/, uint32 &damage) override + { + if (!_enraged && me->HealthBelowPctDamaged(10, damage)) { - _DespawnAtEvade(); + _enraged = true; + DoCastSelf(SPELL_RANDOM_PERIODIC, true); + Talk(EMOTE_ENRAGE, me); + Talk(SAY_ENRAGE); } + } - void DamageTaken(Unit* /*attacker*/, uint32 &damage) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - if (!_enraged && me->HealthBelowPctDamaged(10, damage)) - { - _enraged = true; - DoCastSelf(SPELL_RANDOM_PERIODIC, true); - Talk(EMOTE_ENRAGE, me); - Talk(SAY_ENRAGE); - } + case EVENT_RANDOM_BEAM: + DoCastSelf(BeamTriggers[urand(0, 3)]); + events.Repeat(Seconds(30)); + break; + case EVENT_PRISMATIC_SHIELD: + DoCastSelf(PrismaticAuras[urand(0, 5)]); + events.Repeat(Seconds(15)); + break; + case EVENT_FATAL_ATTRACTION: + Talk(SAY_SPELL); + me->CastCustomSpell(SPELL_FATAL_ATTACTION_TELEPORT, SPELLVALUE_MAX_TARGETS, 3, me); + events.Repeat(Seconds(30)); + break; + case EVENT_SILENCING_SHRIEK: + DoCastVictim(SPELL_SILENCING_SHRIEK); + events.Repeat(Seconds(18), Seconds(30)); + break; + case EVENT_TAUNT: + Talk(SAY_TAUNT); + events.Repeat(Seconds(30), Seconds(40)); + break; + case EVENT_BERSERK: + Talk(EMOTE_BERSERK, me); + DoCastSelf(SPELL_BERSERK); + break; + default: + break; } + } - void ExecuteEvent(uint32 eventId) override +private: + bool _enraged; +}; + +// 40869 - Fatal Attraction +class spell_mother_shahraz_fatal_attraction : public SpellScript +{ + PrepareSpellScript(spell_mother_shahraz_fatal_attraction); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( { - switch (eventId) - { - case EVENT_RANDOM_BEAM: - DoCastSelf(BeamTriggers[urand(0, 3)]); - events.Repeat(Seconds(30)); - break; - case EVENT_PRISMATIC_SHIELD: - DoCastSelf(PrismaticAuras[urand(0, 5)]); - events.Repeat(Seconds(15)); - break; - case EVENT_FATAL_ATTRACTION: - Talk(SAY_SPELL); - me->CastCustomSpell(SPELL_FATAL_ATTACTION_TELEPORT, SPELLVALUE_MAX_TARGETS, 3, me); - events.Repeat(Seconds(30)); - break; - case EVENT_SILENCING_SHRIEK: - DoCastVictim(SPELL_SILENCING_SHRIEK); - events.Repeat(Seconds(18), Seconds(30)); - break; - case EVENT_TAUNT: - Talk(SAY_TAUNT); - events.Repeat(Seconds(30), Seconds(40)); - break; - case EVENT_BERSERK: - Talk(EMOTE_BERSERK, me); - DoCastSelf(SPELL_BERSERK); - break; - default: - break; - } - } + SPELL_SABER_LASH_IMMUNITY, + SPELL_FATAL_ATTRACTION + }); + } - private: - bool _enraged; - }; + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_SABER_LASH_IMMUNITY)); + } - CreatureAI* GetAI(Creature* creature) const override + void SetDest(SpellDestination& dest) { - return GetBlackTempleAI<boss_shahrazAI>(creature); + dest.Relocate(TeleportPoint[urand(0, 6)]); } -}; -// 40869 - Fatal Attraction -class spell_mother_shahraz_fatal_attraction : public SpellScriptLoader -{ - public: - spell_mother_shahraz_fatal_attraction() : SpellScriptLoader("spell_mother_shahraz_fatal_attraction") { } + void HandleTeleport(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_FATAL_ATTRACTION, true); + } - class spell_mother_shahraz_fatal_attraction_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mother_shahraz_fatal_attraction_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo( - { - SPELL_SABER_LASH_IMMUNITY, - SPELL_FATAL_ATTRACTION - }); - } - - void FilterTargets(std::list<WorldObject*>& targets) - { - targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_SABER_LASH_IMMUNITY)); - } - - void SetDest(SpellDestination& dest) - { - dest.Relocate(TeleportPoint[urand(0, 6)]); - } - - void HandleTeleport(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell(GetHitUnit(), SPELL_FATAL_ATTRACTION, true); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mother_shahraz_fatal_attraction_SpellScript::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY); - OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_mother_shahraz_fatal_attraction_SpellScript::SetDest, EFFECT_1, TARGET_DEST_CASTER_RANDOM); - OnEffectHitTarget += SpellEffectFn(spell_mother_shahraz_fatal_attraction_SpellScript::HandleTeleport, EFFECT_1, SPELL_EFFECT_TELEPORT_UNITS); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_mother_shahraz_fatal_attraction_SpellScript(); - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mother_shahraz_fatal_attraction::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY); + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_mother_shahraz_fatal_attraction::SetDest, EFFECT_1, TARGET_DEST_CASTER_RANDOM); + OnEffectHitTarget += SpellEffectFn(spell_mother_shahraz_fatal_attraction::HandleTeleport, EFFECT_1, SPELL_EFFECT_TELEPORT_UNITS); + } }; // 40870 - Fatal Attraction Dummy Visual -class spell_mother_shahraz_fatal_attraction_link : public SpellScriptLoader +class spell_mother_shahraz_fatal_attraction_link : public SpellScript { - public: - spell_mother_shahraz_fatal_attraction_link() : SpellScriptLoader("spell_mother_shahraz_fatal_attraction_link") { } - - class spell_mother_shahraz_fatal_attraction_link_SpellScript : public SpellScript - { - PrepareSpellScript(spell_mother_shahraz_fatal_attraction_link_SpellScript); + PrepareSpellScript(spell_mother_shahraz_fatal_attraction_link); - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_FATAL_ATTRACTION_DAMAGE }); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell(GetCaster(), SPELL_FATAL_ATTRACTION_DAMAGE, true); - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_FATAL_ATTRACTION_DAMAGE }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_mother_shahraz_fatal_attraction_link_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetCaster(), SPELL_FATAL_ATTRACTION_DAMAGE, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_mother_shahraz_fatal_attraction_link_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_mother_shahraz_fatal_attraction_link::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 40816 - Saber Lash -class spell_mother_shahraz_saber_lash : public SpellScriptLoader +class spell_mother_shahraz_saber_lash : public AuraScript { - public: - spell_mother_shahraz_saber_lash() : SpellScriptLoader("spell_mother_shahraz_saber_lash") { } - - class spell_mother_shahraz_saber_lash_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mother_shahraz_saber_lash_AuraScript); + PrepareAuraScript(spell_mother_shahraz_saber_lash); - bool Validate(SpellInfo const* spellInfo) override - { - return ValidateSpellInfo({ spellInfo->Effects[EFFECT_1].TriggerSpell }); - } - - void OnTrigger(AuraEffect const* aurEff) - { - PreventDefaultAction(); + bool Validate(SpellInfo const* spellInfo) override + { + return ValidateSpellInfo({ spellInfo->Effects[EFFECT_1].TriggerSpell }); + } - uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; - if (Unit* target = GetUnitOwner()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0)) - GetUnitOwner()->CastSpell(target, triggerSpell, true); - } + void OnTrigger(AuraEffect const* aurEff) + { + PreventDefaultAction(); - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_mother_shahraz_saber_lash_AuraScript::OnTrigger, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; + uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; + if (Unit* target = GetUnitOwner()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0)) + GetUnitOwner()->CastSpell(target, triggerSpell, true); + } - AuraScript* GetAuraScript() const override - { - return new spell_mother_shahraz_saber_lash_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_mother_shahraz_saber_lash::OnTrigger, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; /* 40863 - Sinister Periodic 40865 - Vile Periodic 40866 - Wicked Periodic 40862 - Sinful Periodic */ -class spell_mother_shahraz_generic_periodic : public SpellScriptLoader +class spell_mother_shahraz_generic_periodic : public AuraScript { - public: - spell_mother_shahraz_generic_periodic() : SpellScriptLoader("spell_mother_shahraz_generic_periodic") { } - - class spell_mother_shahraz_generic_periodic_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mother_shahraz_generic_periodic_AuraScript); - - bool Validate(SpellInfo const* spellInfo) override - { - return ValidateSpellInfo({ spellInfo->Effects[EFFECT_0].TriggerSpell }); - } + PrepareAuraScript(spell_mother_shahraz_generic_periodic); - void OnTrigger(AuraEffect const* aurEff) - { - PreventDefaultAction(); + bool Validate(SpellInfo const* spellInfo) override + { + return ValidateSpellInfo({ spellInfo->Effects[EFFECT_0].TriggerSpell }); + } - uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; - if (Unit* target = GetUnitOwner()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0)) - GetUnitOwner()->CastSpell(target, triggerSpell, true); - } + void OnTrigger(AuraEffect const* aurEff) + { + PreventDefaultAction(); - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_mother_shahraz_generic_periodic_AuraScript::OnTrigger, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; + uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; + if (Unit* target = GetUnitOwner()->GetAI()->SelectTarget(SELECT_TARGET_RANDOM, 0)) + GetUnitOwner()->CastSpell(target, triggerSpell, true); + } - AuraScript* GetAuraScript() const override - { - return new spell_mother_shahraz_generic_periodic_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_mother_shahraz_generic_periodic::OnTrigger, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; // 40867 - Random Periodic -class spell_mother_shahraz_random_periodic : public SpellScriptLoader +class spell_mother_shahraz_random_periodic : public AuraScript { - public: - spell_mother_shahraz_random_periodic() : SpellScriptLoader("spell_mother_shahraz_random_periodic") { } + PrepareAuraScript(spell_mother_shahraz_random_periodic); - class spell_mother_shahraz_random_periodic_AuraScript : public AuraScript - { - PrepareAuraScript(spell_mother_shahraz_random_periodic_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo(RandomBeam); - } - - void OnPeriodic(AuraEffect const* /*aurEffect*/) - { - PreventDefaultAction(); - GetUnitOwner()->CastSpell(GetUnitOwner(), RandomBeam[urand(0, 3)], true); - } - - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_mother_shahraz_random_periodic_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_mother_shahraz_random_periodic_AuraScript(); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo(RandomBeam); + } + + void OnPeriodic(AuraEffect const* /*aurEffect*/) + { + PreventDefaultAction(); + GetUnitOwner()->CastSpell(GetUnitOwner(), RandomBeam[urand(0, 3)], true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_mother_shahraz_random_periodic::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; void AddSC_boss_mother_shahraz() { - new boss_mother_shahraz(); - new spell_mother_shahraz_fatal_attraction(); - new spell_mother_shahraz_fatal_attraction_link(); - new spell_mother_shahraz_saber_lash(); - new spell_mother_shahraz_generic_periodic(); - new spell_mother_shahraz_random_periodic(); + RegisterBlackTempleCreatureAI(boss_mother_shahraz); + RegisterSpellScript(spell_mother_shahraz_fatal_attraction); + RegisterSpellScript(spell_mother_shahraz_fatal_attraction_link); + RegisterAuraScript(spell_mother_shahraz_saber_lash); + RegisterAuraScript(spell_mother_shahraz_generic_periodic); + RegisterAuraScript(spell_mother_shahraz_random_periodic); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp index ae3d961ca68..c069482f139 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp @@ -136,737 +136,638 @@ class EnslavedSoulEvent : public BasicEvent Creature* _owner; }; -class boss_reliquary_of_souls : public CreatureScript +struct boss_reliquary_of_souls : public BossAI { -public: - boss_reliquary_of_souls() : CreatureScript("boss_reliquary_of_souls") { } + boss_reliquary_of_souls(Creature* creature) : BossAI(creature, DATA_RELIQUARY_OF_SOULS), _inCombat(false) + { + creature->m_SightDistance = 70.0f; + } - struct boss_reliquary_of_soulsAI : public BossAI + void Reset() override { - boss_reliquary_of_soulsAI(Creature* creature) : BossAI(creature, DATA_RELIQUARY_OF_SOULS), _inCombat(false) - { - creature->m_SightDistance = 70.0f; - } + _Reset(); + me->SetReactState(REACT_PASSIVE); + _inCombat = false; + events.SetPhase(PHASE_ESSENCE_OF_SUFFERING); + } - void Reset() override + void MoveInLineOfSight(Unit* who) override + { + if (!_inCombat && who->GetTypeId() == TYPEID_PLAYER && !who->ToPlayer()->IsGameMaster() && CanAIAttack(who)) { - _Reset(); - me->SetReactState(REACT_PASSIVE); - _inCombat = false; - events.SetPhase(PHASE_ESSENCE_OF_SUFFERING); + _inCombat = true; + DoZoneInCombat(); + me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_STAND); + events.ScheduleEvent(EVENT_SUBMERGE, Seconds(10)); } + } - void MoveInLineOfSight(Unit* who) override - { - if (!_inCombat && who->GetTypeId() == TYPEID_PLAYER && !who->ToPlayer()->IsGameMaster() && CanAIAttack(who)) - { - _inCombat = true; - DoZoneInCombat(); - me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_STAND); - events.ScheduleEvent(EVENT_SUBMERGE, Seconds(10)); - } - } + uint32 GetSummonSpell() + { + if (events.IsInPhase(PHASE_ESSENCE_OF_SUFFERING)) + return SPELL_SUMMON_ESSENCE_OF_SUFFERING; + else if (events.IsInPhase(PHASE_ESSENCE_OF_DESIRE)) + return SPELL_SUMMON_ESSENCE_OF_DESIRE; + else if (events.IsInPhase(PHASE_ESSENCE_OF_ANGER)) + return SPELL_SUMMON_ESSENCE_OF_ANGER; + else //Should never happen + return 0; + } - uint32 GetSummonSpell() - { - if (events.IsInPhase(PHASE_ESSENCE_OF_SUFFERING)) - return SPELL_SUMMON_ESSENCE_OF_SUFFERING; - else if (events.IsInPhase(PHASE_ESSENCE_OF_DESIRE)) - return SPELL_SUMMON_ESSENCE_OF_DESIRE; - else if (events.IsInPhase(PHASE_ESSENCE_OF_ANGER)) - return SPELL_SUMMON_ESSENCE_OF_ANGER; - else //Should never happen - return 0; + void DoAction(int32 actionId) override + { + switch (actionId) + { + case ACTION_ESSENCE_OF_SUFFERING_DEAD: + me->RemoveAurasDueToSpell(SPELL_SUBMERGE_VISUAL); + events.SetPhase(PHASE_ESSENCE_OF_DESIRE); + HandleSpirits(); + events.ScheduleEvent(EVENT_SUBMERGE, Seconds(40)); + break; + case ACTION_ESSENCE_OF_DESIRE_DEAD: + me->RemoveAurasDueToSpell(SPELL_SUBMERGE_VISUAL); + events.SetPhase(PHASE_ESSENCE_OF_ANGER); + HandleSpirits(); + events.ScheduleEvent(EVENT_SUBMERGE, Seconds(40)); + break; + case ACTION_KILL_SELF: + me->KillSelf(); + break; } + } - void DoAction(int32 actionId) override - { - switch (actionId) - { - case ACTION_ESSENCE_OF_SUFFERING_DEAD: - me->RemoveAurasDueToSpell(SPELL_SUBMERGE_VISUAL); - events.SetPhase(PHASE_ESSENCE_OF_DESIRE); - HandleSpirits(); - events.ScheduleEvent(EVENT_SUBMERGE, Seconds(40)); - break; - case ACTION_ESSENCE_OF_DESIRE_DEAD: - me->RemoveAurasDueToSpell(SPELL_SUBMERGE_VISUAL); - events.SetPhase(PHASE_ESSENCE_OF_ANGER); - HandleSpirits(); - events.ScheduleEvent(EVENT_SUBMERGE, Seconds(40)); - break; - case ACTION_KILL_SELF: - me->KillSelf(); - break; - } + void HandleSpirits() + { + std::vector<Creature*> _worldTriggerList; + me->GetCreatureListWithEntryInGrid(_worldTriggerList, NPC_RELIQUARY_WORLD_TRIGGER, 70.0f); + + if (_worldTriggerList.empty()) + return; + + //Get random creatures + Trinity::Containers::RandomShuffle(_worldTriggerList); + _worldTriggerList.resize(21); + + for (uint8 i = 0; i < 21; i++) + { + Creature* wTrigger = _worldTriggerList[i]; + if (i < 3) + wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(4000)); + else if (i < 6) + wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(8000)); + else if (i < 9) + wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(12000)); + else if (i < 12) + wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(16000)); + else if (i < 15) + wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(20000)); + else if (i < 18) + wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(24000)); + else + wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(28000)); } + } - void HandleSpirits() - { - std::vector<Creature*> _worldTriggerList; - me->GetCreatureListWithEntryInGrid(_worldTriggerList, NPC_RELIQUARY_WORLD_TRIGGER, 70.0f); - - if (_worldTriggerList.empty()) - return; + void KillAssyncEvents() + { + std::vector<Creature*> _worldTriggerList; + me->GetCreatureListWithEntryInGrid(_worldTriggerList, NPC_RELIQUARY_WORLD_TRIGGER, 70.0f); - //Get random creatures - Trinity::Containers::RandomShuffle(_worldTriggerList); - _worldTriggerList.resize(21); + if (_worldTriggerList.empty()) + return; - for (uint8 i = 0; i < 21; i++) - { - Creature* wTrigger = _worldTriggerList[i]; - if (i < 3) - wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(4000)); - else if (i < 6) - wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(8000)); - else if (i < 9) - wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(12000)); - else if (i < 12) - wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(16000)); - else if (i < 15) - wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(20000)); - else if (i < 18) - wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(24000)); - else - wTrigger->m_Events.AddEvent(new EnslavedSoulEvent(wTrigger), wTrigger->m_Events.CalculateTime(28000)); - } - } + for (Creature* trigger : _worldTriggerList) + trigger->m_Events.KillAllEvents(true); + } - void KillAssyncEvents() - { - std::vector<Creature*> _worldTriggerList; - me->GetCreatureListWithEntryInGrid(_worldTriggerList, NPC_RELIQUARY_WORLD_TRIGGER, 70.0f); + void EnterEvadeMode(EvadeReason /*why*/) override + { + events.Reset(); + summons.DespawnAll(); + KillAssyncEvents(); + _DespawnAtEvade(); + } - if (_worldTriggerList.empty()) - return; + void JustDied(Unit* /*killer*/) override + { + events.Reset(); + instance->SetBossState(DATA_RELIQUARY_OF_SOULS, DONE); + } - for (Creature* trigger : _worldTriggerList) - trigger->m_Events.KillAllEvents(true); - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - void EnterEvadeMode(EvadeReason /*why*/) override - { - events.Reset(); - summons.DespawnAll(); - KillAssyncEvents(); - _DespawnAtEvade(); - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void JustDied(Unit* /*killer*/) override - { - events.Reset(); - instance->SetBossState(DATA_RELIQUARY_OF_SOULS, DONE); - } + events.Update(diff); - void UpdateAI(uint32 diff) override + while (uint32 eventId = events.ExecuteEvent()) { - if (!UpdateVictim()) - return; - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) + switch (eventId) { - switch (eventId) + case EVENT_SUBMERGE: + DoCastSelf(SPELL_SUBMERGE_VISUAL, true); + events.ScheduleEvent(EVENT_SUMMON_ESSENCE, Seconds(3)); + break; + case EVENT_SUMMON_ESSENCE: { - case EVENT_SUBMERGE: - DoCastSelf(SPELL_SUBMERGE_VISUAL, true); - events.ScheduleEvent(EVENT_SUMMON_ESSENCE, Seconds(3)); - break; - case EVENT_SUMMON_ESSENCE: - { - EntryCheckPredicate pred(NPC_ENSLAVED_SOUL); - summons.DoAction(ACTION_KILL_SELF, pred); - DoCastSelf(GetSummonSpell()); - break; - } - default: - break; + EntryCheckPredicate pred(NPC_ENSLAVED_SOUL); + summons.DoAction(ACTION_KILL_SELF, pred); + DoCastSelf(GetSummonSpell()); + break; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + default: + break; } - } - - private: - bool _inCombat; - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<boss_reliquary_of_soulsAI>(creature); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } } + +private: + bool _inCombat; }; -class boss_essence_of_suffering : public CreatureScript +struct boss_essence_of_suffering : public BossAI { -public: - boss_essence_of_suffering() : CreatureScript("boss_essence_of_suffering") { } + boss_essence_of_suffering(Creature* creature) : BossAI(creature, DATA_ESSENCE_OF_SUFFERING), _dead(false) + { + SetBoundary(instance->GetBossBoundary(DATA_RELIQUARY_OF_SOULS)); + } - struct boss_essence_of_sufferingAI : public BossAI + void Reset() override { - boss_essence_of_sufferingAI(Creature* creature) : BossAI(creature, DATA_ESSENCE_OF_SUFFERING), _dead(false) - { - SetBoundary(instance->GetBossBoundary(DATA_RELIQUARY_OF_SOULS)); - } + DoCastAOE(SPELL_AURA_OF_SUFFERING, true); + events.Reset(); + _dead = false; + } - void Reset() override - { - DoCastAOE(SPELL_AURA_OF_SUFFERING, true); - events.Reset(); - _dead = false; - } + void MovementInform(uint32 motionType, uint32 pointId) override + { + if (motionType != POINT_MOTION_TYPE) + return; - void MovementInform(uint32 motionType, uint32 pointId) override + if (pointId == RELIQUARY_DESPAWN_WAYPOINT) { - if (motionType != POINT_MOTION_TYPE) - return; - - if (pointId == RELIQUARY_DESPAWN_WAYPOINT) - { - if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) - reliquary->AI()->DoAction(ACTION_ESSENCE_OF_SUFFERING_DEAD); + if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) + reliquary->AI()->DoAction(ACTION_ESSENCE_OF_SUFFERING_DEAD); - DoCastSelf(SPELL_SUBMERGE_VISUAL, true); - me->DespawnOrUnsummon(Seconds(2)); - } + DoCastSelf(SPELL_SUBMERGE_VISUAL, true); + me->DespawnOrUnsummon(Seconds(2)); } + } - void DamageTaken(Unit* /*done_by*/, uint32 &damage) override + void DamageTaken(Unit* /*done_by*/, uint32 &damage) override + { + if (damage >= me->GetHealth()) { - if (damage >= me->GetHealth()) + damage = 0; + if (!_dead) { - damage = 0; - if (!_dead) - { - _dead = true; - Talk(SUFF_SAY_RECAP); - me->AttackStop(); - me->SetReactState(REACT_PASSIVE); - events.Reset(); - me->InterruptNonMeleeSpells(false); - me->GetMotionMaster()->MovePoint(RELIQUARY_DESPAWN_WAYPOINT, DespawnPoint); - } + _dead = true; + Talk(SUFF_SAY_RECAP); + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + events.Reset(); + me->InterruptNonMeleeSpells(false); + me->GetMotionMaster()->MovePoint(RELIQUARY_DESPAWN_WAYPOINT, DespawnPoint); } } + } - void EnterCombat(Unit* /*who*/) override - { - me->SetCombatPulseDelay(5); - me->setActive(true); - DoZoneInCombat(); + void EnterCombat(Unit* /*who*/) override + { + me->SetCombatPulseDelay(5); + me->setActive(true); + DoZoneInCombat(); - events.ScheduleEvent(EVENT_SOUL_DRAIN, Seconds(20)); - events.ScheduleEvent(EVENT_FRENZY, Seconds(45)); - Talk(SUFF_SAY_AGRO); - } + events.ScheduleEvent(EVENT_SOUL_DRAIN, Seconds(20)); + events.ScheduleEvent(EVENT_FRENZY, Seconds(45)); + Talk(SUFF_SAY_AGRO); + } - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SUFF_SAY_SLAY); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SUFF_SAY_SLAY); + } - void EnterEvadeMode(EvadeReason /*why*/) override - { - if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) - reliquary->AI()->EnterEvadeMode(EVADE_REASON_OTHER); - } + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) + reliquary->AI()->EnterEvadeMode(EVADE_REASON_OTHER); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - events.Update(diff); + events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_SOUL_DRAIN: - me->CastCustomSpell(SPELL_SOUL_DRAIN, SPELLVALUE_MAX_TARGETS, 5, me); - events.Repeat(Seconds(30), Seconds(35)); - break; - case EVENT_FRENZY: - Talk(SUFF_SAY_ENRAGE); - DoCastSelf(SPELL_FRENZY); - events.Repeat(Seconds(45), Seconds(50)); - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + case EVENT_SOUL_DRAIN: + me->CastCustomSpell(SPELL_SOUL_DRAIN, SPELLVALUE_MAX_TARGETS, 5, me); + events.Repeat(Seconds(30), Seconds(35)); + break; + case EVENT_FRENZY: + Talk(SUFF_SAY_ENRAGE); + DoCastSelf(SPELL_FRENZY); + events.Repeat(Seconds(45), Seconds(50)); + break; + default: + break; } - DoMeleeAttackIfReady(); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - private: - bool _dead; - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<boss_essence_of_sufferingAI>(creature); + DoMeleeAttackIfReady(); } +private: + bool _dead; }; -class boss_essence_of_desire : public CreatureScript +struct boss_essence_of_desire : public BossAI { -public: - boss_essence_of_desire() : CreatureScript("boss_essence_of_desire") { } - - struct boss_essence_of_desireAI : public BossAI + boss_essence_of_desire(Creature* creature) : BossAI(creature, DATA_ESSENCE_OF_DESIRE), _dead(false) { - boss_essence_of_desireAI(Creature* creature) : BossAI(creature, DATA_ESSENCE_OF_DESIRE), _dead(false) - { - SetBoundary(instance->GetBossBoundary(DATA_RELIQUARY_OF_SOULS)); - } + SetBoundary(instance->GetBossBoundary(DATA_RELIQUARY_OF_SOULS)); + } - void Reset() override - { - DoCastSelf(SPELL_AURA_OF_DESIRE, true); - events.Reset(); - _dead = false; - } + void Reset() override + { + DoCastSelf(SPELL_AURA_OF_DESIRE, true); + events.Reset(); + _dead = false; + } - void EnterCombat(Unit* /*who*/) override - { - events.ScheduleEvent(EVENT_SPIRIT_SHOCK, Seconds(11)); - events.ScheduleEvent(EVENT_RUNE_SHIELD, Seconds(16)); - events.ScheduleEvent(EVENT_DEADEN, Seconds(31)); + void EnterCombat(Unit* /*who*/) override + { + events.ScheduleEvent(EVENT_SPIRIT_SHOCK, Seconds(11)); + events.ScheduleEvent(EVENT_RUNE_SHIELD, Seconds(16)); + events.ScheduleEvent(EVENT_DEADEN, Seconds(31)); + + me->SetCombatPulseDelay(5); + me->setActive(true); + DoZoneInCombat(); + Talk(DESI_SAY_FREED); + } - me->SetCombatPulseDelay(5); - me->setActive(true); - DoZoneInCombat(); - Talk(DESI_SAY_FREED); - } + void MovementInform(uint32 motionType, uint32 pointId) override + { + if (motionType != POINT_MOTION_TYPE) + return; - void MovementInform(uint32 motionType, uint32 pointId) override + if (pointId == RELIQUARY_DESPAWN_WAYPOINT) { - if (motionType != POINT_MOTION_TYPE) - return; - - if (pointId == RELIQUARY_DESPAWN_WAYPOINT) - { - if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) - reliquary->AI()->DoAction(ACTION_ESSENCE_OF_DESIRE_DEAD); + if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) + reliquary->AI()->DoAction(ACTION_ESSENCE_OF_DESIRE_DEAD); - DoCastSelf(SPELL_SUBMERGE_VISUAL, true); - me->DespawnOrUnsummon(Seconds(2)); - } + DoCastSelf(SPELL_SUBMERGE_VISUAL, true); + me->DespawnOrUnsummon(Seconds(2)); } + } - void DamageTaken(Unit* /*done_by*/, uint32 &damage) override + void DamageTaken(Unit* /*done_by*/, uint32 &damage) override + { + if (damage >= me->GetHealth()) { - if (damage >= me->GetHealth()) + damage = 0; + if (!_dead) { - damage = 0; - if (!_dead) - { - _dead = true; - Talk(DESI_SAY_RECAP); - me->AttackStop(); - me->SetReactState(REACT_PASSIVE); - events.Reset(); - me->InterruptNonMeleeSpells(false); - me->GetMotionMaster()->MovePoint(RELIQUARY_DESPAWN_WAYPOINT, DespawnPoint); - } + _dead = true; + Talk(DESI_SAY_RECAP); + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + events.Reset(); + me->InterruptNonMeleeSpells(false); + me->GetMotionMaster()->MovePoint(RELIQUARY_DESPAWN_WAYPOINT, DespawnPoint); } } + } - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(DESI_SAY_SLAY); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(DESI_SAY_SLAY); + } - void EnterEvadeMode(EvadeReason /*why*/) override - { - if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) - reliquary->AI()->EnterEvadeMode(EVADE_REASON_OTHER); - } + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) + reliquary->AI()->EnterEvadeMode(EVADE_REASON_OTHER); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - events.Update(diff); + events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_SPIRIT_SHOCK: - DoCastVictim(SPELL_SPIRIT_SHOCK); - events.Repeat(Seconds(10), Seconds(15)); - break; - case EVENT_RUNE_SHIELD: - DoCastSelf(SPELL_RUNE_SHIELD); - events.Repeat(Seconds(16)); - break; - case EVENT_DEADEN: - Talk(DESI_SAY_SPEC); - DoCastVictim(SPELL_DEADEN); - events.Repeat(Seconds(31)); - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + case EVENT_SPIRIT_SHOCK: + DoCastVictim(SPELL_SPIRIT_SHOCK); + events.Repeat(Seconds(10), Seconds(15)); + break; + case EVENT_RUNE_SHIELD: + DoCastSelf(SPELL_RUNE_SHIELD); + events.Repeat(Seconds(16)); + break; + case EVENT_DEADEN: + Talk(DESI_SAY_SPEC); + DoCastVictim(SPELL_DEADEN); + events.Repeat(Seconds(31)); + break; + default: + break; } - DoMeleeAttackIfReady(); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - private: - bool _dead; - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<boss_essence_of_desireAI>(creature); + DoMeleeAttackIfReady(); } +private: + bool _dead; }; -class boss_essence_of_anger : public CreatureScript +struct boss_essence_of_anger : public BossAI { -public: - boss_essence_of_anger() : CreatureScript("boss_essence_of_anger") { } - - struct boss_essence_of_angerAI : public BossAI + boss_essence_of_anger(Creature* creature) :BossAI(creature, DATA_ESSENCE_OF_ANGER) { - boss_essence_of_angerAI(Creature* creature) :BossAI(creature, DATA_ESSENCE_OF_ANGER) - { - SetBoundary(instance->GetBossBoundary(DATA_RELIQUARY_OF_SOULS)); - } + SetBoundary(instance->GetBossBoundary(DATA_RELIQUARY_OF_SOULS)); + } - void Reset() override - { - events.Reset(); - _targetGUID.Clear(); - DoCastSelf(SPELL_AURA_OF_ANGER); - } + void Reset() override + { + events.Reset(); + _targetGUID.Clear(); + DoCastSelf(SPELL_AURA_OF_ANGER); + } - void EnterCombat(Unit* /*who*/) override - { - Talk(ANGER_SAY_FREED); + void EnterCombat(Unit* /*who*/) override + { + Talk(ANGER_SAY_FREED); - events.ScheduleEvent(EVENT_START_CHECK_TANKER, Seconds(5)); - events.ScheduleEvent(EVENT_SOUL_SCREAM, Seconds(11)); - events.ScheduleEvent(EVENT_SPITE, Seconds(20)); - events.ScheduleEvent(EVENT_FREED_2, Seconds(1), Minutes(3)); + events.ScheduleEvent(EVENT_START_CHECK_TANKER, Seconds(5)); + events.ScheduleEvent(EVENT_SOUL_SCREAM, Seconds(11)); + events.ScheduleEvent(EVENT_SPITE, Seconds(20)); + events.ScheduleEvent(EVENT_FREED_2, Seconds(1), Minutes(3)); - me->SetCombatPulseDelay(5); - me->setActive(true); - DoZoneInCombat(); - } + me->SetCombatPulseDelay(5); + me->setActive(true); + DoZoneInCombat(); + } - void JustDied(Unit* /*killer*/) override - { - DoPlaySoundToSet(me, ANGER_SOUND_ID_DEATH); - if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) - reliquary->AI()->DoAction(ACTION_KILL_SELF); - } + void JustDied(Unit* /*killer*/) override + { + DoPlaySoundToSet(me, ANGER_SOUND_ID_DEATH); + if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) + reliquary->AI()->DoAction(ACTION_KILL_SELF); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - events.Update(diff); + events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) + case EVENT_CHECK_TANKER: { - case EVENT_CHECK_TANKER: + Unit* target = me->GetVictim(); + if (!_targetGUID || !target) + return; + + if (target->GetGUID() != _targetGUID) { - Unit* target = me->GetVictim(); - if (!_targetGUID || !target) - return; - - if (target->GetGUID() != _targetGUID) - { - Talk(ANGER_SAY_SEETHE); - Talk(ANGER_EMOTE_SEETHE, me); - _targetGUID = target->GetGUID(); - DoCastSelf(SPELL_SEETHE, true); - } - break; + Talk(ANGER_SAY_SEETHE); + Talk(ANGER_EMOTE_SEETHE, me); + _targetGUID = target->GetGUID(); + DoCastSelf(SPELL_SEETHE, true); } - case EVENT_SOUL_SCREAM: - DoCastSelf(SPELL_SOUL_SCREAM); - events.Repeat(Seconds(11)); - break; - case EVENT_SPITE: - Talk(ANGER_SAY_SPITE); - me->CastCustomSpell(SPELL_SPITE, SPELLVALUE_MAX_TARGETS, 3, me); - events.Repeat(Seconds(20)); - break; - case EVENT_START_CHECK_TANKER: - if (Unit* target = me->GetVictim()) - { - _targetGUID = target->GetGUID(); - events.ScheduleEvent(EVENT_CHECK_TANKER, Seconds(1)); - } - else - events.Repeat(Seconds(1)); - break; - case EVENT_FREED_2: - Talk(ANGER_SAY_FREED_2); - break; - default: - break; + break; } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + case EVENT_SOUL_SCREAM: + DoCastSelf(SPELL_SOUL_SCREAM); + events.Repeat(Seconds(11)); + break; + case EVENT_SPITE: + Talk(ANGER_SAY_SPITE); + me->CastCustomSpell(SPELL_SPITE, SPELLVALUE_MAX_TARGETS, 3, me); + events.Repeat(Seconds(20)); + break; + case EVENT_START_CHECK_TANKER: + if (Unit* target = me->GetVictim()) + { + _targetGUID = target->GetGUID(); + events.ScheduleEvent(EVENT_CHECK_TANKER, Seconds(1)); + } + else + events.Repeat(Seconds(1)); + break; + case EVENT_FREED_2: + Talk(ANGER_SAY_FREED_2); + break; + default: + break; } - DoMeleeAttackIfReady(); - } - - void EnterEvadeMode(EvadeReason /*why*/) override - { - if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) - reliquary->AI()->EnterEvadeMode(EVADE_REASON_OTHER); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - private: - ObjectGuid _targetGUID; - }; + DoMeleeAttackIfReady(); + } - CreatureAI* GetAI(Creature* creature) const override + void EnterEvadeMode(EvadeReason /*why*/) override { - return GetBlackTempleAI<boss_essence_of_angerAI>(creature); + if (Creature* reliquary = instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) + reliquary->AI()->EnterEvadeMode(EVADE_REASON_OTHER); } + +private: + ObjectGuid _targetGUID; }; -class npc_enslaved_soul : public CreatureScript +struct npc_enslaved_soul : public ScriptedAI { -public: - npc_enslaved_soul() : CreatureScript("npc_enslaved_soul") { } + npc_enslaved_soul(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - struct npc_enslaved_soulAI : public ScriptedAI + void Reset() override { - npc_enslaved_soulAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } - - void Reset() override - { - me->SetReactState(REACT_PASSIVE); - if (Creature* reliquary = _instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) - reliquary->AI()->JustSummoned(me); + me->SetReactState(REACT_PASSIVE); + if (Creature* reliquary = _instance->GetCreature(DATA_RELIQUARY_OF_SOULS)) + reliquary->AI()->JustSummoned(me); - DoCastSelf(SPELL_ENSLAVED_SOUL_PASSIVE, true); + DoCastSelf(SPELL_ENSLAVED_SOUL_PASSIVE, true); - _scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) - { - me->SetReactState(REACT_AGGRESSIVE); - me->SetInCombatWithZone(); - }); - } - - void DoAction(int32 actionId) override + _scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) { - if (actionId == ACTION_KILL_SELF) - me->KillSelf(); - } + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + }); + } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void DoAction(int32 actionId) override + { + if (actionId == ACTION_KILL_SELF) + me->KillSelf(); + } - _scheduler.Update(diff); + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - DoMeleeAttackIfReady(); - } + _scheduler.Update(diff); - void JustDied(Unit* /*killer*/) override - { - DoCastSelf(SPELL_SOUL_RELEASE, true); - } - - private: - InstanceScript* _instance; - TaskScheduler _scheduler; - }; + DoMeleeAttackIfReady(); + } - CreatureAI* GetAI(Creature* creature) const override + void JustDied(Unit* /*killer*/) override { - return GetBlackTempleAI<npc_enslaved_soulAI>(creature); + DoCastSelf(SPELL_SOUL_RELEASE, true); } + +private: + InstanceScript* _instance; + TaskScheduler _scheduler; }; // 41350 - Aura of Desire -class spell_reliquary_of_souls_aura_of_desire : public SpellScriptLoader +class spell_reliquary_of_souls_aura_of_desire : public AuraScript { - public: - spell_reliquary_of_souls_aura_of_desire() : SpellScriptLoader("spell_reliquary_of_souls_aura_of_desire") { } - - class spell_reliquary_of_souls_aura_of_desire_AuraScript : public AuraScript - { - PrepareAuraScript(spell_reliquary_of_souls_aura_of_desire_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_AURA_OF_DESIRE_DAMAGE }); - } + PrepareAuraScript(spell_reliquary_of_souls_aura_of_desire); - void OnProcSpell(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - DamageInfo* damageInfo = eventInfo.GetDamageInfo(); - if (!damageInfo || !damageInfo->GetDamage()) - return; - - Unit* caster = eventInfo.GetActor(); - int32 bp = damageInfo->GetDamage() / 2; - caster->CastCustomSpell(SPELL_AURA_OF_DESIRE_DAMAGE, SPELLVALUE_BASE_POINT0, bp, caster, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_AURA_OF_DESIRE_DAMAGE }); + } - void UpdateAmount(AuraEffect* /*aurEff*/) - { - if (AuraEffect* effect = GetAura()->GetEffect(EFFECT_1)) - effect->ChangeAmount(effect->GetAmount() - 5); - } + void OnProcSpell(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + Unit* caster = eventInfo.GetActor(); + int32 bp = damageInfo->GetDamage() / 2; + caster->CastCustomSpell(SPELL_AURA_OF_DESIRE_DAMAGE, SPELLVALUE_BASE_POINT0, bp, caster, true, nullptr, aurEff); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_reliquary_of_souls_aura_of_desire_AuraScript::OnProcSpell, EFFECT_0, SPELL_AURA_MOD_HEALING_PCT); - OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_reliquary_of_souls_aura_of_desire_AuraScript::UpdateAmount, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; + void UpdateAmount(AuraEffect* /*aurEff*/) + { + if (AuraEffect* effect = GetAura()->GetEffect(EFFECT_1)) + effect->ChangeAmount(effect->GetAmount() - 5); + } - AuraScript* GetAuraScript() const override - { - return new spell_reliquary_of_souls_aura_of_desire_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_reliquary_of_souls_aura_of_desire::OnProcSpell, EFFECT_0, SPELL_AURA_MOD_HEALING_PCT); + OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_reliquary_of_souls_aura_of_desire::UpdateAmount, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; // 28819 - Submerge Visual -class spell_reliquary_of_souls_submerge : public SpellScriptLoader +class spell_reliquary_of_souls_submerge : public AuraScript { - public: - spell_reliquary_of_souls_submerge() : SpellScriptLoader("spell_reliquary_of_souls_submerge") { } - - class spell_reliquary_of_souls_submerge_AuraScript : public AuraScript - { - PrepareAuraScript(spell_reliquary_of_souls_submerge_AuraScript); + PrepareAuraScript(spell_reliquary_of_souls_submerge); - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - GetTarget()->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_SUBMERGED); - } + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_SUBMERGED); + } - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - GetTarget()->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_STAND); - } + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_STAND); + } - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_reliquary_of_souls_submerge_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_reliquary_of_souls_submerge_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_reliquary_of_souls_submerge_AuraScript(); - } + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_reliquary_of_souls_submerge::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_reliquary_of_souls_submerge::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } }; // 41376 - Spite -class spell_reliquary_of_souls_spite : public SpellScriptLoader +class spell_reliquary_of_souls_spite : public AuraScript { - public: - spell_reliquary_of_souls_spite() : SpellScriptLoader("spell_reliquary_of_souls_spite") { } - - class spell_reliquary_of_souls_spite_AuraScript : public AuraScript - { - PrepareAuraScript(spell_reliquary_of_souls_spite_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SPITE_DAMAGE }); - } + PrepareAuraScript(spell_reliquary_of_souls_spite); - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - caster->CastSpell(GetTarget(), SPELL_SPITE_DAMAGE, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SPITE_DAMAGE }); + } - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_reliquary_of_souls_spite_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DAMAGE_IMMUNITY, AURA_EFFECT_HANDLE_REAL); - } - }; + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + caster->CastSpell(GetTarget(), SPELL_SPITE_DAMAGE, true); + } - AuraScript* GetAuraScript() const - { - return new spell_reliquary_of_souls_spite_AuraScript(); - } + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_reliquary_of_souls_spite::OnRemove, EFFECT_0, SPELL_AURA_DAMAGE_IMMUNITY, AURA_EFFECT_HANDLE_REAL); + } }; // 41305 - Frenzy -class spell_reliquary_of_souls_frenzy : public SpellScriptLoader +class spell_reliquary_of_souls_frenzy : public SpellScript { - public: - spell_reliquary_of_souls_frenzy() : SpellScriptLoader("spell_reliquary_of_souls_frenzy") { } - - class spell_reliquary_of_souls_frenzy_SpellScript : public SpellScript - { - PrepareSpellScript(spell_reliquary_of_souls_frenzy_SpellScript); - - void HandleAfterCast() - { - if (Creature* caster = GetCaster()->ToCreature()) - caster->AI()->Talk(SUFF_EMOTE_ENRAGE, caster); - } + PrepareSpellScript(spell_reliquary_of_souls_frenzy); - void Register() override - { - AfterCast += SpellCastFn(spell_reliquary_of_souls_frenzy_SpellScript::HandleAfterCast); - } - }; + void HandleAfterCast() + { + if (Creature* caster = GetCaster()->ToCreature()) + caster->AI()->Talk(SUFF_EMOTE_ENRAGE, caster); + } - SpellScript* GetSpellScript() const override - { - return new spell_reliquary_of_souls_frenzy_SpellScript(); - } + void Register() override + { + AfterCast += SpellCastFn(spell_reliquary_of_souls_frenzy::HandleAfterCast); + } }; void AddSC_boss_reliquary_of_souls() { - new boss_reliquary_of_souls(); - new boss_essence_of_suffering(); - new boss_essence_of_desire(); - new boss_essence_of_anger(); - new npc_enslaved_soul(); - new spell_reliquary_of_souls_aura_of_desire(); - new spell_reliquary_of_souls_submerge(); - new spell_reliquary_of_souls_spite(); - new spell_reliquary_of_souls_frenzy(); + RegisterBlackTempleCreatureAI(boss_reliquary_of_souls); + RegisterBlackTempleCreatureAI(boss_essence_of_suffering); + RegisterBlackTempleCreatureAI(boss_essence_of_desire); + RegisterBlackTempleCreatureAI(boss_essence_of_anger); + RegisterBlackTempleCreatureAI(npc_enslaved_soul); + RegisterAuraScript(spell_reliquary_of_souls_aura_of_desire); + RegisterAuraScript(spell_reliquary_of_souls_submerge); + RegisterAuraScript(spell_reliquary_of_souls_spite); + RegisterSpellScript(spell_reliquary_of_souls_frenzy); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp index 41090701682..17fd7d91c21 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp @@ -32,14 +32,14 @@ enum Says { // Akama - SAY_BROKEN_FREE_0 = 0, - SAY_BROKEN_FREE_1 = 1, - SAY_BROKEN_FREE_2 = 2, - SAY_LOW_HEALTH = 3, - SAY_DEAD = 4, + SAY_BROKEN_FREE_0 = 0, + SAY_BROKEN_FREE_1 = 1, + SAY_BROKEN_FREE_2 = 2, + SAY_LOW_HEALTH = 3, + SAY_DEAD = 4, // Ashtongue Broken - SAY_BROKEN_SPECIAL = 0, - SAY_BROKEN_HAIL = 1 + SAY_BROKEN_SPECIAL = 0, + SAY_BROKEN_HAIL = 1 }; enum Spells @@ -81,20 +81,20 @@ enum Spells enum Creatures { - NPC_ASHTONGUE_CHANNELER = 23421, - NPC_ASHTONGUE_BROKEN = 23319, - NPC_CREATURE_SPAWNER_AKAMA = 23210 + NPC_ASHTONGUE_CHANNELER = 23421, + NPC_ASHTONGUE_BROKEN = 23319, + NPC_CREATURE_SPAWNER_AKAMA = 23210 }; enum Actions { - ACTION_START_SPAWNING = 0, - ACTION_STOP_SPAWNING = 1, - ACTION_DESPAWN_ALL_SPAWNS = 2, - ACTION_SHADE_OF_AKAMA_DEAD = 3, - ACTION_BROKEN_SPECIAL = 4, - ACTION_BROKEN_EMOTE = 5, - ACTION_BROKEN_HAIL = 6 + ACTION_START_SPAWNING = 0, + ACTION_STOP_SPAWNING = 1, + ACTION_DESPAWN_ALL_SPAWNS = 2, + ACTION_SHADE_OF_AKAMA_DEAD = 3, + ACTION_BROKEN_SPECIAL = 4, + ACTION_BROKEN_EMOTE = 5, + ACTION_BROKEN_HAIL = 6 }; enum Events @@ -139,10 +139,9 @@ enum Events enum Misc { - AKAMA_CHANNEL_WAYPOINT = 0, - AKAMA_INTRO_WAYPOINT = 1, - - SUMMON_GROUP_RESET = 1 + AKAMA_CHANNEL_WAYPOINT = 0, + AKAMA_INTRO_WAYPOINT = 1, + SUMMON_GROUP_RESET = 1 }; Position const AkamaWP[2] = @@ -199,1073 +198,941 @@ static float const MIDDLE_OF_ROOM = 400.0f; static float const FACE_THE_DOOR = 0.08726646f; static float const FACE_THE_PLATFORM = 3.118662f; -class boss_shade_of_akama : public CreatureScript + +struct boss_shade_of_akama : public BossAI { -public: - boss_shade_of_akama() : CreatureScript("boss_shade_of_akama") { } + boss_shade_of_akama(Creature* creature) : BossAI(creature, DATA_SHADE_OF_AKAMA) + { + Initialize(); + } - struct boss_shade_of_akamaAI : public BossAI + void Initialize() { - boss_shade_of_akamaAI(Creature* creature) : BossAI(creature, DATA_SHADE_OF_AKAMA) - { - Initialize(); - } + _spawners.clear(); + _isInPhaseOne = true; + } - void Initialize() - { - _spawners.clear(); - _isInPhaseOne = true; - } + void Reset() override + { + _Reset(); + Initialize(); + me->SetImmuneToPC(true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); + me->SetWalk(true); + events.ScheduleEvent(EVENT_INITIALIZE_SPAWNERS, Seconds(1)); + me->SummonCreatureGroup(SUMMON_GROUP_RESET); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + events.Reset(); + summons.DespawnAll(); + + for (ObjectGuid const spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); - void Reset() override + _DespawnAtEvade(); + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_AKAMA_SOUL_CHANNEL) { - _Reset(); - Initialize(); - me->SetImmuneToPC(true); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); - me->SetWalk(true); - events.ScheduleEvent(EVENT_INITIALIZE_SPAWNERS, Seconds(1)); - me->SummonCreatureGroup(SUMMON_GROUP_RESET); + events.ScheduleEvent(EVENT_START_CHANNELERS_AND_SPAWNERS, Seconds(1)); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); + events.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(10)); + if (Creature* akama = instance->GetCreature(DATA_AKAMA_SHADE)) + AttackStart(akama); } - void EnterEvadeMode(EvadeReason /*why*/) override + if (spell->Id == SPELL_AKAMA_SOUL_RETRIEVE) + DoCastSelf(SPELL_AKAMA_SOUL_EXPEL_CHANNEL); + } + + void MovementInform(uint32 motionType, uint32 /*pointId*/) override + { + if (_isInPhaseOne && motionType == CHASE_MOTION_TYPE) { - events.Reset(); - summons.DespawnAll(); + _isInPhaseOne = false; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetImmuneToPC(false); + me->SetWalk(false); + events.ScheduleEvent(EVENT_ADD_THREAT, Milliseconds(100)); for (ObjectGuid const spawnerGuid : _spawners) if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) - spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); - - _DespawnAtEvade(); + spawner->AI()->DoAction(ACTION_STOP_SPAWNING); } + } - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override - { - if (spell->Id == SPELL_AKAMA_SOUL_CHANNEL) - { - events.ScheduleEvent(EVENT_START_CHANNELERS_AND_SPAWNERS, Seconds(1)); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); - events.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(10)); - if (Creature* akama = instance->GetCreature(DATA_AKAMA_SHADE)) - AttackStart(akama); - } - - if (spell->Id == SPELL_AKAMA_SOUL_RETRIEVE) - DoCastSelf(SPELL_AKAMA_SOUL_EXPEL_CHANNEL); - } + void JustDied(Unit* /*killer*/) override + { + DoCastSelf(SPELL_SHADE_OF_AKAMA_TRIGGER); - void MovementInform(uint32 motionType, uint32 /*pointId*/) override - { - if (_isInPhaseOne && motionType == CHASE_MOTION_TYPE) - { - _isInPhaseOne = false; - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetImmuneToPC(false); - me->SetWalk(false); - events.ScheduleEvent(EVENT_ADD_THREAT, Milliseconds(100)); - - for (ObjectGuid const spawnerGuid : _spawners) - if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) - spawner->AI()->DoAction(ACTION_STOP_SPAWNING); - } - } + if (Creature* akama = instance->GetCreature(DATA_AKAMA_SHADE)) + akama->AI()->DoAction(ACTION_SHADE_OF_AKAMA_DEAD); - void JustDied(Unit* /*killer*/) override - { - DoCastSelf(SPELL_SHADE_OF_AKAMA_TRIGGER); + for (ObjectGuid const spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); - if (Creature* akama = instance->GetCreature(DATA_AKAMA_SHADE)) - akama->AI()->DoAction(ACTION_SHADE_OF_AKAMA_DEAD); + events.Reset(); + summons.DespawnEntry(NPC_ASHTONGUE_CHANNELER); + instance->SetBossState(DATA_SHADE_OF_AKAMA, DONE); + } - for (ObjectGuid const spawnerGuid : _spawners) - if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) - spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); + void EnterEvadeModeIfNeeded() + { + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + if (Player* player = i->GetSource()) + if (player->IsAlive() && !player->IsGameMaster() && CheckBoundary(player)) + return; - events.Reset(); - summons.DespawnEntry(NPC_ASHTONGUE_CHANNELER); - instance->SetBossState(DATA_SHADE_OF_AKAMA, DONE); - } + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } - void EnterEvadeModeIfNeeded() - { - Map::PlayerList const& players = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) - if (Player* player = i->GetSource()) - if (player->IsAlive() && !player->IsGameMaster() && CheckBoundary(player)) - return; + void UpdateAI(uint32 diff) override + { + events.Update(diff); - EnterEvadeMode(EVADE_REASON_NO_HOSTILES); - } + if (!UpdateVictim()) + return; - void UpdateAI(uint32 diff) override + while (uint32 eventId = events.ExecuteEvent()) { - events.Update(diff); - - if (!UpdateVictim()) - return; - - while (uint32 eventId = events.ExecuteEvent()) + switch (eventId) { - switch (eventId) + case EVENT_INITIALIZE_SPAWNERS: { - case EVENT_INITIALIZE_SPAWNERS: - { - std::list<Creature*> SpawnerList; - me->GetCreatureListWithEntryInGrid(SpawnerList, NPC_CREATURE_SPAWNER_AKAMA); - for (Creature* spawner : SpawnerList) - _spawners.push_back(spawner->GetGUID()); + std::list<Creature*> SpawnerList; + me->GetCreatureListWithEntryInGrid(SpawnerList, NPC_CREATURE_SPAWNER_AKAMA); + for (Creature* spawner : SpawnerList) + _spawners.push_back(spawner->GetGUID()); - break; - } - case EVENT_START_CHANNELERS_AND_SPAWNERS: - { - for (ObjectGuid const summonGuid : summons) - if (Creature* channeler = ObjectAccessor::GetCreature(*me, summonGuid)) - channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + break; + } + case EVENT_START_CHANNELERS_AND_SPAWNERS: + { + for (ObjectGuid const summonGuid : summons) + if (Creature* channeler = ObjectAccessor::GetCreature(*me, summonGuid)) + channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - for (ObjectGuid const spawnerGuid : _spawners) - if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) - spawner->AI()->DoAction(ACTION_START_SPAWNING); + for (ObjectGuid const spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_START_SPAWNING); - break; - } - case EVENT_ADD_THREAT: - DoCast(SPELL_THREAT); - events.Repeat(Seconds(3) + Milliseconds(500)); - break; - case EVENT_EVADE_CHECK: - EnterEvadeModeIfNeeded(); - events.Repeat(Seconds(10)); - break; - default: - break; + break; } + case EVENT_ADD_THREAT: + DoCast(SPELL_THREAT); + events.Repeat(Seconds(3) + Milliseconds(500)); + break; + case EVENT_EVADE_CHECK: + EnterEvadeModeIfNeeded(); + events.Repeat(Seconds(10)); + break; + default: + break; } - - DoMeleeAttackIfReady(); } - private: - GuidVector _spawners; - bool _isInPhaseOne; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<boss_shade_of_akamaAI>(creature); + DoMeleeAttackIfReady(); } + +private: + GuidVector _spawners; + bool _isInPhaseOne; }; -class npc_akama_shade : public CreatureScript +struct npc_akama_shade : public ScriptedAI { -public: - npc_akama_shade() : CreatureScript("npc_akama_shade") { } - - struct npc_akamaAI : public ScriptedAI + npc_akama_shade(Creature* creature) : ScriptedAI(creature), _summons(me) { - npc_akamaAI(Creature* creature) : ScriptedAI(creature), _summons(me) - { - Initialize(); - _instance = creature->GetInstanceScript(); - } + Initialize(); + _instance = creature->GetInstanceScript(); + } - void Initialize() - { - _isInCombat = false; - _hasYelledOnce = false; - _chosen.Clear(); - _summons.DespawnAll(); - _events.Reset(); - } + void Initialize() + { + _isInCombat = false; + _hasYelledOnce = false; + _chosen.Clear(); + _summons.DespawnAll(); + _events.Reset(); + } - void Reset() override - { - Initialize(); - me->SetFaction(FACTION_ASHTONGUE_DEATHSWORN); - DoCastSelf(SPELL_STEALTH); + void Reset() override + { + Initialize(); + me->SetFaction(FACTION_ASHTONGUE_DEATHSWORN); + DoCastSelf(SPELL_STEALTH); - if (_instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE) - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - } + if (_instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE) + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } - void JustSummoned(Creature* summon) override - { - _summons.Summon(summon); - } + void JustSummoned(Creature* summon) override + { + _summons.Summon(summon); + } - void EnterEvadeMode(EvadeReason /*why*/) override { } + void EnterEvadeMode(EvadeReason /*why*/) override { } - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_THREAT && !_isInCombat) { - if (spell->Id == SPELL_THREAT && !_isInCombat) + _isInCombat = true; + me->SetWalk(false); + me->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) { - _isInCombat = true; - me->SetWalk(false); - me->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); - if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) - { - shade->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); - AttackStart(shade); - _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, Seconds(2)); - _events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, Seconds(5)); - } + shade->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); + AttackStart(shade); + _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, Seconds(2)); + _events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, Seconds(5)); } } + } - void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override + void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override + { + if (me->HealthBelowPct(20) && !_hasYelledOnce) { - if (me->HealthBelowPct(20) && !_hasYelledOnce) - { - _hasYelledOnce = true; - Talk(SAY_LOW_HEALTH); - } + _hasYelledOnce = true; + Talk(SAY_LOW_HEALTH); } + } - void DoAction(int32 actionId) override + void DoAction(int32 actionId) override + { + if (actionId == ACTION_SHADE_OF_AKAMA_DEAD) { - if (actionId == ACTION_SHADE_OF_AKAMA_DEAD) - { - _isInCombat = false; - me->CombatStop(true); - me->SetFaction(FACTION_ASHTONGUE_DEATHSWORN); - me->SetWalk(true); - _events.Reset(); - me->GetMotionMaster()->MovePoint(AKAMA_INTRO_WAYPOINT, AkamaWP[1]); - } + _isInCombat = false; + me->CombatStop(true); + me->SetFaction(FACTION_ASHTONGUE_DEATHSWORN); + me->SetWalk(true); + _events.Reset(); + me->GetMotionMaster()->MovePoint(AKAMA_INTRO_WAYPOINT, AkamaWP[1]); } + } - void MovementInform(uint32 motionType, uint32 pointId) override - { - if (motionType != POINT_MOTION_TYPE) - return; + void MovementInform(uint32 motionType, uint32 pointId) override + { + if (motionType != POINT_MOTION_TYPE) + return; - if (pointId == AKAMA_CHANNEL_WAYPOINT) - _events.ScheduleEvent(EVENT_SHADE_CHANNEL, Seconds(1)); + if (pointId == AKAMA_CHANNEL_WAYPOINT) + _events.ScheduleEvent(EVENT_SHADE_CHANNEL, Seconds(1)); - else if (pointId == AKAMA_INTRO_WAYPOINT) - { - me->SetWalk(false); - _events.ScheduleEvent(EVENT_START_SOUL_RETRIEVE, Seconds(1)); - } + else if (pointId == AKAMA_INTRO_WAYPOINT) + { + me->SetWalk(false); + _events.ScheduleEvent(EVENT_START_SOUL_RETRIEVE, Seconds(1)); } + } - void SummonBrokens() + void SummonBrokens() + { + for (uint8 i = 0; i < 18; i++) { - for (uint8 i = 0; i < 18; i++) + if (TempSummon* summoned = me->SummonCreature(NPC_ASHTONGUE_BROKEN, BrokenPos[i])) { - if (TempSummon* summoned = me->SummonCreature(NPC_ASHTONGUE_BROKEN, BrokenPos[i])) - { - summoned->SetWalk(true); - summoned->GetMotionMaster()->MovePoint(0, BrokenWP[i]); - if (i == 9) //On Sniffs, npc that Yell "Special" is the tenth to be created - _chosen = summoned->GetGUID(); - } + summoned->SetWalk(true); + summoned->GetMotionMaster()->MovePoint(0, BrokenWP[i]); + if (i == 9) //On Sniffs, npc that Yell "Special" is the tenth to be created + _chosen = summoned->GetGUID(); } } + } - void UpdateAI(uint32 diff) override - { - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SHADE_START: - _instance->SetBossState(DATA_SHADE_OF_AKAMA, IN_PROGRESS); - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - me->RemoveAurasDueToSpell(SPELL_STEALTH); - me->SetWalk(true); - me->GetMotionMaster()->MovePoint(AKAMA_CHANNEL_WAYPOINT, AkamaWP[0], false); - break; - case EVENT_SHADE_CHANNEL: - me->SetFacingTo(FACE_THE_PLATFORM); - DoCastSelf(SPELL_AKAMA_SOUL_CHANNEL); - me->SetFaction(FACTION_MONSTER_SPAR_BUDDY); - _events.ScheduleEvent(EVENT_FIXATE, Seconds(5)); - break; - case EVENT_FIXATE: - DoCast(SPELL_FIXATE); - break; - case EVENT_CHAIN_LIGHTNING: - DoCastVictim(SPELL_CHAIN_LIGHTNING); - _events.Repeat(Seconds(8), Seconds(15)); - break; - case EVENT_DESTRUCTIVE_POISON: - DoCastSelf(SPELL_DESTRUCTIVE_POISON); - _events.Repeat(Seconds(3), Seconds(7)); - break; - case EVENT_START_SOUL_RETRIEVE: - me->SetFacingTo(FACE_THE_DOOR); - DoCast(SPELL_AKAMA_SOUL_RETRIEVE); - _events.ScheduleEvent(EVENT_START_BROKEN_FREE, Seconds(15)); - break; - case EVENT_START_BROKEN_FREE: - me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); - Talk(SAY_BROKEN_FREE_0); - SummonBrokens(); - _events.ScheduleEvent(EVENT_BROKEN_FREE_1, Seconds(10)); - break; - case EVENT_BROKEN_FREE_1: - Talk(SAY_BROKEN_FREE_1); - _events.ScheduleEvent(EVENT_BROKEN_FREE_2, Seconds(12)); - break; - case EVENT_BROKEN_FREE_2: - Talk(SAY_BROKEN_FREE_2); - _events.ScheduleEvent(EVENT_BROKEN_FREE_3, Seconds(15)); - break; - case EVENT_BROKEN_FREE_3: - if (Creature* special = ObjectAccessor::GetCreature(*me, _chosen)) - special->AI()->Talk(SAY_BROKEN_SPECIAL); - - _summons.DoAction(ACTION_BROKEN_EMOTE, _pred); - _events.ScheduleEvent(EVENT_BROKEN_FREE_4, Seconds(5)); - break; - case EVENT_BROKEN_FREE_4: - _summons.DoAction(ACTION_BROKEN_HAIL, _pred); - break; - default: - break; - } - } + void UpdateAI(uint32 diff) override + { + _events.Update(diff); - if (me->GetFaction() == FACTION_MONSTER_SPAR_BUDDY) + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - if (!UpdateVictim()) - return; + case EVENT_SHADE_START: + _instance->SetBossState(DATA_SHADE_OF_AKAMA, IN_PROGRESS); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + me->RemoveAurasDueToSpell(SPELL_STEALTH); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(AKAMA_CHANNEL_WAYPOINT, AkamaWP[0], false); + break; + case EVENT_SHADE_CHANNEL: + me->SetFacingTo(FACE_THE_PLATFORM); + DoCastSelf(SPELL_AKAMA_SOUL_CHANNEL); + me->SetFaction(FACTION_MONSTER_SPAR_BUDDY); + _events.ScheduleEvent(EVENT_FIXATE, Seconds(5)); + break; + case EVENT_FIXATE: + DoCast(SPELL_FIXATE); + break; + case EVENT_CHAIN_LIGHTNING: + DoCastVictim(SPELL_CHAIN_LIGHTNING); + _events.Repeat(Seconds(8), Seconds(15)); + break; + case EVENT_DESTRUCTIVE_POISON: + DoCastSelf(SPELL_DESTRUCTIVE_POISON); + _events.Repeat(Seconds(3), Seconds(7)); + break; + case EVENT_START_SOUL_RETRIEVE: + me->SetFacingTo(FACE_THE_DOOR); + DoCast(SPELL_AKAMA_SOUL_RETRIEVE); + _events.ScheduleEvent(EVENT_START_BROKEN_FREE, Seconds(15)); + break; + case EVENT_START_BROKEN_FREE: + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + Talk(SAY_BROKEN_FREE_0); + SummonBrokens(); + _events.ScheduleEvent(EVENT_BROKEN_FREE_1, Seconds(10)); + break; + case EVENT_BROKEN_FREE_1: + Talk(SAY_BROKEN_FREE_1); + _events.ScheduleEvent(EVENT_BROKEN_FREE_2, Seconds(12)); + break; + case EVENT_BROKEN_FREE_2: + Talk(SAY_BROKEN_FREE_2); + _events.ScheduleEvent(EVENT_BROKEN_FREE_3, Seconds(15)); + break; + case EVENT_BROKEN_FREE_3: + if (Creature* special = ObjectAccessor::GetCreature(*me, _chosen)) + special->AI()->Talk(SAY_BROKEN_SPECIAL); - DoMeleeAttackIfReady(); + _summons.DoAction(ACTION_BROKEN_EMOTE, _pred); + _events.ScheduleEvent(EVENT_BROKEN_FREE_4, Seconds(5)); + break; + case EVENT_BROKEN_FREE_4: + _summons.DoAction(ACTION_BROKEN_HAIL, _pred); + break; + default: + break; } } - void JustDied(Unit* /*killer*/) override + if (me->GetFaction() == FACTION_MONSTER_SPAR_BUDDY) { - _summons.DespawnAll(); - Talk(SAY_DEAD); - if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) - if (shade->IsAlive()) - shade->AI()->EnterEvadeMode(EVADE_REASON_OTHER); - } + if (!UpdateVictim()) + return; - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - if (gossipListId == 0) - { - CloseGossipMenuFor(player); - _events.ScheduleEvent(EVENT_SHADE_START, Milliseconds(500)); - } - return false; + DoMeleeAttackIfReady(); } + } - private: - InstanceScript* _instance; - EventMap _events; - SummonList _summons; - DummyEntryCheckPredicate _pred; - ObjectGuid _chosen; //Creature that should yell the speech special. - bool _isInCombat; - bool _hasYelledOnce; - }; + void JustDied(Unit* /*killer*/) override + { + _summons.DespawnAll(); + Talk(SAY_DEAD); + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) + if (shade->IsAlive()) + shade->AI()->EnterEvadeMode(EVADE_REASON_OTHER); + } - CreatureAI* GetAI(Creature* creature) const override + bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override { - return GetBlackTempleAI<npc_akamaAI>(creature); + if (gossipListId == 0) + { + CloseGossipMenuFor(player); + _events.ScheduleEvent(EVENT_SHADE_START, Milliseconds(500)); + } + return false; } + +private: + InstanceScript* _instance; + EventMap _events; + SummonList _summons; + DummyEntryCheckPredicate _pred; + ObjectGuid _chosen; //Creature that should yell the speech special. + bool _isInCombat; + bool _hasYelledOnce; }; -class npc_ashtongue_channeler : public CreatureScript +struct npc_ashtongue_channeler : public PassiveAI { -public: - npc_ashtongue_channeler() : CreatureScript("npc_ashtongue_channeler") { } - - struct npc_ashtongue_channelerAI : public PassiveAI + npc_ashtongue_channeler(Creature* creature) : PassiveAI(creature) { - npc_ashtongue_channelerAI(Creature* creature) : PassiveAI(creature) - { - _instance = creature->GetInstanceScript(); - } + _instance = creature->GetInstanceScript(); + } - void Reset() override + void Reset() override + { + _scheduler.Schedule(Seconds(2), [this](TaskContext channel) { - _scheduler.Schedule(Seconds(2), [this](TaskContext channel) + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) { - if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) - { - if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - DoCastSelf(SPELL_SHADE_SOUL_CHANNEL); - - else - me->DespawnOrUnsummon(Seconds(3)); - } - - channel.Repeat(Seconds(2)); - }); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - } + if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + DoCastSelf(SPELL_SHADE_SOUL_CHANNEL); - void UpdateAI(uint32 diff) override - { - _scheduler.Update(diff); - } + else + me->DespawnOrUnsummon(Seconds(3)); + } - private: - InstanceScript* _instance; - TaskScheduler _scheduler; - }; + channel.Repeat(Seconds(2)); + }); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetBlackTempleAI<npc_ashtongue_channelerAI>(creature); + _scheduler.Update(diff); } + +private: + InstanceScript* _instance; + TaskScheduler _scheduler; }; -class npc_creature_generator_akama : public CreatureScript +struct npc_creature_generator_akama : public ScriptedAI { -public: - npc_creature_generator_akama() : CreatureScript("npc_creature_generator_akama") { } + npc_creature_generator_akama(Creature* creature) : ScriptedAI(creature), _summons(me) + { + Initialize(); + } - struct npc_creature_generator_akamaAI : public ScriptedAI + void Initialize() { - npc_creature_generator_akamaAI(Creature* creature) : ScriptedAI(creature), _summons(me) - { - Initialize(); - } + _leftSide = false; + _events.Reset(); + _summons.DespawnAll(); + } - void Initialize() - { - _leftSide = false; - _events.Reset(); - _summons.DespawnAll(); - } + void Reset() override + { + Initialize(); - void Reset() override - { - Initialize(); + if (me->GetPositionY() < MIDDLE_OF_ROOM) + _leftSide = true; + } - if (me->GetPositionY() < MIDDLE_OF_ROOM) - _leftSide = true; - } + void JustSummoned(Creature* summon) override + { + _summons.Summon(summon); + } - void JustSummoned(Creature* summon) override + void DoAction(int32 actionId) override + { + switch (actionId) { - _summons.Summon(summon); + case ACTION_START_SPAWNING: + if (_leftSide) + { + _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Milliseconds(100)); + _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, Seconds(2), Seconds(5)); + } + else + { + _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Seconds(10)); + _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, Seconds(2), Seconds(5)); + } + break; + case ACTION_STOP_SPAWNING: + _events.Reset(); + break; + case ACTION_DESPAWN_ALL_SPAWNS: + _events.Reset(); + _summons.DespawnAll(); + break; + default: + break; } + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); - void DoAction(int32 actionId) override + while (uint32 eventId = _events.ExecuteEvent()) { - switch (actionId) + switch (eventId) { - case ACTION_START_SPAWNING: - if (_leftSide) - { - _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Milliseconds(100)); - _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, Seconds(2), Seconds(5)); - } - else - { - _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Seconds(10)); - _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, Seconds(2), Seconds(5)); - } + case EVENT_SPAWN_WAVE_B: + DoCastSelf(SPELL_ASHTONGUE_WAVE_B); + _events.Repeat(Seconds(50), Seconds(60)); break; - case ACTION_STOP_SPAWNING: - _events.Reset(); + case EVENT_SUMMON_ASHTONGUE_SORCERER: // left + DoCastSelf(SPELL_SUMMON_ASHTONGUE_SORCERER); + _events.Repeat(Seconds(30), Seconds(35)); break; - case ACTION_DESPAWN_ALL_SPAWNS: - _events.Reset(); - _summons.DespawnAll(); + case EVENT_SUMMON_ASHTONGUE_DEFENDER: // right + DoCastSelf(SPELL_SUMMON_ASHTONGUE_DEFENDER); + _events.Repeat(Seconds(30), Seconds(40)); break; default: break; } } + } - void UpdateAI(uint32 diff) override - { - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_SPAWN_WAVE_B: - DoCastSelf(SPELL_ASHTONGUE_WAVE_B); - _events.Repeat(Seconds(50), Seconds(60)); - break; - case EVENT_SUMMON_ASHTONGUE_SORCERER: // left - DoCastSelf(SPELL_SUMMON_ASHTONGUE_SORCERER); - _events.Repeat(Seconds(30), Seconds(35)); - break; - case EVENT_SUMMON_ASHTONGUE_DEFENDER: // right - DoCastSelf(SPELL_SUMMON_ASHTONGUE_DEFENDER); - _events.Repeat(Seconds(30), Seconds(40)); - break; - default: - break; - } - } - } - - private: - EventMap _events; - SummonList _summons; - bool _leftSide; - }; +private: + EventMap _events; + SummonList _summons; + bool _leftSide; +}; - CreatureAI* GetAI(Creature* creature) const override +struct npc_ashtongue_sorcerer : public ScriptedAI +{ + npc_ashtongue_sorcerer(Creature* creature) : ScriptedAI(creature) { - return GetBlackTempleAI<npc_creature_generator_akamaAI>(creature); + Initialize(); + _instance = creature->GetInstanceScript(); } -}; -class npc_ashtongue_sorcerer : public CreatureScript -{ -public: - npc_ashtongue_sorcerer() : CreatureScript("npc_ashtongue_sorcerer") { } + void Initialize() + { + _switchToCombat = false; + _inBanish = false; + } - struct npc_ashtongue_sorcererAI : public ScriptedAI + void Reset() override { - npc_ashtongue_sorcererAI(Creature* creature) : ScriptedAI(creature) + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) { - Initialize(); - _instance = creature->GetInstanceScript(); - } + if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + me->GetMotionMaster()->MovePoint(0, shade->GetPosition()); - void Initialize() - { - _switchToCombat = false; - _inBanish = false; + else if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) + AttackStart(akama); } + Initialize(); + } - void Reset() override - { - if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) - { - if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - me->GetMotionMaster()->MovePoint(0, shade->GetPosition()); + void JustDied(Unit* /*killer*/) override + { + me->DespawnOrUnsummon(Seconds(5)); + } - else if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) - AttackStart(akama); - } - Initialize(); - } + void EnterEvadeMode(EvadeReason /*why*/) override { } + void EnterCombat(Unit* /*who*/) override { } - void JustDied(Unit* /*killer*/) override - { - me->DespawnOrUnsummon(Seconds(5)); - } + void AttackStart(Unit* who) override + { + if (!_switchToCombat) + return; - void EnterEvadeMode(EvadeReason /*why*/) override { } - void EnterCombat(Unit* /*who*/) override { } + ScriptedAI::AttackStart(who); + } - void AttackStart(Unit* who) override + void MoveInLineOfSight(Unit* who) override + { + if (!_inBanish && who->GetGUID() == _instance->GetGuidData(DATA_SHADE_OF_AKAMA) && me->IsWithinDist(who, 20.0f, false)) { - if (!_switchToCombat) - return; + _inBanish = true; + me->StopMoving(); + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MovePoint(1, me->GetPositionX() + frand(-8.0f, 8.0f), me->GetPositionY() + frand(-8.0f, 8.0f), me->GetPositionZ()); - ScriptedAI::AttackStart(who); - } - - void MoveInLineOfSight(Unit* who) override - { - if (!_inBanish && who->GetGUID() == _instance->GetGuidData(DATA_SHADE_OF_AKAMA) && me->IsWithinDist(who, 20.0f, false)) + _scheduler.Schedule(Seconds(1) + Milliseconds(500), [this](TaskContext sorcer_channel) { - _inBanish = true; - me->StopMoving(); - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MovePoint(1, me->GetPositionX() + frand(-8.0f, 8.0f), me->GetPositionY() + frand(-8.0f, 8.0f), me->GetPositionZ()); - - _scheduler.Schedule(Seconds(1) + Milliseconds(500), [this](TaskContext sorcer_channel) + if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) { - if (Creature* shade = _instance->GetCreature(DATA_SHADE_OF_AKAMA)) + if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + { + me->SetFacingToObject(shade); + DoCastSelf(SPELL_SHADE_SOUL_CHANNEL); + sorcer_channel.Repeat(Seconds(2)); + } + else { - if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - { - me->SetFacingToObject(shade); - DoCastSelf(SPELL_SHADE_SOUL_CHANNEL); - sorcer_channel.Repeat(Seconds(2)); - } - else - { - me->InterruptSpell(CURRENT_CHANNELED_SPELL); - _switchToCombat = true; - if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) - AttackStart(akama); - } + me->InterruptSpell(CURRENT_CHANNELED_SPELL); + _switchToCombat = true; + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) + AttackStart(akama); } - }); - } + } + }); } + } - void UpdateAI(uint32 diff) override - { - _scheduler.Update(diff); + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (!UpdateVictim()) - return; + if (!UpdateVictim()) + return; - DoMeleeAttackIfReady(); - } + DoMeleeAttackIfReady(); + } - private: - InstanceScript* _instance; - TaskScheduler _scheduler; - bool _switchToCombat; - bool _inBanish; - }; +private: + InstanceScript* _instance; + TaskScheduler _scheduler; + bool _switchToCombat; + bool _inBanish; +}; - CreatureAI* GetAI(Creature* creature) const override +struct npc_ashtongue_defender : public ScriptedAI +{ + npc_ashtongue_defender(Creature* creature) : ScriptedAI(creature) { - return GetBlackTempleAI<npc_ashtongue_sorcererAI>(creature); + _instance = creature->GetInstanceScript(); } -}; -class npc_ashtongue_defender : public CreatureScript -{ -public: - npc_ashtongue_defender() : CreatureScript("npc_ashtongue_defender") { } + void Reset() override + { + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) + AttackStart(akama); + } - struct npc_ashtongue_defenderAI : public ScriptedAI + void JustDied(Unit* /*killer*/) override { - npc_ashtongue_defenderAI(Creature* creature) : ScriptedAI(creature) - { - _instance = creature->GetInstanceScript(); - } + me->DespawnOrUnsummon(Seconds(5)); + } - void Reset() override - { - if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) - AttackStart(akama); - } + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_HEROIC_STRIKE, Seconds(5)); + _events.ScheduleEvent(EVENT_SHIELD_BASH, Seconds(10), Seconds(16)); + _events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, Seconds(10), Seconds(16)); + _events.ScheduleEvent(EVENT_WINDFURY, Seconds(8), Seconds(12)); + } - void JustDied(Unit* /*killer*/) override - { - me->DespawnOrUnsummon(Seconds(5)); - } - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_HEROIC_STRIKE, Seconds(5)); - _events.ScheduleEvent(EVENT_SHIELD_BASH, Seconds(10), Seconds(16)); - _events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, Seconds(10), Seconds(16)); - _events.ScheduleEvent(EVENT_WINDFURY, Seconds(8), Seconds(12)); - } + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + _events.Update(diff); - void UpdateAI(uint32 diff) override + while (uint32 eventId = _events.ExecuteEvent()) { - if (!UpdateVictim()) - return; - - _events.Update(diff); - - while (uint32 eventId = _events.ExecuteEvent()) + switch (eventId) { - switch (eventId) - { - case EVENT_DEBILITATING_STRIKE: - DoCastVictim(SPELL_DEBILITATING_STRIKE); - _events.Repeat(Seconds(20), Seconds(25)); - break; - case EVENT_HEROIC_STRIKE: - DoCastSelf(SPELL_HEROIC_STRIKE); - _events.Repeat(Seconds(5), Seconds(15)); - break; - case EVENT_SHIELD_BASH: - DoCastVictim(SPELL_SHIELD_BASH); - _events.Repeat(Seconds(10), Seconds(20)); - break; - case EVENT_WINDFURY: - DoCastVictim(SPELL_WINDFURY); - _events.Repeat(Seconds(6), Seconds(8)); - break; - default: - break; - } + case EVENT_DEBILITATING_STRIKE: + DoCastVictim(SPELL_DEBILITATING_STRIKE); + _events.Repeat(Seconds(20), Seconds(25)); + break; + case EVENT_HEROIC_STRIKE: + DoCastSelf(SPELL_HEROIC_STRIKE); + _events.Repeat(Seconds(5), Seconds(15)); + break; + case EVENT_SHIELD_BASH: + DoCastVictim(SPELL_SHIELD_BASH); + _events.Repeat(Seconds(10), Seconds(20)); + break; + case EVENT_WINDFURY: + DoCastVictim(SPELL_WINDFURY); + _events.Repeat(Seconds(6), Seconds(8)); + break; + default: + break; } - - DoMeleeAttackIfReady(); } - private: - InstanceScript* _instance; - EventMap _events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_ashtongue_defenderAI>(creature); + DoMeleeAttackIfReady(); } + +private: + InstanceScript* _instance; + EventMap _events; }; -class npc_ashtongue_rogue : public CreatureScript +struct npc_ashtongue_rogue : public ScriptedAI { -public: - npc_ashtongue_rogue() : CreatureScript("npc_ashtongue_rogue") { } - - struct npc_ashtongue_rogueAI : public ScriptedAI + npc_ashtongue_rogue(Creature* creature) : ScriptedAI(creature) { - npc_ashtongue_rogueAI(Creature* creature) : ScriptedAI(creature) - { - _instance = creature->GetInstanceScript(); - } + _instance = creature->GetInstanceScript(); + } - void Reset() override - { - if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) - AttackStart(akama); - } + void Reset() override + { + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) + AttackStart(akama); + } - void JustDied(Unit* /*killer*/) override - { - me->DespawnOrUnsummon(Seconds(5)); - } + void JustDied(Unit* /*killer*/) override + { + me->DespawnOrUnsummon(Seconds(5)); + } - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_DEBILITATING_POISON, Milliseconds(500), Seconds(2)); - _events.ScheduleEvent(EVENT_EVISCERATE, Seconds(2), Seconds(5)); - } + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_DEBILITATING_POISON, Milliseconds(500), Seconds(2)); + _events.ScheduleEvent(EVENT_EVISCERATE, Seconds(2), Seconds(5)); + } - void EnterEvadeMode(EvadeReason /*why*/) override { } + void EnterEvadeMode(EvadeReason /*why*/) override { } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - _events.Update(diff); + _events.Update(diff); - while (uint32 eventId = _events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_DEBILITATING_POISON: - DoCastVictim(SPELL_DEBILITATING_POISON); - _events.Repeat(Seconds(15), Seconds(20)); - break; - case EVENT_EVISCERATE: - DoCastVictim(SPELL_EVISCERATE); - _events.Repeat(Seconds(12), Seconds(20)); - break; - default: - break; - } + case EVENT_DEBILITATING_POISON: + DoCastVictim(SPELL_DEBILITATING_POISON); + _events.Repeat(Seconds(15), Seconds(20)); + break; + case EVENT_EVISCERATE: + DoCastVictim(SPELL_EVISCERATE); + _events.Repeat(Seconds(12), Seconds(20)); + break; + default: + break; } - - DoMeleeAttackIfReady(); } - private: - InstanceScript* _instance; - EventMap _events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_ashtongue_rogueAI>(creature); + DoMeleeAttackIfReady(); } + +private: + InstanceScript* _instance; + EventMap _events; }; -class npc_ashtongue_elementalist : public CreatureScript +struct npc_ashtongue_elementalist : public ScriptedAI { -public: - npc_ashtongue_elementalist() : CreatureScript("npc_ashtongue_elementalist") { } - - struct npc_ashtongue_elementalistAI : public ScriptedAI + npc_ashtongue_elementalist(Creature* creature) : ScriptedAI(creature) { - npc_ashtongue_elementalistAI(Creature* creature) : ScriptedAI(creature) - { - _instance = creature->GetInstanceScript(); - } + _instance = creature->GetInstanceScript(); + } - void Reset() override - { - if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) - AttackStart(akama); - } + void Reset() override + { + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) + AttackStart(akama); + } - void JustDied(Unit* /*killer*/) override - { - me->DespawnOrUnsummon(Seconds(5)); - } + void JustDied(Unit* /*killer*/) override + { + me->DespawnOrUnsummon(Seconds(5)); + } - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_RAIN_OF_FIRE, Seconds(18)); - _events.ScheduleEvent(EVENT_LIGHTNING_BOLT, Seconds(6)); - } + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_RAIN_OF_FIRE, Seconds(18)); + _events.ScheduleEvent(EVENT_LIGHTNING_BOLT, Seconds(6)); + } - void EnterEvadeMode(EvadeReason /*why*/) override { } + void EnterEvadeMode(EvadeReason /*why*/) override { } - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; - _events.Update(diff); + _events.Update(diff); - while (uint32 eventId = _events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_RAIN_OF_FIRE: - DoCastVictim(SPELL_RAIN_OF_FIRE); - _events.Repeat(Seconds(15), Seconds(20)); - break; - case EVENT_LIGHTNING_BOLT: - DoCastVictim(SPELL_LIGHTNING_BOLT); - _events.Repeat(Seconds(8), Seconds(15)); - break; - default: - break; - } + case EVENT_RAIN_OF_FIRE: + DoCastVictim(SPELL_RAIN_OF_FIRE); + _events.Repeat(Seconds(15), Seconds(20)); + break; + case EVENT_LIGHTNING_BOLT: + DoCastVictim(SPELL_LIGHTNING_BOLT); + _events.Repeat(Seconds(8), Seconds(15)); + break; + default: + break; } - - DoMeleeAttackIfReady(); } - private: - InstanceScript* _instance; - EventMap _events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_ashtongue_elementalistAI>(creature); + DoMeleeAttackIfReady(); } + +private: + InstanceScript* _instance; + EventMap _events; }; -class npc_ashtongue_spiritbinder : public CreatureScript +struct npc_ashtongue_spiritbinder : public ScriptedAI { -public: - npc_ashtongue_spiritbinder() : CreatureScript("npc_ashtongue_spiritbinder") { } - - struct npc_ashtongue_spiritbinderAI : public ScriptedAI + npc_ashtongue_spiritbinder(Creature* creature) : ScriptedAI(creature) { - npc_ashtongue_spiritbinderAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - _instance = creature->GetInstanceScript(); - } - - void Initialize() - { - _spiritMend = false; - _chainHeal = false; - } - - void Reset() override - { - Initialize(); - - if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) - AttackStart(akama); - } - - void JustDied(Unit* /*killer*/) override - { - me->DespawnOrUnsummon(Seconds(5)); - } - - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_SPIRIT_HEAL, Seconds(5), Seconds(6)); - } + Initialize(); + _instance = creature->GetInstanceScript(); + } - void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override - { - if (!_spiritMend) - if (HealthBelowPct(30)) - { - DoCastSelf(SPELL_SPIRIT_MEND); - _spiritMend = true; - _events.ScheduleEvent(EVENT_SPIRIT_MEND_RESET, Seconds(10),Seconds(15)); - } + void Initialize() + { + _spiritMend = false; + _chainHeal = false; + } - if (!_chainHeal) - if (HealthBelowPct(50)) - { - DoCastSelf(SPELL_CHAIN_HEAL); - _chainHeal = true; - _events.ScheduleEvent(EVENT_CHAIN_HEAL_RESET, Seconds(10), Seconds(15)); - } + void Reset() override + { + Initialize(); - } + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) + AttackStart(akama); + } - void EnterEvadeMode(EvadeReason /*why*/) override { } + void JustDied(Unit* /*killer*/) override + { + me->DespawnOrUnsummon(Seconds(5)); + } - void UpdateAI(uint32 diff) override - { - _events.Update(diff); + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_SPIRIT_HEAL, Seconds(5), Seconds(6)); + } - while (uint32 eventId = _events.ExecuteEvent()) + void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override + { + if (!_spiritMend) + if (HealthBelowPct(30)) { - switch (eventId) - { - case EVENT_SPIRIT_HEAL: - DoCastSelf(SPELL_SPIRITBINDER_SPIRIT_HEAL); - _events.Repeat(Seconds(13), Seconds(16)); - break; - case EVENT_SPIRIT_MEND_RESET: - _spiritMend = false; - break; - case EVENT_CHAIN_HEAL_RESET: - _chainHeal = false; - break; - default: - break; - } + DoCastSelf(SPELL_SPIRIT_MEND); + _spiritMend = true; + _events.ScheduleEvent(EVENT_SPIRIT_MEND_RESET, Seconds(10), Seconds(15)); } - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); - } - - private: - InstanceScript* _instance; - EventMap _events; - bool _spiritMend; - bool _chainHeal; - }; + if (!_chainHeal) + if (HealthBelowPct(50)) + { + DoCastSelf(SPELL_CHAIN_HEAL); + _chainHeal = true; + _events.ScheduleEvent(EVENT_CHAIN_HEAL_RESET, Seconds(10), Seconds(15)); + } - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_ashtongue_spiritbinderAI>(creature); } -}; -class npc_ashtongue_broken : public CreatureScript -{ -public: - npc_ashtongue_broken() : CreatureScript("npc_ashtongue_broken") { } + void EnterEvadeMode(EvadeReason /*why*/) override { } - struct npc_ashtongue_brokenAI : public ScriptedAI + void UpdateAI(uint32 diff) override { - npc_ashtongue_brokenAI(Creature* creature) : ScriptedAI(creature) - { - _instance = me->GetInstanceScript(); - } - - void MovementInform(uint32 motionType, uint32 /*pointId*/) override - { - if (motionType != POINT_MOTION_TYPE) - return; - - if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) - me->SetFacingToObject(akama); - } + _events.Update(diff); - void DoAction(int32 actionId) override + while (uint32 eventId = _events.ExecuteEvent()) { - switch (actionId) + switch (eventId) { - case ACTION_BROKEN_SPECIAL: - Talk(SAY_BROKEN_SPECIAL); + case EVENT_SPIRIT_HEAL: + DoCastSelf(SPELL_SPIRITBINDER_SPIRIT_HEAL); + _events.Repeat(Seconds(13), Seconds(16)); break; - case ACTION_BROKEN_HAIL: - me->SetFaction(FACTION_ASHTONGUE_DEATHSWORN); - Talk(SAY_BROKEN_HAIL); + case EVENT_SPIRIT_MEND_RESET: + _spiritMend = false; break; - case ACTION_BROKEN_EMOTE: - me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_KNEEL); + case EVENT_CHAIN_HEAL_RESET: + _chainHeal = false; break; default: break; } } - private: - InstanceScript* _instance; - }; - + if (!UpdateVictim()) + return; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_ashtongue_brokenAI>(creature); + DoMeleeAttackIfReady(); } + +private: + InstanceScript* _instance; + EventMap _events; + bool _spiritMend; + bool _chainHeal; }; -// 40401 - Shade Soul Channel (serverside spell) -class spell_shade_soul_channel_serverside : public SpellScriptLoader +struct npc_ashtongue_broken : public ScriptedAI { -public: - spell_shade_soul_channel_serverside() : SpellScriptLoader("spell_shade_soul_channel_serverside") { } + npc_ashtongue_broken(Creature* creature) : ScriptedAI(creature) + { + _instance = me->GetInstanceScript(); + } - class spell_shade_soul_channel_serverside_AuraScript : public AuraScript + void MovementInform(uint32 motionType, uint32 /*pointId*/) override { - PrepareAuraScript(spell_shade_soul_channel_serverside_AuraScript); + if (motionType != POINT_MOTION_TYPE) + return; - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_SHADE_SOUL_CHANNEL_2 }); - } + if (Creature* akama = _instance->GetCreature(DATA_AKAMA_SHADE)) + me->SetFacingToObject(akama); + } - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + void DoAction(int32 actionId) override + { + switch (actionId) { - GetTarget()->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); + case ACTION_BROKEN_SPECIAL: + Talk(SAY_BROKEN_SPECIAL); + break; + case ACTION_BROKEN_HAIL: + me->SetFaction(FACTION_ASHTONGUE_DEATHSWORN); + Talk(SAY_BROKEN_HAIL); + break; + case ACTION_BROKEN_EMOTE: + me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_KNEEL); + break; + default: + break; } + } - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_shade_soul_channel_serverside_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; +private: + InstanceScript* _instance; +}; + +// 40401 - Shade Soul Channel (serverside spell) +class spell_shade_soul_channel_serverside : public AuraScript +{ + PrepareAuraScript(spell_shade_soul_channel_serverside); - AuraScript* GetAuraScript() const override + bool Validate(SpellInfo const* /*spell*/) override { - return new spell_shade_soul_channel_serverside_AuraScript(); + return ValidateSpellInfo({ SPELL_SHADE_SOUL_CHANNEL_2 }); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_shade_soul_channel_serverside::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); } }; // 40520 - Shade Soul Channel -class spell_shade_soul_channel : public SpellScriptLoader +class spell_shade_soul_channel : public AuraScript { -public: - spell_shade_soul_channel() : SpellScriptLoader("spell_shade_soul_channel") { } + PrepareAuraScript(spell_shade_soul_channel); - class spell_shade_soul_channel_AuraScript : public AuraScript + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) { - PrepareAuraScript(spell_shade_soul_channel_AuraScript); - - void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - int32 const maxSlowEff = -99; - if (aurEff->GetAmount() < maxSlowEff) - if (AuraEffect* slowEff = GetEffect(EFFECT_0)) - slowEff->ChangeAmount(maxSlowEff); - } - - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_shade_soul_channel_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - } - }; + int32 const maxSlowEff = -99; + if (aurEff->GetAmount() < maxSlowEff) + if (AuraEffect* slowEff = GetEffect(EFFECT_0)) + slowEff->ChangeAmount(maxSlowEff); + } - AuraScript* GetAuraScript() const override + void Register() override { - return new spell_shade_soul_channel_AuraScript(); + AfterEffectApply += AuraEffectApplyFn(spell_shade_soul_channel::OnApply, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); } }; void AddSC_boss_shade_of_akama() { - new boss_shade_of_akama(); - new npc_akama_shade(); - new npc_ashtongue_channeler(); - new npc_creature_generator_akama(); - new npc_ashtongue_sorcerer(); - new npc_ashtongue_defender(); - new npc_ashtongue_rogue(); - new npc_ashtongue_elementalist(); - new npc_ashtongue_spiritbinder(); - new npc_ashtongue_broken(); - new spell_shade_soul_channel_serverside(); - new spell_shade_soul_channel(); + RegisterBlackTempleCreatureAI(boss_shade_of_akama); + RegisterBlackTempleCreatureAI(npc_akama_shade); + RegisterBlackTempleCreatureAI(npc_ashtongue_channeler); + RegisterBlackTempleCreatureAI(npc_creature_generator_akama); + RegisterBlackTempleCreatureAI(npc_ashtongue_sorcerer); + RegisterBlackTempleCreatureAI(npc_ashtongue_defender); + RegisterBlackTempleCreatureAI(npc_ashtongue_rogue); + RegisterBlackTempleCreatureAI(npc_ashtongue_elementalist); + RegisterBlackTempleCreatureAI(npc_ashtongue_spiritbinder); + RegisterBlackTempleCreatureAI(npc_ashtongue_broken); + RegisterAuraScript(spell_shade_soul_channel_serverside); + RegisterAuraScript(spell_shade_soul_channel); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp index c84a3504f61..ca7bc790aa1 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp @@ -63,200 +63,165 @@ enum Actions { ACTION_DISABLE_VULCANO = 1 }; - -class boss_supremus : public CreatureScript +struct boss_supremus : public BossAI { -public: - boss_supremus() : CreatureScript("boss_supremus") { } + boss_supremus(Creature* creature) : BossAI(creature, DATA_SUPREMUS) { } - struct boss_supremusAI : public BossAI + void Reset() override { - boss_supremusAI(Creature* creature) : BossAI(creature, DATA_SUPREMUS) { } + _Reset(); + events.SetPhase(PHASE_INITIAL); + me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); + me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false); + } - void Reset() override + void EnterEvadeMode(EvadeReason /*why*/) override + { + summons.DespawnAll(); + _DespawnAtEvade(); + } + + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + ChangePhase(); + events.ScheduleEvent(EVENT_BERSERK, Minutes(15)); + events.ScheduleEvent(EVENT_FLAME, Seconds(20)); + } + + void ChangePhase() + { + if (events.IsInPhase(PHASE_INITIAL) || events.IsInPhase(PHASE_CHASE)) { - _Reset(); - events.SetPhase(PHASE_INITIAL); + events.SetPhase(PHASE_STRIKE); + DummyEntryCheckPredicate pred; + summons.DoAction(ACTION_DISABLE_VULCANO, pred); + events.ScheduleEvent(EVENT_HATEFUL_STRIKE, Seconds(2), 0, PHASE_STRIKE); + me->RemoveAurasDueToSpell(SPELL_SNARE_SELF); me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false); } - - void EnterEvadeMode(EvadeReason /*why*/) override - { - summons.DespawnAll(); - _DespawnAtEvade(); - } - - void EnterCombat(Unit* /*who*/) override + else { - _EnterCombat(); - ChangePhase(); - events.ScheduleEvent(EVENT_BERSERK, Minutes(15)); - events.ScheduleEvent(EVENT_FLAME, Seconds(20)); + events.SetPhase(PHASE_CHASE); + events.ScheduleEvent(EVENT_VOLCANO, Seconds(5), 0, PHASE_CHASE); + events.ScheduleEvent(EVENT_SWITCH_TARGET, Seconds(10), 0, PHASE_CHASE); + me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); + DoCast(SPELL_SNARE_SELF); } + ResetThreatList(); + DoZoneInCombat(); + events.ScheduleEvent(EVENT_SWITCH_PHASE, Seconds(60)); + } - void ChangePhase() - { - if (events.IsInPhase(PHASE_INITIAL) || events.IsInPhase(PHASE_CHASE)) - { - events.SetPhase(PHASE_STRIKE); - DummyEntryCheckPredicate pred; - summons.DoAction(ACTION_DISABLE_VULCANO, pred); - events.ScheduleEvent(EVENT_HATEFUL_STRIKE, Seconds(2), 0, PHASE_STRIKE); - me->RemoveAurasDueToSpell(SPELL_SNARE_SELF); - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false); - } - else - { - events.SetPhase(PHASE_CHASE); - events.ScheduleEvent(EVENT_VOLCANO, Seconds(5), 0, PHASE_CHASE); - events.ScheduleEvent(EVENT_SWITCH_TARGET, Seconds(10), 0, PHASE_CHASE); - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); - DoCast(SPELL_SNARE_SELF); - } - ResetThreatList(); - DoZoneInCombat(); - events.ScheduleEvent(EVENT_SWITCH_PHASE, Seconds(60)); - } + Unit* CalculateHatefulStrikeTarget() + { + uint32 health = 0; + Unit* target = nullptr; - Unit* CalculateHatefulStrikeTarget() + for (auto* ref : me->GetThreatManager().GetUnsortedThreatList()) { - uint32 health = 0; - Unit* target = nullptr; - - for (auto* ref : me->GetThreatManager().GetUnsortedThreatList()) + Unit* unit = ref->GetVictim(); + if (me->IsWithinMeleeRange(unit)) { - Unit* unit = ref->GetVictim(); - if (me->IsWithinMeleeRange(unit)) + if (unit->GetHealth() > health) { - if (unit->GetHealth() > health) - { - health = unit->GetHealth(); - target = unit; - } + health = unit->GetHealth(); + target = unit; } } - - return target; } - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_BERSERK: - DoCastSelf(SPELL_BERSERK, true); - break; - case EVENT_FLAME: - DoCast(SPELL_MOLTEN_PUNCH); - events.Repeat(Seconds(15), Seconds(20)); - break; - case EVENT_HATEFUL_STRIKE: - if (Unit* target = CalculateHatefulStrikeTarget()) - DoCast(target, SPELL_HATEFUL_STRIKE); - events.Repeat(Seconds(5)); - break; - case EVENT_SWITCH_TARGET: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100.0f, true)) - { - ResetThreatList(); - AddThreat(target, 1000000.0f); - DoCast(target, SPELL_CHARGE); - Talk(EMOTE_NEW_TARGET); - } - events.Repeat(Seconds(10)); - break; - case EVENT_VOLCANO: - DoCastAOE(SPELL_VOLCANIC_SUMMON, true); - Talk(EMOTE_GROUND_CRACK); - events.Repeat(Seconds(10)); - break; - case EVENT_SWITCH_PHASE: - ChangePhase(); - break; - default: - break; - } - } - - }; + return target; + } - CreatureAI* GetAI(Creature* creature) const override + void ExecuteEvent(uint32 eventId) override { - return GetBlackTempleAI<boss_supremusAI>(creature); + switch (eventId) + { + case EVENT_BERSERK: + DoCastSelf(SPELL_BERSERK, true); + break; + case EVENT_FLAME: + DoCast(SPELL_MOLTEN_PUNCH); + events.Repeat(Seconds(15), Seconds(20)); + break; + case EVENT_HATEFUL_STRIKE: + if (Unit* target = CalculateHatefulStrikeTarget()) + DoCast(target, SPELL_HATEFUL_STRIKE); + events.Repeat(Seconds(5)); + break; + case EVENT_SWITCH_TARGET: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100.0f, true)) + { + ResetThreatList(); + AddThreat(target, 1000000.0f); + DoCast(target, SPELL_CHARGE); + Talk(EMOTE_NEW_TARGET); + } + events.Repeat(Seconds(10)); + break; + case EVENT_VOLCANO: + DoCastAOE(SPELL_VOLCANIC_SUMMON, true); + Talk(EMOTE_GROUND_CRACK); + events.Repeat(Seconds(10)); + break; + case EVENT_SWITCH_PHASE: + ChangePhase(); + break; + default: + break; + } } }; -class npc_molten_flame : public CreatureScript +struct npc_molten_flame : public NullCreatureAI { -public: - npc_molten_flame() : CreatureScript("npc_molten_flame") { } + npc_molten_flame(Creature* creature) : NullCreatureAI(creature) { } - struct npc_molten_flameAI : public NullCreatureAI + void InitializeAI() override { - npc_molten_flameAI(Creature* creature) : NullCreatureAI(creature) { } - - void InitializeAI() override - { - float x, y, z; - me->GetNearPoint(me, x, y, z, 1, 100.0f, frand(0.f, 2.f * float(M_PI))); - me->GetMotionMaster()->MovePoint(0, x, y, z); - DoCastSelf(SPELL_MOLTEN_FLAME, true); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_molten_flameAI>(creature); + float x, y, z; + me->GetNearPoint(me, x, y, z, 1, 100.0f, frand(0.f, 2.f * float(M_PI))); + me->GetMotionMaster()->MovePoint(0, x, y, z); + DoCastSelf(SPELL_MOLTEN_FLAME, true); } }; -class npc_volcano : public CreatureScript +struct npc_volcano : public NullCreatureAI { -public: - npc_volcano() : CreatureScript("npc_volcano") { } + npc_volcano(Creature* creature) : NullCreatureAI(creature) { } - struct npc_volcanoAI : public NullCreatureAI + void Reset() override { - npc_volcanoAI(Creature* creature) : NullCreatureAI(creature) { } - - void Reset() override + _scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) { - _scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) - { - DoCastSelf(SPELL_VOLCANIC_ERUPTION); - }); - } - - void DoAction(int32 action) override - { - if (action == ACTION_DISABLE_VULCANO) - { - me->RemoveAurasDueToSpell(SPELL_VOLCANIC_ERUPTION); - me->RemoveAurasDueToSpell(SPELL_VOLCANIC_GEYSER); - } - } + DoCastSelf(SPELL_VOLCANIC_ERUPTION); + }); + } - void UpdateAI(uint32 diff) override + void DoAction(int32 action) override + { + if (action == ACTION_DISABLE_VULCANO) { - _scheduler.Update(diff); + me->RemoveAurasDueToSpell(SPELL_VOLCANIC_ERUPTION); + me->RemoveAurasDueToSpell(SPELL_VOLCANIC_GEYSER); } + } - private: - TaskScheduler _scheduler; - }; - - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetBlackTempleAI<npc_volcanoAI>(creature); + _scheduler.Update(diff); } + +private: + TaskScheduler _scheduler; }; void AddSC_boss_supremus() { - new boss_supremus(); - new npc_molten_flame(); - new npc_volcano(); + RegisterBlackTempleCreatureAI(boss_supremus); + RegisterBlackTempleCreatureAI(npc_molten_flame); + RegisterBlackTempleCreatureAI(npc_volcano); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp index 67ed253f247..496212ed692 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp @@ -69,14 +69,13 @@ enum Spells SPELL_SPIRIT_VOLLEY = 40314, SPELL_SPIRIT_SHIELD = 40322, SPELL_SPIRIT_LANCE = 40157 - }; enum Npcs { - NPC_DOOM_BLOSSOM = 23123, - NPC_SHADOWY_CONSTRUCT = 23111, - NPC_VENGEFUL_SPIRIT = 23109 //Npc controlled by player + NPC_DOOM_BLOSSOM = 23123, + NPC_SHADOWY_CONSTRUCT = 23111, + NPC_VENGEFUL_SPIRIT = 23109 //Npc controlled by player }; enum Events @@ -108,411 +107,348 @@ uint32 const SkeletronSpells[4] = SPELL_SUMMON_SKELETRON_4 }; -class boss_teron_gorefiend : public CreatureScript +struct boss_teron_gorefiend : public BossAI { -public: - boss_teron_gorefiend() : CreatureScript("boss_teron_gorefiend") { } + boss_teron_gorefiend(Creature* creature) : BossAI(creature, DATA_TERON_GOREFIEND) { } - struct boss_teron_gorefiendAI : public BossAI + void Reset() override { - boss_teron_gorefiendAI(Creature* creature) : BossAI(creature, DATA_TERON_GOREFIEND), _intro(false) + _Reset(); + if (instance->GetData(DATA_TERON_GOREFIEND_INTRO)) { - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - creature->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_PASSIVE); } + } - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - Talk(SAY_AGGRO); - events.SetPhase(PHASE_COMBAT); - events.ScheduleEvent(EVENT_ENRAGE, Minutes(10)); - events.ScheduleEvent(EVENT_INCINERATE, Seconds(12)); - events.ScheduleEvent(EVENT_SUMMON_DOOM_BLOSSOM, Seconds(8)); - events.ScheduleEvent(EVENT_SHADOW_DEATH, Seconds(8)); - events.ScheduleEvent(EVENT_CRUSHING_SHADOWS, Seconds(18)); - } + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + events.SetPhase(PHASE_COMBAT); + events.ScheduleEvent(EVENT_ENRAGE, Minutes(10)); + events.ScheduleEvent(EVENT_INCINERATE, Seconds(12)); + events.ScheduleEvent(EVENT_SUMMON_DOOM_BLOSSOM, Seconds(8)); + events.ScheduleEvent(EVENT_SHADOW_DEATH, Seconds(8)); + events.ScheduleEvent(EVENT_CRUSHING_SHADOWS, Seconds(18)); + } - void EnterEvadeMode(EvadeReason /*why*/) override - { - DoCast(SPELL_SHADOW_OF_DEATH_REMOVE); - summons.DespawnAll(); - _DespawnAtEvade(); - } + void EnterEvadeMode(EvadeReason /*why*/) override + { + DoCast(SPELL_SHADOW_OF_DEATH_REMOVE); + summons.DespawnAll(); + _DespawnAtEvade(); + } - void DoAction(int32 action) override + void DoAction(int32 action) override + { + if (action == ACTION_START_INTRO && me->IsAlive()) { - if (action == ACTION_START_INTRO && !_intro && me->IsAlive()) - { - _intro = true; - Talk(SAY_INTRO); - events.SetPhase(PHASE_INTRO); - events.ScheduleEvent(EVENT_FINISH_INTRO, Seconds(20)); - } + instance->SetData(DATA_TERON_GOREFIEND_INTRO, 0); + Talk(SAY_INTRO); + events.SetPhase(PHASE_INTRO); + events.ScheduleEvent(EVENT_FINISH_INTRO, Seconds(20)); } + } - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - DoCast(SPELL_SHADOW_OF_DEATH_REMOVE); - _JustDied(); - } + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + DoCast(SPELL_SHADOW_OF_DEATH_REMOVE); + _JustDied(); + } - void UpdateAI(uint32 diff) override - { - if (!events.IsInPhase(PHASE_INTRO) && !UpdateVictim()) - return; + void UpdateAI(uint32 diff) override + { + if (!events.IsInPhase(PHASE_INTRO) && !UpdateVictim()) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - events.Update(diff); + events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_ENRAGE: - DoCast(SPELL_BERSERK); - break; - case EVENT_INCINERATE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_INCINERATE); - Talk(SAY_INCINERATE); - events.Repeat(Seconds(12), Seconds(20)); - break; - case EVENT_SUMMON_DOOM_BLOSSOM: - DoCastSelf(SPELL_SUMMON_DOOM_BLOSSOM, true); - Talk(SAY_BLOSSOM); - events.Repeat(Seconds(30), Seconds(40)); - break; - case EVENT_SHADOW_DEATH: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100.0f, true, true, -SPELL_SPIRITUAL_VENGEANCE)) - DoCast(target, SPELL_SHADOW_OF_DEATH); - events.Repeat(Seconds(30), Seconds(35)); - break; - case EVENT_CRUSHING_SHADOWS: - me->CastCustomSpell(SPELL_CRUSHING_SHADOWS, SPELLVALUE_MAX_TARGETS, 5, me); - Talk(SAY_CRUSHING); - events.Repeat(Seconds(18), Seconds(30)); - break; - case EVENT_FINISH_INTRO: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - me->SetReactState(REACT_AGGRESSIVE); - break; - default: - break; - } - - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + case EVENT_ENRAGE: + DoCast(SPELL_BERSERK); + break; + case EVENT_INCINERATE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_INCINERATE); + Talk(SAY_INCINERATE); + events.Repeat(Seconds(12), Seconds(20)); + break; + case EVENT_SUMMON_DOOM_BLOSSOM: + DoCastSelf(SPELL_SUMMON_DOOM_BLOSSOM, true); + Talk(SAY_BLOSSOM); + events.Repeat(Seconds(30), Seconds(40)); + break; + case EVENT_SHADOW_DEATH: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100.0f, true, true, -SPELL_SPIRITUAL_VENGEANCE)) + DoCast(target, SPELL_SHADOW_OF_DEATH); + events.Repeat(Seconds(30), Seconds(35)); + break; + case EVENT_CRUSHING_SHADOWS: + me->CastCustomSpell(SPELL_CRUSHING_SHADOWS, SPELLVALUE_MAX_TARGETS, 5, me); + Talk(SAY_CRUSHING); + events.Repeat(Seconds(18), Seconds(30)); + break; + case EVENT_FINISH_INTRO: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_AGGRESSIVE); + break; + default: + break; } - DoMeleeAttackIfReady(); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; } - private: - bool _intro; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<boss_teron_gorefiendAI>(creature); + DoMeleeAttackIfReady(); } }; -class npc_doom_blossom : public CreatureScript +struct npc_doom_blossom : public NullCreatureAI { -public: - npc_doom_blossom() : CreatureScript("npc_doom_blossom") { } - struct npc_doom_blossomAI : public NullCreatureAI - { - npc_doom_blossomAI(Creature* creature) : NullCreatureAI(creature), _instance(me->GetInstanceScript()) { } + npc_doom_blossom(Creature* creature) : NullCreatureAI(creature), _instance(me->GetInstanceScript()) { } - void Reset() override - { - /* Workaround - Until SMSG_SET_PLAY_HOVER_ANIM be implemented */ - Position pos; - pos.Relocate(me); - pos.m_positionZ += 8.0f; - me->GetMotionMaster()->MoveTakeoff(0, pos); - - DoCast(SPELL_SUMMON_BLOSSOM_MOVE_TARGET); - _scheduler.CancelAll(); - me->SetInCombatWithZone(); - _scheduler.Schedule(Seconds(12), [this](TaskContext shadowBolt) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_SHADOWBOLT); - - shadowBolt.Repeat(Seconds(2)); - }); - } - - void UpdateAI(uint32 diff) override + void Reset() override + { + /* Workaround - Until SMSG_SET_PLAY_HOVER_ANIM be implemented */ + Position pos; + pos.Relocate(me); + pos.m_positionZ += 8.0f; + me->GetMotionMaster()->MoveTakeoff(0, pos); + + DoCast(SPELL_SUMMON_BLOSSOM_MOVE_TARGET); + _scheduler.CancelAll(); + me->SetInCombatWithZone(); + _scheduler.Schedule(Seconds(12), [this](TaskContext shadowBolt) { - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - _scheduler.Update(diff); - } + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_SHADOWBOLT); - private: - TaskScheduler _scheduler; - InstanceScript* _instance; - }; + shadowBolt.Repeat(Seconds(2)); + }); + } - CreatureAI* GetAI(Creature* creature) const override + void UpdateAI(uint32 diff) override { - return GetBlackTempleAI<npc_doom_blossomAI>(creature); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + _scheduler.Update(diff); } + +private: + TaskScheduler _scheduler; + InstanceScript* _instance; }; -class npc_shadowy_construct : public CreatureScript +struct npc_shadowy_construct : public ScriptedAI { -public: - npc_shadowy_construct() : CreatureScript("npc_shadowy_construct") { } + npc_shadowy_construct(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) + { + //This creature must be immune everything, except spells of Vengeful Spirit. + creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true); + creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_MAGIC, true); + } - struct npc_shadowy_constructAI : public ScriptedAI + void Reset() override { - npc_shadowy_constructAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) + if (_instance->GetBossState(DATA_TERON_GOREFIEND) != IN_PROGRESS) { - //This creature must be immune everything, except spells of Vengeful Spirit. - creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true); - creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_MAGIC, true); + me->DespawnOrUnsummon(); + return; } - void Reset() override + targetGUID.Clear(); + _scheduler.CancelAll(); + _scheduler.Schedule(Seconds(12), [this](TaskContext atrophy) { - if (_instance->GetBossState(DATA_TERON_GOREFIEND) != IN_PROGRESS) + DoCastVictim(SPELL_ATROPHY); + atrophy.Repeat(Seconds(10), Seconds(12)); + }); + _scheduler.Schedule(Milliseconds(200), [this](TaskContext checkPlayer) + { + if (Unit* target = ObjectAccessor::GetUnit(*me, targetGUID)) { - me->DespawnOrUnsummon(); - return; + if (!target->IsAlive() || !me->CanCreatureAttack(target)) + SelectNewTarget(); } + else + SelectNewTarget(); - targetGUID.Clear(); - _scheduler.CancelAll(); - _scheduler.Schedule(Seconds(12), [this](TaskContext atrophy) - { - DoCastVictim(SPELL_ATROPHY); - atrophy.Repeat(Seconds(10), Seconds(12)); - }); - _scheduler.Schedule(Milliseconds(200), [this](TaskContext checkPlayer) - { - if (Unit* target = ObjectAccessor::GetUnit(*me, targetGUID)) - { - if (!target->IsAlive() || !me->CanCreatureAttack(target)) - SelectNewTarget(); - } - else - SelectNewTarget(); + checkPlayer.Repeat(Seconds(1)); + }); - checkPlayer.Repeat(Seconds(1)); - }); + if (Creature* teron = _instance->GetCreature(DATA_TERON_GOREFIEND)) + teron->AI()->JustSummoned(me); - if (Creature* teron = _instance->GetCreature(DATA_TERON_GOREFIEND)) - teron->AI()->JustSummoned(me); + SelectNewTarget(); + } - SelectNewTarget(); - } + void UpdateAI(uint32 diff) override + { + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - void UpdateAI(uint32 diff) override + _scheduler.Update(diff, [this] { - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - _scheduler.Update(diff, [this] - { - DoMeleeAttackIfReady(); - }); - } + DoMeleeAttackIfReady(); + }); + } - void SelectNewTarget() + void SelectNewTarget() + { + if (Creature* teron = _instance->GetCreature(DATA_TERON_GOREFIEND)) { - if (Creature* teron = _instance->GetCreature(DATA_TERON_GOREFIEND)) + Unit* target = teron->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true, true, -SPELL_SPIRITUAL_VENGEANCE); + // He should target Vengeful Spirits only if has no other player available + if (!target) + target = teron->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0); + + if (target) { - Unit* target = teron->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true, true, -SPELL_SPIRITUAL_VENGEANCE); - // He should target Vengeful Spirits only if has no other player available - if (!target) - target = teron->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0); - - if (target) - { - ResetThreatList(); - AttackStart(target); - AddThreat(target, 1000000.0f); - targetGUID = target->GetGUID(); - } + ResetThreatList(); + AttackStart(target); + AddThreat(target, 1000000.0f); + targetGUID = target->GetGUID(); } } - - private: - TaskScheduler _scheduler; - InstanceScript* _instance; - ObjectGuid targetGUID; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_shadowy_constructAI>(creature); } -}; - -class at_teron_gorefiend_entrance : public AreaTriggerScript -{ -public: - at_teron_gorefiend_entrance() : AreaTriggerScript("at_teron_gorefiend_entrance") { } - - bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override - { - if (InstanceScript* instance = player->GetInstanceScript()) - if (Creature* teron = instance->GetCreature(DATA_TERON_GOREFIEND)) - teron->AI()->DoAction(ACTION_START_INTRO); - return true; - } +private: + TaskScheduler _scheduler; + InstanceScript* _instance; + ObjectGuid targetGUID; }; // 40251 - Shadow of Death -class spell_teron_gorefiend_shadow_of_death : public SpellScriptLoader +class spell_teron_gorefiend_shadow_of_death : public AuraScript { - public: - spell_teron_gorefiend_shadow_of_death() : SpellScriptLoader("spell_teron_gorefiend_shadow_of_death") { } + PrepareAuraScript(spell_teron_gorefiend_shadow_of_death); - class spell_teron_gorefiend_shadow_of_death_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_teron_gorefiend_shadow_of_death_AuraScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo( - { - SPELL_SUMMON_SPIRIT, - SPELL_POSSESS_SPIRIT_IMMUNE, - SPELL_SPIRITUAL_VENGEANCE, - SPELL_SUMMON_SKELETRON_1, - SPELL_SUMMON_SKELETRON_2, - SPELL_SUMMON_SKELETRON_3, - SPELL_SUMMON_SKELETRON_4 - }); - } - - void Absorb(AuraEffect* /*aurEff*/, DamageInfo& /*dmgInfo*/, uint32& /*absorbAmount*/) - { - PreventDefaultAction(); - } - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) - { - Unit* target = GetTarget(); - target->CastSpell(target, SPELL_SUMMON_SPIRIT, true); + SPELL_SUMMON_SPIRIT, + SPELL_POSSESS_SPIRIT_IMMUNE, + SPELL_SPIRITUAL_VENGEANCE, + SPELL_SUMMON_SKELETRON_1, + SPELL_SUMMON_SKELETRON_2, + SPELL_SUMMON_SKELETRON_3, + SPELL_SUMMON_SKELETRON_4 + }); + } - for (uint8 i = 0; i < 4; ++i) - target->CastSpell(target, SkeletronSpells[i], true); + void Absorb(AuraEffect* /*aurEff*/, DamageInfo& /*dmgInfo*/, uint32& /*absorbAmount*/) + { + PreventDefaultAction(); + } - target->CastSpell(target, SPELL_POSSESS_SPIRIT_IMMUNE, true); - target->CastSpell(target, SPELL_SPIRITUAL_VENGEANCE, true); - } - } + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + { + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_SUMMON_SPIRIT, true); - void Register() override - { - OnEffectAbsorb += AuraEffectAbsorbFn(spell_teron_gorefiend_shadow_of_death_AuraScript::Absorb, EFFECT_0); - AfterEffectRemove += AuraEffectRemoveFn(spell_teron_gorefiend_shadow_of_death_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_OVERRIDE_CLASS_SCRIPTS, AURA_EFFECT_HANDLE_REAL); - } - }; + for (uint8 i = 0; i < 4; ++i) + target->CastSpell(target, SkeletronSpells[i], true); - AuraScript* GetAuraScript() const override - { - return new spell_teron_gorefiend_shadow_of_death_AuraScript(); + target->CastSpell(target, SPELL_POSSESS_SPIRIT_IMMUNE, true); + target->CastSpell(target, SPELL_SPIRITUAL_VENGEANCE, true); } + } + + void Register() override + { + OnEffectAbsorb += AuraEffectAbsorbFn(spell_teron_gorefiend_shadow_of_death::Absorb, EFFECT_0); + AfterEffectRemove += AuraEffectRemoveFn(spell_teron_gorefiend_shadow_of_death::OnRemove, EFFECT_1, SPELL_AURA_OVERRIDE_CLASS_SCRIPTS, AURA_EFFECT_HANDLE_REAL); + } }; // 40268 - Spiritual Vengeance -class spell_teron_gorefiend_spiritual_vengeance : public SpellScriptLoader +class spell_teron_gorefiend_spiritual_vengeance : public AuraScript { - public: - spell_teron_gorefiend_spiritual_vengeance() : SpellScriptLoader("spell_teron_gorefiend_spiritual_vengeance") { } + PrepareAuraScript(spell_teron_gorefiend_spiritual_vengeance); - class spell_teron_gorefiend_spiritual_vengeance_AuraScript : public AuraScript - { - PrepareAuraScript(spell_teron_gorefiend_spiritual_vengeance_AuraScript); - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - GetTarget()->KillSelf(); - } - - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_teron_gorefiend_spiritual_vengeance_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_POSSESS, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_teron_gorefiend_spiritual_vengeance_AuraScript::OnRemove, EFFECT_2, SPELL_AURA_MOD_PACIFY_SILENCE, AURA_EFFECT_HANDLE_REAL); - } - }; + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->KillSelf(); + } - AuraScript* GetAuraScript() const override - { - return new spell_teron_gorefiend_spiritual_vengeance_AuraScript(); - } + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_teron_gorefiend_spiritual_vengeance::OnRemove, EFFECT_0, SPELL_AURA_MOD_POSSESS, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_teron_gorefiend_spiritual_vengeance::OnRemove, EFFECT_2, SPELL_AURA_MOD_PACIFY_SILENCE, AURA_EFFECT_HANDLE_REAL); + } }; // 41999 - Shadow of Death Remove -class spell_teron_gorefiend_shadow_of_death_remove : public SpellScriptLoader +class spell_teron_gorefiend_shadow_of_death_remove : public SpellScript { - public: - spell_teron_gorefiend_shadow_of_death_remove() : SpellScriptLoader("spell_teron_gorefiend_shadow_of_death_remove") { } + PrepareSpellScript(spell_teron_gorefiend_shadow_of_death_remove); - class spell_teron_gorefiend_shadow_of_death_remove_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_teron_gorefiend_shadow_of_death_remove_SpellScript); + SPELL_SHADOW_OF_DEATH, + SPELL_POSSESS_SPIRIT_IMMUNE, + SPELL_SPIRITUAL_VENGEANCE + }); + } - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo( - { - SPELL_SHADOW_OF_DEATH, - SPELL_POSSESS_SPIRIT_IMMUNE, - SPELL_SPIRITUAL_VENGEANCE - }); - } + void RemoveAuras() + { + Unit* target = GetHitUnit(); - void RemoveAuras() - { - Unit* target = GetHitUnit(); + target->RemoveAurasDueToSpell(SPELL_POSSESS_SPIRIT_IMMUNE); + target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_VENGEANCE); + target->RemoveAurasDueToSpell(SPELL_SHADOW_OF_DEATH); + } - target->RemoveAurasDueToSpell(SPELL_POSSESS_SPIRIT_IMMUNE); - target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_VENGEANCE); - target->RemoveAurasDueToSpell(SPELL_SHADOW_OF_DEATH); - } + void Register() override + { + OnHit += SpellHitFn(spell_teron_gorefiend_shadow_of_death_remove::RemoveAuras); + } +}; - void Register() override - { - OnHit += SpellHitFn(spell_teron_gorefiend_shadow_of_death_remove_SpellScript::RemoveAuras); - } +class at_teron_gorefiend_entrance : public OnlyOnceAreaTriggerScript +{ +public: + at_teron_gorefiend_entrance() : OnlyOnceAreaTriggerScript("at_teron_gorefiend_entrance") { } - }; + bool _OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override + { + if (InstanceScript* instance = player->GetInstanceScript()) + if (Creature* teron = instance->GetCreature(DATA_TERON_GOREFIEND)) + teron->AI()->DoAction(ACTION_START_INTRO); - SpellScript* GetSpellScript() const override - { - return new spell_teron_gorefiend_shadow_of_death_remove_SpellScript(); - } + return true; + } }; void AddSC_boss_teron_gorefiend() { - new boss_teron_gorefiend(); - new npc_doom_blossom(); - new npc_shadowy_construct(); + RegisterBlackTempleCreatureAI(boss_teron_gorefiend); + RegisterBlackTempleCreatureAI(npc_doom_blossom); + RegisterBlackTempleCreatureAI(npc_shadowy_construct); + RegisterAuraScript(spell_teron_gorefiend_shadow_of_death); + RegisterAuraScript(spell_teron_gorefiend_spiritual_vengeance); + RegisterSpellScript(spell_teron_gorefiend_shadow_of_death_remove); new at_teron_gorefiend_entrance(); - new spell_teron_gorefiend_shadow_of_death(); - new spell_teron_gorefiend_spiritual_vengeance(); - new spell_teron_gorefiend_shadow_of_death_remove(); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp b/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp index b49ad6e3f20..4a35acc20db 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp @@ -28,220 +28,207 @@ enum Texts { - SAY_AGGRO = 0, - SAY_NEEDLE = 1, - SAY_SLAY = 2, - SAY_SPECIAL = 3, - SAY_ENRAGE = 4, - SAY_DEATH = 5 + SAY_AGGRO = 0, + SAY_NEEDLE = 1, + SAY_SLAY = 2, + SAY_SPECIAL = 3, + SAY_ENRAGE = 4, + SAY_DEATH = 5 }; enum Spells { - SPELL_NEEDLE_SPINE_TARGETING = 39992, - SPELL_NEEDLE_SPINE = 39835, - SPELL_TIDAL_BURST = 39878, - SPELL_TIDAL_SHIELD = 39872, - SPELL_IMPALING_SPINE = 39837, - SPELL_CREATE_NAJENTUS_SPINE = 39956, - SPELL_HURL_SPINE = 39948, - SPELL_BERSERK = 26662 - + SPELL_NEEDLE_SPINE_TARGETING = 39992, + SPELL_NEEDLE_SPINE = 39835, + SPELL_TIDAL_BURST = 39878, + SPELL_TIDAL_SHIELD = 39872, + SPELL_IMPALING_SPINE = 39837, + SPELL_CREATE_NAJENTUS_SPINE = 39956, + SPELL_HURL_SPINE = 39948, + SPELL_BERSERK = 26662 }; enum Events { - EVENT_BERSERK = 1, - EVENT_YELL = 2, - EVENT_NEEDLE = 3, - EVENT_SPINE = 4, - EVENT_SHIELD = 5 + EVENT_BERSERK = 1, + EVENT_YELL = 2, + EVENT_NEEDLE = 3, + EVENT_SPINE = 4, + EVENT_SHIELD = 5 }; -class boss_najentus : public CreatureScript +enum Misc { -public: - boss_najentus() : CreatureScript("boss_najentus") { } - - struct boss_najentusAI : public BossAI - { - boss_najentusAI(Creature* creature) : BossAI(creature, DATA_HIGH_WARLORD_NAJENTUS) { } - - void Reset() override - { - _Reset(); - SpineTargetGUID.Clear(); - } - - void EnterEvadeMode(EvadeReason /*why*/) override - { - _DespawnAtEvade(); - } + DATA_REMOVE_IMPALING_SPINE = 1, + ACTION_RESET_IMPALING_TARGET = 2 +}; - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } +struct boss_najentus : public BossAI +{ + boss_najentus(Creature* creature) : BossAI(creature, DATA_HIGH_WARLORD_NAJENTUS) { } - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - Talk(SAY_DEATH); - } + void Reset() override + { + _Reset(); + _spineTargetGUID.Clear(); + } - void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override - { - if (spell->Id == SPELL_HURL_SPINE && me->HasAura(SPELL_TIDAL_SHIELD)) - { - me->RemoveAurasDueToSpell(SPELL_TIDAL_SHIELD); - DoCastSelf(SPELL_TIDAL_BURST, true); - events.RescheduleEvent(EVENT_SPINE, Seconds(2)); - } - } + void EnterEvadeMode(EvadeReason /*why*/) override + { + _EnterEvadeMode(); + _DespawnAtEvade(); + } - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - Talk(SAY_AGGRO); - events.ScheduleEvent(EVENT_NEEDLE, Seconds(2)); - events.ScheduleEvent(EVENT_SHIELD, Seconds(60)); - events.ScheduleEvent(EVENT_SPINE, Seconds(30)); - events.ScheduleEvent(EVENT_BERSERK, Seconds(480)); - events.ScheduleEvent(EVENT_YELL, Seconds(45), Seconds(100)); - } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - bool RemoveImpalingSpine() - { - if (!SpineTargetGUID) - return false; - - Unit* target = ObjectAccessor::GetUnit(*me, SpineTargetGUID); - if (target && target->HasAura(SPELL_IMPALING_SPINE)) - target->RemoveAurasDueToSpell(SPELL_IMPALING_SPINE); - SpineTargetGUID.Clear(); - return true; - } + void JustDied(Unit* /*killer*/) override + { + _JustDied(); + Talk(SAY_DEATH); + } - void ExecuteEvent(uint32 eventId) override + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id == SPELL_HURL_SPINE && me->HasAura(SPELL_TIDAL_SHIELD)) { - switch (eventId) - { - case EVENT_SHIELD: - DoCastSelf(SPELL_TIDAL_SHIELD, true); - events.RescheduleEvent(EVENT_SPINE, Seconds(50)); - events.Repeat(Seconds(55), Seconds(60)); - break; - case EVENT_BERSERK: - Talk(SAY_ENRAGE); - DoCastSelf(SPELL_BERSERK, true); - break; - case EVENT_SPINE: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 200.0f, true)) - { - DoCast(target, SPELL_IMPALING_SPINE, true); - SpineTargetGUID = target->GetGUID(); - //must let target summon, otherwise you cannot click the spine - target->SummonGameObject(GO_NAJENTUS_SPINE, *target, QuaternionData(), 30); - Talk(SAY_NEEDLE); - } - events.Repeat(Seconds(20), Seconds(25)); - break; - case EVENT_NEEDLE: - DoCastSelf(SPELL_NEEDLE_SPINE_TARGETING, true); - events.Repeat(Seconds(2)); - break; - case EVENT_YELL: - Talk(SAY_SPECIAL); - events.Repeat(Seconds(25), Seconds(100)); - break; - default: - break; - } + me->RemoveAurasDueToSpell(SPELL_TIDAL_SHIELD); + DoCastSelf(SPELL_TIDAL_BURST, true); + events.RescheduleEvent(EVENT_SPINE, Seconds(2)); } + } - private: - ObjectGuid SpineTargetGUID; - }; + void EnterCombat(Unit* /*who*/) override + { + _EnterCombat(); + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_NEEDLE, Seconds(2)); + events.ScheduleEvent(EVENT_SHIELD, Seconds(60)); + events.ScheduleEvent(EVENT_SPINE, Seconds(30)); + events.ScheduleEvent(EVENT_BERSERK, Seconds(480)); + events.ScheduleEvent(EVENT_YELL, Seconds(45), Seconds(100)); + } - CreatureAI* GetAI(Creature* creature) const override + uint32 GetData(uint32 data) const override { - return GetBlackTempleAI<boss_najentusAI>(creature); + if (data == DATA_REMOVE_IMPALING_SPINE) + return RemoveImpalingSpine() ? 1 : 0; + return 0; } -}; -class go_najentus_spine : public GameObjectScript -{ -public: - go_najentus_spine() : GameObjectScript("go_najentus_spine") { } + void DoAction(int32 actionId) override + { + if (actionId == ACTION_RESET_IMPALING_TARGET) + _spineTargetGUID.Clear(); + } - struct go_najentus_spineAI : public GameObjectAI + bool RemoveImpalingSpine() const { - go_najentus_spineAI(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { } + if (!_spineTargetGUID) + return false; - InstanceScript* instance; + Unit* target = ObjectAccessor::GetUnit(*me, _spineTargetGUID); + if (target && target->HasAura(SPELL_IMPALING_SPINE)) + target->RemoveAurasDueToSpell(SPELL_IMPALING_SPINE); + return true; + } - bool GossipHello(Player* player) override + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) { - if (Creature* najentus = instance->GetCreature(DATA_HIGH_WARLORD_NAJENTUS)) - { - if (ENSURE_AI(boss_najentus::boss_najentusAI, najentus->AI())->RemoveImpalingSpine()) + case EVENT_SHIELD: + DoCastSelf(SPELL_TIDAL_SHIELD, true); + events.RescheduleEvent(EVENT_SPINE, Seconds(50)); + events.Repeat(Seconds(55), Seconds(60)); + break; + case EVENT_BERSERK: + Talk(SAY_ENRAGE); + DoCastSelf(SPELL_BERSERK, true); + break; + case EVENT_SPINE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 200.0f, true)) { - me->CastSpell(player, SPELL_CREATE_NAJENTUS_SPINE, true); - me->Delete(); + DoCast(target, SPELL_IMPALING_SPINE, true); + _spineTargetGUID = target->GetGUID(); + //must let target summon, otherwise you cannot click the spine + target->SummonGameObject(GO_NAJENTUS_SPINE, *target, QuaternionData(), 30); + Talk(SAY_NEEDLE); } - } - return true; + events.Repeat(Seconds(20), Seconds(25)); + break; + case EVENT_NEEDLE: + DoCastSelf(SPELL_NEEDLE_SPINE_TARGETING, true); + events.Repeat(Seconds(2)); + break; + case EVENT_YELL: + Talk(SAY_SPECIAL); + events.Repeat(Seconds(25), Seconds(100)); + break; + default: + break; } - }; - - GameObjectAI* GetAI(GameObject* go) const override - { - return GetBlackTempleAI<go_najentus_spineAI>(go); } + +private: + ObjectGuid _spineTargetGUID; }; -// 39992 - Needle Spine Targeting -class spell_najentus_needle_spine : public SpellScriptLoader +struct go_najentus_spine : public GameObjectAI { - public: - spell_najentus_needle_spine() : SpellScriptLoader("spell_najentus_needle_spine") { } + go_najentus_spine(GameObject* go) : GameObjectAI(go), _instance(go->GetInstanceScript()) { } - class spell_najentus_needle_spine_SpellScript : public SpellScript - { - PrepareSpellScript(spell_najentus_needle_spine_SpellScript); + bool GossipHello(Player* player) override + { + if (!_instance) + return false; - bool Validate(SpellInfo const* /*spellInfo*/) override + if (Creature* najentus = _instance->GetCreature(DATA_HIGH_WARLORD_NAJENTUS)) + if (najentus->AI()->GetData(DATA_REMOVE_IMPALING_SPINE)) { - return ValidateSpellInfo({ SPELL_NEEDLE_SPINE }); + najentus->AI()->DoAction(ACTION_RESET_IMPALING_TARGET); + me->CastSpell(player, SPELL_CREATE_NAJENTUS_SPINE, true); + me->Delete(); } + return true; + } +private: + InstanceScript* _instance; +}; - void FilterTargets(std::list<WorldObject*>& targets) - { - targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_IMPALING_SPINE)); - } +// 39992 - Needle Spine Targeting +class spell_najentus_needle_spine : public SpellScript +{ + PrepareSpellScript(spell_najentus_needle_spine); - void HandleScript(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell(GetHitUnit(), SPELL_NEEDLE_SPINE, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_NEEDLE_SPINE }); + } - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_najentus_needle_spine_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); - OnEffectHitTarget += SpellEffectFn(spell_najentus_needle_spine_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_IMPALING_SPINE)); + } - SpellScript* GetSpellScript() const override - { - return new spell_najentus_needle_spine_SpellScript(); - } + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), SPELL_NEEDLE_SPINE, true); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_najentus_needle_spine::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_najentus_needle_spine::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; void AddSC_boss_najentus() { - new boss_najentus(); - new go_najentus_spine(); - new spell_najentus_needle_spine(); + RegisterBlackTempleCreatureAI(boss_najentus); + RegisterGameObjectAI(go_najentus_spine); + RegisterSpellScript(spell_najentus_needle_spine); } diff --git a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp index f679428e44e..3a4132ac8c7 100644 --- a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp +++ b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp @@ -101,7 +101,9 @@ class instance_black_temple : public InstanceMapScript LoadDoorData(doorData); LoadObjectData(creatureData, gameObjectData); LoadBossBoundaries(boundaries); - akamaState = AKAMA_INTRO; + AkamaState = AKAMA_INTRO; + TeronGorefiendIntro = 1; + AkamaIllidanIntro = 1; } void OnGameObjectCreate(GameObject* go) override @@ -126,7 +128,7 @@ class instance_black_temple : public InstanceMapScript case NPC_ASHTONGUE_STORMCALLER: case NPC_ASHTONGUE_FERAL_SPIRIT: case NPC_STORM_FURY: - AshtongueGUIDs.emplace_back(creature->GetGUID()); + AshtongueGUIDs.push_back(creature->GetGUID()); if (GetBossState(DATA_SHADE_OF_AKAMA) == DONE) creature->SetFaction(FACTION_ASHTONGUE_DEATHSWORN); break; @@ -135,25 +137,37 @@ class instance_black_temple : public InstanceMapScript } } - uint32 GetData(uint32 data) const override + uint32 GetData(uint32 type) const override { - if (data == DATA_AKAMA) - return akamaState; - - return 0; + switch (type) + { + case DATA_AKAMA: + return AkamaState; + case DATA_TERON_GOREFIEND_INTRO: + return TeronGorefiendIntro; + case DATA_AKAMA_ILLIDAN_INTRO: + return AkamaIllidanIntro; + default: + return 0; + } } - void SetData(uint32 data, uint32 value) override + void SetData(uint32 type, uint32 data) override { - switch (data) + switch (type) { case DATA_AKAMA: - akamaState = value; + AkamaState = data; break; case ACTION_OPEN_DOOR: if (GameObject* illidanGate = GetGameObject(DATA_GO_ILLIDAN_GATE)) HandleGameObject(ObjectGuid::Empty, true, illidanGate); break; + case DATA_TERON_GOREFIEND_INTRO: + TeronGorefiendIntro = data; + break; + case DATA_AKAMA_ILLIDAN_INTRO: + AkamaIllidanIntro = data; default: break; } @@ -211,7 +225,9 @@ class instance_black_temple : public InstanceMapScript protected: GuidVector AshtongueGUIDs; - uint8 akamaState; + uint8 AkamaState; + uint8 TeronGorefiendIntro; + uint8 AkamaIllidanIntro; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp index 29185f77185..2b0305f374a 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp @@ -332,9 +332,7 @@ public: if (instance->GetGuidData(DATA_LEOTHERAS_EVENT_STARTER)) { - Unit* victim = nullptr; - victim = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_LEOTHERAS_EVENT_STARTER)); - if (victim) + if (Unit* victim = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_LEOTHERAS_EVENT_STARTER))) AddThreat(victim, 1); StartEvent(); } @@ -586,9 +584,7 @@ public: //at this point he divides himself in two parts CastConsumingMadness(); DespawnDemon(); - Creature* Copy = nullptr; - Copy = DoSpawnCreature(DEMON_FORM, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 6000); - if (Copy) + if (Creature* Copy = DoSpawnCreature(DEMON_FORM, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 6000)) { Demon = Copy->GetGUID(); if (me->GetVictim()) @@ -738,7 +734,7 @@ public: instance->SetGuidData(DATA_LEOTHERAS_EVENT_STARTER, who->GetGUID()); } - void JustRespawned() override + void JustAppeared() override { AddedBanish = false; Reset(); @@ -764,9 +760,7 @@ public: if (!me->IsInCombat() && instance->GetGuidData(DATA_LEOTHERAS_EVENT_STARTER)) { - Unit* victim = nullptr; - victim = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_LEOTHERAS_EVENT_STARTER)); - if (victim) + if (Unit* victim = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_LEOTHERAS_EVENT_STARTER))) AttackStart(victim); } @@ -784,10 +778,8 @@ public: if (Mindblast_Timer <= diff) { - Unit* target = nullptr; - target = SelectTarget(SELECT_TARGET_RANDOM, 0); - - if (target)DoCast(target, SPELL_MINDBLAST); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_MINDBLAST); Mindblast_Timer = urand(10000, 15000); } else Mindblast_Timer -= diff; diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp index 4b694ab5861..0a26f129bf7 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/instance_serpent_shrine.cpp @@ -148,7 +148,7 @@ class instance_serpent_shrine : public InstanceMapScript player->CastSpell(player, SPELL_SCALDINGWATER, true); } - else if (Water == WATERSTATE_FRENZY) + else { //spawn frenzy if (DoSpawnFrenzy) diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp index c5bb049d443..b7d7c10f6c7 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp @@ -308,7 +308,7 @@ public: void Initialize() { me->SetReactState(REACT_PASSIVE); - me->setRegeneratingHealth(false); + me->SetRegenerateHealth(false); DoCast(me, SPELL_FROZEN_CORE_GETS_HIT); DoCast(me, SPELL_ICE_SPEAR_AURA); } diff --git a/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp b/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp index f6ad97f6328..ac6c9e62072 100644 --- a/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp +++ b/src/server/scripts/Outland/GruulsLair/boss_gruul.cpp @@ -285,7 +285,7 @@ class spell_gruul_shatter : public SpellScriptLoader if (Unit* target = GetHitUnit()) { target->RemoveAurasDueToSpell(SPELL_STONED); - target->CastSpell((Unit*)nullptr, SPELL_SHATTER_EFFECT, true); + target->CastSpell(nullptr, SPELL_SHATTER_EFFECT, true); } } diff --git a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp index 57423f63ab0..2b43de82bb5 100644 --- a/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp +++ b/src/server/scripts/Outland/GruulsLair/boss_high_king_maulgar.cpp @@ -181,9 +181,7 @@ public: //Charging_Timer if (Charging_Timer <= diff) { - Unit* target = nullptr; - target = SelectTarget(SELECT_TARGET_RANDOM, 0); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) { AttackStart(target); DoCast(target, SPELL_BERSERKER_C); @@ -293,9 +291,7 @@ public: //DeathCoil Timer /need correct timer if (DeathCoil_Timer <= diff) { - Unit* target = nullptr; - target = SelectTarget(SELECT_TARGET_RANDOM, 0); - if (target) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) DoCast(target, SPELL_DEATH_COIL); DeathCoil_Timer = 20000; } else DeathCoil_Timer -= diff; diff --git a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp index 354e4b0fce3..f0cf459acfe 100644 --- a/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/MagtheridonsLair/boss_magtheridon.cpp @@ -477,9 +477,9 @@ public: return true; if (Creature* trigger = player->FindNearestCreature(NPC_HELFIRE_RAID_TRIGGER, 10.0f)) - trigger->CastSpell((Unit*)nullptr, SPELL_SHADOW_GRASP_VISUAL); + trigger->CastSpell(nullptr, SPELL_SHADOW_GRASP_VISUAL); - player->CastSpell((Unit*)nullptr, SPELL_SHADOW_GRASP, true); + player->CastSpell(nullptr, SPELL_SHADOW_GRASP, true); return true; } }; diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp index 9b683770be0..6d65ceae758 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp @@ -436,9 +436,7 @@ class boss_alar : public CreatureScript } else { - Unit* target = nullptr; - target = me->SelectNearestTargetInAttackDistance(5); - if (target) + if (Unit* target = me->SelectNearestTargetInAttackDistance(5)) AttackStart(target); else { diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index d3aa6b7e1fa..3323c53d546 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -1124,8 +1124,7 @@ class boss_grand_astromancer_capernian : public CreatureScript //Conflagration_Timer if (Conflagration_Timer <= diff) { - Unit* target = nullptr; - target = SelectTarget(SELECT_TARGET_RANDOM, 0); + Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0); if (target && me->IsWithinDistInMap(target, 30)) DoCast(target, SPELL_CONFLAGRATION); diff --git a/src/server/scripts/Outland/boss_doomlord_kazzak.cpp b/src/server/scripts/Outland/boss_doomlord_kazzak.cpp index 93ea132a545..240ec55c215 100644 --- a/src/server/scripts/Outland/boss_doomlord_kazzak.cpp +++ b/src/server/scripts/Outland/boss_doomlord_kazzak.cpp @@ -83,7 +83,7 @@ class boss_doomlord_kazzak : public CreatureScript _events.ScheduleEvent(EVENT_BERSERK, 180000); } - void JustRespawned() override + void JustAppeared() override { Talk(SAY_INTRO); } diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index 0660d781e57..c56208d77af 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -150,9 +150,9 @@ class npc_ancestral_wolf : public CreatureScript public: npc_ancestral_wolf() : CreatureScript("npc_ancestral_wolf") { } - struct npc_ancestral_wolfAI : public npc_escortAI + struct npc_ancestral_wolfAI : public EscortAI { - npc_ancestral_wolfAI(Creature* creature) : npc_escortAI(creature) + npc_ancestral_wolfAI(Creature* creature) : EscortAI(creature) { if (creature->GetOwner() && creature->GetOwner()->GetTypeId() == TYPEID_PLAYER) Start(false, false, creature->GetOwner()->GetGUID()); @@ -170,11 +170,11 @@ public: // Override Evade Mode event, recast buff that was removed by standard handler void EnterEvadeMode(EvadeReason why) override { - npc_escortAI::EnterEvadeMode(why); + EscortAI::EnterEvadeMode(why); DoCast(me, SPELL_ANCESTRAL_WOLF_BUFF, true); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -258,9 +258,9 @@ class npc_wounded_blood_elf : public CreatureScript public: npc_wounded_blood_elf() : CreatureScript("npc_wounded_blood_elf") { } - struct npc_wounded_blood_elfAI : public npc_escortAI + struct npc_wounded_blood_elfAI : public EscortAI { - npc_wounded_blood_elfAI(Creature* creature) : npc_escortAI(creature) { } + npc_wounded_blood_elfAI(Creature* creature) : EscortAI(creature) { } void Reset() override { } @@ -280,11 +280,11 @@ public: if (quest->GetQuestId() == QUEST_ROAD_TO_FALCON_WATCH) { me->SetFaction(FACTION_ESCORTEE_H_PASSIVE); - npc_escortAI::Start(true, false, player->GetGUID()); + EscortAI::Start(true, false, player->GetGUID()); } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) diff --git a/src/server/scripts/Outland/zone_nagrand.cpp b/src/server/scripts/Outland/zone_nagrand.cpp index ce71c80e5f0..d19e77a6d0f 100644 --- a/src/server/scripts/Outland/zone_nagrand.cpp +++ b/src/server/scripts/Outland/zone_nagrand.cpp @@ -73,9 +73,9 @@ class npc_maghar_captive : public CreatureScript public: npc_maghar_captive() : CreatureScript("npc_maghar_captive") { } - struct npc_maghar_captiveAI : public npc_escortAI + struct npc_maghar_captiveAI : public EscortAI { - npc_maghar_captiveAI(Creature* creature) : npc_escortAI(creature) { Reset(); } + npc_maghar_captiveAI(Creature* creature) : EscortAI(creature) { Reset(); } uint32 ChainLightningTimer; uint32 HealTimer; @@ -105,7 +105,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -157,7 +157,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!UpdateVictim()) return; @@ -434,9 +434,9 @@ class npc_kurenai_captive : public CreatureScript public: npc_kurenai_captive() : CreatureScript("npc_kurenai_captive") { } - struct npc_kurenai_captiveAI : public npc_escortAI + struct npc_kurenai_captiveAI : public EscortAI { - npc_kurenai_captiveAI(Creature* creature) : npc_escortAI(creature) + npc_kurenai_captiveAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -474,7 +474,7 @@ public: } } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { switch (waypointId) { @@ -538,7 +538,7 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); if (!UpdateVictim()) return; diff --git a/src/server/scripts/Outland/zone_netherstorm.cpp b/src/server/scripts/Outland/zone_netherstorm.cpp index 6fcf5c37004..8104282117c 100644 --- a/src/server/scripts/Outland/zone_netherstorm.cpp +++ b/src/server/scripts/Outland/zone_netherstorm.cpp @@ -488,9 +488,9 @@ class npc_bessy : public CreatureScript public: npc_bessy() : CreatureScript("npc_bessy") { } - struct npc_bessyAI : public npc_escortAI + struct npc_bessyAI : public EscortAI { - npc_bessyAI(Creature* creature) : npc_escortAI(creature) { } + npc_bessyAI(Creature* creature) : EscortAI(creature) { } void JustDied(Unit* /*killer*/) override { @@ -498,7 +498,7 @@ public: player->FailQuest(Q_ALMABTRIEB); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -569,9 +569,9 @@ class npc_maxx_a_million_escort : public CreatureScript public: npc_maxx_a_million_escort() : CreatureScript("npc_maxx_a_million_escort") { } - struct npc_maxx_a_million_escortAI : public npc_escortAI + struct npc_maxx_a_million_escortAI : public EscortAI { - npc_maxx_a_million_escortAI(Creature* creature) : npc_escortAI(creature) + npc_maxx_a_million_escortAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -590,7 +590,7 @@ public: Initialize(); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -624,7 +624,7 @@ public: void UpdateAI(uint32 uiDiff) override { - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); if (bTake) { @@ -668,7 +668,11 @@ public: enum CaptainTyralius { NPC_CAPTAIN_TYRALIUS = 20787, + NPC_ETHEREUM_PRISONER = 20825, + SPELL_TELEPORT_VISUAL = 51347, SAY_FREE = 0, + ACTION_FREED = 0, + EVENT_TELEPORT = 1 }; class go_captain_tyralius_prison : public GameObjectScript @@ -680,15 +684,26 @@ class go_captain_tyralius_prison : public GameObjectScript { go_captain_tyralius_prisonAI(GameObject* go) : GameObjectAI(go) { } + void Reset() override + { + me->SummonCreature(NPC_CAPTAIN_TYRALIUS, me->GetPosition(), TEMPSUMMON_MANUAL_DESPAWN); + me->SummonCreature(NPC_ETHEREUM_PRISONER, me->GetPosition(), TEMPSUMMON_MANUAL_DESPAWN); + } + bool GossipHello(Player* player) override { - me->UseDoorOrButton(); + me->SetRespawnTime(60); + me->SetLootState(GO_JUST_DEACTIVATED); + if (Creature* tyralius = me->FindNearestCreature(NPC_CAPTAIN_TYRALIUS, 1.0f)) { player->KilledMonsterCredit(NPC_CAPTAIN_TYRALIUS); - tyralius->AI()->Talk(SAY_FREE); - tyralius->DespawnOrUnsummon(8000); + tyralius->AI()->DoAction(ACTION_FREED); } + + if (Creature* prisoner = me->FindNearestCreature(NPC_ETHEREUM_PRISONER, 1.0f)) + prisoner->DespawnOrUnsummon(0); + return true; } }; @@ -699,6 +714,47 @@ class go_captain_tyralius_prison : public GameObjectScript } }; +class npc_captain_tyralius : public CreatureScript +{ +public: + npc_captain_tyralius() : CreatureScript("npc_captain_tyralius") { } + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_captain_tyraliusAI(creature); + } + + struct npc_captain_tyraliusAI : public ScriptedAI + { + npc_captain_tyraliusAI(Creature* creature) : ScriptedAI(creature) { } + + void DoAction(int32 /*action*/) override + { + Talk(SAY_FREE); + _events.ScheduleEvent(EVENT_TELEPORT, Seconds(5)); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + if (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_TELEPORT: + DoCastSelf(SPELL_TELEPORT_VISUAL); + me->DespawnOrUnsummon(Seconds(2)); + break; + } + } + } + + private: + EventMap _events; + }; +}; + void AddSC_netherstorm() { new npc_commander_dawnforge(); @@ -707,4 +763,5 @@ void AddSC_netherstorm() new npc_bessy(); new npc_maxx_a_million_escort(); new go_captain_tyralius_prison(); + new npc_captain_tyralius(); } diff --git a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp index 65eaefa2d7b..9ee82504a49 100644 --- a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp +++ b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp @@ -592,9 +592,9 @@ class npc_earthmender_wilda : public CreatureScript public: npc_earthmender_wilda() : CreatureScript("npc_earthmender_wilda") { } - struct npc_earthmender_wildaAI : public npc_escortAI + struct npc_earthmender_wildaAI : public EscortAI { - npc_earthmender_wildaAI(Creature* creature) : npc_escortAI(creature) + npc_earthmender_wildaAI(Creature* creature) : EscortAI(creature) { Initialize(); } @@ -611,7 +611,7 @@ public: Initialize(); } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -704,7 +704,7 @@ public: void UpdateAI(uint32 uiDiff) override { - npc_escortAI::UpdateAI(uiDiff); + EscortAI::UpdateAI(uiDiff); if (!UpdateVictim()) return; @@ -1376,7 +1376,7 @@ public: enum Enraged_Dpirits { -// QUESTS + // QUESTS QUEST_ENRAGED_SPIRITS_FIRE_EARTH = 10458, QUEST_ENRAGED_SPIRITS_AIR = 10481, QUEST_ENRAGED_SPIRITS_WATER = 10480, @@ -1391,6 +1391,22 @@ enum Enraged_Dpirits NPC_ENRAGED_AIR_SPIRIT = 21060, NPC_ENRAGED_WATER_SPIRIT = 21059, + // ENRAGED WATER SPIRIT SPELLS + SPELL_STORMBOLT = 38032, + + // ENRAGED AIR SPIRIT SPELLS + SPELL_AIR_SPIRIT_CHAIN_LIGHTNING = 12058, + SPELL_HURRICANE = 32717, + SPELL_ENRAGE = 8599, + + // ENRAGED FIRE SPIRIT SPELLS - Will be using the enrage spell from Air Spirit + SPELL_FEL_FIREBALL = 36247, + SPELL_FEL_FIRE_AURA = 36006, // Earth spirit uses this one + + // ENRAGED EARTH SPIRIT SPELLS + SPELL_FIERY_BOULDER = 38498, + SPELL_SUMMON_ENRAGED_EARTH_SHARD = 38365, + // SOULS NPC_EARTHEN_SOUL = 21073, NPC_FIERY_SOUL = 21097, @@ -1413,6 +1429,15 @@ enum Enraged_Dpirits SPELL_SOUL_CAPTURED = 36115 }; +enum Enraged_Spirits_Events +{ + EVENT_ENRAGED_WATER_SPIRIT = 1, + EVENT_ENRAGED_FIRE_SPIRIT = 2, + EVENT_ENRAGED_EARTH_SPIRIT = 3, + EVENT_ENRAGED_AIR_SPIRIT_CHAIN_LIGHTNING = 4, + EVENT_ENRAGED_AIR_SPIRIT_HURRICANE = 5 +}; + class npc_enraged_spirit : public CreatureScript { public: @@ -1420,7 +1445,7 @@ public: CreatureAI* GetAI(Creature* creature) const override { - return new npc_enraged_spiritAI(creature); + return new npc_enraged_spiritAI(creature); } struct npc_enraged_spiritAI : public ScriptedAI @@ -1429,7 +1454,77 @@ public: void Reset() override { } - void EnterCombat(Unit* /*who*/) override { } + void EnterCombat(Unit* /*who*/) override + { + switch (me->GetEntry()) + { + case NPC_ENRAGED_WATER_SPIRIT: + _events.ScheduleEvent(EVENT_ENRAGED_WATER_SPIRIT, Seconds(0), Seconds(1)); + break; + case NPC_ENRAGED_FIRE_SPIRIT: + if (!me->GetAura(SPELL_FEL_FIRE_AURA)) + DoCastSelf(SPELL_FEL_FIRE_AURA); + _events.ScheduleEvent(EVENT_ENRAGED_FIRE_SPIRIT, Seconds(2), Seconds(10)); + break; + case NPC_ENRAGED_EARTH_SPIRIT: + if (!me->GetAura(SPELL_FEL_FIRE_AURA)) + DoCastSelf(SPELL_FEL_FIRE_AURA); + _events.ScheduleEvent(EVENT_ENRAGED_EARTH_SPIRIT, Seconds(3), Seconds(4)); + break; + case NPC_ENRAGED_AIR_SPIRIT: + _events.ScheduleEvent(EVENT_ENRAGED_AIR_SPIRIT_CHAIN_LIGHTNING, Seconds(10)); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_ENRAGED_WATER_SPIRIT: + if (UpdateVictim()) + DoCastVictim(SPELL_STORMBOLT); + _events.Repeat(Seconds(17), Seconds(23)); + break; + case EVENT_ENRAGED_FIRE_SPIRIT: + if (UpdateVictim()) + DoCastVictim(SPELL_FEL_FIREBALL); + _events.Repeat(Seconds(6), Seconds(12)); + break; + case EVENT_ENRAGED_EARTH_SPIRIT: + if (UpdateVictim()) + DoCastVictim(SPELL_FIERY_BOULDER); + _events.Repeat(Seconds(6), Seconds(9)); + break; + case EVENT_ENRAGED_AIR_SPIRIT_CHAIN_LIGHTNING: + if (UpdateVictim()) + DoCastVictim(SPELL_CHAIN_LIGHTNING); + _events.ScheduleEvent(EVENT_ENRAGED_AIR_SPIRIT_HURRICANE, Seconds(3), Seconds(5)); + break; + case EVENT_ENRAGED_AIR_SPIRIT_HURRICANE: + if (UpdateVictim()) + DoCastVictim(SPELL_HURRICANE); + _events.ScheduleEvent(EVENT_ENRAGED_AIR_SPIRIT_CHAIN_LIGHTNING, Seconds(15), Seconds(20)); + break; + default: + break; + } + } + + if (me->GetEntry() == NPC_ENRAGED_FIRE_SPIRIT || me->GetEntry() == NPC_ENRAGED_AIR_SPIRIT) + if (HealthBelowPct(35) && !me->GetAura(SPELL_ENRAGE)) + DoCastSelf(SPELL_ENRAGE); + + DoMeleeAttackIfReady(); + } void JustDied(Unit* /*killer*/) override { @@ -1450,6 +1545,7 @@ public: entry = NPC_EARTHEN_SOUL; //credit = SPELL_EARTHEN_SOUL_CAPTURED_CREDIT; credit = NPC_CREDIT_EARTH; + DoCastSelf(SPELL_SUMMON_ENRAGED_EARTH_SHARD); break; case NPC_ENRAGED_AIR_SPIRIT: entry = NPC_ENRAGED_AIRY_SOUL; @@ -1488,6 +1584,9 @@ public: } } } + + private: + EventMap _events; }; }; diff --git a/src/server/scripts/Outland/zone_shattrath_city.cpp b/src/server/scripts/Outland/zone_shattrath_city.cpp index 87143a32e7b..073897b5689 100644 --- a/src/server/scripts/Outland/zone_shattrath_city.cpp +++ b/src/server/scripts/Outland/zone_shattrath_city.cpp @@ -19,346 +19,21 @@ /* ScriptData SDName: Shattrath_City SD%Complete: 100 -SDComment: Quest support: 10004, 10009, 10211. Flask vendors, Teleport to Caverns of Time +SDComment: Quest support: 10211. SDCategory: Shattrath City EndScriptData */ /* ContentData -npc_raliq_the_drunk -npc_salsalabim -npc_shattrathflaskvendors -npc_zephyr npc_kservant EndContentData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" -#include "ScriptedGossip.h" #include "ScriptedEscortAI.h" #include "Player.h" #include "WorldSession.h" /*###### -## npc_raliq_the_drunk -######*/ - -enum RaliqTheDrunk -{ - SAY_RALIQ_ATTACK = 0, - OPTION_ID_COLLECT_A_DEBT = 0, - MENU_ID_COLLECT_A_DEBT = 7729, - NPC_TEXT_WUT_YOU_WANT = 9440, - CRACKIN_SOME_SKULLS = 10009, - SPELL_UPPERCUT = 10966 -}; - -class npc_raliq_the_drunk : public CreatureScript -{ -public: - npc_raliq_the_drunk() : CreatureScript("npc_raliq_the_drunk") { } - - struct npc_raliq_the_drunkAI : public ScriptedAI - { - npc_raliq_the_drunkAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - Uppercut_Timer = 5000; - } - - uint32 Uppercut_Timer; - - void Reset() override - { - Initialize(); - me->RestoreFaction(); - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (Uppercut_Timer <= diff) - { - DoCastVictim(SPELL_UPPERCUT); - Uppercut_Timer = 15000; - } else Uppercut_Timer -= diff; - - DoMeleeAttackIfReady(); - } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - CloseGossipMenuFor(player); - me->SetFaction(FACTION_OGRE); - Talk(SAY_RALIQ_ATTACK, player); - AttackStart(player); - } - return true; - } - - bool GossipHello(Player* player) override - { - if (player->GetQuestStatus(CRACKIN_SOME_SKULLS) == QUEST_STATUS_INCOMPLETE) - { - AddGossipItemFor(player, MENU_ID_COLLECT_A_DEBT, OPTION_ID_COLLECT_A_DEBT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, NPC_TEXT_WUT_YOU_WANT, me->GetGUID()); - } - else - { - ClearGossipMenuFor(player); - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - } - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_raliq_the_drunkAI(creature); - } -}; - -/*###### -# npc_salsalabim -######*/ - -enum Salsalabim -{ - SAY_DEMONIC_AGGRO = 0, - OPTION_ID_ALTRUIS_SENT_ME = 0, - MENU_ID_ALTRUIS_SENT_ME = 7725, - NPC_TEXT_SAL_GROWLS_AT_YOU = 9435, - PATIENCE_AND_UNDERSTANDING = 10004, - SPELL_MAGNETIC_PULL = 31705 -}; - -class npc_salsalabim : public CreatureScript -{ -public: - npc_salsalabim() : CreatureScript("npc_salsalabim") { } - - struct npc_salsalabimAI : public ScriptedAI - { - npc_salsalabimAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } - - void Initialize() - { - MagneticPull_Timer = 15000; - } - - uint32 MagneticPull_Timer; - - void Reset() override - { - Initialize(); - me->RestoreFaction(); - } - - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (done_by->GetTypeId() == TYPEID_PLAYER && me->HealthBelowPctDamaged(20, damage)) - { - done_by->ToPlayer()->GroupEventHappens(PATIENCE_AND_UNDERSTANDING, me); - damage = 0; - EnterEvadeMode(); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (MagneticPull_Timer <= diff) - { - DoCastVictim(SPELL_MAGNETIC_PULL); - MagneticPull_Timer = 15000; - } else MagneticPull_Timer -= diff; - - DoMeleeAttackIfReady(); - } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - CloseGossipMenuFor(player); - me->SetFaction(FACTION_DEMON); - Talk(SAY_DEMONIC_AGGRO, player); - AttackStart(player); - } - return true; - } - - bool GossipHello(Player* player) override - { - if (player->GetQuestStatus(PATIENCE_AND_UNDERSTANDING) == QUEST_STATUS_INCOMPLETE) - { - AddGossipItemFor(player, MENU_ID_ALTRUIS_SENT_ME, OPTION_ID_ALTRUIS_SENT_ME, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, NPC_TEXT_SAL_GROWLS_AT_YOU, me->GetGUID()); - } - else - { - if (me->IsQuestGiver()) - player->PrepareQuestMenu(me->GetGUID()); - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - } - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_salsalabimAI(creature); - } -}; - -/* -################################################## -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 -################################################## -*/ - -enum FlaskVendors -{ - ALDOR_REPUTATION = 932, - SCRYERS_REPUTATION = 934, - THE_SHA_TAR_REPUTATION = 935, - CENARION_EXPEDITION_REP = 942, - NPC_TEXT_EXALTED_ALDOR = 11083, // (need to be exalted with Sha'tar, Cenarion Expedition and the Aldor) - NPC_TEXT_EXALTED_SCRYERS = 11084, // (need to be exalted with Sha'tar, Cenarion Expedition and the Scryers) - NPC_TEXT_WELCOME_NAME = 11085, // Access granted - ARCANIST_XORITH = 23483, // Scryer Apothecary - HALDOR_THE_COMPULSIVE = 23484 // Aldor Apothecary -}; - -class npc_shattrathflaskvendors : public CreatureScript -{ -public: - npc_shattrathflaskvendors() : CreatureScript("npc_shattrathflaskvendors") { } - - struct npc_shattrathflaskvendorsAI : public ScriptedAI - { - npc_shattrathflaskvendorsAI(Creature* creature) : ScriptedAI(creature) { } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(me->GetGUID()); - - return true; - } - - bool GossipHello(Player* player) override - { - if (me->GetEntry() == HALDOR_THE_COMPULSIVE) - { - // Aldor vendor - if (me->IsVendor() && (player->GetReputationRank(ALDOR_REPUTATION) == REP_EXALTED) && (player->GetReputationRank(THE_SHA_TAR_REPUTATION) == REP_EXALTED) && (player->GetReputationRank(CENARION_EXPEDITION_REP) == REP_EXALTED)) - { - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - SendGossipMenuFor(player, NPC_TEXT_WELCOME_NAME, me->GetGUID()); - } - else - { - SendGossipMenuFor(player, NPC_TEXT_EXALTED_ALDOR, me->GetGUID()); - } - } - - if (me->GetEntry() == ARCANIST_XORITH) - { - // Scryers vendor - if (me->IsVendor() && (player->GetReputationRank(SCRYERS_REPUTATION) == REP_EXALTED) && (player->GetReputationRank(THE_SHA_TAR_REPUTATION) == REP_EXALTED) && (player->GetReputationRank(CENARION_EXPEDITION_REP) == REP_EXALTED)) - { - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - SendGossipMenuFor(player, NPC_TEXT_WELCOME_NAME, me->GetGUID()); - } - else - { - SendGossipMenuFor(player, NPC_TEXT_EXALTED_SCRYERS, me->GetGUID()); - } - } - - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_shattrathflaskvendorsAI(creature); - } -}; - -/*###### -# npc_zephyr -######*/ - -enum Zephyr -{ - OPTION_ID_TAKE_ME_TO_C_O_T = 0, - KEEPERS_OF_TIME_REPUTATION = 989, - MENU_ID_TAKE_ME_TO_C_O_T = 9205, - TELEPORT_CAVERNS_OF_TIME = 37778 -}; - -class npc_zephyr : public CreatureScript -{ -public: - npc_zephyr() : CreatureScript("npc_zephyr") { } - - struct npc_zephyrAI : public ScriptedAI - { - npc_zephyrAI(Creature* creature) : ScriptedAI(creature) { } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - player->CastSpell(player, TELEPORT_CAVERNS_OF_TIME, false); - - return true; - } - - bool GossipHello(Player* player) override - { - if (player->GetReputationRank(KEEPERS_OF_TIME_REPUTATION) >= REP_REVERED) - AddGossipItemFor(player, MENU_ID_TAKE_ME_TO_C_O_T, OPTION_ID_TAKE_ME_TO_C_O_T, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_zephyrAI(creature); - } -}; - -/*###### # npc_kservant ######*/ @@ -399,12 +74,12 @@ public: return new npc_kservantAI(creature); } - struct npc_kservantAI : public npc_escortAI + struct npc_kservantAI : public EscortAI { public: - npc_kservantAI(Creature* creature) : npc_escortAI(creature) { } + npc_kservantAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -505,9 +180,5 @@ public: void AddSC_shattrath_city() { - new npc_raliq_the_drunk(); - new npc_salsalabim(); - new npc_shattrathflaskvendors(); - new npc_zephyr(); new npc_kservant(); } diff --git a/src/server/scripts/Outland/zone_terokkar_forest.cpp b/src/server/scripts/Outland/zone_terokkar_forest.cpp index 758db799ea4..a8b91b913e5 100644 --- a/src/server/scripts/Outland/zone_terokkar_forest.cpp +++ b/src/server/scripts/Outland/zone_terokkar_forest.cpp @@ -19,26 +19,22 @@ /* ScriptData SDName: Terokkar_Forest SD%Complete: 85 -SDComment: Quest support: 9889, 10009, 10873, 10896, 10898, 11096, 10052, 10051. Skettis->Ogri'la Flight +SDComment: Quest support: 9889, 10898, 10052, 10051. SDCategory: Terokkar Forest EndScriptData */ /* ContentData npc_unkor_the_ruthless -npc_infested_root_walker -npc_rotting_forest_rager -npc_floon npc_isla_starmane -npc_slim +npc_skywing +npc_akuno EndContentData */ #include "ScriptMgr.h" #include "GameObject.h" -#include "GameObjectAI.h" #include "Group.h" #include "Player.h" #include "ScriptedEscortAI.h" -#include "ScriptedGossip.h" #include "WorldSession.h" /*###### @@ -166,43 +162,6 @@ public: }; /*###### -## npc_infested_root_walker -######*/ - -enum InfestedRootWalker -{ - SPELL_SUMMON_WOOD_MITES = 39130 -}; - -class npc_infested_root_walker : public CreatureScript -{ -public: - npc_infested_root_walker() : CreatureScript("npc_infested_root_walker") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_infested_root_walkerAI(creature); - } - - struct npc_infested_root_walkerAI : public ScriptedAI - { - npc_infested_root_walkerAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override { } - void EnterCombat(Unit* /*who*/) override { } - - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (done_by && done_by->GetTypeId() == TYPEID_PLAYER) - if (me->GetHealth() <= damage) - if (rand32() % 100 < 75) - //Summon Wood Mites - DoCast(me, SPELL_SUMMON_WOOD_MITES, true); - } - }; -}; - -/*###### ## npc_skywing ######*/ @@ -221,12 +180,12 @@ public: return new npc_skywingAI(creature); } - struct npc_skywingAI : public npc_escortAI + struct npc_skywingAI : public EscortAI { public: - npc_skywingAI(Creature* creature) : npc_escortAI(creature) { } + npc_skywingAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -258,162 +217,9 @@ public: void UpdateAI(uint32 diff) override { - npc_escortAI::UpdateAI(diff); - } - }; -}; - -/*###### -## npc_rotting_forest_rager -######*/ - -enum RottingForestRager -{ - SPELL_SUMMON_LOTS_OF_WOOD_MITES = 39134 -}; - -class npc_rotting_forest_rager : public CreatureScript -{ -public: - npc_rotting_forest_rager() : CreatureScript("npc_rotting_forest_rager") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_rotting_forest_ragerAI(creature); - } - - struct npc_rotting_forest_ragerAI : public ScriptedAI - { - npc_rotting_forest_ragerAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override { } - void EnterCombat(Unit* /*who*/) override { } - - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (done_by->GetTypeId() == TYPEID_PLAYER) - if (me->GetHealth() <= damage) - if (rand32() % 100 < 75) - //Summon Lots of Wood Mites - DoCast(me, SPELL_SUMMON_LOTS_OF_WOOD_MITES, true); - } - }; -}; - -/*###### -## npc_floon -######*/ - -enum Floon -{ - SAY_FLOON_ATTACK = 0, - OPTION_ID_PAY_UP_OR_DIE = 0, - OPTION_ID_COLLECT_A_DEBT = 0, - MENU_ID_PAY_UP_OR_DIE = 7731, - MENU_ID_COLLECT_A_DEBT = 7732, - GOSSIP_FLOON_STRANGE_SOUNDS = 9442, - GOSSIP_HE_ALREADY_KILLED_ME = 9443, - - SPELL_SILENCE = 6726, - SPELL_FROSTBOLT = 9672, - SPELL_FROST_NOVA = 11831, - - QUEST_CRACKIN_SOME_SKULLS = 10009 -}; - -class npc_floon : public CreatureScript -{ -public: - npc_floon() : CreatureScript("npc_floon") { } - - struct npc_floonAI : public ScriptedAI - { - npc_floonAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - m_uiNormFaction = creature->GetFaction(); - } - - void Initialize() - { - Silence_Timer = 2000; - Frostbolt_Timer = 4000; - FrostNova_Timer = 9000; - } - - uint32 m_uiNormFaction; - uint32 Silence_Timer; - uint32 Frostbolt_Timer; - uint32 FrostNova_Timer; - - void Reset() override - { - Initialize(); - if (me->GetFaction() != m_uiNormFaction) - me->SetFaction(m_uiNormFaction); - } - - void EnterCombat(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (Silence_Timer <= diff) - { - DoCastVictim(SPELL_SILENCE); - Silence_Timer = 30000; - } else Silence_Timer -= diff; - - if (FrostNova_Timer <= diff) - { - DoCast(me, SPELL_FROST_NOVA); - FrostNova_Timer = 20000; - } else FrostNova_Timer -= diff; - - if (Frostbolt_Timer <= diff) - { - DoCastVictim(SPELL_FROSTBOLT); - Frostbolt_Timer = 5000; - } else Frostbolt_Timer -= diff; - - DoMeleeAttackIfReady(); - } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF) - { - AddGossipItemFor(player, MENU_ID_PAY_UP_OR_DIE, OPTION_ID_PAY_UP_OR_DIE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, GOSSIP_HE_ALREADY_KILLED_ME, me->GetGUID()); - } - if (action == GOSSIP_ACTION_INFO_DEF + 1) - { - CloseGossipMenuFor(player); - me->SetFaction(FACTION_ARAKKOA); - Talk(SAY_FLOON_ATTACK, player); - AttackStart(player); - } - return true; - } - - bool GossipHello(Player* player) override - { - if (player->GetQuestStatus(QUEST_CRACKIN_SOME_SKULLS) == QUEST_STATUS_INCOMPLETE) - AddGossipItemFor(player, MENU_ID_COLLECT_A_DEBT, OPTION_ID_COLLECT_A_DEBT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - - SendGossipMenuFor(player, GOSSIP_FLOON_STRANGE_SOUNDS, me->GetGUID()); - return true; + EscortAI::UpdateAI(diff); } }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_floonAI(creature); - } }; /*###### @@ -437,11 +243,11 @@ class npc_isla_starmane : public CreatureScript public: npc_isla_starmane() : CreatureScript("npc_isla_starmane") { } - struct npc_isla_starmaneAI : public npc_escortAI + struct npc_isla_starmaneAI : public EscortAI { - npc_isla_starmaneAI(Creature* creature) : npc_escortAI(creature) { } + npc_isla_starmaneAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -512,135 +318,6 @@ public: } }; -/*###### -## go_skull_pile -######*/ - -enum SkullPile -{ - OPTION_ID_GEZZARAK_THE_HUNTRESS = 0, - OPTION_ID_DARKSCREECHER_AKKARAI = 1, - OPTION_ID_KARROG = 2, - OPTION_ID_VAKKIZ_THE_WINDRAGER = 3, - GOSSIP_MENU_ID_SKULL_PILE = 8660, - ADVERSARIAL_BLOOD = 11885, - SUMMON_GEZZARAK_THE_HUNTRESS = 40632, - SUMMON_KARROG = 40640, - SUMMON_DARKSCREECHER_AKKARAI = 40642, - SUMMON_VAKKIZ_THE_WINDRAGER = 40644 -}; - -class go_skull_pile : public GameObjectScript -{ -public: - go_skull_pile() : GameObjectScript("go_skull_pile") { } - - struct go_skull_pileAI : public GameObjectAI - { - go_skull_pileAI(GameObject* go) : GameObjectAI(go) { } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const sender = player->PlayerTalkClass->GetGossipOptionSender(gossipListId); - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - switch (sender) - { - case GOSSIP_SENDER_MAIN: SendActionMenu(player, action); break; - } - return true; - } - - bool GossipHello(Player* player) override - { - if ((player->GetQuestStatus(ADVERSARIAL_BLOOD) == QUEST_STATUS_INCOMPLETE) || player->GetQuestRewardStatus(ADVERSARIAL_BLOOD)) - { - AddGossipItemFor(player, GOSSIP_MENU_ID_SKULL_PILE, OPTION_ID_GEZZARAK_THE_HUNTRESS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - AddGossipItemFor(player, GOSSIP_MENU_ID_SKULL_PILE, OPTION_ID_DARKSCREECHER_AKKARAI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - AddGossipItemFor(player, GOSSIP_MENU_ID_SKULL_PILE, OPTION_ID_KARROG, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - AddGossipItemFor(player, GOSSIP_MENU_ID_SKULL_PILE, OPTION_ID_VAKKIZ_THE_WINDRAGER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); - } - - SendGossipMenuFor(player, me->GetGOInfo()->questgiver.gossipID, me->GetGUID()); - return true; - } - - void SendActionMenu(Player* player, uint32 action) - { - switch (action) - { - case GOSSIP_ACTION_INFO_DEF + 1: - player->CastSpell(player, SUMMON_GEZZARAK_THE_HUNTRESS, false); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - player->CastSpell(player, SUMMON_DARKSCREECHER_AKKARAI, false); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - player->CastSpell(player, SUMMON_KARROG, false); - break; - case GOSSIP_ACTION_INFO_DEF + 4: - player->CastSpell(player, SUMMON_VAKKIZ_THE_WINDRAGER, false); - break; - } - } - }; - - GameObjectAI* GetAI(GameObject* go) const override - { - return new go_skull_pileAI(go); - } -}; - -/*###### -## npc_slim -######*/ - -enum Slim -{ - FACTION_CONSORTIUM = 933, - NPC_TEXT_NEITHER_SLIM_NOR_SHADY = 9895, - NPC_TEXT_I_SEE_YOU_ARE_A_FRIEND = 9896 -}; - -class npc_slim : public CreatureScript -{ -public: - npc_slim() : CreatureScript("npc_slim") { } - - struct npc_slimAI : public ScriptedAI - { - npc_slimAI(Creature* creature) : ScriptedAI(creature) { } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(me->GetGUID()); - - return true; - } - - bool GossipHello(Player* player) override - { - if (me->IsVendor() && player->GetReputationRank(FACTION_CONSORTIUM) >= REP_FRIENDLY) - { - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - SendGossipMenuFor(player, NPC_TEXT_I_SEE_YOU_ARE_A_FRIEND, me->GetGUID()); - } - else - SendGossipMenuFor(player, NPC_TEXT_NEITHER_SLIM_NOR_SHADY, me->GetGUID()); - - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_slimAI(creature); - } -}; - /*######## ####npc_akuno #####*/ @@ -656,11 +333,11 @@ class npc_akuno : public CreatureScript public: npc_akuno() : CreatureScript("npc_akuno") { } - struct npc_akunoAI : public npc_escortAI + struct npc_akunoAI : public EscortAI { - npc_akunoAI(Creature* creature) : npc_escortAI(creature) { } + npc_akunoAI(Creature* creature) : EscortAI(creature) { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -707,12 +384,7 @@ public: void AddSC_terokkar_forest() { new npc_unkor_the_ruthless(); - new npc_infested_root_walker(); - new npc_rotting_forest_rager(); - new npc_floon(); new npc_isla_starmane(); - new go_skull_pile(); new npc_skywing(); - new npc_slim(); new npc_akuno(); } diff --git a/src/server/scripts/Outland/zone_zangarmarsh.cpp b/src/server/scripts/Outland/zone_zangarmarsh.cpp index fabf7290129..9eff40f296c 100644 --- a/src/server/scripts/Outland/zone_zangarmarsh.cpp +++ b/src/server/scripts/Outland/zone_zangarmarsh.cpp @@ -19,14 +19,12 @@ /* ScriptData SDName: Zangarmarsh SD%Complete: 100 -SDComment: Quest support: 9752, 9785, 9803, 10009. Mark Of ... buffs. +SDComment: Quest support: 9752, 9785, Mark Of ... buffs. SDCategory: Zangarmarsh EndScriptData */ /* ContentData npcs_ashyen_and_keleth -npc_cooshcoosh -npc_elder_kuruti npc_kayra_longmane EndContentData */ @@ -157,159 +155,6 @@ public: }; /*###### -## npc_cooshcoosh -######*/ - -#define GOSSIP_COOSH "You owe Sim'salabim money. Hand them over or die!" - -enum Cooshhooosh -{ - SPELL_LIGHTNING_BOLT = 9532, - QUEST_CRACK_SKULLS = 10009 -}; - -class npc_cooshcoosh : public CreatureScript -{ -public: - npc_cooshcoosh() : CreatureScript("npc_cooshcoosh") { } - - struct npc_cooshcooshAI : public ScriptedAI - { - npc_cooshcooshAI(Creature* creature) : ScriptedAI(creature) - { - m_uiNormFaction = creature->GetFaction(); - Initialize(); - } - - void Initialize() - { - LightningBolt_Timer = 2000; - } - - uint32 m_uiNormFaction; - uint32 LightningBolt_Timer; - - void Reset() override - { - Initialize(); - if (me->GetFaction() != m_uiNormFaction) - me->SetFaction(m_uiNormFaction); - } - - void EnterCombat(Unit* /*who*/) override { } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (LightningBolt_Timer <= diff) - { - DoCastVictim(SPELL_LIGHTNING_BOLT); - LightningBolt_Timer = 5000; - } else LightningBolt_Timer -= diff; - - DoMeleeAttackIfReady(); - } - - bool GossipHello(Player* player) override - { - if (player->GetQuestStatus(QUEST_CRACK_SKULLS) == QUEST_STATUS_INCOMPLETE) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_COOSH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - - SendGossipMenuFor(player, 9441, me->GetGUID()); - return true; - } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - if (action == GOSSIP_ACTION_INFO_DEF) - { - CloseGossipMenuFor(player); - me->SetFaction(FACTION_OGRE); - AttackStart(player); - } - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_cooshcooshAI(creature); - } -}; - -/*###### -## npc_elder_kuruti -######*/ - -#define GOSSIP_ITEM_KUR1 "Greetings, elder. It is time for your people to end their hostility towards the draenei and their allies." -#define GOSSIP_ITEM_KUR2 "I did not mean to deceive you, elder. The draenei of Telredor thought to approach you in a way that would seem familiar to you." -#define GOSSIP_ITEM_KUR3 "I will tell them. Farewell, elder." - -class npc_elder_kuruti : public CreatureScript -{ -public: - npc_elder_kuruti() : CreatureScript("npc_elder_kuruti") { } - - struct npc_elder_kurutiAI : public ScriptedAI - { - npc_elder_kurutiAI(Creature* creature) : ScriptedAI(creature) { } - - bool GossipHello(Player* player) override - { - if (player->GetQuestStatus(9803) == QUEST_STATUS_INCOMPLETE) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_KUR1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - - SendGossipMenuFor(player, 9226, me->GetGUID()); - return true; - } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF: - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_KUR2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - SendGossipMenuFor(player, 9227, me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 1: - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_KUR3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - SendGossipMenuFor(player, 9229, me->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - { - if (!player->HasItemCount(24573)) - { - ItemPosCountVec dest; - uint32 itemId = 24573; - InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, 1, nullptr); - if (msg == EQUIP_ERR_OK) - { - player->StoreNewItem(dest, itemId, true); - } - else - player->SendEquipError(msg, nullptr, nullptr, itemId); - } - SendGossipMenuFor(player, 9231, me->GetGUID()); - break; - } - } - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_elder_kurutiAI(creature); - } -}; - -/*###### ## npc_kayra_longmane ######*/ @@ -330,13 +175,13 @@ class npc_kayra_longmane : public CreatureScript public: npc_kayra_longmane() : CreatureScript("npc_kayra_longmane") { } - struct npc_kayra_longmaneAI : public npc_escortAI + struct npc_kayra_longmaneAI : public EscortAI { - npc_kayra_longmaneAI(Creature* creature) : npc_escortAI(creature) { } + npc_kayra_longmaneAI(Creature* creature) : EscortAI(creature) { } void Reset() override { } - void WaypointReached(uint32 waypointId) override + void WaypointReached(uint32 waypointId, uint32 /*pathId*/) override { Player* player = GetPlayerForEscort(); if (!player) @@ -391,7 +236,5 @@ public: void AddSC_zangarmarsh() { new npcs_ashyen_and_keleth(); - new npc_cooshcoosh(); - new npc_elder_kuruti(); new npc_kayra_longmane(); } diff --git a/src/server/scripts/Pet/pet_hunter.cpp b/src/server/scripts/Pet/pet_hunter.cpp index 20dcf5fb5d7..ed5f9ea48ed 100644 --- a/src/server/scripts/Pet/pet_hunter.cpp +++ b/src/server/scripts/Pet/pet_hunter.cpp @@ -236,7 +236,7 @@ class spell_pet_guard_dog : public SpellScriptLoader PreventDefaultAction(); Unit* caster = eventInfo.GetActor(); - caster->CastSpell((Unit*)nullptr, SPELL_PET_GUARD_DOG_HAPPINESS, true, nullptr, aurEff); + caster->CastSpell(nullptr, SPELL_PET_GUARD_DOG_HAPPINESS, true, nullptr, aurEff); float addThreat = CalculatePct(ASSERT_NOTNULL(eventInfo.GetSpellInfo())->Effects[EFFECT_0].CalcValue(caster), aurEff->GetAmount()); eventInfo.GetProcTarget()->GetThreatManager().AddThreat(caster, addThreat, GetSpellInfo(), false, true); @@ -288,7 +288,7 @@ class spell_pet_silverback : public SpellScriptLoader PreventDefaultAction(); uint32 spellId = triggerSpell[GetSpellInfo()->GetRank() - 1]; - eventInfo.GetActor()->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + eventInfo.GetActor()->CastSpell(nullptr, spellId, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index bd2e36bc30f..1cbe0537563 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -1546,7 +1546,7 @@ class spell_dk_pvp_4p_bonus : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - eventInfo.GetActionTarget()->CastSpell((Unit*)nullptr, SPELL_DK_RUNIC_RETURN, true, nullptr, aurEff); + eventInfo.GetActionTarget()->CastSpell(nullptr, SPELL_DK_RUNIC_RETURN, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index d57bd1e3008..0a4b7ae303e 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -495,7 +495,7 @@ class spell_dru_glyph_of_barkskin : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_DRUID_BARKSKIN_01, true, nullptr, aurEff); + eventInfo.GetActor()->CastSpell(nullptr, SPELL_DRUID_BARKSKIN_01, true, nullptr, aurEff); } void Register() override @@ -1160,7 +1160,7 @@ class spell_dru_omen_of_clarity : public SpellScriptLoader { Unit* target = GetTarget(); if (target->HasAura(SPELL_DRUID_BALANCE_T10_BONUS)) - target->CastSpell((Unit*)nullptr, SPELL_DRUID_BALANCE_T10_BONUS_PROC, true, nullptr, aurEff); + target->CastSpell(nullptr, SPELL_DRUID_BALANCE_T10_BONUS_PROC, true, nullptr, aurEff); } void Register() override @@ -1874,7 +1874,7 @@ class spell_dru_t4_2p_bonus : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_DRUID_INFUSION, true, nullptr, aurEff); + eventInfo.GetActor()->CastSpell(nullptr, SPELL_DRUID_INFUSION, true, nullptr, aurEff); } void Register() override @@ -1940,7 +1940,7 @@ class spell_dru_item_t6_trinket : public SpellScriptLoader return; if (roll_chance_i(chance)) - eventInfo.GetActor()->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + eventInfo.GetActor()->CastSpell(nullptr, spellId, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index cbec8270e18..1b06b8c3a4a 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -42,39 +42,28 @@ #include "SpellScript.h" #include "Vehicle.h" -class spell_gen_absorb0_hitlimit1 : public SpellScriptLoader +class spell_gen_absorb0_hitlimit1 : public AuraScript { - public: - spell_gen_absorb0_hitlimit1() : SpellScriptLoader("spell_gen_absorb0_hitlimit1") { } - - class spell_gen_absorb0_hitlimit1_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_absorb0_hitlimit1_AuraScript); - - uint32 limit = 0; + PrepareAuraScript(spell_gen_absorb0_hitlimit1); - bool Load() override - { - // Max absorb stored in 1 dummy effect - limit = GetSpellInfo()->Effects[EFFECT_1].CalcValue(); - return true; - } + uint32 limit = 0; - void Absorb(AuraEffect* /*aurEff*/, DamageInfo& /*dmgInfo*/, uint32& absorbAmount) - { - absorbAmount = std::min(limit, absorbAmount); - } + bool Load() override + { + // Max absorb stored in 1 dummy effect + limit = GetSpellInfo()->Effects[EFFECT_1].CalcValue(); + return true; + } - void Register() override - { - OnEffectAbsorb += AuraEffectAbsorbFn(spell_gen_absorb0_hitlimit1_AuraScript::Absorb, EFFECT_0); - } - }; + void Absorb(AuraEffect* /*aurEff*/, DamageInfo& /*dmgInfo*/, uint32& absorbAmount) + { + absorbAmount = std::min(limit, absorbAmount); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_absorb0_hitlimit1_AuraScript(); - } + void Register() override + { + OnEffectAbsorb += AuraEffectAbsorbFn(spell_gen_absorb0_hitlimit1::Absorb, EFFECT_0); + } }; // 28764 - Adaptive Warding (Frostfire Regalia Set) @@ -87,115 +76,93 @@ enum AdaptiveWarding SPELL_GEN_ADAPTIVE_WARDING_ARCANE = 28770 }; -class spell_gen_adaptive_warding : public SpellScriptLoader +class spell_gen_adaptive_warding : public AuraScript { - public: - spell_gen_adaptive_warding() : SpellScriptLoader("spell_gen_adaptive_warding") { } - - class spell_gen_adaptive_warding_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_adaptive_warding_AuraScript); + PrepareAuraScript(spell_gen_adaptive_warding); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_GEN_ADAPTIVE_WARDING_FIRE, - SPELL_GEN_ADAPTIVE_WARDING_NATURE, - SPELL_GEN_ADAPTIVE_WARDING_FROST, - SPELL_GEN_ADAPTIVE_WARDING_SHADOW, - SPELL_GEN_ADAPTIVE_WARDING_ARCANE - }); - } - - bool CheckProc(ProcEventInfo& eventInfo) - { - DamageInfo* damageInfo = eventInfo.GetDamageInfo(); - if (!damageInfo || !damageInfo->GetSpellInfo()) - return false; - - // find Mage Armor - if (!GetTarget()->GetAuraEffect(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT, SPELLFAMILY_MAGE, 0x10000000, 0x0, 0x0)) - return false; - - switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask())) - { - case SPELL_SCHOOL_NORMAL: - case SPELL_SCHOOL_HOLY: - return false; - default: - break; - } - return true; - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_GEN_ADAPTIVE_WARDING_FIRE, + SPELL_GEN_ADAPTIVE_WARDING_NATURE, + SPELL_GEN_ADAPTIVE_WARDING_FROST, + SPELL_GEN_ADAPTIVE_WARDING_SHADOW, + SPELL_GEN_ADAPTIVE_WARDING_ARCANE + }); + } - uint32 spellId = 0; - switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask())) - { - case SPELL_SCHOOL_FIRE: - spellId = SPELL_GEN_ADAPTIVE_WARDING_FIRE; - break; - case SPELL_SCHOOL_NATURE: - spellId = SPELL_GEN_ADAPTIVE_WARDING_NATURE; - break; - case SPELL_SCHOOL_FROST: - spellId = SPELL_GEN_ADAPTIVE_WARDING_FROST; - break; - case SPELL_SCHOOL_SHADOW: - spellId = SPELL_GEN_ADAPTIVE_WARDING_SHADOW; - break; - case SPELL_SCHOOL_ARCANE: - spellId = SPELL_GEN_ADAPTIVE_WARDING_ARCANE; - break; - default: - return; - } - GetTarget()->CastSpell(GetTarget(), spellId, true, nullptr, aurEff); - } + bool CheckProc(ProcEventInfo& eventInfo) + { + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetSpellInfo()) + return false; - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_gen_adaptive_warding_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_gen_adaptive_warding_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + // find Mage Armor + if (!GetTarget()->GetAuraEffect(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT, SPELLFAMILY_MAGE, 0x10000000, 0x0, 0x0)) + return false; - AuraScript* GetAuraScript() const override + switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask())) { - return new spell_gen_adaptive_warding_AuraScript(); + case SPELL_SCHOOL_NORMAL: + case SPELL_SCHOOL_HOLY: + return false; + default: + break; } -}; + return true; + } -class spell_gen_allow_cast_from_item_only : public SpellScriptLoader -{ - public: - spell_gen_allow_cast_from_item_only() : SpellScriptLoader("spell_gen_allow_cast_from_item_only") { } + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + uint32 spellId = 0; + switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask())) + { + case SPELL_SCHOOL_FIRE: + spellId = SPELL_GEN_ADAPTIVE_WARDING_FIRE; + break; + case SPELL_SCHOOL_NATURE: + spellId = SPELL_GEN_ADAPTIVE_WARDING_NATURE; + break; + case SPELL_SCHOOL_FROST: + spellId = SPELL_GEN_ADAPTIVE_WARDING_FROST; + break; + case SPELL_SCHOOL_SHADOW: + spellId = SPELL_GEN_ADAPTIVE_WARDING_SHADOW; + break; + case SPELL_SCHOOL_ARCANE: + spellId = SPELL_GEN_ADAPTIVE_WARDING_ARCANE; + break; + default: + return; + } + GetTarget()->CastSpell(GetTarget(), spellId, true, nullptr, aurEff); + } - class spell_gen_allow_cast_from_item_only_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_allow_cast_from_item_only_SpellScript); + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_gen_adaptive_warding::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_gen_adaptive_warding::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } +}; - SpellCastResult CheckRequirement() - { - if (!GetCastItem()) - return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; - return SPELL_CAST_OK; - } +class spell_gen_allow_cast_from_item_only : public SpellScript +{ + PrepareSpellScript(spell_gen_allow_cast_from_item_only); - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_gen_allow_cast_from_item_only_SpellScript::CheckRequirement); - } - }; + SpellCastResult CheckRequirement() + { + if (!GetCastItem()) + return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; + return SPELL_CAST_OK; + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_allow_cast_from_item_only_SpellScript(); - } + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_gen_allow_cast_from_item_only::CheckRequirement); + } }; enum AnimalBloodPoolSpell @@ -204,74 +171,52 @@ enum AnimalBloodPoolSpell SPELL_SPAWN_BLOOD_POOL = 63471 }; -class spell_gen_animal_blood : public SpellScriptLoader +class spell_gen_animal_blood : public AuraScript { - public: - spell_gen_animal_blood() : SpellScriptLoader("spell_gen_animal_blood") { } - - class spell_gen_animal_blood_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_animal_blood_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SPAWN_BLOOD_POOL }); - } + PrepareAuraScript(spell_gen_animal_blood); - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - // Remove all auras with spell id 46221, except the one currently being applied - while (Aura* aur = GetUnitOwner()->GetOwnedAura(SPELL_ANIMAL_BLOOD, ObjectGuid::Empty, ObjectGuid::Empty, 0, GetAura())) - GetUnitOwner()->RemoveOwnedAura(aur); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SPAWN_BLOOD_POOL }); + } - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* owner = GetUnitOwner()) - if (owner->IsInWater()) - owner->CastSpell(owner, SPELL_SPAWN_BLOOD_POOL, true); - } + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // Remove all auras with spell id 46221, except the one currently being applied + while (Aura* aur = GetUnitOwner()->GetOwnedAura(SPELL_ANIMAL_BLOOD, ObjectGuid::Empty, ObjectGuid::Empty, 0, GetAura())) + GetUnitOwner()->RemoveOwnedAura(aur); + } - void Register() override - { - AfterEffectApply += AuraEffectRemoveFn(spell_gen_animal_blood_AuraScript::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_gen_animal_blood_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); - } - }; + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* owner = GetUnitOwner()) + if (owner->IsInWater()) + owner->CastSpell(owner, SPELL_SPAWN_BLOOD_POOL, true); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_animal_blood_AuraScript(); - } + void Register() override + { + AfterEffectApply += AuraEffectRemoveFn(spell_gen_animal_blood::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_gen_animal_blood::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } }; // 41337 Aura of Anger -class spell_gen_aura_of_anger : public SpellScriptLoader +class spell_gen_aura_of_anger : public AuraScript { - public: - spell_gen_aura_of_anger() : SpellScriptLoader("spell_gen_aura_of_anger") { } - - class spell_gen_aura_of_anger_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_aura_of_anger_AuraScript); - - void HandleEffectPeriodicUpdate(AuraEffect* aurEff) - { - if (AuraEffect* aurEff1 = aurEff->GetBase()->GetEffect(EFFECT_1)) - aurEff1->ChangeAmount(aurEff1->GetAmount() + 5); - aurEff->SetAmount(100 * aurEff->GetTickNumber()); - } + PrepareAuraScript(spell_gen_aura_of_anger); - void Register() override - { - OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_gen_aura_of_anger_AuraScript::HandleEffectPeriodicUpdate, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); - } - }; + void HandleEffectPeriodicUpdate(AuraEffect* aurEff) + { + if (AuraEffect* aurEff1 = aurEff->GetBase()->GetEffect(EFFECT_1)) + aurEff1->ChangeAmount(aurEff1->GetAmount() + 5); + aurEff->SetAmount(100 * aurEff->GetTickNumber()); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_aura_of_anger_AuraScript(); - } + void Register() override + { + OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_gen_aura_of_anger::HandleEffectPeriodicUpdate, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } }; enum ServiceUniform @@ -284,94 +229,72 @@ enum ServiceUniform MODEL_GOBLIN_FEMALE = 31003 }; -class spell_gen_aura_service_uniform : public SpellScriptLoader +class spell_gen_aura_service_uniform : public AuraScript { - public: - spell_gen_aura_service_uniform() : SpellScriptLoader("spell_gen_aura_service_uniform") { } - - class spell_gen_aura_service_uniform_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_aura_service_uniform_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SERVICE_UNIFORM }); - } - - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - // Apply model goblin - Unit* target = GetTarget(); - if (target->GetTypeId() == TYPEID_PLAYER) - { - if (target->getGender() == GENDER_MALE) - target->SetDisplayId(MODEL_GOBLIN_MALE); - else - target->SetDisplayId(MODEL_GOBLIN_FEMALE); - } - } + PrepareAuraScript(spell_gen_aura_service_uniform); - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (target->GetTypeId() == TYPEID_PLAYER) - target->RestoreDisplayId(); - } - - void Register() override - { - AfterEffectApply += AuraEffectRemoveFn(spell_gen_aura_service_uniform_AuraScript::OnApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_gen_aura_service_uniform_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SERVICE_UNIFORM }); + } - AuraScript* GetAuraScript() const override + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // Apply model goblin + Unit* target = GetTarget(); + if (target->GetTypeId() == TYPEID_PLAYER) { - return new spell_gen_aura_service_uniform_AuraScript(); + if (target->getGender() == GENDER_MALE) + target->SetDisplayId(MODEL_GOBLIN_MALE); + else + target->SetDisplayId(MODEL_GOBLIN_FEMALE); } -}; - -class spell_gen_av_drekthar_presence : public SpellScriptLoader -{ - public: - spell_gen_av_drekthar_presence() : SpellScriptLoader("spell_gen_av_drekthar_presence") { } + } - class spell_gen_av_drekthar_presence_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_av_drekthar_presence_AuraScript); + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + if (target->GetTypeId() == TYPEID_PLAYER) + target->RestoreDisplayId(); + } - bool CheckAreaTarget(Unit* target) - { - switch (target->GetEntry()) - { - // alliance - case 14762: // Dun Baldar North Marshal - case 14763: // Dun Baldar South Marshal - case 14764: // Icewing Marshal - case 14765: // Stonehearth Marshal - case 11948: // Vandar Stormspike - // horde - case 14772: // East Frostwolf Warmaster - case 14776: // Tower Point Warmaster - case 14773: // Iceblood Warmaster - case 14777: // West Frostwolf Warmaster - case 11946: // Drek'thar - return true; - default: - return false; - } - } + void Register() override + { + AfterEffectApply += AuraEffectRemoveFn(spell_gen_aura_service_uniform::OnApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_gen_aura_service_uniform::OnRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); + } +}; - void Register() override - { - DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_gen_av_drekthar_presence_AuraScript::CheckAreaTarget); - } - }; +class spell_gen_av_drekthar_presence : public AuraScript +{ + PrepareAuraScript(spell_gen_av_drekthar_presence); - AuraScript* GetAuraScript() const override - { - return new spell_gen_av_drekthar_presence_AuraScript(); + bool CheckAreaTarget(Unit* target) + { + switch (target->GetEntry()) + { + // alliance + case 14762: // Dun Baldar North Marshal + case 14763: // Dun Baldar South Marshal + case 14764: // Icewing Marshal + case 14765: // Stonehearth Marshal + case 11948: // Vandar Stormspike + // horde + case 14772: // East Frostwolf Warmaster + case 14776: // Tower Point Warmaster + case 14773: // Iceblood Warmaster + case 14777: // West Frostwolf Warmaster + case 11946: // Drek'thar + return true; + default: + return false; } + } + + void Register() override + { + DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_gen_av_drekthar_presence::CheckAreaTarget); + } }; enum GenericBandage @@ -379,47 +302,36 @@ enum GenericBandage SPELL_RECENTLY_BANDAGED = 11196 }; -class spell_gen_bandage : public SpellScriptLoader +class spell_gen_bandage : public SpellScript { - public: - spell_gen_bandage() : SpellScriptLoader("spell_gen_bandage") { } - - class spell_gen_bandage_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_bandage_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_RECENTLY_BANDAGED }); - } - - SpellCastResult CheckCast() - { - if (Unit* target = GetExplTargetUnit()) - { - if (target->HasAura(SPELL_RECENTLY_BANDAGED)) - return SPELL_FAILED_TARGET_AURASTATE; - } - return SPELL_CAST_OK; - } - - void HandleScript() - { - if (Unit* target = GetHitUnit()) - GetCaster()->CastSpell(target, SPELL_RECENTLY_BANDAGED, true); - } + PrepareSpellScript(spell_gen_bandage); - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_gen_bandage_SpellScript::CheckCast); - AfterHit += SpellHitFn(spell_gen_bandage_SpellScript::HandleScript); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_RECENTLY_BANDAGED }); + } - SpellScript* GetSpellScript() const override + SpellCastResult CheckCast() + { + if (Unit* target = GetExplTargetUnit()) { - return new spell_gen_bandage_SpellScript(); + if (target->HasAura(SPELL_RECENTLY_BANDAGED)) + return SPELL_FAILED_TARGET_AURASTATE; } + return SPELL_CAST_OK; + } + + void HandleScript() + { + if (Unit* target = GetHitUnit()) + GetCaster()->CastSpell(target, SPELL_RECENTLY_BANDAGED, true); + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_gen_bandage::CheckCast); + AfterHit += SpellHitFn(spell_gen_bandage::HandleScript); + } }; // Blood Reserve - 64568 @@ -429,50 +341,39 @@ enum BloodReserve SPELL_GEN_BLOOD_RESERVE_HEAL = 64569 }; -class spell_gen_blood_reserve : public SpellScriptLoader +class spell_gen_blood_reserve : public AuraScript { - public: - spell_gen_blood_reserve() : SpellScriptLoader("spell_gen_blood_reserve") { } - - class spell_gen_blood_reserve_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_blood_reserve_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_GEN_BLOOD_RESERVE_HEAL }); - } + PrepareAuraScript(spell_gen_blood_reserve); - bool CheckProc(ProcEventInfo& eventInfo) - { - if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) - if (Unit* caster = eventInfo.GetActionTarget()) - if (caster->HealthBelowPctDamaged(35, dmgInfo->GetDamage())) - return true; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_GEN_BLOOD_RESERVE_HEAL }); + } - return false; - } + bool CheckProc(ProcEventInfo& eventInfo) + { + if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) + if (Unit* caster = eventInfo.GetActionTarget()) + if (caster->HealthBelowPctDamaged(35, dmgInfo->GetDamage())) + return true; - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); + return false; + } - Unit* caster = eventInfo.GetActionTarget(); - caster->CastCustomSpell(SPELL_GEN_BLOOD_RESERVE_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), caster, TRIGGERED_FULL_MASK, nullptr, aurEff); - caster->RemoveAura(SPELL_GEN_BLOOD_RESERVE_AURA); - } + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_gen_blood_reserve_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_gen_blood_reserve_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); - } - }; + Unit* caster = eventInfo.GetActionTarget(); + caster->CastCustomSpell(SPELL_GEN_BLOOD_RESERVE_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), caster, TRIGGERED_FULL_MASK, nullptr, aurEff); + caster->RemoveAura(SPELL_GEN_BLOOD_RESERVE_AURA); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_blood_reserve_AuraScript(); - } + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_gen_blood_reserve::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_gen_blood_reserve::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } }; // Blade Warding - 64440 @@ -481,46 +382,35 @@ enum BladeWarding SPELL_GEN_BLADE_WARDING_TRIGGERED = 64442 }; -class spell_gen_blade_warding : public SpellScriptLoader +class spell_gen_blade_warding : public AuraScript { - public: - spell_gen_blade_warding() : SpellScriptLoader("spell_gen_blade_warding") { } + PrepareAuraScript(spell_gen_blade_warding); - class spell_gen_blade_warding_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_blade_warding_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_GEN_BLADE_WARDING_TRIGGERED }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_GEN_BLADE_WARDING_TRIGGERED }); + } - Unit* caster = eventInfo.GetActionTarget(); - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_GEN_BLADE_WARDING_TRIGGERED); + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); - uint8 stacks = GetStackAmount(); - int32 bp = 0; + Unit* caster = eventInfo.GetActionTarget(); + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_GEN_BLADE_WARDING_TRIGGERED); - for (uint8 i = 0; i < stacks; ++i) - bp += spellInfo->Effects[EFFECT_0].CalcValue(caster); + uint8 stacks = GetStackAmount(); + int32 bp = 0; - caster->CastCustomSpell(SPELL_GEN_BLADE_WARDING_TRIGGERED, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetActor(), TRIGGERED_FULL_MASK, nullptr, aurEff); - } + for (uint8 i = 0; i < stacks; ++i) + bp += spellInfo->Effects[EFFECT_0].CalcValue(caster); - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_gen_blade_warding_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_PROC_TRIGGER_SPELL); - } - }; + caster->CastCustomSpell(SPELL_GEN_BLADE_WARDING_TRIGGERED, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetActor(), TRIGGERED_FULL_MASK, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_blade_warding_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_gen_blade_warding::HandleProc, EFFECT_1, SPELL_AURA_PROC_TRIGGER_SPELL); + } }; enum Bonked @@ -530,42 +420,31 @@ enum Bonked SPELL_ON_GUARD = 62972 }; -class spell_gen_bonked : public SpellScriptLoader +class spell_gen_bonked : public SpellScript { - public: - spell_gen_bonked() : SpellScriptLoader("spell_gen_bonked") { } + PrepareSpellScript(spell_gen_bonked); - class spell_gen_bonked_SpellScript : public SpellScript + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Player* target = GetHitPlayer()) { - PrepareSpellScript(spell_gen_bonked_SpellScript); - - void HandleScript(SpellEffIndex /*effIndex*/) - { - if (Player* target = GetHitPlayer()) - { - Aura const* aura = GetHitAura(); - if (!(aura && aura->GetStackAmount() == 3)) - return; - - target->CastSpell(target, SPELL_FOAM_SWORD_DEFEAT, true); - target->RemoveAurasDueToSpell(SPELL_BONKED); - - if (Aura const* auraOnGuard = target->GetAura(SPELL_ON_GUARD)) - if (Item* item = target->GetItemByGuid(auraOnGuard->GetCastItemGUID())) - target->DestroyItemCount(item->GetEntry(), 1, true); - } - } + Aura const* aura = GetHitAura(); + if (!(aura && aura->GetStackAmount() == 3)) + return; - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_bonked_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + target->CastSpell(target, SPELL_FOAM_SWORD_DEFEAT, true); + target->RemoveAurasDueToSpell(SPELL_BONKED); - SpellScript* GetSpellScript() const override - { - return new spell_gen_bonked_SpellScript(); + if (Aura const* auraOnGuard = target->GetAura(SPELL_ON_GUARD)) + if (Item* item = target->GetItemByGuid(auraOnGuard->GetCastItemGUID())) + target->DestroyItemCount(item->GetEntry(), 1, true); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_bonked::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } }; /* DOCUMENTATION: Break-Shield spells @@ -678,70 +557,48 @@ class spell_gen_break_shield: public SpellScriptLoader }; // 46394 Brutallus Burn -class spell_gen_burn_brutallus : public SpellScriptLoader +class spell_gen_burn_brutallus : public AuraScript { - public: - spell_gen_burn_brutallus() : SpellScriptLoader("spell_gen_burn_brutallus") { } - - class spell_gen_burn_brutallus_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_burn_brutallus_AuraScript); - - void HandleEffectPeriodicUpdate(AuraEffect* aurEff) - { - if (aurEff->GetTickNumber() % 11 == 0) - aurEff->SetAmount(aurEff->GetAmount() * 2); - } + PrepareAuraScript(spell_gen_burn_brutallus); - void Register() override - { - OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_gen_burn_brutallus_AuraScript::HandleEffectPeriodicUpdate, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); - } - }; + void HandleEffectPeriodicUpdate(AuraEffect* aurEff) + { + if (aurEff->GetTickNumber() % 11 == 0) + aurEff->SetAmount(aurEff->GetAmount() * 2); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_burn_brutallus_AuraScript(); - } + void Register() override + { + OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_gen_burn_brutallus::HandleEffectPeriodicUpdate, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } }; // 48750 - Burning Depths Necrolyte Image -class spell_gen_burning_depths_necrolyte_image : public SpellScriptLoader +class spell_gen_burning_depths_necrolyte_image : public AuraScript { - public: - spell_gen_burning_depths_necrolyte_image() : SpellScriptLoader("spell_gen_burning_depths_necrolyte_image") { } - - class spell_gen_burning_depths_necrolyte_image_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_burning_depths_necrolyte_image_AuraScript); - - bool Validate(SpellInfo const* spellInfo) override - { - return ValidateSpellInfo({ static_cast<uint32>(spellInfo->Effects[EFFECT_2].CalcValue()) }); - } + PrepareAuraScript(spell_gen_burning_depths_necrolyte_image); - void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - caster->CastSpell(GetTarget(), uint32(GetSpellInfo()->Effects[EFFECT_2].CalcValue())); - } + bool Validate(SpellInfo const* spellInfo) override + { + return ValidateSpellInfo({ static_cast<uint32>(spellInfo->Effects[EFFECT_2].CalcValue()) }); + } - void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - GetTarget()->RemoveAurasDueToSpell(uint32(GetSpellInfo()->Effects[EFFECT_2].CalcValue()), GetCasterGUID()); - } + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + caster->CastSpell(GetTarget(), uint32(GetSpellInfo()->Effects[EFFECT_2].CalcValue())); + } - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_gen_burning_depths_necrolyte_image_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_gen_burning_depths_necrolyte_image_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); - } - }; + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveAurasDueToSpell(uint32(GetSpellInfo()->Effects[EFFECT_2].CalcValue()), GetCasterGUID()); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_burning_depths_necrolyte_image_AuraScript(); - } + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_gen_burning_depths_necrolyte_image::HandleApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_gen_burning_depths_necrolyte_image::HandleRemove, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL); + } }; enum CannibalizeSpells @@ -749,52 +606,41 @@ enum CannibalizeSpells SPELL_CANNIBALIZE_TRIGGERED = 20578 }; -class spell_gen_cannibalize : public SpellScriptLoader +class spell_gen_cannibalize : public SpellScript { - public: - spell_gen_cannibalize() : SpellScriptLoader("spell_gen_cannibalize") { } + PrepareSpellScript(spell_gen_cannibalize); - class spell_gen_cannibalize_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_cannibalize_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_CANNIBALIZE_TRIGGERED }); - } - - SpellCastResult CheckIfCorpseNear() - { - Unit* caster = GetCaster(); - float max_range = GetSpellInfo()->GetMaxRange(false); - WorldObject* result = nullptr; - // search for nearby enemy corpse in range - Trinity::AnyDeadUnitSpellTargetInRangeCheck check(caster, max_range, GetSpellInfo(), TARGET_CHECK_ENEMY); - Trinity::WorldObjectSearcher<Trinity::AnyDeadUnitSpellTargetInRangeCheck> searcher(caster, result, check); - Cell::VisitWorldObjects(caster, searcher, max_range); - if (!result) - Cell::VisitGridObjects(caster, searcher, max_range); - if (!result) - return SPELL_FAILED_NO_EDIBLE_CORPSES; - return SPELL_CAST_OK; - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_CANNIBALIZE_TRIGGERED }); + } - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetCaster()->CastSpell(GetCaster(), SPELL_CANNIBALIZE_TRIGGERED, false); - } + SpellCastResult CheckIfCorpseNear() + { + Unit* caster = GetCaster(); + float max_range = GetSpellInfo()->GetMaxRange(false); + WorldObject* result = nullptr; + // search for nearby enemy corpse in range + Trinity::AnyDeadUnitSpellTargetInRangeCheck check(caster, max_range, GetSpellInfo(), TARGET_CHECK_ENEMY); + Trinity::WorldObjectSearcher<Trinity::AnyDeadUnitSpellTargetInRangeCheck> searcher(caster, result, check); + Cell::VisitWorldObjects(caster, searcher, max_range); + if (!result) + Cell::VisitGridObjects(caster, searcher, max_range); + if (!result) + return SPELL_FAILED_NO_EDIBLE_CORPSES; + return SPELL_CAST_OK; + } - void Register() override - { - OnEffectHit += SpellEffectFn(spell_gen_cannibalize_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - OnCheckCast += SpellCheckCastFn(spell_gen_cannibalize_SpellScript::CheckIfCorpseNear); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetCaster(), SPELL_CANNIBALIZE_TRIGGERED, false); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_cannibalize_SpellScript(); - } + void Register() override + { + OnEffectHit += SpellEffectFn(spell_gen_cannibalize::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + OnCheckCast += SpellCheckCastFn(spell_gen_cannibalize::CheckIfCorpseNear); + } }; enum ChaosBlast @@ -802,65 +648,43 @@ enum ChaosBlast SPELL_CHAOS_BLAST = 37675 }; -class spell_gen_chaos_blast : public SpellScriptLoader +class spell_gen_chaos_blast : public SpellScript { - public: - spell_gen_chaos_blast() : SpellScriptLoader("spell_gen_chaos_blast") { } - - class spell_gen_chaos_blast_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_chaos_blast_SpellScript); + PrepareSpellScript(spell_gen_chaos_blast); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_CHAOS_BLAST }); - } - void HandleDummy(SpellEffIndex /* effIndex */) - { - int32 basepoints0 = 100; - Unit* caster = GetCaster(); - if (Unit* target = GetHitUnit()) - caster->CastCustomSpell(target, SPELL_CHAOS_BLAST, &basepoints0, nullptr, nullptr, true); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_chaos_blast_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_CHAOS_BLAST }); + } + void HandleDummy(SpellEffIndex /* effIndex */) + { + int32 basepoints0 = 100; + Unit* caster = GetCaster(); + if (Unit* target = GetHitUnit()) + caster->CastCustomSpell(target, SPELL_CHAOS_BLAST, &basepoints0, nullptr, nullptr, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_chaos_blast_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_chaos_blast::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_gen_clone : public SpellScriptLoader +class spell_gen_clone : public SpellScript { - public: - spell_gen_clone() : SpellScriptLoader("spell_gen_clone") { } - - class spell_gen_clone_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_clone_SpellScript); + PrepareSpellScript(spell_gen_clone); - void HandleScriptEffect(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - GetHitUnit()->CastSpell(GetCaster(), uint32(GetEffectValue()), true); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_clone_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); - OnEffectHitTarget += SpellEffectFn(spell_gen_clone_SpellScript::HandleScriptEffect, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScriptEffect(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetHitUnit()->CastSpell(GetCaster(), uint32(GetEffectValue()), true); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_clone_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_clone::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + OnEffectHitTarget += SpellEffectFn(spell_gen_clone::HandleScriptEffect, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum CloneWeaponSpells @@ -875,147 +699,125 @@ enum CloneWeaponSpells SPELL_COPY_RANGED_AURA = 57594 }; -class spell_gen_clone_weapon : public SpellScriptLoader +class spell_gen_clone_weapon : public SpellScript { - public: - spell_gen_clone_weapon() : SpellScriptLoader("spell_gen_clone_weapon") { } - - class spell_gen_clone_weapon_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_clone_weapon_SpellScript); - - void HandleScriptEffect(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - GetHitUnit()->CastSpell(GetCaster(), uint32(GetEffectValue()), true); - } + PrepareSpellScript(spell_gen_clone_weapon); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_clone_weapon_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScriptEffect(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetHitUnit()->CastSpell(GetCaster(), uint32(GetEffectValue()), true); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_clone_weapon_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_clone_weapon::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; -class spell_gen_clone_weapon_aura : public SpellScriptLoader +class spell_gen_clone_weapon_aura : public AuraScript { - public: - spell_gen_clone_weapon_aura() : SpellScriptLoader("spell_gen_clone_weapon_aura") { } + PrepareAuraScript(spell_gen_clone_weapon_aura); - class spell_gen_clone_weapon_auraScript : public AuraScript - { - PrepareAuraScript(spell_gen_clone_weapon_auraScript); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_COPY_WEAPON_AURA, + SPELL_COPY_WEAPON_2_AURA, + SPELL_COPY_WEAPON_3_AURA, + SPELL_COPY_OFFHAND_AURA, + SPELL_COPY_OFFHAND_2_AURA, + SPELL_COPY_RANGED_AURA + }); + } - bool Validate(SpellInfo const* /*spellInfo*/) override + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* caster = GetCaster(); + Unit* target = GetTarget(); + if (!caster) + return; + + switch (GetSpellInfo()->Id) + { + case SPELL_COPY_WEAPON_AURA: + case SPELL_COPY_WEAPON_2_AURA: + case SPELL_COPY_WEAPON_3_AURA: { - return ValidateSpellInfo( + prevItem = target->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID); + + if (Player* player = caster->ToPlayer()) { - SPELL_COPY_WEAPON_AURA, - SPELL_COPY_WEAPON_2_AURA, - SPELL_COPY_WEAPON_3_AURA, - SPELL_COPY_OFFHAND_AURA, - SPELL_COPY_OFFHAND_2_AURA, - SPELL_COPY_RANGED_AURA - }); + if (Item* mainItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND)) + target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, mainItem->GetEntry()); + } + else + target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID)); + break; } - - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + case SPELL_COPY_OFFHAND_AURA: + case SPELL_COPY_OFFHAND_2_AURA: { - Unit* caster = GetCaster(); - Unit* target = GetTarget(); - if (!caster) - return; + prevItem = target->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID) + 1; - switch (GetSpellInfo()->Id) + if (Player* player = caster->ToPlayer()) { - case SPELL_COPY_WEAPON_AURA: - case SPELL_COPY_WEAPON_2_AURA: - case SPELL_COPY_WEAPON_3_AURA: - { - prevItem = target->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID); - - if (Player* player = caster->ToPlayer()) - { - if (Item* mainItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND)) - target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, mainItem->GetEntry()); - } - else - target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID)); - break; - } - case SPELL_COPY_OFFHAND_AURA: - case SPELL_COPY_OFFHAND_2_AURA: - { - prevItem = target->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID) + 1; - - if (Player* player = caster->ToPlayer()) - { - if (Item* offItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) - target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, offItem->GetEntry()); - } - else - target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1)); - break; - } - case SPELL_COPY_RANGED_AURA: - { - prevItem = target->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID) + 2; - - if (Player* player = caster->ToPlayer()) - { - if (Item* rangedItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED)) - target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, rangedItem->GetEntry()); - } - else - target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2)); - break; - } - default: - break; + if (Item* offItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) + target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, offItem->GetEntry()); } + else + target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1)); + break; } - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + case SPELL_COPY_RANGED_AURA: { - Unit* target = GetTarget(); + prevItem = target->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID) + 2; - switch (GetSpellInfo()->Id) + if (Player* player = caster->ToPlayer()) { - case SPELL_COPY_WEAPON_AURA: - case SPELL_COPY_WEAPON_2_AURA: - case SPELL_COPY_WEAPON_3_AURA: - target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, prevItem); - break; - case SPELL_COPY_OFFHAND_AURA: - case SPELL_COPY_OFFHAND_2_AURA: - target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, prevItem); - break; - case SPELL_COPY_RANGED_AURA: - target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, prevItem); - break; - default: - break; + if (Item* rangedItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED)) + target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, rangedItem->GetEntry()); } + else + target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2)); + break; } + default: + break; + } + } - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_gen_clone_weapon_auraScript::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - OnEffectRemove += AuraEffectRemoveFn(spell_gen_clone_weapon_auraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - } + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + + switch (GetSpellInfo()->Id) + { + case SPELL_COPY_WEAPON_AURA: + case SPELL_COPY_WEAPON_2_AURA: + case SPELL_COPY_WEAPON_3_AURA: + target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, prevItem); + break; + case SPELL_COPY_OFFHAND_AURA: + case SPELL_COPY_OFFHAND_2_AURA: + target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, prevItem); + break; + case SPELL_COPY_RANGED_AURA: + target->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, prevItem); + break; + default: + break; + } + } - uint32 prevItem = 0; - }; + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_gen_clone_weapon_aura::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + OnEffectRemove += AuraEffectRemoveFn(spell_gen_clone_weapon_aura::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_clone_weapon_auraScript(); - } + uint32 prevItem = 0; }; class spell_gen_count_pct_from_max_hp : public SpellScriptLoader @@ -1063,86 +865,64 @@ enum CreateLanceSpells SPELL_CREATE_LANCE_HORDE = 63919 }; -class spell_gen_create_lance : public SpellScriptLoader +class spell_gen_create_lance : public SpellScript { - public: - spell_gen_create_lance() : SpellScriptLoader("spell_gen_create_lance") { } + PrepareSpellScript(spell_gen_create_lance); - class spell_gen_create_lance_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_gen_create_lance_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_CREATE_LANCE_ALLIANCE, - SPELL_CREATE_LANCE_HORDE - }); - } - - void HandleScript(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - - if (Player* target = GetHitPlayer()) - { - if (target->GetTeam() == ALLIANCE) - GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_ALLIANCE, true); - else - GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_HORDE, true); - } - } + SPELL_CREATE_LANCE_ALLIANCE, + SPELL_CREATE_LANCE_HORDE + }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_create_lance_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); - SpellScript* GetSpellScript() const override + if (Player* target = GetHitPlayer()) { - return new spell_gen_create_lance_SpellScript(); + if (target->GetTeam() == ALLIANCE) + GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_ALLIANCE, true); + else + GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_HORDE, true); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_create_lance::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; -class spell_gen_creature_permanent_feign_death : public SpellScriptLoader +class spell_gen_creature_permanent_feign_death : public AuraScript { - public: - spell_gen_creature_permanent_feign_death() : SpellScriptLoader("spell_gen_creature_permanent_feign_death") { } - - class spell_gen_creature_permanent_feign_death_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_creature_permanent_feign_death_AuraScript); - - void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - target->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); - target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); + PrepareAuraScript(spell_gen_creature_permanent_feign_death); - if (target->GetTypeId() == TYPEID_UNIT) - target->ToCreature()->SetReactState(REACT_PASSIVE); - } + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + target->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); - target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); - } + if (target->GetTypeId() == TYPEID_UNIT) + target->ToCreature()->SetReactState(REACT_PASSIVE); + } - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_gen_creature_permanent_feign_death_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - OnEffectRemove += AuraEffectRemoveFn(spell_gen_creature_permanent_feign_death_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_creature_permanent_feign_death_AuraScript(); - } + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_gen_creature_permanent_feign_death::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_gen_creature_permanent_feign_death::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } }; enum DalaranDisguiseSpells @@ -1233,50 +1013,38 @@ enum DamageReductionAura SPELL_DAMAGE_REDUCTION_AURA = 68066 }; -class spell_gen_damage_reduction_aura : public SpellScriptLoader +class spell_gen_damage_reduction_aura : public AuraScript { - public: - spell_gen_damage_reduction_aura() : SpellScriptLoader("spell_gen_damage_reduction_aura") { } - - class spell_gen_damage_reduction_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_damage_reduction_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DAMAGE_REDUCTION_AURA }); - } - - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - target->CastSpell(target, SPELL_DAMAGE_REDUCTION_AURA, true); - } - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (target->HasAura(SPELL_DAMAGE_REDUCTION_AURA) && !(target->HasAura(SPELL_BLESSING_OF_SANCTUARY) || - target->HasAura(SPELL_GREATER_BLESSING_OF_SANCTUARY) || - target->HasAura(SPELL_RENEWED_HOPE) || - target->HasAura(SPELL_VIGILANCE))) - { - target->RemoveAurasDueToSpell(SPELL_DAMAGE_REDUCTION_AURA); - } - } + PrepareAuraScript(spell_gen_damage_reduction_aura); - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_gen_damage_reduction_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - OnEffectRemove += AuraEffectRemoveFn(spell_gen_damage_reduction_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DAMAGE_REDUCTION_AURA }); + } - }; + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_DAMAGE_REDUCTION_AURA, true); + } - AuraScript* GetAuraScript() const override + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + if (target->HasAura(SPELL_DAMAGE_REDUCTION_AURA) && !(target->HasAura(SPELL_BLESSING_OF_SANCTUARY) || + target->HasAura(SPELL_GREATER_BLESSING_OF_SANCTUARY) || + target->HasAura(SPELL_RENEWED_HOPE) || + target->HasAura(SPELL_VIGILANCE))) { - return new spell_gen_damage_reduction_AuraScript(); + target->RemoveAurasDueToSpell(SPELL_DAMAGE_REDUCTION_AURA); } + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_gen_damage_reduction_aura::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + OnEffectRemove += AuraEffectRemoveFn(spell_gen_damage_reduction_aura::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + } }; enum DefendVisuals @@ -1286,114 +1054,92 @@ enum DefendVisuals SPELL_VISUAL_SHIELD_3 = 63132 }; -class spell_gen_defend : public SpellScriptLoader +class spell_gen_defend : public AuraScript { - public: - spell_gen_defend() : SpellScriptLoader("spell_gen_defend") { } + PrepareAuraScript(spell_gen_defend); - class spell_gen_defend_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_gen_defend_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_VISUAL_SHIELD_1, - SPELL_VISUAL_SHIELD_2, - SPELL_VISUAL_SHIELD_3 - }); - } - - void RefreshVisualShields(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - if (GetCaster()) - { - Unit* target = GetTarget(); + SPELL_VISUAL_SHIELD_1, + SPELL_VISUAL_SHIELD_2, + SPELL_VISUAL_SHIELD_3 + }); + } - for (uint8 i = 0; i < GetSpellInfo()->StackAmount; ++i) - target->RemoveAurasDueToSpell(SPELL_VISUAL_SHIELD_1 + i); + void RefreshVisualShields(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (GetCaster()) + { + Unit* target = GetTarget(); - target->CastSpell(target, SPELL_VISUAL_SHIELD_1 + GetAura()->GetStackAmount() - 1, true, nullptr, aurEff); - } - else - GetTarget()->RemoveAurasDueToSpell(GetId()); - } + for (uint8 i = 0; i < GetSpellInfo()->StackAmount; ++i) + target->RemoveAurasDueToSpell(SPELL_VISUAL_SHIELD_1 + i); - void RemoveVisualShields(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - for (uint8 i = 0; i < GetSpellInfo()->StackAmount; ++i) - GetTarget()->RemoveAurasDueToSpell(SPELL_VISUAL_SHIELD_1 + i); - } + target->CastSpell(target, SPELL_VISUAL_SHIELD_1 + GetAura()->GetStackAmount() - 1, true, nullptr, aurEff); + } + else + GetTarget()->RemoveAurasDueToSpell(GetId()); + } - void RemoveDummyFromDriver(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - if (TempSummon* vehicle = caster->ToTempSummon()) - if (Unit* rider = vehicle->GetSummoner()) - rider->RemoveAurasDueToSpell(GetId()); - } + void RemoveVisualShields(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + for (uint8 i = 0; i < GetSpellInfo()->StackAmount; ++i) + GetTarget()->RemoveAurasDueToSpell(SPELL_VISUAL_SHIELD_1 + i); + } - void Register() override - { - SpellInfo const* spell = sSpellMgr->AssertSpellInfo(m_scriptSpellId); + void RemoveDummyFromDriver(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + if (TempSummon* vehicle = caster->ToTempSummon()) + if (Unit* rider = vehicle->GetSummoner()) + rider->RemoveAurasDueToSpell(GetId()); + } - // Defend spells cast by NPCs (add visuals) - if (spell->Effects[EFFECT_0].ApplyAuraName == SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN) - { - AfterEffectApply += AuraEffectApplyFn(spell_gen_defend_AuraScript::RefreshVisualShields, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - OnEffectRemove += AuraEffectRemoveFn(spell_gen_defend_AuraScript::RemoveVisualShields, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK); - } + void Register() override + { + SpellInfo const* spell = sSpellMgr->AssertSpellInfo(m_scriptSpellId); - // Remove Defend spell from player when he dismounts - if (spell->Effects[EFFECT_2].ApplyAuraName == SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN) - OnEffectRemove += AuraEffectRemoveFn(spell_gen_defend_AuraScript::RemoveDummyFromDriver, EFFECT_2, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); + // Defend spells cast by NPCs (add visuals) + if (spell->Effects[EFFECT_0].ApplyAuraName == SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN) + { + AfterEffectApply += AuraEffectApplyFn(spell_gen_defend::RefreshVisualShields, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + OnEffectRemove += AuraEffectRemoveFn(spell_gen_defend::RemoveVisualShields, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK); + } - // Defend spells cast by players (add/remove visuals) - if (spell->Effects[EFFECT_1].ApplyAuraName == SPELL_AURA_DUMMY) - { - AfterEffectApply += AuraEffectApplyFn(spell_gen_defend_AuraScript::RefreshVisualShields, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - OnEffectRemove += AuraEffectRemoveFn(spell_gen_defend_AuraScript::RemoveVisualShields, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK); - } - } - }; + // Remove Defend spell from player when he dismounts + if (spell->Effects[EFFECT_2].ApplyAuraName == SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN) + OnEffectRemove += AuraEffectRemoveFn(spell_gen_defend::RemoveDummyFromDriver, EFFECT_2, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); - AuraScript* GetAuraScript() const override + // Defend spells cast by players (add/remove visuals) + if (spell->Effects[EFFECT_1].ApplyAuraName == SPELL_AURA_DUMMY) { - return new spell_gen_defend_AuraScript(); + AfterEffectApply += AuraEffectApplyFn(spell_gen_defend::RefreshVisualShields, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + OnEffectRemove += AuraEffectRemoveFn(spell_gen_defend::RemoveVisualShields, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK); } + } }; -class spell_gen_despawn_self : public SpellScriptLoader +class spell_gen_despawn_self : public SpellScript { - public: - spell_gen_despawn_self() : SpellScriptLoader("spell_gen_despawn_self") { } - - class spell_gen_despawn_self_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_despawn_self_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_UNIT; - } + PrepareSpellScript(spell_gen_despawn_self); - void HandleDummy(SpellEffIndex effIndex) - { - if (GetSpellInfo()->Effects[effIndex].Effect == SPELL_EFFECT_DUMMY || GetSpellInfo()->Effects[effIndex].Effect == SPELL_EFFECT_SCRIPT_EFFECT) - GetCaster()->ToCreature()->DespawnOrUnsummon(); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_despawn_self_SpellScript::HandleDummy, EFFECT_ALL, SPELL_EFFECT_ANY); - } - }; + void HandleDummy(SpellEffIndex effIndex) + { + if (GetSpellInfo()->Effects[effIndex].Effect == SPELL_EFFECT_DUMMY || GetSpellInfo()->Effects[effIndex].Effect == SPELL_EFFECT_SCRIPT_EFFECT) + GetCaster()->ToCreature()->DespawnOrUnsummon(); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_despawn_self_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_despawn_self::HandleDummy, EFFECT_ALL, SPELL_EFFECT_ANY); + } }; enum DivineStormSpell @@ -1402,119 +1148,86 @@ enum DivineStormSpell }; // 70769 Divine Storm! -class spell_gen_divine_storm_cd_reset : public SpellScriptLoader +class spell_gen_divine_storm_cd_reset : public SpellScript { - public: - spell_gen_divine_storm_cd_reset() : SpellScriptLoader("spell_gen_divine_storm_cd_reset") { } - - class spell_gen_divine_storm_cd_reset_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_divine_storm_cd_reset_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + PrepareSpellScript(spell_gen_divine_storm_cd_reset); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DIVINE_STORM }); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void HandleScript(SpellEffIndex /*effIndex*/) - { - Player* caster = GetCaster()->ToPlayer(); - if (caster->GetSpellHistory()->HasCooldown(SPELL_DIVINE_STORM)) - caster->GetSpellHistory()->ResetCooldown(SPELL_DIVINE_STORM, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DIVINE_STORM }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_divine_storm_cd_reset_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleScript(SpellEffIndex /*effIndex*/) + { + Player* caster = GetCaster()->ToPlayer(); + if (caster->GetSpellHistory()->HasCooldown(SPELL_DIVINE_STORM)) + caster->GetSpellHistory()->ResetCooldown(SPELL_DIVINE_STORM, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_divine_storm_cd_reset_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_divine_storm_cd_reset::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_gen_ds_flush_knockback : public SpellScriptLoader +class spell_gen_ds_flush_knockback : public SpellScript { - public: - spell_gen_ds_flush_knockback() : SpellScriptLoader("spell_gen_ds_flush_knockback") { } + PrepareSpellScript(spell_gen_ds_flush_knockback); - class spell_gen_ds_flush_knockback_SpellScript : public SpellScript + void HandleScript(SpellEffIndex /*effIndex*/) + { + // Here the target is the water spout and determines the position where the player is knocked from + if (Unit* target = GetHitUnit()) { - PrepareSpellScript(spell_gen_ds_flush_knockback_SpellScript); - - void HandleScript(SpellEffIndex /*effIndex*/) - { - // Here the target is the water spout and determines the position where the player is knocked from - if (Unit* target = GetHitUnit()) - { - if (Player* player = GetCaster()->ToPlayer()) - { - float horizontalSpeed = 20.0f + (40.0f - GetCaster()->GetDistance(target)); - float verticalSpeed = 8.0f; - // This method relies on the Dalaran Sewer map disposition and Water Spout position - // What we do is knock the player from a position exactly behind him and at the end of the pipe - player->KnockbackFrom(target->GetPositionX(), player->GetPositionY(), horizontalSpeed, verticalSpeed); - } - } - } - - void Register() override + if (Player* player = GetCaster()->ToPlayer()) { - OnEffectHitTarget += SpellEffectFn(spell_gen_ds_flush_knockback_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + float horizontalSpeed = 20.0f + (40.0f - GetCaster()->GetDistance(target)); + float verticalSpeed = 8.0f; + // This method relies on the Dalaran Sewer map disposition and Water Spout position + // What we do is knock the player from a position exactly behind him and at the end of the pipe + player->KnockbackFrom(target->GetPositionX(), player->GetPositionY(), horizontalSpeed, verticalSpeed); } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_gen_ds_flush_knockback_SpellScript(); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_ds_flush_knockback::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_gen_dungeon_credit : public SpellScriptLoader +class spell_gen_dungeon_credit : public SpellScript { - public: - spell_gen_dungeon_credit() : SpellScriptLoader("spell_gen_dungeon_credit") { } + PrepareSpellScript(spell_gen_dungeon_credit); - class spell_gen_dungeon_credit_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_dungeon_credit_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_UNIT; - } - - void CreditEncounter() - { - // This hook is executed for every target, make sure we only credit instance once - if (_handled) - return; - - _handled = true; - Unit* caster = GetCaster(); - if (InstanceScript* instance = caster->GetInstanceScript()) - instance->UpdateEncounterStateForSpellCast(GetSpellInfo()->Id, caster); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } - void Register() override - { - AfterHit += SpellHitFn(spell_gen_dungeon_credit_SpellScript::CreditEncounter); - } + void CreditEncounter() + { + // This hook is executed for every target, make sure we only credit instance once + if (_handled) + return; + + _handled = true; + Unit* caster = GetCaster(); + if (InstanceScript* instance = caster->GetInstanceScript()) + instance->UpdateEncounterStateForSpellCast(GetSpellInfo()->Id, caster); + } - bool _handled = false; - }; + void Register() override + { + AfterHit += SpellHitFn(spell_gen_dungeon_credit::CreditEncounter); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_dungeon_credit_SpellScript(); - } + bool _handled = false; }; enum EluneCandle @@ -1530,65 +1243,54 @@ enum EluneCandle SPELL_ELUNE_CANDLE_NORMAL = 26636 }; -class spell_gen_elune_candle : public SpellScriptLoader +class spell_gen_elune_candle : public SpellScript { - public: - spell_gen_elune_candle() : SpellScriptLoader("spell_gen_elune_candle") { } + PrepareSpellScript(spell_gen_elune_candle); - class spell_gen_elune_candle_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_elune_candle_SpellScript); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_ELUNE_CANDLE_OMEN_HEAD, + SPELL_ELUNE_CANDLE_OMEN_CHEST, + SPELL_ELUNE_CANDLE_OMEN_HAND_R, + SPELL_ELUNE_CANDLE_OMEN_HAND_L, + SPELL_ELUNE_CANDLE_NORMAL + }); + } - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_ELUNE_CANDLE_OMEN_HEAD, - SPELL_ELUNE_CANDLE_OMEN_CHEST, - SPELL_ELUNE_CANDLE_OMEN_HAND_R, - SPELL_ELUNE_CANDLE_OMEN_HAND_L, - SPELL_ELUNE_CANDLE_NORMAL - }); - } + void HandleScript(SpellEffIndex /*effIndex*/) + { + uint32 spellId = 0; - void HandleScript(SpellEffIndex /*effIndex*/) + if (GetHitUnit()->GetEntry() == NPC_OMEN) + { + switch (urand(0, 3)) { - uint32 spellId = 0; - - if (GetHitUnit()->GetEntry() == NPC_OMEN) - { - switch (urand(0, 3)) - { - case 0: - spellId = SPELL_ELUNE_CANDLE_OMEN_HEAD; - break; - case 1: - spellId = SPELL_ELUNE_CANDLE_OMEN_CHEST; - break; - case 2: - spellId = SPELL_ELUNE_CANDLE_OMEN_HAND_R; - break; - case 3: - spellId = SPELL_ELUNE_CANDLE_OMEN_HAND_L; - break; - } - } - else - spellId = SPELL_ELUNE_CANDLE_NORMAL; - - GetCaster()->CastSpell(GetHitUnit(), spellId, true, nullptr); + case 0: + spellId = SPELL_ELUNE_CANDLE_OMEN_HEAD; + break; + case 1: + spellId = SPELL_ELUNE_CANDLE_OMEN_CHEST; + break; + case 2: + spellId = SPELL_ELUNE_CANDLE_OMEN_HAND_R; + break; + case 3: + spellId = SPELL_ELUNE_CANDLE_OMEN_HAND_L; + break; } + } + else + spellId = SPELL_ELUNE_CANDLE_NORMAL; - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_elune_candle_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + GetCaster()->CastSpell(GetHitUnit(), spellId, true, nullptr); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_elune_candle_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_elune_candle::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum TransporterBackfires @@ -1598,100 +1300,78 @@ enum TransporterBackfires SPELL_TRANSPORTER_MALFUNCTION_MISS = 36902 }; -class spell_gen_gadgetzan_transporter_backfire : public SpellScriptLoader +class spell_gen_gadgetzan_transporter_backfire : public SpellScript { - public: - spell_gen_gadgetzan_transporter_backfire() : SpellScriptLoader("spell_gen_gadgetzan_transporter_backfire") { } + PrepareSpellScript(spell_gen_gadgetzan_transporter_backfire); - class spell_gen_gadgetzan_transporter_backfire_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_gen_gadgetzan_transporter_backfire_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_TRANSPORTER_MALFUNCTION_POLYMORPH, - SPELL_TRANSPORTER_EVIL_TWIN, - SPELL_TRANSPORTER_MALFUNCTION_MISS - }); - } - - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - int32 r = irand(0, 119); - if (r < 20) // Transporter Malfunction - 1/6 polymorph - caster->CastSpell(caster, SPELL_TRANSPORTER_MALFUNCTION_POLYMORPH, true); - else if (r < 100) // Evil Twin - 4/6 evil twin - caster->CastSpell(caster, SPELL_TRANSPORTER_EVIL_TWIN, true); - else // Transporter Malfunction - 1/6 miss the target - caster->CastSpell(caster, SPELL_TRANSPORTER_MALFUNCTION_MISS, true); - } + SPELL_TRANSPORTER_MALFUNCTION_POLYMORPH, + SPELL_TRANSPORTER_EVIL_TWIN, + SPELL_TRANSPORTER_MALFUNCTION_MISS + }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_gadgetzan_transporter_backfire_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + int32 r = irand(0, 119); + if (r < 20) // Transporter Malfunction - 1/6 polymorph + caster->CastSpell(caster, SPELL_TRANSPORTER_MALFUNCTION_POLYMORPH, true); + else if (r < 100) // Evil Twin - 4/6 evil twin + caster->CastSpell(caster, SPELL_TRANSPORTER_EVIL_TWIN, true); + else // Transporter Malfunction - 1/6 miss the target + caster->CastSpell(caster, SPELL_TRANSPORTER_MALFUNCTION_MISS, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_gadgetzan_transporter_backfire_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_gadgetzan_transporter_backfire::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_gen_gift_of_naaru : public SpellScriptLoader +class spell_gen_gift_of_naaru : public AuraScript { - public: - spell_gen_gift_of_naaru() : SpellScriptLoader("spell_gen_gift_of_naaru") { } - - class spell_gen_gift_of_naaru_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_gift_of_naaru_AuraScript); - - void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) - { - if (!GetCaster()) - return; - - float heal = 0.0f; - switch (GetSpellInfo()->SpellFamilyName) - { - case SPELLFAMILY_MAGE: - case SPELLFAMILY_WARLOCK: - case SPELLFAMILY_PRIEST: - heal = 1.885f * float(GetCaster()->SpellBaseDamageBonusDone(GetSpellInfo()->GetSchoolMask())); - break; - case SPELLFAMILY_PALADIN: - case SPELLFAMILY_SHAMAN: - heal = std::max(1.885f * float(GetCaster()->SpellBaseDamageBonusDone(GetSpellInfo()->GetSchoolMask())), 1.1f * float(GetCaster()->GetTotalAttackPowerValue(BASE_ATTACK))); - break; - case SPELLFAMILY_WARRIOR: - case SPELLFAMILY_HUNTER: - case SPELLFAMILY_DEATHKNIGHT: - heal = 1.1f * float(std::max(GetCaster()->GetTotalAttackPowerValue(BASE_ATTACK), GetCaster()->GetTotalAttackPowerValue(RANGED_ATTACK))); - break; - case SPELLFAMILY_GENERIC: - default: - break; - } - - int32 healTick = std::floor(heal / aurEff->GetTotalTicks()); - amount += int32(std::max(healTick, 0)); - } + PrepareAuraScript(spell_gen_gift_of_naaru); - void Register() override - { - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_gift_of_naaru_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_HEAL); - } - }; + void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) + { + if (!GetCaster()) + return; + + float heal = 0.0f; + switch (GetSpellInfo()->SpellFamilyName) + { + case SPELLFAMILY_MAGE: + case SPELLFAMILY_WARLOCK: + case SPELLFAMILY_PRIEST: + heal = 1.885f * float(GetCaster()->SpellBaseDamageBonusDone(GetSpellInfo()->GetSchoolMask())); + break; + case SPELLFAMILY_PALADIN: + case SPELLFAMILY_SHAMAN: + heal = std::max(1.885f * float(GetCaster()->SpellBaseDamageBonusDone(GetSpellInfo()->GetSchoolMask())), 1.1f * float(GetCaster()->GetTotalAttackPowerValue(BASE_ATTACK))); + break; + case SPELLFAMILY_WARRIOR: + case SPELLFAMILY_HUNTER: + case SPELLFAMILY_DEATHKNIGHT: + heal = 1.1f * float(std::max(GetCaster()->GetTotalAttackPowerValue(BASE_ATTACK), GetCaster()->GetTotalAttackPowerValue(RANGED_ATTACK))); + break; + case SPELLFAMILY_GENERIC: + default: + break; + } + + int32 healTick = std::floor(heal / aurEff->GetTotalTicks()); + amount += int32(std::max(healTick, 0)); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_gift_of_naaru_AuraScript(); - } + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_gift_of_naaru::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_HEAL); + } }; enum GnomishTransporter @@ -1700,66 +1380,44 @@ enum GnomishTransporter SPELL_TRANSPORTER_FAILURE = 23446 }; -class spell_gen_gnomish_transporter : public SpellScriptLoader +class spell_gen_gnomish_transporter : public SpellScript { - public: - spell_gen_gnomish_transporter() : SpellScriptLoader("spell_gen_gnomish_transporter") { } + PrepareSpellScript(spell_gen_gnomish_transporter); - class spell_gen_gnomish_transporter_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_gen_gnomish_transporter_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_TRANSPORTER_SUCCESS, - SPELL_TRANSPORTER_FAILURE - }); - } - - void HandleDummy(SpellEffIndex /* effIndex */) - { - GetCaster()->CastSpell(GetCaster(), roll_chance_i(50) ? SPELL_TRANSPORTER_SUCCESS : SPELL_TRANSPORTER_FAILURE, true); - } + SPELL_TRANSPORTER_SUCCESS, + SPELL_TRANSPORTER_FAILURE + }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_gnomish_transporter_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /* effIndex */) + { + GetCaster()->CastSpell(GetCaster(), roll_chance_i(50) ? SPELL_TRANSPORTER_SUCCESS : SPELL_TRANSPORTER_FAILURE, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_gnomish_transporter_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_gnomish_transporter::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_gen_lifeblood : public SpellScriptLoader +class spell_gen_lifeblood : public AuraScript { - public: - spell_gen_lifeblood() : SpellScriptLoader("spell_gen_lifeblood") { } - - class spell_gen_lifeblood_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_lifeblood_AuraScript); - - void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) - { - if (Unit* owner = GetUnitOwner()) - amount += int32(CalculatePct(owner->GetMaxHealth(), 1.5f / aurEff->GetTotalTicks())); - } + PrepareAuraScript(spell_gen_lifeblood); - void Register() override - { - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_lifeblood_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_HEAL); - } - }; + void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) + { + if (Unit* owner = GetUnitOwner()) + amount += int32(CalculatePct(owner->GetMaxHealth(), 1.5f / aurEff->GetTotalTicks())); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_lifeblood_AuraScript(); - } + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_lifeblood::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_HEAL); + } }; enum GenericLifebloom @@ -1791,7 +1449,7 @@ class spell_gen_lifebloom : public SpellScriptLoader void AfterRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) { - // Final heal only on duration end + // final heal only on duration end or dispel if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE && GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL) return; @@ -1823,52 +1481,41 @@ enum MagicRoosterSpells SPELL_MAGIC_ROOSTER_TAUREN_MALE = 66124 }; -class spell_gen_magic_rooster : public SpellScriptLoader +class spell_gen_magic_rooster : public SpellScript { - public: - spell_gen_magic_rooster() : SpellScriptLoader("spell_gen_magic_rooster") { } + PrepareSpellScript(spell_gen_magic_rooster); - class spell_gen_magic_rooster_SpellScript : public SpellScript + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + if (Player* target = GetHitPlayer()) { - PrepareSpellScript(spell_gen_magic_rooster_SpellScript); - - void HandleScript(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - if (Player* target = GetHitPlayer()) - { - // prevent client crashes from stacking mounts - target->RemoveAurasByType(SPELL_AURA_MOUNTED); - - uint32 spellId = SPELL_MAGIC_ROOSTER_NORMAL; - switch (target->getRace()) - { - case RACE_DRAENEI: - if (target->getGender() == GENDER_MALE) - spellId = SPELL_MAGIC_ROOSTER_DRAENEI_MALE; - break; - case RACE_TAUREN: - if (target->getGender() == GENDER_MALE) - spellId = SPELL_MAGIC_ROOSTER_TAUREN_MALE; - break; - default: - break; - } - - target->CastSpell(target, spellId, true); - } - } + // prevent client crashes from stacking mounts + target->RemoveAurasByType(SPELL_AURA_MOUNTED); - void Register() override + uint32 spellId = SPELL_MAGIC_ROOSTER_NORMAL; + switch (target->getRace()) { - OnEffectHitTarget += SpellEffectFn(spell_gen_magic_rooster_SpellScript::HandleScript, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT); + case RACE_DRAENEI: + if (target->getGender() == GENDER_MALE) + spellId = SPELL_MAGIC_ROOSTER_DRAENEI_MALE; + break; + case RACE_TAUREN: + if (target->getGender() == GENDER_MALE) + spellId = SPELL_MAGIC_ROOSTER_TAUREN_MALE; + break; + default: + break; } - }; - SpellScript* GetSpellScript() const override - { - return new spell_gen_magic_rooster_SpellScript(); + target->CastSpell(target, spellId, true); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_magic_rooster::HandleScript, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum Mounts @@ -2089,115 +1736,104 @@ enum ChargeSpells SPELL_CHARGE_MISS_EFFECT = 62977, }; -class spell_gen_mounted_charge: public SpellScriptLoader +class spell_gen_mounted_charge : public SpellScript { - public: - spell_gen_mounted_charge() : SpellScriptLoader("spell_gen_mounted_charge") { } - - class spell_gen_mounted_charge_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_mounted_charge_SpellScript); - - void HandleScriptEffect(SpellEffIndex effIndex) - { - Unit* target = GetHitUnit(); - - switch (effIndex) - { - case EFFECT_0: // On spells wich trigger the damaging spell (and also the visual) - { - uint32 spellId; - - switch (GetSpellInfo()->Id) - { - case SPELL_CHARGE_TRIGGER_TRIAL_CHAMPION: - spellId = SPELL_CHARGE_CHARGING_EFFECT_20K_1; - break; - case SPELL_CHARGE_TRIGGER_FACTION_MOUNTS: - spellId = SPELL_CHARGE_CHARGING_EFFECT_8K5; - break; - default: - return; - } - - // If target isn't a training dummy there's a chance of failing the charge - if (!target->IsCharmedOwnedByPlayerOrPlayer() && roll_chance_f(12.5f)) - spellId = SPELL_CHARGE_MISS_EFFECT; + PrepareSpellScript(spell_gen_mounted_charge); - if (Unit* vehicle = GetCaster()->GetVehicleBase()) - vehicle->CastSpell(target, spellId, false); - else - GetCaster()->CastSpell(target, spellId, false); - break; - } - case EFFECT_1: // On damaging spells, for removing a defend layer - case EFFECT_2: - { - Unit::AuraApplicationMap const& auras = target->GetAppliedAuras(); - for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - if (Aura* aura = itr->second->GetBase()) - { - SpellInfo const* auraInfo = aura->GetSpellInfo(); - if (auraInfo && auraInfo->SpellIconID == 2007 && aura->HasEffectType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN)) - { - aura->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL); - // Remove dummys from rider (Necessary for updating visual shields) - if (Unit* rider = target->GetCharmer()) - if (Aura* defend = rider->GetAura(aura->GetId())) - defend->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL); - break; - } - } - } - break; - } - } - } + void HandleScriptEffect(SpellEffIndex effIndex) + { + Unit* target = GetHitUnit(); - void HandleChargeEffect(SpellEffIndex /*effIndex*/) + switch (effIndex) + { + case EFFECT_0: // On spells wich trigger the damaging spell (and also the visual) { uint32 spellId; switch (GetSpellInfo()->Id) { - case SPELL_CHARGE_CHARGING_EFFECT_8K5: - spellId = SPELL_CHARGE_DAMAGE_8K5; - break; - case SPELL_CHARGE_CHARGING_EFFECT_20K_1: - case SPELL_CHARGE_CHARGING_EFFECT_20K_2: - spellId = SPELL_CHARGE_DAMAGE_20K; + case SPELL_CHARGE_TRIGGER_TRIAL_CHAMPION: + spellId = SPELL_CHARGE_CHARGING_EFFECT_20K_1; break; - case SPELL_CHARGE_CHARGING_EFFECT_45K_1: - case SPELL_CHARGE_CHARGING_EFFECT_45K_2: - spellId = SPELL_CHARGE_DAMAGE_45K; + case SPELL_CHARGE_TRIGGER_FACTION_MOUNTS: + spellId = SPELL_CHARGE_CHARGING_EFFECT_8K5; break; default: return; } - if (Unit* rider = GetCaster()->GetCharmer()) - rider->CastSpell(GetHitUnit(), spellId, false); + // If target isn't a training dummy there's a chance of failing the charge + if (!target->IsCharmedOwnedByPlayerOrPlayer() && roll_chance_f(12.5f)) + spellId = SPELL_CHARGE_MISS_EFFECT; + + if (Unit* vehicle = GetCaster()->GetVehicleBase()) + vehicle->CastSpell(target, spellId, false); else - GetCaster()->CastSpell(GetHitUnit(), spellId, false); + GetCaster()->CastSpell(target, spellId, false); + break; } - - void Register() override + case EFFECT_1: // On damaging spells, for removing a defend layer + case EFFECT_2: { - SpellInfo const* spell = sSpellMgr->AssertSpellInfo(m_scriptSpellId); - - if (spell->HasEffect(SPELL_EFFECT_SCRIPT_EFFECT)) - OnEffectHitTarget += SpellEffectFn(spell_gen_mounted_charge_SpellScript::HandleScriptEffect, EFFECT_FIRST_FOUND, SPELL_EFFECT_SCRIPT_EFFECT); - - if (spell->Effects[EFFECT_0].Effect == SPELL_EFFECT_CHARGE) - OnEffectHitTarget += SpellEffectFn(spell_gen_mounted_charge_SpellScript::HandleChargeEffect, EFFECT_0, SPELL_EFFECT_CHARGE); + Unit::AuraApplicationMap const& auras = target->GetAppliedAuras(); + for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + { + if (Aura* aura = itr->second->GetBase()) + { + SpellInfo const* auraInfo = aura->GetSpellInfo(); + if (auraInfo && auraInfo->SpellIconID == 2007 && aura->HasEffectType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN)) + { + aura->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL); + // Remove dummys from rider (Necessary for updating visual shields) + if (Unit* rider = target->GetCharmer()) + if (Aura* defend = rider->GetAura(aura->GetId())) + defend->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL); + break; + } + } + } + break; } - }; + } + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_mounted_charge_SpellScript(); + void HandleChargeEffect(SpellEffIndex /*effIndex*/) + { + uint32 spellId; + + switch (GetSpellInfo()->Id) + { + case SPELL_CHARGE_CHARGING_EFFECT_8K5: + spellId = SPELL_CHARGE_DAMAGE_8K5; + break; + case SPELL_CHARGE_CHARGING_EFFECT_20K_1: + case SPELL_CHARGE_CHARGING_EFFECT_20K_2: + spellId = SPELL_CHARGE_DAMAGE_20K; + break; + case SPELL_CHARGE_CHARGING_EFFECT_45K_1: + case SPELL_CHARGE_CHARGING_EFFECT_45K_2: + spellId = SPELL_CHARGE_DAMAGE_45K; + break; + default: + return; } + + if (Unit* rider = GetCaster()->GetCharmer()) + rider->CastSpell(GetHitUnit(), spellId, false); + else + GetCaster()->CastSpell(GetHitUnit(), spellId, false); + } + + void Register() override + { + SpellInfo const* spell = sSpellMgr->AssertSpellInfo(m_scriptSpellId); + + if (spell->HasEffect(SPELL_EFFECT_SCRIPT_EFFECT)) + OnEffectHitTarget += SpellEffectFn(spell_gen_mounted_charge::HandleScriptEffect, EFFECT_FIRST_FOUND, SPELL_EFFECT_SCRIPT_EFFECT); + + if (spell->Effects[EFFECT_0].Effect == SPELL_EFFECT_CHARGE) + OnEffectHitTarget += SpellEffectFn(spell_gen_mounted_charge::HandleChargeEffect, EFFECT_0, SPELL_EFFECT_CHARGE); + } }; enum MossCoveredFeet @@ -2207,36 +1843,25 @@ enum MossCoveredFeet // 6870 Moss Covered Feet // 31399 Moss Covered Feet -class spell_gen_moss_covered_feet : public SpellScriptLoader +class spell_gen_moss_covered_feet : public AuraScript { - public: - spell_gen_moss_covered_feet() : SpellScriptLoader("spell_gen_moss_covered_feet") { } - - class spell_gen_moss_covered_feet_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_moss_covered_feet_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_FALL_DOWN }); - } + PrepareAuraScript(spell_gen_moss_covered_feet); - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - eventInfo.GetActionTarget()->CastSpell((Unit*)nullptr, SPELL_FALL_DOWN, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FALL_DOWN }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_gen_moss_covered_feet_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActionTarget()->CastSpell(nullptr, SPELL_FALL_DOWN, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_moss_covered_feet_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_gen_moss_covered_feet::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; enum Netherbloom : uint32 @@ -2245,57 +1870,46 @@ enum Netherbloom : uint32 }; // 28702 - Netherbloom -class spell_gen_netherbloom : public SpellScriptLoader +class spell_gen_netherbloom : public SpellScript { - public: - spell_gen_netherbloom() : SpellScriptLoader("spell_gen_netherbloom") { } - - class spell_gen_netherbloom_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_netherbloom_SpellScript); + PrepareSpellScript(spell_gen_netherbloom); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - for (uint8 i = 0; i < 5; ++i) - if (!ValidateSpellInfo({ SPELL_NETHERBLOOM_POLLEN_1 + i })) - return false; - - return true; - } - - void HandleScript(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + for (uint8 i = 0; i < 5; ++i) + if (!ValidateSpellInfo({ SPELL_NETHERBLOOM_POLLEN_1 + i })) + return false; - if (Unit* target = GetHitUnit()) - { - // 25% chance of casting a random buff - if (roll_chance_i(75)) - return; + return true; + } - // triggered spells are 28703 to 28707 - // Note: some sources say, that there was the possibility of - // receiving a debuff. However, this seems to be removed by a patch. + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); - // don't overwrite an existing aura - for (uint8 i = 0; i < 5; ++i) - if (target->HasAura(SPELL_NETHERBLOOM_POLLEN_1 + i)) - return; + if (Unit* target = GetHitUnit()) + { + // 25% chance of casting a random buff + if (roll_chance_i(75)) + return; - target->CastSpell(target, SPELL_NETHERBLOOM_POLLEN_1 + urand(0, 4), true); - } - } + // triggered spells are 28703 to 28707 + // Note: some sources say, that there was the possibility of + // receiving a debuff. However, this seems to be removed by a patch. - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_netherbloom_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + // don't overwrite an existing aura + for (uint8 i = 0; i < 5; ++i) + if (target->HasAura(SPELL_NETHERBLOOM_POLLEN_1 + i)) + return; - SpellScript* GetSpellScript() const override - { - return new spell_gen_netherbloom_SpellScript(); + target->CastSpell(target, SPELL_NETHERBLOOM_POLLEN_1 + urand(0, 4), true); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_netherbloom::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum NightmareVine @@ -2304,42 +1918,31 @@ enum NightmareVine }; // 28720 - Nightmare Vine -class spell_gen_nightmare_vine : public SpellScriptLoader +class spell_gen_nightmare_vine : public SpellScript { - public: - spell_gen_nightmare_vine() : SpellScriptLoader("spell_gen_nightmare_vine") { } - - class spell_gen_nightmare_vine_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_nightmare_vine_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_NIGHTMARE_POLLEN }); - } + PrepareSpellScript(spell_gen_nightmare_vine); - void HandleScript(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - - if (Unit* target = GetHitUnit()) - { - // 25% chance of casting Nightmare Pollen - if (roll_chance_i(25)) - target->CastSpell(target, SPELL_NIGHTMARE_POLLEN, true); - } - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_NIGHTMARE_POLLEN }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_nightmare_vine_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); - SpellScript* GetSpellScript() const override + if (Unit* target = GetHitUnit()) { - return new spell_gen_nightmare_vine_SpellScript(); + // 25% chance of casting Nightmare Pollen + if (roll_chance_i(25)) + target->CastSpell(target, SPELL_NIGHTMARE_POLLEN, true); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_nightmare_vine::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum ObsidianArmor @@ -2353,126 +1956,104 @@ enum ObsidianArmor }; // 27539 - Obsidian Armor -class spell_gen_obsidian_armor : public SpellScriptLoader +class spell_gen_obsidian_armor : public AuraScript { - public: - spell_gen_obsidian_armor() : SpellScriptLoader("spell_gen_obsidian_armor") { } - - class spell_gen_obsidian_armor_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_obsidian_armor_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_GEN_OBSIDIAN_ARMOR_HOLY, - SPELL_GEN_OBSIDIAN_ARMOR_FIRE, - SPELL_GEN_OBSIDIAN_ARMOR_NATURE, - SPELL_GEN_OBSIDIAN_ARMOR_FROST, - SPELL_GEN_OBSIDIAN_ARMOR_SHADOW, - SPELL_GEN_OBSIDIAN_ARMOR_ARCANE - }); - } - - bool CheckProc(ProcEventInfo& eventInfo) - { - DamageInfo* damageInfo = eventInfo.GetDamageInfo(); - if (!damageInfo || !damageInfo->GetSpellInfo()) - return false; - - if (GetFirstSchoolInMask(eventInfo.GetSchoolMask()) == SPELL_SCHOOL_NORMAL) - return false; + PrepareAuraScript(spell_gen_obsidian_armor); - return true; - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_GEN_OBSIDIAN_ARMOR_HOLY, + SPELL_GEN_OBSIDIAN_ARMOR_FIRE, + SPELL_GEN_OBSIDIAN_ARMOR_NATURE, + SPELL_GEN_OBSIDIAN_ARMOR_FROST, + SPELL_GEN_OBSIDIAN_ARMOR_SHADOW, + SPELL_GEN_OBSIDIAN_ARMOR_ARCANE + }); + } - void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); + bool CheckProc(ProcEventInfo& eventInfo) + { + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetSpellInfo()) + return false; - uint32 spellId = 0; - switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask())) - { - case SPELL_SCHOOL_HOLY: - spellId = SPELL_GEN_OBSIDIAN_ARMOR_HOLY; - break; - case SPELL_SCHOOL_FIRE: - spellId = SPELL_GEN_OBSIDIAN_ARMOR_FIRE; - break; - case SPELL_SCHOOL_NATURE: - spellId = SPELL_GEN_OBSIDIAN_ARMOR_NATURE; - break; - case SPELL_SCHOOL_FROST: - spellId = SPELL_GEN_OBSIDIAN_ARMOR_FROST; - break; - case SPELL_SCHOOL_SHADOW: - spellId = SPELL_GEN_OBSIDIAN_ARMOR_SHADOW; - break; - case SPELL_SCHOOL_ARCANE: - spellId = SPELL_GEN_OBSIDIAN_ARMOR_ARCANE; - break; - default: - return; - } - GetTarget()->CastSpell(GetTarget(), spellId, true, nullptr, aurEff); - } + if (GetFirstSchoolInMask(eventInfo.GetSchoolMask()) == SPELL_SCHOOL_NORMAL) + return false; - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_gen_obsidian_armor_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_gen_obsidian_armor_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + return true; + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_obsidian_armor_AuraScript(); + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + uint32 spellId = 0; + switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_HOLY; + break; + case SPELL_SCHOOL_FIRE: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_FIRE; + break; + case SPELL_SCHOOL_NATURE: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_NATURE; + break; + case SPELL_SCHOOL_FROST: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_FROST; + break; + case SPELL_SCHOOL_SHADOW: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_SHADOW; + break; + case SPELL_SCHOOL_ARCANE: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_ARCANE; + break; + default: + return; } + GetTarget()->CastSpell(GetTarget(), spellId, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_gen_obsidian_armor::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_gen_obsidian_armor::OnProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; -class spell_gen_oracle_wolvar_reputation : public SpellScriptLoader +class spell_gen_oracle_wolvar_reputation : public SpellScript { - public: - spell_gen_oracle_wolvar_reputation() : SpellScriptLoader("spell_gen_oracle_wolvar_reputation") { } - - class spell_gen_oracle_wolvar_reputation_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_oracle_wolvar_reputation_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + PrepareSpellScript(spell_gen_oracle_wolvar_reputation); - void HandleDummy(SpellEffIndex effIndex) - { - Player* player = GetCaster()->ToPlayer(); - uint32 factionId = GetSpellInfo()->Effects[effIndex].CalcValue(); - int32 repChange = GetSpellInfo()->Effects[EFFECT_1].CalcValue(); + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId); - if (!factionEntry) - return; + void HandleDummy(SpellEffIndex effIndex) + { + Player* player = GetCaster()->ToPlayer(); + uint32 factionId = GetSpellInfo()->Effects[effIndex].CalcValue(); + int32 repChange = GetSpellInfo()->Effects[EFFECT_1].CalcValue(); - // Set rep to baserep + basepoints (expecting spillover for oposite faction -> become hated) - // Not when player already has equal or higher rep with this faction - if (player->GetReputationMgr().GetReputation(factionEntry) < repChange) - player->GetReputationMgr().SetReputation(factionEntry, repChange); + FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId); + if (!factionEntry) + return; - // EFFECT_INDEX_2 most likely update at war state, we already handle this in SetReputation - } + // Set rep to baserep + basepoints (expecting spillover for oposite faction -> become hated) + // Not when player already has equal or higher rep with this faction + if (player->GetReputationMgr().GetReputation(factionEntry) < repChange) + player->GetReputationMgr().SetReputation(factionEntry, repChange); - void Register() override - { - OnEffectHit += SpellEffectFn(spell_gen_oracle_wolvar_reputation_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + // EFFECT_INDEX_2 most likely update at war state, we already handle this in SetReputation + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_oracle_wolvar_reputation_SpellScript(); - } + void Register() override + { + OnEffectHit += SpellEffectFn(spell_gen_oracle_wolvar_reputation::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum OrcDisguiseSpells @@ -2482,48 +2063,37 @@ enum OrcDisguiseSpells SPELL_ORC_DISGUISE_FEMALE = 45762 }; -class spell_gen_orc_disguise : public SpellScriptLoader +class spell_gen_orc_disguise : public SpellScript { - public: - spell_gen_orc_disguise() : SpellScriptLoader("spell_gen_orc_disguise") { } + PrepareSpellScript(spell_gen_orc_disguise); - class spell_gen_orc_disguise_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_gen_orc_disguise_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_ORC_DISGUISE_TRIGGER, - SPELL_ORC_DISGUISE_MALE, - SPELL_ORC_DISGUISE_FEMALE - }); - } - - void HandleScript(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - if (Player* target = GetHitPlayer()) - { - uint8 gender = target->getGender(); - if (!gender) - caster->CastSpell(target, SPELL_ORC_DISGUISE_MALE, true); - else - caster->CastSpell(target, SPELL_ORC_DISGUISE_FEMALE, true); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_orc_disguise_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + SPELL_ORC_DISGUISE_TRIGGER, + SPELL_ORC_DISGUISE_MALE, + SPELL_ORC_DISGUISE_FEMALE + }); + } - SpellScript* GetSpellScript() const override + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + if (Player* target = GetHitPlayer()) { - return new spell_gen_orc_disguise_SpellScript(); + uint8 gender = target->getGender(); + if (!gender) + caster->CastSpell(target, SPELL_ORC_DISGUISE_MALE, true); + else + caster->CastSpell(target, SPELL_ORC_DISGUISE_FEMALE, true); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_orc_disguise::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum ParalyticPoison @@ -2532,38 +2102,27 @@ enum ParalyticPoison }; // 35201 - Paralytic Poison -class spell_gen_paralytic_poison : public SpellScriptLoader +class spell_gen_paralytic_poison : public AuraScript { - public: - spell_gen_paralytic_poison() : SpellScriptLoader("spell_gen_paralytic_poison") { } - - class spell_gen_paralytic_poison_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_paralytic_poison_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_PARALYSIS }); - } + PrepareAuraScript(spell_gen_paralytic_poison); - void HandleStun(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE) - return; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PARALYSIS }); + } - GetTarget()->CastSpell((Unit*)nullptr, SPELL_PARALYSIS, true, nullptr, aurEff); - } + void HandleStun(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE) + return; - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_gen_paralytic_poison_AuraScript::HandleStun, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); - } - }; + GetTarget()->CastSpell(nullptr, SPELL_PARALYSIS, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_paralytic_poison_AuraScript(); - } + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_gen_paralytic_poison::HandleStun, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } }; class spell_gen_proc_below_pct_damaged : public SpellScriptLoader @@ -2601,30 +2160,19 @@ class spell_gen_proc_below_pct_damaged : public SpellScriptLoader } }; -class spell_gen_proc_charge_drop_only : public SpellScriptLoader +class spell_gen_proc_charge_drop_only : public AuraScript { - public: - spell_gen_proc_charge_drop_only() : SpellScriptLoader("spell_gen_proc_charge_drop_only") { } - - class spell_gen_proc_charge_drop_only_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_proc_charge_drop_only_AuraScript); + PrepareAuraScript(spell_gen_proc_charge_drop_only); - void HandleChargeDrop(ProcEventInfo& /*eventInfo*/) - { - PreventDefaultAction(); - } - - void Register() override - { - OnProc += AuraProcFn(spell_gen_proc_charge_drop_only_AuraScript::HandleChargeDrop); - } - }; + void HandleChargeDrop(ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_proc_charge_drop_only_AuraScript(); - } + void Register() override + { + OnProc += AuraProcFn(spell_gen_proc_charge_drop_only::HandleChargeDrop); + } }; enum ParachuteSpells @@ -2634,44 +2182,33 @@ enum ParachuteSpells }; // 45472 Parachute -class spell_gen_parachute : public SpellScriptLoader +class spell_gen_parachute : public AuraScript { - public: - spell_gen_parachute() : SpellScriptLoader("spell_gen_parachute") { } + PrepareAuraScript(spell_gen_parachute); - class spell_gen_parachute_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_gen_parachute_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_PARACHUTE, - SPELL_PARACHUTE_BUFF - }); - } - - void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) - { - if (Player* target = GetTarget()->ToPlayer()) - if (target->IsFalling()) - { - target->RemoveAurasDueToSpell(SPELL_PARACHUTE); - target->CastSpell(target, SPELL_PARACHUTE_BUFF, true); - } - } + SPELL_PARACHUTE, + SPELL_PARACHUTE_BUFF + }); + } - void Register() override + void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) + { + if (Player* target = GetTarget()->ToPlayer()) + if (target->IsFalling()) { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_parachute_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + target->RemoveAurasDueToSpell(SPELL_PARACHUTE); + target->CastSpell(target, SPELL_PARACHUTE_BUFF, true); } - }; + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_parachute_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_parachute::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } }; enum PetSummoned @@ -2681,141 +2218,108 @@ enum PetSummoned NPC_IMP = 416 }; -class spell_gen_pet_summoned : public SpellScriptLoader +class spell_gen_pet_summoned : public SpellScript { - public: - spell_gen_pet_summoned() : SpellScriptLoader("spell_gen_pet_summoned") { } - - class spell_gen_pet_summoned_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_pet_summoned_SpellScript); + PrepareSpellScript(spell_gen_pet_summoned); - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void HandleScript(SpellEffIndex /*effIndex*/) + void HandleScript(SpellEffIndex /*effIndex*/) + { + Player* player = GetCaster()->ToPlayer(); + if (player->GetLastPetNumber()) + { + PetType newPetType = (player->getClass() == CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET; + Pet* newPet = new Pet(player, newPetType); + if (newPet->LoadPetFromDB(player, 0, player->GetLastPetNumber(), true)) { - Player* player = GetCaster()->ToPlayer(); - if (player->GetLastPetNumber()) - { - PetType newPetType = (player->getClass() == CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET; - Pet* newPet = new Pet(player, newPetType); - if (newPet->LoadPetFromDB(player, 0, player->GetLastPetNumber(), true)) - { - // revive the pet if it is dead - if (newPet->getDeathState() == DEAD) - newPet->setDeathState(ALIVE); + // revive the pet if it is dead + if (newPet->getDeathState() == DEAD) + newPet->setDeathState(ALIVE); - newPet->SetFullHealth(); - newPet->SetPower(newPet->getPowerType(), newPet->GetMaxPower(newPet->getPowerType())); + newPet->SetFullHealth(); + newPet->SetPower(newPet->getPowerType(), newPet->GetMaxPower(newPet->getPowerType())); - switch (newPet->GetEntry()) - { - case NPC_DOOMGUARD: - case NPC_INFERNAL: - newPet->SetEntry(NPC_IMP); - break; - default: - break; - } - } - else - delete newPet; + switch (newPet->GetEntry()) + { + case NPC_DOOMGUARD: + case NPC_INFERNAL: + newPet->SetEntry(NPC_IMP); + break; + default: + break; } } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_pet_summoned_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_gen_pet_summoned_SpellScript(); + else + delete newPet; } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_pet_summoned::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; -class spell_gen_profession_research : public SpellScriptLoader +class spell_gen_profession_research : public SpellScript { - public: - spell_gen_profession_research() : SpellScriptLoader("spell_gen_profession_research") { } + PrepareSpellScript(spell_gen_profession_research); - class spell_gen_profession_research_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_profession_research_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - - SpellCastResult CheckRequirement() - { - if (HasDiscoveredAllSpells(GetSpellInfo()->Id, GetCaster()->ToPlayer())) - { - SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_NOTHING_TO_DISCOVER); - return SPELL_FAILED_CUSTOM_ERROR; - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - return SPELL_CAST_OK; - } + SpellCastResult CheckRequirement() + { + if (HasDiscoveredAllSpells(GetSpellInfo()->Id, GetCaster()->ToPlayer())) + { + SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_NOTHING_TO_DISCOVER); + return SPELL_FAILED_CUSTOM_ERROR; + } - void HandleScript(SpellEffIndex /*effIndex*/) - { - Player* caster = GetCaster()->ToPlayer(); - uint32 spellId = GetSpellInfo()->Id; + return SPELL_CAST_OK; + } - // learn random explicit discovery recipe (if any) - if (uint32 discoveredSpellId = GetExplicitDiscoverySpell(spellId, caster)) - caster->LearnSpell(discoveredSpellId, false); + void HandleScript(SpellEffIndex /*effIndex*/) + { + Player* caster = GetCaster()->ToPlayer(); + uint32 spellId = GetSpellInfo()->Id; - caster->UpdateCraftSkill(spellId); - } + // learn random explicit discovery recipe (if any) + if (uint32 discoveredSpellId = GetExplicitDiscoverySpell(spellId, caster)) + caster->LearnSpell(discoveredSpellId, false); - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_gen_profession_research_SpellScript::CheckRequirement); - OnEffectHitTarget += SpellEffectFn(spell_gen_profession_research_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + caster->UpdateCraftSkill(spellId); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_profession_research_SpellScript(); - } + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_gen_profession_research::CheckRequirement); + OnEffectHitTarget += SpellEffectFn(spell_gen_profession_research::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } }; -class spell_gen_remove_flight_auras : public SpellScriptLoader +class spell_gen_remove_flight_auras : public SpellScript { - public: - spell_gen_remove_flight_auras() : SpellScriptLoader("spell_gen_remove_flight_auras") { } - - class spell_gen_remove_flight_auras_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_remove_flight_auras_SpellScript); - - void HandleScript(SpellEffIndex /*effIndex*/) - { - if (Unit* target = GetHitUnit()) - { - target->RemoveAurasByType(SPELL_AURA_FLY); - target->RemoveAurasByType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_remove_flight_auras_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + PrepareSpellScript(spell_gen_remove_flight_auras); - SpellScript* GetSpellScript() const override + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) { - return new spell_gen_remove_flight_auras_SpellScript(); + target->RemoveAurasByType(SPELL_AURA_FLY); + target->RemoveAurasByType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_remove_flight_auras::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum Replenishment @@ -2836,84 +2340,68 @@ public: } }; -class spell_gen_replenishment : public SpellScriptLoader +class spell_gen_replenishment : public SpellScript { - public: - spell_gen_replenishment() : SpellScriptLoader("spell_gen_replenishment") { } + PrepareSpellScript(spell_gen_replenishment); - class spell_gen_replenishment_SpellScript : public SpellScript + void RemoveInvalidTargets(std::list<WorldObject*>& targets) + { + // In arenas Replenishment may only affect the caster + if (Player* caster = GetCaster()->ToPlayer()) { - PrepareSpellScript(spell_gen_replenishment_SpellScript); - - void RemoveInvalidTargets(std::list<WorldObject*>& targets) + if (caster->InArena()) { - // In arenas Replenishment may only affect the caster - if (Player* caster = GetCaster()->ToPlayer()) - { - if (caster->InArena()) - { - targets.clear(); - targets.push_back(caster); - return; - } - } - - targets.remove_if(ReplenishmentCheck()); - - uint8 const maxTargets = 10; - - if (targets.size() > maxTargets) - { - targets.sort(Trinity::PowerPctOrderPred(POWER_MANA)); - targets.resize(maxTargets); - } + targets.clear(); + targets.push_back(caster); + return; } + } - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gen_replenishment_SpellScript::RemoveInvalidTargets, EFFECT_ALL, TARGET_UNIT_CASTER_AREA_RAID); - } - }; + targets.remove_if(ReplenishmentCheck()); - SpellScript* GetSpellScript() const override - { - return new spell_gen_replenishment_SpellScript(); - } + uint8 const maxTargets = 10; - class spell_gen_replenishment_AuraScript : public AuraScript + if (targets.size() > maxTargets) { - PrepareAuraScript(spell_gen_replenishment_AuraScript); + targets.sort(Trinity::PowerPctOrderPred(POWER_MANA)); + targets.resize(maxTargets); + } + } - bool Load() override - { - return GetUnitOwner()->getPowerType() == POWER_MANA; - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gen_replenishment::RemoveInvalidTargets, EFFECT_ALL, TARGET_UNIT_CASTER_AREA_RAID); + } +}; - void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) - { - switch (GetSpellInfo()->Id) - { - case SPELL_REPLENISHMENT: - amount = GetUnitOwner()->GetMaxPower(POWER_MANA) * 0.002f; - break; - case SPELL_INFINITE_REPLENISHMENT: - amount = GetUnitOwner()->GetMaxPower(POWER_MANA) * 0.0025f; - break; - default: - break; - } - } +class spell_gen_replenishment_aura : public AuraScript +{ + PrepareAuraScript(spell_gen_replenishment_aura); - void Register() override - { - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_replenishment_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_ENERGIZE); - } - }; + bool Load() override + { + return GetUnitOwner()->getPowerType() == POWER_MANA; + } - AuraScript* GetAuraScript() const override + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + switch (GetSpellInfo()->Id) { - return new spell_gen_replenishment_AuraScript(); + case SPELL_REPLENISHMENT: + amount = GetUnitOwner()->GetMaxPower(POWER_MANA) * 0.002f; + break; + case SPELL_INFINITE_REPLENISHMENT: + amount = GetUnitOwner()->GetMaxPower(POWER_MANA) * 0.0025f; + break; + default: + break; } + } + + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_replenishment_aura::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_ENERGIZE); + } }; enum SeaforiumSpells @@ -2921,45 +2409,34 @@ enum SeaforiumSpells SPELL_PLANT_CHARGES_CREDIT_ACHIEVEMENT = 60937 }; -class spell_gen_seaforium_blast : public SpellScriptLoader +class spell_gen_seaforium_blast : public SpellScript { - public: - spell_gen_seaforium_blast() : SpellScriptLoader("spell_gen_seaforium_blast") { } - - class spell_gen_seaforium_blast_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_seaforium_blast_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_PLANT_CHARGES_CREDIT_ACHIEVEMENT }); - } + PrepareSpellScript(spell_gen_seaforium_blast); - bool Load() override - { - // OriginalCaster is always available in Spell::prepare - return GetOriginalCaster()->GetTypeId() == TYPEID_PLAYER; - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PLANT_CHARGES_CREDIT_ACHIEVEMENT }); + } - void AchievementCredit(SpellEffIndex /*effIndex*/) - { - // but in effect handling OriginalCaster can become nullptr - if (Unit* originalCaster = GetOriginalCaster()) - if (GameObject* go = GetHitGObj()) - if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) - originalCaster->CastSpell(originalCaster, SPELL_PLANT_CHARGES_CREDIT_ACHIEVEMENT, true); - } + bool Load() override + { + // OriginalCaster is always available in Spell::prepare + return GetOriginalCaster()->GetTypeId() == TYPEID_PLAYER; + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_seaforium_blast_SpellScript::AchievementCredit, EFFECT_1, SPELL_EFFECT_GAMEOBJECT_DAMAGE); - } - }; + void AchievementCredit(SpellEffIndex /*effIndex*/) + { + // but in effect handling OriginalCaster can become nullptr + if (Unit* originalCaster = GetOriginalCaster()) + if (GameObject* go = GetHitGObj()) + if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING) + originalCaster->CastSpell(originalCaster, SPELL_PLANT_CHARGES_CREDIT_ACHIEVEMENT, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_seaforium_blast_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_seaforium_blast::AchievementCredit, EFFECT_1, SPELL_EFFECT_GAMEOBJECT_DAMAGE); + } }; enum SpectatorCheerTrigger @@ -2971,67 +2448,45 @@ enum SpectatorCheerTrigger uint8 const EmoteArray[3] = { EMOTE_ONE_SHOT_CHEER, EMOTE_ONE_SHOT_EXCLAMATION, EMOTE_ONE_SHOT_APPLAUD }; -class spell_gen_spectator_cheer_trigger : public SpellScriptLoader +class spell_gen_spectator_cheer_trigger : public SpellScript { - public: - spell_gen_spectator_cheer_trigger() : SpellScriptLoader("spell_gen_spectator_cheer_trigger") { } - - class spell_gen_spectator_cheer_trigger_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_spectator_cheer_trigger_SpellScript); + PrepareSpellScript(spell_gen_spectator_cheer_trigger); - void HandleDummy(SpellEffIndex /*effIndex*/) - { - GetCaster()->HandleEmoteCommand(EmoteArray[urand(0, 2)]); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_spectator_cheer_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetCaster()->HandleEmoteCommand(EmoteArray[urand(0, 2)]); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_spectator_cheer_trigger_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_spectator_cheer_trigger::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_gen_spirit_healer_res : public SpellScriptLoader +class spell_gen_spirit_healer_res : public SpellScript { - public: - spell_gen_spirit_healer_res(): SpellScriptLoader("spell_gen_spirit_healer_res") { } - - class spell_gen_spirit_healer_res_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_spirit_healer_res_SpellScript); - - bool Load() override - { - return GetOriginalCaster() && GetOriginalCaster()->GetTypeId() == TYPEID_PLAYER; - } - - void HandleDummy(SpellEffIndex /* effIndex */) - { - Player* originalCaster = GetOriginalCaster()->ToPlayer(); - if (Unit* target = GetHitUnit()) - { - WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8); - data << uint64(target->GetGUID()); - originalCaster->SendDirectMessage(&data); - } - } + PrepareSpellScript(spell_gen_spirit_healer_res); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_spirit_healer_res_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Load() override + { + return GetOriginalCaster() && GetOriginalCaster()->GetTypeId() == TYPEID_PLAYER; + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /* effIndex */) + { + Player* originalCaster = GetOriginalCaster()->ToPlayer(); + if (Unit* target = GetHitUnit()) { - return new spell_gen_spirit_healer_res_SpellScript(); + WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8); + data << uint64(target->GetGUID()); + originalCaster->SendDirectMessage(&data); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_spirit_healer_res::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum SummonElemental @@ -3096,72 +2551,50 @@ enum TournamentMountsSpells SPELL_LANCE_EQUIPPED = 62853 }; -class spell_gen_summon_tournament_mount : public SpellScriptLoader +class spell_gen_summon_tournament_mount : public SpellScript { - public: - spell_gen_summon_tournament_mount() : SpellScriptLoader("spell_gen_summon_tournament_mount") { } - - class spell_gen_summon_tournament_mount_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_summon_tournament_mount_SpellScript); + PrepareSpellScript(spell_gen_summon_tournament_mount); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_LANCE_EQUIPPED }); - } - - SpellCastResult CheckIfLanceEquiped() - { - if (GetCaster()->IsInDisallowedMountForm()) - GetCaster()->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); - - if (!GetCaster()->HasAura(SPELL_LANCE_EQUIPPED)) - { - SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_HAVE_LANCE_EQUIPPED); - return SPELL_FAILED_CUSTOM_ERROR; - } - - return SPELL_CAST_OK; - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_LANCE_EQUIPPED }); + } - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_gen_summon_tournament_mount_SpellScript::CheckIfLanceEquiped); - } - }; + SpellCastResult CheckIfLanceEquiped() + { + if (GetCaster()->IsInDisallowedMountForm()) + GetCaster()->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT); - SpellScript* GetSpellScript() const override + if (!GetCaster()->HasAura(SPELL_LANCE_EQUIPPED)) { - return new spell_gen_summon_tournament_mount_SpellScript(); + SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_HAVE_LANCE_EQUIPPED); + return SPELL_FAILED_CUSTOM_ERROR; } + + return SPELL_CAST_OK; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_gen_summon_tournament_mount::CheckIfLanceEquiped); + } }; // 41213, 43416, 69222, 73076 - Throw Shield -class spell_gen_throw_shield : public SpellScriptLoader +class spell_gen_throw_shield : public SpellScript { - public: - spell_gen_throw_shield() : SpellScriptLoader("spell_gen_throw_shield") { } - - class spell_gen_throw_shield_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_throw_shield_SpellScript); - - void HandleScriptEffect(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - GetCaster()->CastSpell(GetHitUnit(), uint32(GetEffectValue()), true); - } + PrepareSpellScript(spell_gen_throw_shield); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_throw_shield_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScriptEffect(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetCaster()->CastSpell(GetHitUnit(), uint32(GetEffectValue()), true); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_throw_shield_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_throw_shield::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum MountedDuelSpells @@ -3170,84 +2603,62 @@ enum MountedDuelSpells SPELL_MOUNTED_DUEL = 62875 }; -class spell_gen_tournament_duel : public SpellScriptLoader +class spell_gen_tournament_duel : public SpellScript { - public: - spell_gen_tournament_duel() : SpellScriptLoader("spell_gen_tournament_duel") { } + PrepareSpellScript(spell_gen_tournament_duel); - class spell_gen_tournament_duel_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_gen_tournament_duel_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_ON_TOURNAMENT_MOUNT, - SPELL_MOUNTED_DUEL - }); - } + SPELL_ON_TOURNAMENT_MOUNT, + SPELL_MOUNTED_DUEL + }); + } - void HandleScriptEffect(SpellEffIndex /*effIndex*/) + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* rider = GetCaster()->GetCharmer()) + { + if (Player* playerTarget = GetHitPlayer()) { - if (Unit* rider = GetCaster()->GetCharmer()) - { - if (Player* playerTarget = GetHitPlayer()) - { - if (playerTarget->HasAura(SPELL_ON_TOURNAMENT_MOUNT) && playerTarget->GetVehicleBase()) - rider->CastSpell(playerTarget, SPELL_MOUNTED_DUEL, true); - } - else if (Unit* unitTarget = GetHitUnit()) - { - if (unitTarget->GetCharmer() && unitTarget->GetCharmer()->GetTypeId() == TYPEID_PLAYER && unitTarget->GetCharmer()->HasAura(SPELL_ON_TOURNAMENT_MOUNT)) - rider->CastSpell(unitTarget->GetCharmer(), SPELL_MOUNTED_DUEL, true); - } - } + if (playerTarget->HasAura(SPELL_ON_TOURNAMENT_MOUNT) && playerTarget->GetVehicleBase()) + rider->CastSpell(playerTarget, SPELL_MOUNTED_DUEL, true); } - - void Register() override + else if (Unit* unitTarget = GetHitUnit()) { - OnEffectHitTarget += SpellEffectFn(spell_gen_tournament_duel_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + if (unitTarget->GetCharmer() && unitTarget->GetCharmer()->GetTypeId() == TYPEID_PLAYER && unitTarget->GetCharmer()->HasAura(SPELL_ON_TOURNAMENT_MOUNT)) + rider->CastSpell(unitTarget->GetCharmer(), SPELL_MOUNTED_DUEL, true); } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_gen_tournament_duel_SpellScript(); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_tournament_duel::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; -class spell_gen_tournament_pennant : public SpellScriptLoader +class spell_gen_tournament_pennant : public AuraScript { - public: - spell_gen_tournament_pennant() : SpellScriptLoader("spell_gen_tournament_pennant") { } - - class spell_gen_tournament_pennant_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_tournament_pennant_AuraScript); - - bool Load() override - { - return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + PrepareAuraScript(spell_gen_tournament_pennant); - void HandleApplyEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - if (!caster->GetVehicleBase()) - caster->RemoveAurasDueToSpell(GetId()); - } + bool Load() override + { + return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_gen_tournament_pennant_AuraScript::HandleApplyEffect, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - } - }; + void HandleApplyEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + if (!caster->GetVehicleBase()) + caster->RemoveAurasDueToSpell(GetId()); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_tournament_pennant_AuraScript(); - } + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_gen_tournament_pennant::HandleApplyEffect, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + } }; enum PvPTrinketTriggeredSpells @@ -3294,7 +2705,7 @@ class spell_pvp_trinket_wotf_shared_cd : public SpellScriptLoader */ // Spell flags need further research, until then just cast not triggered - GetCaster()->CastSpell((Unit*)nullptr, Triggered, false); + GetCaster()->CastSpell(nullptr, Triggered, false); } void Register() override @@ -3314,49 +2725,38 @@ enum FriendOrFowl SPELL_TURKEY_VENGEANCE = 25285 }; -class spell_gen_turkey_marker : public SpellScriptLoader +class spell_gen_turkey_marker : public AuraScript { - public: - spell_gen_turkey_marker() : SpellScriptLoader("spell_gen_turkey_marker") { } - - class spell_gen_turkey_marker_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_turkey_marker_AuraScript); - - void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) - { - // store stack apply times, so we can pop them while they expire - _applyTimes.push_back(GameTime::GetGameTimeMS()); - Unit* target = GetTarget(); + PrepareAuraScript(spell_gen_turkey_marker); - // on stack 15 cast the achievement crediting spell - if (GetStackAmount() >= 15) - target->CastSpell(target, SPELL_TURKEY_VENGEANCE, true, nullptr, aurEff, GetCasterGUID()); - } + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + // store stack apply times, so we can pop them while they expire + _applyTimes.push_back(GameTime::GetGameTimeMS()); + Unit* target = GetTarget(); - void OnPeriodic(AuraEffect const* /*aurEff*/) - { - if (_applyTimes.empty()) - return; + // on stack 15 cast the achievement crediting spell + if (GetStackAmount() >= 15) + target->CastSpell(target, SPELL_TURKEY_VENGEANCE, true, nullptr, aurEff, GetCasterGUID()); + } - // pop stack if it expired for us - if (_applyTimes.front() + GetMaxDuration() < GameTime::GetGameTimeMS()) - ModStackAmount(-1, AURA_REMOVE_BY_EXPIRE); - } + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + if (_applyTimes.empty()) + return; - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_gen_turkey_marker_AuraScript::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_turkey_marker_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); - } + // pop stack if it expired for us + if (_applyTimes.front() + GetMaxDuration() < GameTime::GetGameTimeMS()) + ModStackAmount(-1, AURA_REMOVE_BY_EXPIRE); + } - std::list<uint32> _applyTimes; - }; + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_gen_turkey_marker::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_turkey_marker::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_turkey_marker_AuraScript(); - } + std::list<uint32> _applyTimes; }; enum FoamSword @@ -3368,41 +2768,30 @@ enum FoamSword ITEM_FOAM_SWORD_YELLOW = 45179 }; -class spell_gen_upper_deck_create_foam_sword : public SpellScriptLoader +class spell_gen_upper_deck_create_foam_sword : public SpellScript { - public: - spell_gen_upper_deck_create_foam_sword() : SpellScriptLoader("spell_gen_upper_deck_create_foam_sword") { } + PrepareSpellScript(spell_gen_upper_deck_create_foam_sword); - class spell_gen_upper_deck_create_foam_sword_SpellScript : public SpellScript + void HandleScript(SpellEffIndex effIndex) + { + if (Player* player = GetHitPlayer()) { - PrepareSpellScript(spell_gen_upper_deck_create_foam_sword_SpellScript); - - void HandleScript(SpellEffIndex effIndex) - { - if (Player* player = GetHitPlayer()) - { - static uint32 const itemId[5] = { ITEM_FOAM_SWORD_GREEN, ITEM_FOAM_SWORD_PINK, ITEM_FOAM_SWORD_BLUE, ITEM_FOAM_SWORD_RED, ITEM_FOAM_SWORD_YELLOW }; - // player can only have one of these items - for (uint8 i = 0; i < 5; ++i) - { - if (player->HasItemCount(itemId[i], 1, true)) - return; - } - - CreateItem(effIndex, itemId[urand(0, 4)]); - } - } - - void Register() override + static uint32 const itemId[5] = { ITEM_FOAM_SWORD_GREEN, ITEM_FOAM_SWORD_PINK, ITEM_FOAM_SWORD_BLUE, ITEM_FOAM_SWORD_RED, ITEM_FOAM_SWORD_YELLOW }; + // player can only have one of these items + for (uint8 i = 0; i < 5; ++i) { - OnEffectHitTarget += SpellEffectFn(spell_gen_upper_deck_create_foam_sword_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + if (player->HasItemCount(itemId[i], 1, true)) + return; } - }; - SpellScript* GetSpellScript() const override - { - return new spell_gen_upper_deck_create_foam_sword_SpellScript(); + CreateItem(effIndex, itemId[urand(0, 4)]); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_upper_deck_create_foam_sword::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum VampiricTouch @@ -3412,42 +2801,31 @@ enum VampiricTouch // 52723 - Vampiric Touch // 60501 - Vampiric Touch -class spell_gen_vampiric_touch : public SpellScriptLoader +class spell_gen_vampiric_touch : public AuraScript { - public: - spell_gen_vampiric_touch() : SpellScriptLoader("spell_gen_vampiric_touch") { } + PrepareAuraScript(spell_gen_vampiric_touch); - class spell_gen_vampiric_touch_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_vampiric_touch_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_VAMPIRIC_TOUCH_HEAL }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - DamageInfo* damageInfo = eventInfo.GetDamageInfo(); - if (!damageInfo || !damageInfo->GetDamage()) - return; - - Unit* caster = eventInfo.GetActor(); - int32 bp = damageInfo->GetDamage() / 2; - caster->CastCustomSpell(SPELL_VAMPIRIC_TOUCH_HEAL, SPELLVALUE_BASE_POINT0, bp, caster, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_VAMPIRIC_TOUCH_HEAL }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_gen_vampiric_touch_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + Unit* caster = eventInfo.GetActor(); + int32 bp = damageInfo->GetDamage() / 2; + caster->CastCustomSpell(SPELL_VAMPIRIC_TOUCH_HEAL, SPELLVALUE_BASE_POINT0, bp, caster, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_vampiric_touch_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_gen_vampiric_touch::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; enum VehicleScaling @@ -3455,58 +2833,47 @@ enum VehicleScaling SPELL_GEAR_SCALING = 66668 }; -class spell_gen_vehicle_scaling : public SpellScriptLoader +class spell_gen_vehicle_scaling : public AuraScript { - public: - spell_gen_vehicle_scaling() : SpellScriptLoader("spell_gen_vehicle_scaling") { } - - class spell_gen_vehicle_scaling_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_vehicle_scaling_AuraScript); + PrepareAuraScript(spell_gen_vehicle_scaling); - bool Load() override - { - return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - - void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) - { - Unit* caster = GetCaster(); - float factor; - uint16 baseItemLevel; + bool Load() override + { + return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - /// @todo Reserach coeffs for different vehicles - switch (GetId()) - { - case SPELL_GEAR_SCALING: - factor = 1.0f; - baseItemLevel = 205; - break; - default: - factor = 1.0f; - baseItemLevel = 170; - break; - } + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + Unit* caster = GetCaster(); + float factor; + uint16 baseItemLevel; - float avgILvl = caster->ToPlayer()->GetAverageItemLevel(); - if (avgILvl < baseItemLevel) - return; /// @todo Research possibility of scaling down + /// @todo Reserach coeffs for different vehicles + switch (GetId()) + { + case SPELL_GEAR_SCALING: + factor = 1.0f; + baseItemLevel = 205; + break; + default: + factor = 1.0f; + baseItemLevel = 170; + break; + } - amount = uint16((avgILvl - baseItemLevel) * factor); - } + float avgILvl = caster->ToPlayer()->GetAverageItemLevel(); + if (avgILvl < baseItemLevel) + return; /// @todo Research possibility of scaling down - void Register() override - { - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_vehicle_scaling_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_HEALING_PCT); - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_vehicle_scaling_AuraScript::CalculateAmount, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_vehicle_scaling_AuraScript::CalculateAmount, EFFECT_2, SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT); - } - }; + amount = uint16((avgILvl - baseItemLevel) * factor); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_vehicle_scaling_AuraScript(); - } + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_vehicle_scaling::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_HEALING_PCT); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_vehicle_scaling::CalculateAmount, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_vehicle_scaling::CalculateAmount, EFFECT_2, SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT); + } }; enum VendorBarkTrigger @@ -3515,61 +2882,38 @@ enum VendorBarkTrigger SAY_AMPHITHEATER_VENDOR = 0 }; -class spell_gen_vendor_bark_trigger : public SpellScriptLoader +class spell_gen_vendor_bark_trigger : public SpellScript { - public: - spell_gen_vendor_bark_trigger() : SpellScriptLoader("spell_gen_vendor_bark_trigger") { } - - class spell_gen_vendor_bark_trigger_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_vendor_bark_trigger_SpellScript); + PrepareSpellScript(spell_gen_vendor_bark_trigger); - void HandleDummy(SpellEffIndex /* effIndex */) - { - if (Creature* vendor = GetCaster()->ToCreature()) - if (vendor->GetEntry() == NPC_AMPHITHEATER_VENDOR) - vendor->AI()->Talk(SAY_AMPHITHEATER_VENDOR); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_vendor_bark_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_gen_vendor_bark_trigger_SpellScript(); - } + void HandleDummy(SpellEffIndex /* effIndex */) + { + if (Creature* vendor = GetCaster()->ToCreature()) + if (vendor->GetEntry() == NPC_AMPHITHEATER_VENDOR) + vendor->AI()->Talk(SAY_AMPHITHEATER_VENDOR); + } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_vendor_bark_trigger::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_gen_wg_water : public SpellScriptLoader +class spell_gen_wg_water : public SpellScript { - public: - spell_gen_wg_water() : SpellScriptLoader("spell_gen_wg_water") { } - - class spell_gen_wg_water_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_wg_water_SpellScript); - - SpellCastResult CheckCast() - { - if (!GetSpellInfo()->CheckTargetCreatureType(GetCaster())) - return SPELL_FAILED_DONT_REPORT; - return SPELL_CAST_OK; - } + PrepareSpellScript(spell_gen_wg_water); - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_gen_wg_water_SpellScript::CheckCast); - } - }; + SpellCastResult CheckCast() + { + if (!GetSpellInfo()->CheckTargetCreatureType(GetCaster())) + return SPELL_FAILED_DONT_REPORT; + return SPELL_CAST_OK; + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_wg_water_SpellScript(); - } + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_gen_wg_water::CheckCast); + } }; enum WhisperGulchYoggSaronWhisper @@ -3577,100 +2921,67 @@ enum WhisperGulchYoggSaronWhisper SPELL_YOGG_SARON_WHISPER_DUMMY = 29072 }; -class spell_gen_whisper_gulch_yogg_saron_whisper : public SpellScriptLoader +class spell_gen_whisper_gulch_yogg_saron_whisper : public AuraScript { - public: - spell_gen_whisper_gulch_yogg_saron_whisper() : SpellScriptLoader("spell_gen_whisper_gulch_yogg_saron_whisper") { } - - class spell_gen_whisper_gulch_yogg_saron_whisper_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_whisper_gulch_yogg_saron_whisper_AuraScript); + PrepareAuraScript(spell_gen_whisper_gulch_yogg_saron_whisper); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_YOGG_SARON_WHISPER_DUMMY }); - } - - void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) - { - PreventDefaultAction(); - GetTarget()->CastSpell((Unit*)nullptr, SPELL_YOGG_SARON_WHISPER_DUMMY, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_YOGG_SARON_WHISPER_DUMMY }); + } - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_whisper_gulch_yogg_saron_whisper_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); - } - }; + void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(nullptr, SPELL_YOGG_SARON_WHISPER_DUMMY, true); + } - AuraScript* GetAuraScript() const override - { - return new spell_gen_whisper_gulch_yogg_saron_whisper_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_whisper_gulch_yogg_saron_whisper::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } }; -class spell_gen_eject_all_passengers : public SpellScriptLoader +class spell_gen_eject_all_passengers : public SpellScript { - public: - spell_gen_eject_all_passengers() : SpellScriptLoader("spell_gen_eject_all_passengers") { } - - class spell_gen_eject_all_passengers_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_eject_all_passengers_SpellScript); - - void RemoveVehicleAuras() - { - if (Vehicle* vehicle = GetHitUnit()->GetVehicleKit()) - vehicle->RemoveAllPassengers(); - } + PrepareSpellScript(spell_gen_eject_all_passengers); - void Register() override - { - AfterHit += SpellHitFn(spell_gen_eject_all_passengers_SpellScript::RemoveVehicleAuras); - } - }; + void RemoveVehicleAuras() + { + if (Vehicle* vehicle = GetHitUnit()->GetVehicleKit()) + vehicle->RemoveAllPassengers(); + } - SpellScript* GetSpellScript() const override - { - return new spell_gen_eject_all_passengers_SpellScript(); - } + void Register() override + { + AfterHit += SpellHitFn(spell_gen_eject_all_passengers::RemoveVehicleAuras); + } }; -class spell_gen_eject_passenger : public SpellScriptLoader +class spell_gen_eject_passenger : public SpellScript { - public: - spell_gen_eject_passenger() : SpellScriptLoader("spell_gen_eject_passenger") { } - - class spell_gen_eject_passenger_SpellScript : public SpellScript - { - PrepareSpellScript(spell_gen_eject_passenger_SpellScript); - - bool Validate(SpellInfo const* spellInfo) override - { - if (spellInfo->Effects[EFFECT_0].CalcValue() < 1) - return false; - return true; - } - - void EjectPassenger(SpellEffIndex /*effIndex*/) - { - if (Vehicle* vehicle = GetHitUnit()->GetVehicleKit()) - { - if (Unit* passenger = vehicle->GetPassenger(GetEffectValue() - 1)) - passenger->ExitVehicle(); - } - } + PrepareSpellScript(spell_gen_eject_passenger); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_eject_passenger_SpellScript::EjectPassenger, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + bool Validate(SpellInfo const* spellInfo) override + { + if (spellInfo->Effects[EFFECT_0].CalcValue() < 1) + return false; + return true; + } - SpellScript* GetSpellScript() const override + void EjectPassenger(SpellEffIndex /*effIndex*/) + { + if (Vehicle* vehicle = GetHitUnit()->GetVehicleKit()) { - return new spell_gen_eject_passenger_SpellScript(); + if (Unit* passenger = vehicle->GetPassenger(GetEffectValue() - 1)) + passenger->ExitVehicle(); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_eject_passenger::EjectPassenger, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum GMFreeze @@ -3678,100 +2989,78 @@ enum GMFreeze SPELL_GM_FREEZE = 9454 }; -class spell_gen_gm_freeze : public SpellScriptLoader +class spell_gen_gm_freeze : public AuraScript { - public: - spell_gen_gm_freeze() : SpellScriptLoader("spell_gen_gm_freeze") { } - - class spell_gen_gm_freeze_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_gm_freeze_AuraScript); + PrepareAuraScript(spell_gen_gm_freeze); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_GM_FREEZE }); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_GM_FREEZE }); + } - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - // Do what was done before to the target in HandleFreezeCommand - if (Player* player = GetTarget()->ToPlayer()) - { - // stop combat + make player unattackable + duel stop + stop some spells - player->SetFaction(FACTION_FRIENDLY); - player->CombatStop(); - if (player->IsNonMeleeSpellCast(true)) - player->InterruptNonMeleeSpells(true); - player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - - // if player class = hunter || warlock remove pet if alive - if ((player->getClass() == CLASS_HUNTER) || (player->getClass() == CLASS_WARLOCK)) - { - if (Pet* pet = player->GetPet()) - { - pet->SavePetToDB(PET_SAVE_AS_CURRENT); - // not let dismiss dead pet - if (pet->IsAlive()) - player->RemovePet(pet, PET_SAVE_NOT_IN_SLOT); - } - } - } - } + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // Do what was done before to the target in HandleFreezeCommand + if (Player* player = GetTarget()->ToPlayer()) + { + // stop combat + make player unattackable + duel stop + stop some spells + player->SetFaction(FACTION_FRIENDLY); + player->CombatStop(); + if (player->IsNonMeleeSpellCast(true)) + player->InterruptNonMeleeSpells(true); + player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + // if player class = hunter || warlock remove pet if alive + if ((player->getClass() == CLASS_HUNTER) || (player->getClass() == CLASS_WARLOCK)) { - // Do what was done before to the target in HandleUnfreezeCommand - if (Player* player = GetTarget()->ToPlayer()) + if (Pet* pet = player->GetPet()) { - // Reset player faction + allow combat + allow duels - player->setFactionForRace(player->getRace()); - player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - // save player - player->SaveToDB(); + pet->SavePetToDB(PET_SAVE_AS_CURRENT); + // not let dismiss dead pet + if (pet->IsAlive()) + player->RemovePet(pet, PET_SAVE_NOT_IN_SLOT); } } + } + } - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_gen_gm_freeze_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); - OnEffectRemove += AuraEffectRemoveFn(spell_gen_gm_freeze_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + // Do what was done before to the target in HandleUnfreezeCommand + if (Player* player = GetTarget()->ToPlayer()) { - return new spell_gen_gm_freeze_AuraScript(); + // Reset player faction + allow combat + allow duels + player->setFactionForRace(player->getRace()); + player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + // save player + player->SaveToDB(); } + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_gen_gm_freeze::OnApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + OnEffectRemove += AuraEffectRemoveFn(spell_gen_gm_freeze::OnRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + } }; -class spell_gen_stand : public SpellScriptLoader +class spell_gen_stand : public SpellScript { -public: - spell_gen_stand() : SpellScriptLoader("spell_gen_stand") { } + PrepareSpellScript(spell_gen_stand); - class spell_gen_stand_SpellScript : public SpellScript + void HandleScript(SpellEffIndex /*eff*/) { - PrepareSpellScript(spell_gen_stand_SpellScript); - - void HandleScript(SpellEffIndex /*eff*/) - { - Creature* target = GetHitCreature(); - if (!target) - return; + Creature* target = GetHitCreature(); + if (!target) + return; - target->SetStandState(UNIT_STAND_STATE_STAND); - target->HandleEmoteCommand(EMOTE_STATE_NONE); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_stand_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + target->SetStandState(UNIT_STAND_STATE_STAND); + target->HandleEmoteCommand(EMOTE_STATE_NONE); + } - SpellScript* GetSpellScript() const override + void Register() override { - return new spell_gen_stand_SpellScript(); + OnEffectHitTarget += SpellEffectFn(spell_gen_stand::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; @@ -3863,214 +3152,203 @@ enum RequiredMixologySpells SPELL_ELIXIR_OF_MINOR_DEFENSE = 673 }; -class spell_gen_mixology_bonus : public SpellScriptLoader +class spell_gen_mixology_bonus : public AuraScript { -public: - spell_gen_mixology_bonus() : SpellScriptLoader("spell_gen_mixology_bonus") { } + PrepareAuraScript(spell_gen_mixology_bonus); - class spell_gen_mixology_bonus_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override { - PrepareAuraScript(spell_gen_mixology_bonus_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_MIXOLOGY }); - } + return ValidateSpellInfo({ SPELL_MIXOLOGY }); + } - bool Load() override - { - return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + bool Load() override + { + return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void SetBonusValueForEffect(SpellEffIndex effIndex, int32 value, AuraEffect const* aurEff) - { - if (aurEff->GetEffIndex() == uint32(effIndex)) - bonus = value; - } + void SetBonusValueForEffect(SpellEffIndex effIndex, int32 value, AuraEffect const* aurEff) + { + if (aurEff->GetEffIndex() == uint32(effIndex)) + bonus = value; + } - void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) - { - if (GetCaster()->HasAura(SPELL_MIXOLOGY) && GetCaster()->HasSpell(GetSpellInfo()->Effects[EFFECT_0].TriggerSpell)) - { - switch (GetId()) - { - case SPELL_WEAK_TROLLS_BLOOD_ELIXIR: - case SPELL_MAGEBLOOD_ELIXIR: - bonus = amount; - break; - case SPELL_ELIXIR_OF_FROST_POWER: - case SPELL_LESSER_FLASK_OF_TOUGHNESS: - case SPELL_LESSER_FLASK_OF_RESISTANCE: - bonus = CalculatePct(amount, 80); - break; - case SPELL_ELIXIR_OF_MINOR_DEFENSE: - case SPELL_ELIXIR_OF_LIONS_STRENGTH: - case SPELL_ELIXIR_OF_MINOR_AGILITY: - case SPELL_MAJOR_TROLLS_BLLOOD_ELIXIR: - case SPELL_ELIXIR_OF_SHADOW_POWER: - case SPELL_ELIXIR_OF_BRUTE_FORCE: - case SPELL_MIGHTY_TROLLS_BLOOD_ELIXIR: - case SPELL_ELIXIR_OF_GREATER_FIREPOWER: - case SPELL_ONSLAUGHT_ELIXIR: - case SPELL_EARTHEN_ELIXIR: - case SPELL_ELIXIR_OF_MAJOR_AGILITY: - case SPELL_FLASK_OF_THE_TITANS: - case SPELL_FLASK_OF_RELENTLESS_ASSAULT: - case SPELL_FLASK_OF_STONEBLOOD: - case SPELL_ELIXIR_OF_MINOR_ACCURACY: - bonus = CalculatePct(amount, 50); - break; - case SPELL_ELIXIR_OF_PROTECTION: - bonus = 280; - break; - case SPELL_ELIXIR_OF_MAJOR_DEFENSE: - bonus = 200; - break; - case SPELL_ELIXIR_OF_GREATER_DEFENSE: - case SPELL_ELIXIR_OF_SUPERIOR_DEFENSE: - bonus = 140; - break; - case SPELL_ELIXIR_OF_FORTITUDE: - bonus = 100; - break; - case SPELL_FLASK_OF_ENDLESS_RAGE: - bonus = 82; - break; - case SPELL_ELIXIR_OF_DEFENSE: - bonus = 70; - break; - case SPELL_ELIXIR_OF_DEMONSLAYING: - bonus = 50; - break; - case SPELL_FLASK_OF_THE_FROST_WYRM: - bonus = 47; - break; - case SPELL_WRATH_ELIXIR: - bonus = 32; - break; - case SPELL_ELIXIR_OF_MAJOR_FROST_POWER: - case SPELL_ELIXIR_OF_MAJOR_FIREPOWER: - case SPELL_ELIXIR_OF_MAJOR_SHADOW_POWER: - bonus = 29; - break; - case SPELL_ELIXIR_OF_MIGHTY_TOUGHTS: - bonus = 27; - break; - case SPELL_FLASK_OF_SUPREME_POWER: - case SPELL_FLASK_OF_BLINDING_LIGHT: - case SPELL_FLASK_OF_PURE_DEATH: - case SPELL_SHADOWPOWER_ELIXIR: - bonus = 23; - break; - case SPELL_ELIXIR_OF_MIGHTY_AGILITY: - case SPELL_FLASK_OF_DISTILLED_WISDOM: - case SPELL_ELIXIR_OF_SPIRIT: - case SPELL_ELIXIR_OF_MIGHTY_STRENGTH: - case SPELL_FLASK_OF_PURE_MOJO: - case SPELL_ELIXIR_OF_ACCURACY: - case SPELL_ELIXIR_OF_DEADLY_STRIKES: - case SPELL_ELIXIR_OF_MIGHTY_DEFENSE: - case SPELL_ELIXIR_OF_EXPERTISE: - case SPELL_ELIXIR_OF_ARMOR_PIERCING: - case SPELL_ELIXIR_OF_LIGHTNING_SPEED: - bonus = 20; - break; - case SPELL_FLASK_OF_CHROMATIC_RESISTANCE: - bonus = 17; - break; - case SPELL_ELIXIR_OF_MINOR_FORTITUDE: - case SPELL_ELIXIR_OF_MAJOR_STRENGTH: - bonus = 15; - break; - case SPELL_FLASK_OF_MIGHTY_RESTORATION: - bonus = 13; - break; - case SPELL_ARCANE_ELIXIR: - bonus = 12; - break; - case SPELL_ELIXIR_OF_GREATER_AGILITY: - case SPELL_ELIXIR_OF_GIANTS: - bonus = 11; - break; - case SPELL_ELIXIR_OF_AGILITY: - case SPELL_ELIXIR_OF_GREATER_INTELLECT: - case SPELL_ELIXIR_OF_SAGES: - case SPELL_ELIXIR_OF_IRONSKIN: - case SPELL_ELIXIR_OF_MIGHTY_MAGEBLOOD: - bonus = 10; - break; - case SPELL_ELIXIR_OF_HEALING_POWER: - bonus = 9; - break; - case SPELL_ELIXIR_OF_DRAENIC_WISDOM: - case SPELL_GURUS_ELIXIR: - bonus = 8; - break; - case SPELL_ELIXIR_OF_FIREPOWER: - case SPELL_ELIXIR_OF_MAJOR_MAGEBLOOD: - case SPELL_ELIXIR_OF_MASTERY: - bonus = 6; - break; - case SPELL_ELIXIR_OF_LESSER_AGILITY: - case SPELL_ELIXIR_OF_OGRES_STRENGTH: - case SPELL_ELIXIR_OF_WISDOM: - case SPELL_ELIXIR_OF_THE_MONGOOSE: - bonus = 5; - break; - case SPELL_STRONG_TROLLS_BLOOD_ELIXIR: - case SPELL_FLASK_OF_CHROMATIC_WONDER: - bonus = 4; - break; - case SPELL_ELIXIR_OF_EMPOWERMENT: - bonus = -10; - break; - case SPELL_ADEPTS_ELIXIR: - SetBonusValueForEffect(EFFECT_0, 13, aurEff); - SetBonusValueForEffect(EFFECT_1, 13, aurEff); - SetBonusValueForEffect(EFFECT_2, 8, aurEff); - break; - case SPELL_ELIXIR_OF_MIGHTY_FORTITUDE: - SetBonusValueForEffect(EFFECT_0, 160, aurEff); - break; - case SPELL_ELIXIR_OF_MAJOR_FORTITUDE: - SetBonusValueForEffect(EFFECT_0, 116, aurEff); - SetBonusValueForEffect(EFFECT_1, 6, aurEff); - break; - case SPELL_FEL_STRENGTH_ELIXIR: - SetBonusValueForEffect(EFFECT_0, 40, aurEff); - SetBonusValueForEffect(EFFECT_1, 40, aurEff); - break; - case SPELL_FLASK_OF_FORTIFICATION: - SetBonusValueForEffect(EFFECT_0, 210, aurEff); - SetBonusValueForEffect(EFFECT_1, 5, aurEff); - break; - case SPELL_GREATER_ARCANE_ELIXIR: - SetBonusValueForEffect(EFFECT_0, 19, aurEff); - SetBonusValueForEffect(EFFECT_1, 19, aurEff); - SetBonusValueForEffect(EFFECT_2, 5, aurEff); - break; - case SPELL_ELIXIR_OF_GIANTH_GROWTH: - SetBonusValueForEffect(EFFECT_0, 5, aurEff); - break; - default: - TC_LOG_ERROR("spells", "SpellId %u couldn't be processed in spell_gen_mixology_bonus", GetId()); - break; - } - amount += bonus; - } + void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) + { + if (GetCaster()->HasAura(SPELL_MIXOLOGY) && GetCaster()->HasSpell(GetSpellInfo()->Effects[EFFECT_0].TriggerSpell)) + { + switch (GetId()) + { + case SPELL_WEAK_TROLLS_BLOOD_ELIXIR: + case SPELL_MAGEBLOOD_ELIXIR: + bonus = amount; + break; + case SPELL_ELIXIR_OF_FROST_POWER: + case SPELL_LESSER_FLASK_OF_TOUGHNESS: + case SPELL_LESSER_FLASK_OF_RESISTANCE: + bonus = CalculatePct(amount, 80); + break; + case SPELL_ELIXIR_OF_MINOR_DEFENSE: + case SPELL_ELIXIR_OF_LIONS_STRENGTH: + case SPELL_ELIXIR_OF_MINOR_AGILITY: + case SPELL_MAJOR_TROLLS_BLLOOD_ELIXIR: + case SPELL_ELIXIR_OF_SHADOW_POWER: + case SPELL_ELIXIR_OF_BRUTE_FORCE: + case SPELL_MIGHTY_TROLLS_BLOOD_ELIXIR: + case SPELL_ELIXIR_OF_GREATER_FIREPOWER: + case SPELL_ONSLAUGHT_ELIXIR: + case SPELL_EARTHEN_ELIXIR: + case SPELL_ELIXIR_OF_MAJOR_AGILITY: + case SPELL_FLASK_OF_THE_TITANS: + case SPELL_FLASK_OF_RELENTLESS_ASSAULT: + case SPELL_FLASK_OF_STONEBLOOD: + case SPELL_ELIXIR_OF_MINOR_ACCURACY: + bonus = CalculatePct(amount, 50); + break; + case SPELL_ELIXIR_OF_PROTECTION: + bonus = 280; + break; + case SPELL_ELIXIR_OF_MAJOR_DEFENSE: + bonus = 200; + break; + case SPELL_ELIXIR_OF_GREATER_DEFENSE: + case SPELL_ELIXIR_OF_SUPERIOR_DEFENSE: + bonus = 140; + break; + case SPELL_ELIXIR_OF_FORTITUDE: + bonus = 100; + break; + case SPELL_FLASK_OF_ENDLESS_RAGE: + bonus = 82; + break; + case SPELL_ELIXIR_OF_DEFENSE: + bonus = 70; + break; + case SPELL_ELIXIR_OF_DEMONSLAYING: + bonus = 50; + break; + case SPELL_FLASK_OF_THE_FROST_WYRM: + bonus = 47; + break; + case SPELL_WRATH_ELIXIR: + bonus = 32; + break; + case SPELL_ELIXIR_OF_MAJOR_FROST_POWER: + case SPELL_ELIXIR_OF_MAJOR_FIREPOWER: + case SPELL_ELIXIR_OF_MAJOR_SHADOW_POWER: + bonus = 29; + break; + case SPELL_ELIXIR_OF_MIGHTY_TOUGHTS: + bonus = 27; + break; + case SPELL_FLASK_OF_SUPREME_POWER: + case SPELL_FLASK_OF_BLINDING_LIGHT: + case SPELL_FLASK_OF_PURE_DEATH: + case SPELL_SHADOWPOWER_ELIXIR: + bonus = 23; + break; + case SPELL_ELIXIR_OF_MIGHTY_AGILITY: + case SPELL_FLASK_OF_DISTILLED_WISDOM: + case SPELL_ELIXIR_OF_SPIRIT: + case SPELL_ELIXIR_OF_MIGHTY_STRENGTH: + case SPELL_FLASK_OF_PURE_MOJO: + case SPELL_ELIXIR_OF_ACCURACY: + case SPELL_ELIXIR_OF_DEADLY_STRIKES: + case SPELL_ELIXIR_OF_MIGHTY_DEFENSE: + case SPELL_ELIXIR_OF_EXPERTISE: + case SPELL_ELIXIR_OF_ARMOR_PIERCING: + case SPELL_ELIXIR_OF_LIGHTNING_SPEED: + bonus = 20; + break; + case SPELL_FLASK_OF_CHROMATIC_RESISTANCE: + bonus = 17; + break; + case SPELL_ELIXIR_OF_MINOR_FORTITUDE: + case SPELL_ELIXIR_OF_MAJOR_STRENGTH: + bonus = 15; + break; + case SPELL_FLASK_OF_MIGHTY_RESTORATION: + bonus = 13; + break; + case SPELL_ARCANE_ELIXIR: + bonus = 12; + break; + case SPELL_ELIXIR_OF_GREATER_AGILITY: + case SPELL_ELIXIR_OF_GIANTS: + bonus = 11; + break; + case SPELL_ELIXIR_OF_AGILITY: + case SPELL_ELIXIR_OF_GREATER_INTELLECT: + case SPELL_ELIXIR_OF_SAGES: + case SPELL_ELIXIR_OF_IRONSKIN: + case SPELL_ELIXIR_OF_MIGHTY_MAGEBLOOD: + bonus = 10; + break; + case SPELL_ELIXIR_OF_HEALING_POWER: + bonus = 9; + break; + case SPELL_ELIXIR_OF_DRAENIC_WISDOM: + case SPELL_GURUS_ELIXIR: + bonus = 8; + break; + case SPELL_ELIXIR_OF_FIREPOWER: + case SPELL_ELIXIR_OF_MAJOR_MAGEBLOOD: + case SPELL_ELIXIR_OF_MASTERY: + bonus = 6; + break; + case SPELL_ELIXIR_OF_LESSER_AGILITY: + case SPELL_ELIXIR_OF_OGRES_STRENGTH: + case SPELL_ELIXIR_OF_WISDOM: + case SPELL_ELIXIR_OF_THE_MONGOOSE: + bonus = 5; + break; + case SPELL_STRONG_TROLLS_BLOOD_ELIXIR: + case SPELL_FLASK_OF_CHROMATIC_WONDER: + bonus = 4; + break; + case SPELL_ELIXIR_OF_EMPOWERMENT: + bonus = -10; + break; + case SPELL_ADEPTS_ELIXIR: + SetBonusValueForEffect(EFFECT_0, 13, aurEff); + SetBonusValueForEffect(EFFECT_1, 13, aurEff); + SetBonusValueForEffect(EFFECT_2, 8, aurEff); + break; + case SPELL_ELIXIR_OF_MIGHTY_FORTITUDE: + SetBonusValueForEffect(EFFECT_0, 160, aurEff); + break; + case SPELL_ELIXIR_OF_MAJOR_FORTITUDE: + SetBonusValueForEffect(EFFECT_0, 116, aurEff); + SetBonusValueForEffect(EFFECT_1, 6, aurEff); + break; + case SPELL_FEL_STRENGTH_ELIXIR: + SetBonusValueForEffect(EFFECT_0, 40, aurEff); + SetBonusValueForEffect(EFFECT_1, 40, aurEff); + break; + case SPELL_FLASK_OF_FORTIFICATION: + SetBonusValueForEffect(EFFECT_0, 210, aurEff); + SetBonusValueForEffect(EFFECT_1, 5, aurEff); + break; + case SPELL_GREATER_ARCANE_ELIXIR: + SetBonusValueForEffect(EFFECT_0, 19, aurEff); + SetBonusValueForEffect(EFFECT_1, 19, aurEff); + SetBonusValueForEffect(EFFECT_2, 5, aurEff); + break; + case SPELL_ELIXIR_OF_GIANTH_GROWTH: + SetBonusValueForEffect(EFFECT_0, 5, aurEff); + break; + default: + TC_LOG_ERROR("spells", "SpellId %u couldn't be processed in spell_gen_mixology_bonus", GetId()); + break; + } + amount += bonus; } + } - int32 bonus = 0; - - void Register() override - { - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_mixology_bonus_AuraScript::CalculateAmount, EFFECT_ALL, SPELL_AURA_ANY); - } - }; + int32 bonus = 0; - AuraScript* GetAuraScript() const override + void Register() override { - return new spell_gen_mixology_bonus_AuraScript(); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_gen_mixology_bonus::CalculateAmount, EFFECT_ALL, SPELL_AURA_ANY); } }; @@ -4079,71 +3357,49 @@ enum LandmineKnockbackAchievement SPELL_LANDMINE_KNOCKBACK_ACHIEVEMENT = 57064 }; -class spell_gen_landmine_knockback_achievement : public SpellScriptLoader +class spell_gen_landmine_knockback_achievement : public SpellScript { -public: - spell_gen_landmine_knockback_achievement() : SpellScriptLoader("spell_gen_landmine_knockback_achievement") { } + PrepareSpellScript(spell_gen_landmine_knockback_achievement); - class spell_gen_landmine_knockback_achievement_SpellScript : public SpellScript + void HandleScript(SpellEffIndex /*effIndex*/) { - PrepareSpellScript(spell_gen_landmine_knockback_achievement_SpellScript); - - void HandleScript(SpellEffIndex /*effIndex*/) + if (Player* target = GetHitPlayer()) { - if (Player* target = GetHitPlayer()) - { - Aura const* aura = GetHitAura(); - if (!aura || aura->GetStackAmount() < 10) - return; - - target->CastSpell(target, SPELL_LANDMINE_KNOCKBACK_ACHIEVEMENT, true); - } - } + Aura const* aura = GetHitAura(); + if (!aura || aura->GetStackAmount() < 10) + return; - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_gen_landmine_knockback_achievement_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + target->CastSpell(target, SPELL_LANDMINE_KNOCKBACK_ACHIEVEMENT, true); } - }; + } - SpellScript* GetSpellScript() const override + void Register() override { - return new spell_gen_landmine_knockback_achievement_SpellScript(); + OnEffectHitTarget += SpellEffectFn(spell_gen_landmine_knockback_achievement::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; // 34098 - ClearAllDebuffs -class spell_gen_clear_debuffs : public SpellScriptLoader +class spell_gen_clear_debuffs : public SpellScript { - public: - spell_gen_clear_debuffs() : SpellScriptLoader("spell_gen_clear_debuffs") { } + PrepareSpellScript(spell_gen_clear_debuffs); - class spell_gen_clear_debuffs_SpellScript : public SpellScript + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) { - PrepareSpellScript(spell_gen_clear_debuffs_SpellScript); - - void HandleScript(SpellEffIndex /*effIndex*/) - { - if (Unit* target = GetHitUnit()) - { - target->RemoveOwnedAuras([](Aura const* aura) - { - SpellInfo const* spellInfo = aura->GetSpellInfo(); - return !spellInfo->IsPositive() && !spellInfo->IsPassive(); - }); - } - } - - void Register() override + target->RemoveOwnedAuras([](Aura const* aura) { - OnEffectHitTarget += SpellEffectFn(spell_gen_clear_debuffs_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_gen_clear_debuffs_SpellScript(); + SpellInfo const* spellInfo = aura->GetSpellInfo(); + return !spellInfo->IsPositive() && !spellInfo->IsPassive(); + }); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_clear_debuffs::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum PonySpells @@ -4152,93 +3408,82 @@ enum PonySpells MOUNT_PONY = 29736 }; -class spell_gen_pony_mount_check : public SpellScriptLoader +class spell_gen_pony_mount_check : public AuraScript { - public: - spell_gen_pony_mount_check() : SpellScriptLoader("spell_gen_pony_mount_check") { } - - class spell_gen_pony_mount_check_AuraScript : public AuraScript - { - PrepareAuraScript(spell_gen_pony_mount_check_AuraScript); - - void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) - { - Unit* caster = GetCaster(); - if (!caster) - return; - Player* owner = caster->GetOwner()->ToPlayer(); - if (!owner || !owner->HasAchieved(ACHIEV_PONY_UP)) - return; - - if (owner->IsMounted()) - { - caster->Mount(MOUNT_PONY); - caster->SetSpeedRate(MOVE_RUN, owner->GetSpeedRate(MOVE_RUN)); - } - else if (caster->IsMounted()) - { - caster->Dismount(); - caster->SetSpeedRate(MOVE_RUN, owner->GetSpeedRate(MOVE_RUN)); - } - } + PrepareAuraScript(spell_gen_pony_mount_check); - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_pony_mount_check_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); - } - }; + void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) + { + Unit* caster = GetCaster(); + if (!caster) + return; + Player* owner = caster->GetOwner()->ToPlayer(); + if (!owner || !owner->HasAchieved(ACHIEV_PONY_UP)) + return; - AuraScript* GetAuraScript() const + if (owner->IsMounted()) + { + caster->Mount(MOUNT_PONY); + caster->SetSpeedRate(MOVE_RUN, owner->GetSpeedRate(MOVE_RUN)); + } + else if (caster->IsMounted()) { - return new spell_gen_pony_mount_check_AuraScript(); + caster->Dismount(); + caster->SetSpeedRate(MOVE_RUN, owner->GetSpeedRate(MOVE_RUN)); } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_pony_mount_check::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } }; void AddSC_generic_spell_scripts() { - new spell_gen_absorb0_hitlimit1(); - new spell_gen_adaptive_warding(); - new spell_gen_allow_cast_from_item_only(); - new spell_gen_animal_blood(); - new spell_gen_aura_of_anger(); - new spell_gen_aura_service_uniform(); - new spell_gen_av_drekthar_presence(); - new spell_gen_bandage(); - new spell_gen_blood_reserve(); - new spell_gen_blade_warding(); - new spell_gen_bonked(); + RegisterAuraScript(spell_gen_absorb0_hitlimit1); + RegisterAuraScript(spell_gen_adaptive_warding); + RegisterSpellScript(spell_gen_allow_cast_from_item_only); + RegisterAuraScript(spell_gen_animal_blood); + RegisterAuraScript(spell_gen_aura_of_anger); + RegisterAuraScript(spell_gen_aura_service_uniform); + RegisterAuraScript(spell_gen_av_drekthar_presence); + RegisterSpellScript(spell_gen_bandage); + RegisterAuraScript(spell_gen_blood_reserve); + RegisterAuraScript(spell_gen_blade_warding); + RegisterSpellScript(spell_gen_bonked); new spell_gen_break_shield("spell_gen_break_shield"); new spell_gen_break_shield("spell_gen_tournament_counterattack"); - new spell_gen_burn_brutallus(); - new spell_gen_burning_depths_necrolyte_image(); - new spell_gen_cannibalize(); - new spell_gen_chaos_blast(); - new spell_gen_clone(); - new spell_gen_clone_weapon(); - new spell_gen_clone_weapon_aura(); + RegisterAuraScript(spell_gen_burn_brutallus); + RegisterAuraScript(spell_gen_burning_depths_necrolyte_image); + RegisterSpellScript(spell_gen_cannibalize); + RegisterSpellScript(spell_gen_chaos_blast); + RegisterSpellScript(spell_gen_clone); + RegisterSpellScript(spell_gen_clone_weapon); + RegisterAuraScript(spell_gen_clone_weapon_aura); new spell_gen_count_pct_from_max_hp("spell_gen_default_count_pct_from_max_hp"); new spell_gen_count_pct_from_max_hp("spell_gen_50pct_count_pct_from_max_hp", 50); - new spell_gen_create_lance(); - new spell_gen_creature_permanent_feign_death(); + RegisterSpellScript(spell_gen_create_lance); + RegisterAuraScript(spell_gen_creature_permanent_feign_death); new spell_gen_dalaran_disguise("spell_gen_sunreaver_disguise"); new spell_gen_dalaran_disguise("spell_gen_silver_covenant_disguise"); - new spell_gen_damage_reduction_aura(); - new spell_gen_defend(); - new spell_gen_despawn_self(); - new spell_gen_divine_storm_cd_reset(); - new spell_gen_ds_flush_knockback(); - new spell_gen_dungeon_credit(); - new spell_gen_elune_candle(); - new spell_gen_gadgetzan_transporter_backfire(); - new spell_gen_gift_of_naaru(); - new spell_gen_gnomish_transporter(); - new spell_gen_lifeblood(); + RegisterAuraScript(spell_gen_damage_reduction_aura); + RegisterAuraScript(spell_gen_defend); + RegisterSpellScript(spell_gen_despawn_self); + RegisterSpellScript(spell_gen_divine_storm_cd_reset); + RegisterSpellScript(spell_gen_ds_flush_knockback); + RegisterSpellScript(spell_gen_dungeon_credit); + RegisterSpellScript(spell_gen_elune_candle); + RegisterSpellScript(spell_gen_gadgetzan_transporter_backfire); + RegisterAuraScript(spell_gen_gift_of_naaru); + RegisterSpellScript(spell_gen_gnomish_transporter); + RegisterAuraScript(spell_gen_lifeblood); new spell_gen_lifebloom("spell_hexlord_lifebloom", SPELL_HEXLORD_MALACRASS_LIFEBLOOM_FINAL_HEAL); new spell_gen_lifebloom("spell_tur_ragepaw_lifebloom", SPELL_TUR_RAGEPAW_LIFEBLOOM_FINAL_HEAL); new spell_gen_lifebloom("spell_cenarion_scout_lifebloom", SPELL_CENARION_SCOUT_LIFEBLOOM_FINAL_HEAL); new spell_gen_lifebloom("spell_twisted_visage_lifebloom", SPELL_TWISTED_VISAGE_LIFEBLOOM_FINAL_HEAL); new spell_gen_lifebloom("spell_faction_champion_dru_lifebloom", SPELL_FACTION_CHAMPIONS_DRU_LIFEBLOOM_FINAL_HEAL); - new spell_gen_magic_rooster(); + RegisterSpellScript(spell_gen_magic_rooster); new spell_gen_mount("spell_magic_broom", 0, SPELL_MAGIC_BROOM_60, SPELL_MAGIC_BROOM_100, SPELL_MAGIC_BROOM_150, SPELL_MAGIC_BROOM_280); new spell_gen_mount("spell_headless_horseman_mount", 0, SPELL_HEADLESS_HORSEMAN_MOUNT_60, SPELL_HEADLESS_HORSEMAN_MOUNT_100, SPELL_HEADLESS_HORSEMAN_MOUNT_150, SPELL_HEADLESS_HORSEMAN_MOUNT_280); new spell_gen_mount("spell_winged_steed_of_the_ebon_blade", 0, 0, 0, SPELL_WINGED_STEED_150, SPELL_WINGED_STEED_280); @@ -4248,50 +3493,50 @@ void AddSC_generic_spell_scripts() new spell_gen_mount("spell_blazing_hippogryph", 0, 0, 0, SPELL_BLAZING_HIPPOGRYPH_150, SPELL_BLAZING_HIPPOGRYPH_280); new spell_gen_mount("spell_celestial_steed", 0, SPELL_CELESTIAL_STEED_60, SPELL_CELESTIAL_STEED_100, SPELL_CELESTIAL_STEED_150, SPELL_CELESTIAL_STEED_280, SPELL_CELESTIAL_STEED_310); new spell_gen_mount("spell_x53_touring_rocket", 0, 0, 0, SPELL_X53_TOURING_ROCKET_150, SPELL_X53_TOURING_ROCKET_280, SPELL_X53_TOURING_ROCKET_310); - new spell_gen_mounted_charge(); - new spell_gen_moss_covered_feet(); - new spell_gen_netherbloom(); - new spell_gen_nightmare_vine(); - new spell_gen_obsidian_armor(); - new spell_gen_oracle_wolvar_reputation(); - new spell_gen_orc_disguise(); - new spell_gen_paralytic_poison(); + RegisterSpellScript(spell_gen_mounted_charge); + RegisterAuraScript(spell_gen_moss_covered_feet); + RegisterSpellScript(spell_gen_netherbloom); + RegisterSpellScript(spell_gen_nightmare_vine); + RegisterAuraScript(spell_gen_obsidian_armor); + RegisterSpellScript(spell_gen_oracle_wolvar_reputation); + RegisterSpellScript(spell_gen_orc_disguise); + RegisterAuraScript(spell_gen_paralytic_poison); new spell_gen_proc_below_pct_damaged("spell_item_soul_harvesters_charm"); new spell_gen_proc_below_pct_damaged("spell_item_commendation_of_kaelthas"); new spell_gen_proc_below_pct_damaged("spell_item_corpse_tongue_coin"); new spell_gen_proc_below_pct_damaged("spell_item_corpse_tongue_coin_heroic"); new spell_gen_proc_below_pct_damaged("spell_item_petrified_twilight_scale"); new spell_gen_proc_below_pct_damaged("spell_item_petrified_twilight_scale_heroic"); - new spell_gen_proc_charge_drop_only(); - new spell_gen_parachute(); - new spell_gen_pet_summoned(); - new spell_gen_profession_research(); - new spell_gen_remove_flight_auras(); - new spell_gen_replenishment(); - new spell_gen_seaforium_blast(); - new spell_gen_spectator_cheer_trigger(); - new spell_gen_spirit_healer_res(); + RegisterAuraScript(spell_gen_proc_charge_drop_only); + RegisterAuraScript(spell_gen_parachute); + RegisterSpellScript(spell_gen_pet_summoned); + RegisterSpellScript(spell_gen_profession_research); + RegisterSpellScript(spell_gen_remove_flight_auras); + RegisterSpellAndAuraScriptPair(spell_gen_replenishment, spell_gen_replenishment_aura); + RegisterSpellScript(spell_gen_seaforium_blast); + RegisterSpellScript(spell_gen_spectator_cheer_trigger); + RegisterSpellScript(spell_gen_spirit_healer_res); new spell_gen_summon_elemental("spell_gen_summon_fire_elemental", SPELL_SUMMON_FIRE_ELEMENTAL); new spell_gen_summon_elemental("spell_gen_summon_earth_elemental", SPELL_SUMMON_EARTH_ELEMENTAL); - new spell_gen_summon_tournament_mount(); - new spell_gen_throw_shield(); - new spell_gen_tournament_duel(); - new spell_gen_tournament_pennant(); + RegisterSpellScript(spell_gen_summon_tournament_mount); + RegisterSpellScript(spell_gen_throw_shield); + RegisterSpellScript(spell_gen_tournament_duel); + RegisterAuraScript(spell_gen_tournament_pennant); new spell_pvp_trinket_wotf_shared_cd<SPELL_WILL_OF_THE_FORSAKEN_COOLDOWN_TRIGGER>("spell_pvp_trinket_shared_cd"); new spell_pvp_trinket_wotf_shared_cd<SPELL_WILL_OF_THE_FORSAKEN_COOLDOWN_TRIGGER_WOTF>("spell_wotf_shared_cd"); - new spell_gen_turkey_marker(); - new spell_gen_upper_deck_create_foam_sword(); - new spell_gen_vampiric_touch(); - new spell_gen_vehicle_scaling(); - new spell_gen_vendor_bark_trigger(); - new spell_gen_wg_water(); - new spell_gen_whisper_gulch_yogg_saron_whisper(); - new spell_gen_eject_all_passengers(); - new spell_gen_eject_passenger(); - new spell_gen_gm_freeze(); - new spell_gen_stand(); - new spell_gen_mixology_bonus(); - new spell_gen_landmine_knockback_achievement(); - new spell_gen_clear_debuffs(); - new spell_gen_pony_mount_check(); + RegisterAuraScript(spell_gen_turkey_marker); + RegisterSpellScript(spell_gen_upper_deck_create_foam_sword); + RegisterAuraScript(spell_gen_vampiric_touch); + RegisterAuraScript(spell_gen_vehicle_scaling); + RegisterSpellScript(spell_gen_vendor_bark_trigger); + RegisterSpellScript(spell_gen_wg_water); + RegisterAuraScript(spell_gen_whisper_gulch_yogg_saron_whisper); + RegisterSpellScript(spell_gen_eject_all_passengers); + RegisterSpellScript(spell_gen_eject_passenger); + RegisterAuraScript(spell_gen_gm_freeze); + RegisterSpellScript(spell_gen_stand); + RegisterAuraScript(spell_gen_mixology_bonus); + RegisterSpellScript(spell_gen_landmine_knockback_achievement); + RegisterSpellScript(spell_gen_clear_debuffs); + RegisterAuraScript(spell_gen_pony_mount_check); } diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp index eac0ff10c23..8c395077c96 100644 --- a/src/server/scripts/Spells/spell_holiday.cpp +++ b/src/server/scripts/Spells/spell_holiday.cpp @@ -921,14 +921,14 @@ class spell_pilgrims_bounty_a_serving_of : public SpellScriptLoader void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) { Unit* target = GetTarget(); - target->CastSpell(target, uint32(aurEff->GetBaseAmount()), true); + target->CastSpell(target, uint32(aurEff->GetAmount()), true); HandlePlate(target, true); } void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) { Unit* target = GetTarget(); - target->RemoveAurasDueToSpell(aurEff->GetBaseAmount()); + target->RemoveAurasDueToSpell(aurEff->GetAmount()); HandlePlate(target, false); } @@ -1318,7 +1318,7 @@ class spell_brewfest_relay_race_intro_force_player_to_throw : public SpellScript PreventHitDefaultEffect(effIndex); // All this spells trigger a spell that requires reagents; if the // triggered spell is cast as "triggered", reagents are not consumed - GetHitUnit()->CastSpell((Unit*)nullptr, GetSpellInfo()->Effects[effIndex].TriggerSpell, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)); + GetHitUnit()->CastSpell(nullptr, GetSpellInfo()->Effects[effIndex].TriggerSpell, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)); } void Register() override @@ -1489,117 +1489,175 @@ enum TorchSpells SPELL_TORCH_TOSSING_PRACTICE = 46630, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_ALLIANCE = 45719, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_HORDE = 46651, + SPELL_TARGET_INDICATOR_COSMETIC = 46901, + SPELL_TARGET_INDICATOR = 45723, SPELL_BRAZIERS_HIT = 45724 }; // 45724 - Braziers Hit! -class spell_midsummer_braziers_hit : public SpellScriptLoader +class spell_midsummer_braziers_hit : public AuraScript { - public: - spell_midsummer_braziers_hit() : SpellScriptLoader("spell_midsummer_braziers_hit") { } + PrepareAuraScript(spell_midsummer_braziers_hit); - class spell_midsummer_braziers_hit_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_midsummer_braziers_hit_AuraScript); + SPELL_TORCH_TOSSING_TRAINING, + SPELL_TORCH_TOSSING_PRACTICE, + SPELL_TORCH_TOSSING_TRAINING_SUCCESS_ALLIANCE, + SPELL_TORCH_TOSSING_TRAINING_SUCCESS_HORDE + }); + } - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_TORCH_TOSSING_TRAINING, SPELL_TORCH_TOSSING_PRACTICE }); - } + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Player* player = GetTarget()->ToPlayer(); + if (!player) + return; - void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Player* player = GetTarget()->ToPlayer(); - if (!player) - return; + if ((player->HasAura(SPELL_TORCH_TOSSING_TRAINING) && GetStackAmount() == 8) || (player->HasAura(SPELL_TORCH_TOSSING_PRACTICE) && GetStackAmount() == 20)) + { + if (player->GetTeam() == ALLIANCE) + player->CastSpell(player, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_ALLIANCE, true); + else if (player->GetTeam() == HORDE) + player->CastSpell(player, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_HORDE, true); + Remove(); + } + } - if ((player->HasAura(SPELL_TORCH_TOSSING_TRAINING) && GetStackAmount() == 8) || (player->HasAura(SPELL_TORCH_TOSSING_PRACTICE) && GetStackAmount() == 20)) - { - if (player->GetTeam() == ALLIANCE) - player->CastSpell(player, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_ALLIANCE, true); - else if (player->GetTeam() == HORDE) - player->CastSpell(player, SPELL_TORCH_TOSSING_TRAINING_SUCCESS_HORDE, true); - Remove(); - } - } + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_midsummer_braziers_hit::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAPPLY); + } +}; - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_midsummer_braziers_hit_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AuraEffectHandleModes(AURA_EFFECT_HANDLE_REAPPLY)); - } - }; +// 45907 - Torch Target Picker +class spell_midsummer_torch_target_picker : public SpellScript +{ + PrepareSpellScript(spell_midsummer_torch_target_picker); - AuraScript* GetAuraScript() const override - { - return new spell_midsummer_braziers_hit_AuraScript(); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_TARGET_INDICATOR_COSMETIC, SPELL_TARGET_INDICATOR }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Unit* target = GetHitUnit(); + target->CastSpell(target, SPELL_TARGET_INDICATOR_COSMETIC, true); + target->CastSpell(target, SPELL_TARGET_INDICATOR, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_midsummer_torch_target_picker::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +// 46054 - Torch Toss (land) +class spell_midsummer_torch_toss_land : public SpellScript +{ + PrepareSpellScript(spell_midsummer_torch_toss_land); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_BRAZIERS_HIT }); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetCaster(), SPELL_BRAZIERS_HIT, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_midsummer_torch_toss_land::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum RibbonPoleData { - SPELL_HAS_FULL_MIDSUMMER_SET = 58933, - SPELL_BURNING_HOT_POLE_DANCE = 58934, - SPELL_RIBBON_DANCE_COSMETIC = 29726, - SPELL_RIBBON_DANCE = 29175, - GO_RIBBON_POLE = 181605, + SPELL_HAS_FULL_MIDSUMMER_SET = 58933, + SPELL_BURNING_HOT_POLE_DANCE = 58934, + SPELL_RIBBON_POLE_PERIODIC_VISUAL = 45406, + SPELL_RIBBON_DANCE = 29175, + SPELL_TEST_RIBBON_POLE_1 = 29705, + SPELL_TEST_RIBBON_POLE_2 = 29726, + SPELL_TEST_RIBBON_POLE_3 = 29727 }; -class spell_gen_ribbon_pole_dancer_check : public SpellScriptLoader +// 29705, 29726, 29727 - Test Ribbon Pole Channel +class spell_midsummer_test_ribbon_pole_channel : public AuraScript { - public: - spell_gen_ribbon_pole_dancer_check() : SpellScriptLoader("spell_gen_ribbon_pole_dancer_check") { } + PrepareAuraScript(spell_midsummer_test_ribbon_pole_channel); - class spell_gen_ribbon_pole_dancer_check_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_gen_ribbon_pole_dancer_check_AuraScript); + SPELL_RIBBON_POLE_PERIODIC_VISUAL, + SPELL_BURNING_HOT_POLE_DANCE, + SPELL_HAS_FULL_MIDSUMMER_SET, + SPELL_RIBBON_DANCE + }); + } - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_HAS_FULL_MIDSUMMER_SET, - SPELL_RIBBON_DANCE, - SPELL_BURNING_HOT_POLE_DANCE - }); - } + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveAurasDueToSpell(SPELL_RIBBON_POLE_PERIODIC_VISUAL); + } - void PeriodicTick(AuraEffect const* /*aurEff*/) - { - Unit* target = GetTarget(); + void PeriodicTick(AuraEffect const* /*aurEff*/) + { + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_RIBBON_POLE_PERIODIC_VISUAL, true); - // check if aura needs to be removed - if (!target->FindNearestGameObject(GO_RIBBON_POLE, 8.0f) || !target->HasUnitState(UNIT_STATE_CASTING)) - { - target->InterruptNonMeleeSpells(false); - target->RemoveAurasDueToSpell(GetId()); - target->RemoveAura(SPELL_RIBBON_DANCE_COSMETIC); - return; - } + if (Aura* aur = target->GetAura(SPELL_RIBBON_DANCE)) + { + aur->SetMaxDuration(std::min(3600000, aur->GetMaxDuration() + 180000)); + aur->RefreshDuration(); - // set xp buff duration - if (Aura* aur = target->GetAura(SPELL_RIBBON_DANCE)) - { - aur->SetMaxDuration(std::min(3600000, aur->GetMaxDuration() + 180000)); - aur->RefreshDuration(); + if (aur->GetMaxDuration() == 3600000 && target->HasAura(SPELL_HAS_FULL_MIDSUMMER_SET)) + target->CastSpell(target, SPELL_BURNING_HOT_POLE_DANCE, true); + } + else + target->CastSpell(target, SPELL_RIBBON_DANCE, true); + } - // reward achievement criteria - if (aur->GetMaxDuration() == 3600000 && target->HasAura(SPELL_HAS_FULL_MIDSUMMER_SET)) - target->CastSpell(target, SPELL_BURNING_HOT_POLE_DANCE, true); - } - else - target->AddAura(SPELL_RIBBON_DANCE, target); - } + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_midsummer_test_ribbon_pole_channel::HandleRemove, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_midsummer_test_ribbon_pole_channel::PeriodicTick, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } +}; - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_ribbon_pole_dancer_check_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); - } - }; +// 45406 - Holiday - Midsummer, Ribbon Pole Periodic Visual +class spell_midsummer_ribbon_pole_periodic_visual : public AuraScript +{ + PrepareAuraScript(spell_midsummer_ribbon_pole_periodic_visual); - AuraScript* GetAuraScript() const override + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - return new spell_gen_ribbon_pole_dancer_check_AuraScript(); - } + SPELL_TEST_RIBBON_POLE_1, + SPELL_TEST_RIBBON_POLE_2, + SPELL_TEST_RIBBON_POLE_3 + }); + } + + void PeriodicTick(AuraEffect const* /*aurEff*/) + { + Unit* target = GetTarget(); + if (!target->HasAura(SPELL_TEST_RIBBON_POLE_1) && !target->HasAura(SPELL_TEST_RIBBON_POLE_2) && !target->HasAura(SPELL_TEST_RIBBON_POLE_3)) + Remove(); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_midsummer_ribbon_pole_periodic_visual::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } }; void AddSC_holiday_spell_scripts() @@ -1650,6 +1708,9 @@ void AddSC_holiday_spell_scripts() new spell_brewfest_dismount_ram(); new spell_brewfest_barker_bunny(); // Midsummer Fire Festival - new spell_midsummer_braziers_hit(); - new spell_gen_ribbon_pole_dancer_check(); + RegisterAuraScript(spell_midsummer_braziers_hit); + RegisterSpellScript(spell_midsummer_torch_target_picker); + RegisterSpellScript(spell_midsummer_torch_toss_land); + RegisterAuraScript(spell_midsummer_test_ribbon_pole_channel); + RegisterAuraScript(spell_midsummer_ribbon_pole_periodic_visual); } diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 2b9bfac79e9..a7fed99dccf 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -443,7 +443,7 @@ class spell_hun_glyph_of_mend_pet : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - eventInfo.GetProcTarget()->CastSpell((Unit*)nullptr, SPELL_HUNTER_GLYPH_OF_MEND_PET_HAPPINESS, true, nullptr, aurEff); + eventInfo.GetProcTarget()->CastSpell(nullptr, SPELL_HUNTER_GLYPH_OF_MEND_PET_HAPPINESS, true, nullptr, aurEff); } void Register() override @@ -476,7 +476,7 @@ class spell_hun_hunting_party : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); + eventInfo.GetActor()->CastSpell(nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); } void Register() override @@ -775,7 +775,7 @@ class spell_hun_masters_call : public SpellScriptLoader void HandleScriptEffect(SpellEffIndex /*effIndex*/) { - GetHitUnit()->CastSpell((Unit*)nullptr, SPELL_HUNTER_MASTERS_CALL_TRIGGERED, true); + GetHitUnit()->CastSpell(nullptr, SPELL_HUNTER_MASTERS_CALL_TRIGGERED, true); } void Register() override @@ -1105,7 +1105,7 @@ class spell_hun_rapid_recuperation_trigger : public SpellScriptLoader uint8 rank = GetSpellInfo()->GetRank(); uint32 spellId = triggerSpells[rank - 1]; - eventInfo.GetActor()->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + eventInfo.GetActor()->CastSpell(nullptr, spellId, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 0de0c0c00b9..e78bd3804be 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -85,36 +85,25 @@ enum AegisOfPreservation }; // 23780 - Aegis of Preservation -class spell_item_aegis_of_preservation : public SpellScriptLoader +class spell_item_aegis_of_preservation : public AuraScript { - public: - spell_item_aegis_of_preservation() : SpellScriptLoader("spell_item_aegis_of_preservation") { } - - class spell_item_aegis_of_preservation_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_aegis_of_preservation_AuraScript); + PrepareAuraScript(spell_item_aegis_of_preservation); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_AEGIS_HEAL }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) - { - PreventDefaultAction(); - GetTarget()->CastSpell(GetTarget(), SPELL_AEGIS_HEAL, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_AEGIS_HEAL }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_aegis_of_preservation_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_PROC_TRIGGER_SPELL); - } - }; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_AEGIS_HEAL, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_aegis_of_preservation_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_aegis_of_preservation::HandleProc, EFFECT_1, SPELL_AURA_PROC_TRIGGER_SPELL); + } }; enum AlchemistStone @@ -133,62 +122,51 @@ enum AlchemistStone // Item - 44324: Mighty Alchemist's Stone // 17619 - Alchemist's Stone -class spell_item_alchemists_stone : public SpellScriptLoader +class spell_item_alchemists_stone : public AuraScript { - public: - spell_item_alchemists_stone() : SpellScriptLoader("spell_item_alchemists_stone") { } + PrepareAuraScript(spell_item_alchemists_stone); - class spell_item_alchemists_stone_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_item_alchemists_stone_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_ALCHEMISTS_STONE_EXTRA_HEAL, - SPELL_ALCHEMISTS_STONE_EXTRA_MANA - }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); - if (!spellInfo) - return; + SPELL_ALCHEMISTS_STONE_EXTRA_HEAL, + SPELL_ALCHEMISTS_STONE_EXTRA_MANA + }); + } - Unit* caster = eventInfo.GetActionTarget(); - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - uint32 spellId; - switch (spellInfo->Effects[i].Effect) - { - case SPELL_EFFECT_HEAL: - spellId = SPELL_ALCHEMISTS_STONE_EXTRA_HEAL; - break; - case SPELL_EFFECT_ENERGIZE: - spellId = SPELL_ALCHEMISTS_STONE_EXTRA_MANA; - break; - default: - continue; - } - - int32 amount = CalculatePct(spellInfo->Effects[i].CalcValue(caster), 40); - caster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); - } - } + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (!spellInfo) + return; - void Register() override + Unit* caster = eventInfo.GetActionTarget(); + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + uint32 spellId; + switch (spellInfo->Effects[i].Effect) { - OnEffectProc += AuraEffectProcFn(spell_item_alchemists_stone_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + case SPELL_EFFECT_HEAL: + spellId = SPELL_ALCHEMISTS_STONE_EXTRA_HEAL; + break; + case SPELL_EFFECT_ENERGIZE: + spellId = SPELL_ALCHEMISTS_STONE_EXTRA_MANA; + break; + default: + continue; } - }; - AuraScript* GetAuraScript() const override - { - return new spell_item_alchemists_stone_AuraScript(); + int32 amount = CalculatePct(spellInfo->Effects[i].CalcValue(caster), 40); + caster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_alchemists_stone::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; enum AngerCapacitor @@ -230,7 +208,7 @@ class spell_item_anger_capacitor : public SpellScriptLoader Unit* caster = eventInfo.GetActor(); Unit* target = eventInfo.GetProcTarget(); - caster->CastSpell((Unit*)nullptr, SPELL_MOTE_OF_ANGER, true); + caster->CastSpell(nullptr, SPELL_MOTE_OF_ANGER, true); Aura const* motes = caster->GetAura(SPELL_MOTE_OF_ANGER); if (!motes || motes->GetStackAmount() < Stacks) return; @@ -257,32 +235,21 @@ class spell_item_anger_capacitor : public SpellScriptLoader }; // 26400 - Arcane Shroud -class spell_item_arcane_shroud : public SpellScriptLoader +class spell_item_arcane_shroud : public AuraScript { - public: - spell_item_arcane_shroud() : SpellScriptLoader("spell_item_arcane_shroud") { } - - class spell_item_arcane_shroud_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_arcane_shroud_AuraScript); + PrepareAuraScript(spell_item_arcane_shroud); - void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) - { - int32 diff = GetUnitOwner()->getLevel() - 60; - if (diff > 0) - amount += 2 * diff; - } - - void Register() override - { - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_arcane_shroud_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT); - } - }; + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + int32 diff = GetUnitOwner()->getLevel() - 60; + if (diff > 0) + amount += 2 * diff; + } - AuraScript* GetAuraScript() const override - { - return new spell_item_arcane_shroud_AuraScript(); - } + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_arcane_shroud::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT); + } }; enum AuraOfMadness @@ -305,117 +272,95 @@ enum AuraOfMadness // Item - 31859: Darkmoon Card: Madness // 39446 - Aura of Madness -class spell_item_aura_of_madness : public SpellScriptLoader +class spell_item_aura_of_madness : public AuraScript { - public: - spell_item_aura_of_madness() : SpellScriptLoader("spell_item_aura_of_madness") { } - - class spell_item_aura_of_madness_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_aura_of_madness_AuraScript); + PrepareAuraScript(spell_item_aura_of_madness); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_SOCIOPATH, - SPELL_DELUSIONAL, - SPELL_KLEPTOMANIA, - SPELL_MEGALOMANIA, - SPELL_PARANOIA, - SPELL_MANIC, - SPELL_NARCISSISM, - SPELL_MARTYR_COMPLEX, - SPELL_DEMENTIA - }) && sObjectMgr->GetBroadcastText(SAY_MADNESS); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - static std::vector<uint32> const triggeredSpells[MAX_CLASSES] = - { - //CLASS_NONE - { }, - //CLASS_WARRIOR - { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_MARTYR_COMPLEX }, - //CLASS_PALADIN - { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, - //CLASS_HUNTER - { SPELL_DELUSIONAL, SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, - //CLASS_ROGUE - { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_MARTYR_COMPLEX }, - //CLASS_PRIEST - { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, - //CLASS_DEATH_KNIGHT - { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_MARTYR_COMPLEX }, - //CLASS_SHAMAN - { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, - //CLASS_MAGE - { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, - //CLASS_WARLOCK - { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, - //CLASS_UNK - { }, - //CLASS_DRUID - { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA } - }; - - PreventDefaultAction(); - Unit* caster = eventInfo.GetActor(); - uint32 spellId = Trinity::Containers::SelectRandomContainerElement(triggeredSpells[caster->getClass()]); - caster->CastSpell(caster, spellId, true, nullptr, aurEff); - - if (roll_chance_i(10)) - caster->Unit::Say(SAY_MADNESS); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_SOCIOPATH, + SPELL_DELUSIONAL, + SPELL_KLEPTOMANIA, + SPELL_MEGALOMANIA, + SPELL_PARANOIA, + SPELL_MANIC, + SPELL_NARCISSISM, + SPELL_MARTYR_COMPLEX, + SPELL_DEMENTIA + }) && sObjectMgr->GetBroadcastText(SAY_MADNESS); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_aura_of_madness_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + static std::vector<uint32> const triggeredSpells[MAX_CLASSES] = + { + //CLASS_NONE + { }, + //CLASS_WARRIOR + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_MARTYR_COMPLEX }, + //CLASS_PALADIN + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_HUNTER + { SPELL_DELUSIONAL, SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_ROGUE + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_MARTYR_COMPLEX }, + //CLASS_PRIEST + { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_DEATH_KNIGHT + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_MARTYR_COMPLEX }, + //CLASS_SHAMAN + { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_MAGE + { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_WARLOCK + { SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA }, + //CLASS_UNK + { }, + //CLASS_DRUID + { SPELL_SOCIOPATH, SPELL_DELUSIONAL, SPELL_KLEPTOMANIA, SPELL_MEGALOMANIA, SPELL_PARANOIA, SPELL_MANIC, SPELL_NARCISSISM, SPELL_MARTYR_COMPLEX, SPELL_DEMENTIA } + }; + + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + uint32 spellId = Trinity::Containers::SelectRandomContainerElement(triggeredSpells[caster->getClass()]); + caster->CastSpell(caster, spellId, true, nullptr, aurEff); + + if (roll_chance_i(10)) + caster->Unit::Say(SAY_MADNESS); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_aura_of_madness_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_aura_of_madness::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; // 41404 - Dementia -class spell_item_dementia : public SpellScriptLoader +class spell_item_dementia : public AuraScript { - public: - spell_item_dementia() : SpellScriptLoader("spell_item_dementia") { } + PrepareAuraScript(spell_item_dementia); - class spell_item_dementia_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_item_dementia_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_DEMENTIA_POS, - SPELL_DEMENTIA_NEG - }); - } - - void HandlePeriodicDummy(AuraEffect const* aurEff) - { - PreventDefaultAction(); - GetTarget()->CastSpell(GetTarget(), RAND(SPELL_DEMENTIA_POS, SPELL_DEMENTIA_NEG), true, nullptr, aurEff); - } + SPELL_DEMENTIA_POS, + SPELL_DEMENTIA_NEG + }); + } - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_item_dementia_AuraScript::HandlePeriodicDummy, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); - } - }; + void HandlePeriodicDummy(AuraEffect const* aurEff) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), RAND(SPELL_DEMENTIA_POS, SPELL_DEMENTIA_NEG), true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_dementia_AuraScript(); - } + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_item_dementia::HandlePeriodicDummy, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } }; // 64411 - Blessing of Ancient Kings (Val'anyr, Hammer of Ancient Kings) @@ -424,57 +369,46 @@ enum BlessingOfAncientKings SPELL_PROTECTION_OF_ANCIENT_KINGS = 64413 }; -class spell_item_blessing_of_ancient_kings : public SpellScriptLoader +class spell_item_blessing_of_ancient_kings : public AuraScript { - public: - spell_item_blessing_of_ancient_kings() : SpellScriptLoader("spell_item_blessing_of_ancient_kings") { } - - class spell_item_blessing_of_ancient_kings_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_blessing_of_ancient_kings_AuraScript); + PrepareAuraScript(spell_item_blessing_of_ancient_kings); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_PROTECTION_OF_ANCIENT_KINGS }); - } - - bool CheckProc(ProcEventInfo& eventInfo) - { - return eventInfo.GetProcTarget() != nullptr; - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - - HealInfo* healInfo = eventInfo.GetHealInfo(); - if (!healInfo || !healInfo->GetHeal()) - return; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PROTECTION_OF_ANCIENT_KINGS }); + } - int32 absorb = int32(CalculatePct(healInfo->GetHeal(), 15.0f)); - if (AuraEffect* protEff = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_PROTECTION_OF_ANCIENT_KINGS, 0, eventInfo.GetActor()->GetGUID())) - { - // The shield can grow to a maximum size of 20,000 damage absorbtion - protEff->SetAmount(std::min<int32>(protEff->GetAmount() + absorb, 20000)); + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetProcTarget() != nullptr; + } - // Refresh and return to prevent replacing the aura - protEff->GetBase()->RefreshDuration(); - } - else - GetTarget()->CastCustomSpell(SPELL_PROTECTION_OF_ANCIENT_KINGS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, nullptr, aurEff); - } + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_item_blessing_of_ancient_kings_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_item_blessing_of_ancient_kings_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + HealInfo* healInfo = eventInfo.GetHealInfo(); + if (!healInfo || !healInfo->GetHeal()) + return; - AuraScript* GetAuraScript() const override + int32 absorb = int32(CalculatePct(healInfo->GetHeal(), 15.0f)); + if (AuraEffect* protEff = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_PROTECTION_OF_ANCIENT_KINGS, 0, eventInfo.GetActor()->GetGUID())) { - return new spell_item_blessing_of_ancient_kings_AuraScript(); + // The shield can grow to a maximum size of 20,000 damage absorbtion + protEff->SetAmount(std::min<int32>(protEff->GetAmount() + absorb, 20000)); + + // Refresh and return to prevent replacing the aura + protEff->GetBase()->RefreshDuration(); } + else + GetTarget()->CastCustomSpell(SPELL_PROTECTION_OF_ANCIENT_KINGS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_blessing_of_ancient_kings::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_blessing_of_ancient_kings::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; enum DeadlyPrecision @@ -483,64 +417,42 @@ enum DeadlyPrecision }; // 71564 - Deadly Precision -class spell_item_deadly_precision : public SpellScriptLoader +class spell_item_deadly_precision : public AuraScript { - public: - spell_item_deadly_precision() : SpellScriptLoader("spell_item_deadly_precision") { } + PrepareAuraScript(spell_item_deadly_precision); - class spell_item_deadly_precision_charm_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_deadly_precision_charm_AuraScript); - - void HandleStackDrop(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) - { - PreventDefaultAction(); - GetTarget()->RemoveAuraFromStack(GetId(), GetTarget()->GetGUID()); - } - - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_deadly_precision_charm_AuraScript::HandleStackDrop, EFFECT_0, SPELL_AURA_MOD_RATING); - } - }; + void HandleStackDrop(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->RemoveAuraFromStack(GetId(), GetTarget()->GetGUID()); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_deadly_precision_charm_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_deadly_precision::HandleStackDrop, EFFECT_0, SPELL_AURA_MOD_RATING); + } }; // 71563 - Deadly Precision Dummy -class spell_item_deadly_precision_dummy : public SpellScriptLoader +class spell_item_deadly_precision_dummy : public SpellScript { - public: - spell_item_deadly_precision_dummy() : SpellScriptLoader("spell_item_deadly_precision_dummy") { } - - class spell_item_deadly_precision_dummy_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_deadly_precision_dummy_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DEADLY_PRECISION }); - } + PrepareSpellScript(spell_item_deadly_precision_dummy); - void HandleDummy(SpellEffIndex /*effIndex*/) - { - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_DEADLY_PRECISION); - GetCaster()->CastCustomSpell(spellInfo->Id, SPELLVALUE_AURA_STACK, spellInfo->StackAmount, GetCaster(), true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DEADLY_PRECISION }); + } - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_deadly_precision_dummy_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_DEADLY_PRECISION); + GetCaster()->CastCustomSpell(spellInfo->Id, SPELLVALUE_AURA_STACK, spellInfo->StackAmount, GetCaster(), true); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_deadly_precision_dummy_SpellScript(); - } + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_deadly_precision_dummy::HandleDummy, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } }; enum DeathbringersWill @@ -639,52 +551,41 @@ class spell_item_deathbringers_will : public SpellScriptLoader }; // 47770 - Roll Dice -class spell_item_decahedral_dwarven_dice : public SpellScriptLoader +class spell_item_decahedral_dwarven_dice : public SpellScript { - public: - spell_item_decahedral_dwarven_dice() : SpellScriptLoader("spell_item_decahedral_dwarven_dice") { } - - class spell_item_decahedral_dwarven_dice_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_decahedral_dwarven_dice_SpellScript); + PrepareSpellScript(spell_item_decahedral_dwarven_dice); - enum - { - TEXT_DECAHEDRAL_DWARVEN_DICE = 26147 - }; - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - if (!sObjectMgr->GetBroadcastText(TEXT_DECAHEDRAL_DWARVEN_DICE)) - return false; - return true; - } + enum + { + TEXT_DECAHEDRAL_DWARVEN_DICE = 26147 + }; - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sObjectMgr->GetBroadcastText(TEXT_DECAHEDRAL_DWARVEN_DICE)) + return false; + return true; + } - void HandleScript(SpellEffIndex /*effIndex*/) - { - GetCaster()->TextEmote(TEXT_DECAHEDRAL_DWARVEN_DICE, GetHitUnit()); + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - static uint32 const minimum = 1; - static uint32 const maximum = 100; + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->TextEmote(TEXT_DECAHEDRAL_DWARVEN_DICE, GetHitUnit()); - GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum); - } + static uint32 const minimum = 1; + static uint32 const maximum = 100; - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_decahedral_dwarven_dice_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_decahedral_dwarven_dice_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_decahedral_dwarven_dice::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; // 8342 - Defibrillate (Goblin Jumper Cables) have 33% chance on success @@ -749,36 +650,25 @@ enum DesperateDefense }; // 33896 - Desperate Defense -class spell_item_desperate_defense : public SpellScriptLoader +class spell_item_desperate_defense : public AuraScript { - public: - spell_item_desperate_defense() : SpellScriptLoader("spell_item_desperate_defense") { } - - class spell_item_desperate_defense_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_desperate_defense_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DESPERATE_RAGE }); - } + PrepareAuraScript(spell_item_desperate_defense); - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) - { - PreventDefaultAction(); - GetTarget()->CastSpell(GetTarget(), SPELL_DESPERATE_RAGE, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DESPERATE_RAGE }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_desperate_defense_AuraScript::HandleProc, EFFECT_2, SPELL_AURA_PROC_TRIGGER_SPELL); - } - }; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_DESPERATE_RAGE, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_desperate_defense_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_desperate_defense::HandleProc, EFFECT_2, SPELL_AURA_PROC_TRIGGER_SPELL); + } }; // http://www.wowhead.com/item=6522 Deviate Fish @@ -792,42 +682,31 @@ enum DeviateFishSpells SPELL_HEALTHY_SPIRIT = 8068, }; -class spell_item_deviate_fish : public SpellScriptLoader +class spell_item_deviate_fish : public SpellScript { - public: - spell_item_deviate_fish() : SpellScriptLoader("spell_item_deviate_fish") { } + PrepareSpellScript(spell_item_deviate_fish); - class spell_item_deviate_fish_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_deviate_fish_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SLEEPY, SPELL_INVIGORATE, SPELL_SHRINK, SPELL_PARTY_TIME, SPELL_HEALTHY_SPIRIT }); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - uint32 spellId = urand(SPELL_SLEEPY, SPELL_HEALTHY_SPIRIT); - caster->CastSpell(caster, spellId, true, nullptr); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SLEEPY, SPELL_INVIGORATE, SPELL_SHRINK, SPELL_PARTY_TIME, SPELL_HEALTHY_SPIRIT }); + } - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_deviate_fish_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + uint32 spellId = urand(SPELL_SLEEPY, SPELL_HEALTHY_SPIRIT); + caster->CastSpell(caster, spellId, true, nullptr); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_deviate_fish_SpellScript(); - } + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_deviate_fish::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum DiscerningEyeBeastMisc @@ -836,70 +715,48 @@ enum DiscerningEyeBeastMisc }; // 59915 - Discerning Eye of the Beast Dummy -class spell_item_discerning_eye_beast_dummy : public SpellScriptLoader +class spell_item_discerning_eye_beast_dummy : public AuraScript { - public: - spell_item_discerning_eye_beast_dummy() : SpellScriptLoader("spell_item_discerning_eye_beast_dummy") { } - - class spell_item_discerning_eye_beast_dummy_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_discerning_eye_beast_dummy_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_DISCERNING_EYE_BEAST }); - } + PrepareAuraScript(spell_item_discerning_eye_beast_dummy); - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_DISCERNING_EYE_BEAST, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_DISCERNING_EYE_BEAST }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_discerning_eye_beast_dummy_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(nullptr, SPELL_DISCERNING_EYE_BEAST, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_discerning_eye_beast_dummy_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_discerning_eye_beast_dummy::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; // 71610, 71641 - Echoes of Light (Althor's Abacus) -class spell_item_echoes_of_light : public SpellScriptLoader +class spell_item_echoes_of_light : public SpellScript { - public: - spell_item_echoes_of_light() : SpellScriptLoader("spell_item_echoes_of_light") { } - - class spell_item_echoes_of_light_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_echoes_of_light_SpellScript); - - void FilterTargets(std::list<WorldObject*>& targets) - { - if (targets.size() < 2) - return; + PrepareSpellScript(spell_item_echoes_of_light); - targets.sort(Trinity::HealthPctOrderPred()); + void FilterTargets(std::list<WorldObject*>& targets) + { + if (targets.size() < 2) + return; - WorldObject* target = targets.front(); - targets.clear(); - targets.push_back(target); - } + targets.sort(Trinity::HealthPctOrderPred()); - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_item_echoes_of_light_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ALLY); - } - }; + WorldObject* target = targets.front(); + targets.clear(); + targets.push_back(target); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_echoes_of_light_SpellScript(); - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_item_echoes_of_light::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ALLY); + } }; // 7434 - Fate Rune of Unsurpassed Vigor @@ -908,36 +765,25 @@ enum FateRuneOfUnsurpassedVigor SPELL_UNSURPASSED_VIGOR = 25733 }; -class spell_item_fate_rune_of_unsurpassed_vigor : public SpellScriptLoader +class spell_item_fate_rune_of_unsurpassed_vigor : public AuraScript { - public: - spell_item_fate_rune_of_unsurpassed_vigor() : SpellScriptLoader("spell_item_fate_rune_of_unsurpassed_vigor") { } - - class spell_item_fate_rune_of_unsurpassed_vigor_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_fate_rune_of_unsurpassed_vigor_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_UNSURPASSED_VIGOR }); - } + PrepareAuraScript(spell_item_fate_rune_of_unsurpassed_vigor); - void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) - { - PreventDefaultAction(); - GetTarget()->CastSpell(GetTarget(), SPELL_UNSURPASSED_VIGOR, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_UNSURPASSED_VIGOR }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_fate_rune_of_unsurpassed_vigor_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_UNSURPASSED_VIGOR, true); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_fate_rune_of_unsurpassed_vigor_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_fate_rune_of_unsurpassed_vigor::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; // http://www.wowhead.com/item=47499 Flask of the North @@ -949,63 +795,52 @@ enum FlaskOfTheNorthSpells SPELL_FLASK_OF_THE_NORTH_STR = 67018, }; -class spell_item_flask_of_the_north : public SpellScriptLoader +class spell_item_flask_of_the_north : public SpellScript { - public: - spell_item_flask_of_the_north() : SpellScriptLoader("spell_item_flask_of_the_north") { } - - class spell_item_flask_of_the_north_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_flask_of_the_north_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_FLASK_OF_THE_NORTH_SP, SPELL_FLASK_OF_THE_NORTH_AP, SPELL_FLASK_OF_THE_NORTH_STR }); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - std::vector<uint32> possibleSpells; - switch (caster->getClass()) - { - case CLASS_WARLOCK: - case CLASS_MAGE: - case CLASS_PRIEST: - possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_SP); - break; - case CLASS_DEATH_KNIGHT: - case CLASS_WARRIOR: - possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_STR); - break; - case CLASS_ROGUE: - case CLASS_HUNTER: - possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_AP); - break; - case CLASS_DRUID: - case CLASS_PALADIN: - possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_SP); - possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_STR); - break; - case CLASS_SHAMAN: - possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_SP); - possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_AP); - break; - } + PrepareSpellScript(spell_item_flask_of_the_north); - caster->CastSpell(caster, possibleSpells[urand(0, (possibleSpells.size() - 1))], true, nullptr); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FLASK_OF_THE_NORTH_SP, SPELL_FLASK_OF_THE_NORTH_AP, SPELL_FLASK_OF_THE_NORTH_STR }); + } - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_flask_of_the_north_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + std::vector<uint32> possibleSpells; + switch (caster->getClass()) + { + case CLASS_WARLOCK: + case CLASS_MAGE: + case CLASS_PRIEST: + possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_SP); + break; + case CLASS_DEATH_KNIGHT: + case CLASS_WARRIOR: + possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_STR); + break; + case CLASS_ROGUE: + case CLASS_HUNTER: + possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_AP); + break; + case CLASS_DRUID: + case CLASS_PALADIN: + possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_SP); + possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_STR); + break; + case CLASS_SHAMAN: + possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_SP); + possibleSpells.push_back(SPELL_FLASK_OF_THE_NORTH_AP); + break; + } + + caster->CastSpell(caster, possibleSpells[urand(0, (possibleSpells.size() - 1))], true, nullptr); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_flask_of_the_north_SpellScript(); - } + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_flask_of_the_north::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum FrozenShadoweave @@ -1015,42 +850,31 @@ enum FrozenShadoweave // 39372 - Frozen Shadoweave // Frozen Shadoweave set 3p bonus -class spell_item_frozen_shadoweave : public SpellScriptLoader +class spell_item_frozen_shadoweave : public AuraScript { - public: - spell_item_frozen_shadoweave() : SpellScriptLoader("spell_item_frozen_shadoweave") { } - - class spell_item_frozen_shadoweave_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_frozen_shadoweave_AuraScript); + PrepareAuraScript(spell_item_frozen_shadoweave); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SHADOWMEND }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - DamageInfo* damageInfo = eventInfo.GetDamageInfo(); - if (!damageInfo || !damageInfo->GetDamage()) - return; - - int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); - Unit* caster = eventInfo.GetActor(); - caster->CastCustomSpell(SPELL_SHADOWMEND, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SHADOWMEND }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_frozen_shadoweave_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + Unit* caster = eventInfo.GetActor(); + caster->CastCustomSpell(SPELL_SHADOWMEND, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_frozen_shadoweave_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_frozen_shadoweave::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; // http://www.wowhead.com/item=10645 Gnomish Death Ray @@ -1061,42 +885,31 @@ enum GnomishDeathRay SPELL_GNOMISH_DEATH_RAY_TARGET = 13279, }; -class spell_item_gnomish_death_ray : public SpellScriptLoader +class spell_item_gnomish_death_ray : public SpellScript { - public: - spell_item_gnomish_death_ray() : SpellScriptLoader("spell_item_gnomish_death_ray") { } - - class spell_item_gnomish_death_ray_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_gnomish_death_ray_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_GNOMISH_DEATH_RAY_SELF, SPELL_GNOMISH_DEATH_RAY_TARGET }); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - if (Unit* target = GetHitUnit()) - { - if (urand(0, 99) < 15) - caster->CastSpell(caster, SPELL_GNOMISH_DEATH_RAY_SELF, true); // failure - else - caster->CastSpell(target, SPELL_GNOMISH_DEATH_RAY_TARGET, true); - } - } + PrepareSpellScript(spell_item_gnomish_death_ray); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_gnomish_death_ray_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_GNOMISH_DEATH_RAY_SELF, SPELL_GNOMISH_DEATH_RAY_TARGET }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + if (Unit* target = GetHitUnit()) { - return new spell_item_gnomish_death_ray_SpellScript(); + if (urand(0, 99) < 15) + caster->CastSpell(caster, SPELL_GNOMISH_DEATH_RAY_SELF, true); // failure + else + caster->CastSpell(target, SPELL_GNOMISH_DEATH_RAY_TARGET, true); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_gnomish_death_ray::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // Item 10721: Gnomish Harm Prevention Belt @@ -1106,35 +919,24 @@ enum HarmPreventionBelt SPELL_FORCEFIELD_COLLAPSE = 13235 }; -class spell_item_harm_prevention_belt : public SpellScriptLoader +class spell_item_harm_prevention_belt : public AuraScript { - public: - spell_item_harm_prevention_belt() : SpellScriptLoader("spell_item_harm_prevention_belt") { } - - class spell_item_harm_prevention_belt_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_harm_prevention_belt_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_FORCEFIELD_COLLAPSE }); - } + PrepareAuraScript(spell_item_harm_prevention_belt); - void HandleProc(ProcEventInfo& /*eventInfo*/) - { - GetTarget()->CastSpell((Unit*)nullptr, SPELL_FORCEFIELD_COLLAPSE, true); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FORCEFIELD_COLLAPSE }); + } - void Register() override - { - OnProc += AuraProcFn(spell_item_harm_prevention_belt_AuraScript::HandleProc); - } - }; + void HandleProc(ProcEventInfo& /*eventInfo*/) + { + GetTarget()->CastSpell(nullptr, SPELL_FORCEFIELD_COLLAPSE, true); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_harm_prevention_belt_AuraScript(); - } + void Register() override + { + OnProc += AuraProcFn(spell_item_harm_prevention_belt::HandleProc); + } }; // Item 23004 - Idol of Longevity @@ -1144,36 +946,25 @@ enum IdolOfLongevity SPELL_HEALING_TOUCH_MANA = 28848 }; -class spell_item_healing_touch_refund : public SpellScriptLoader +class spell_item_healing_touch_refund : public AuraScript { - public: - spell_item_healing_touch_refund() : SpellScriptLoader("spell_item_healing_touch_refund") { } - - class spell_item_healing_touch_refund_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_healing_touch_refund_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_HEALING_TOUCH_MANA }); - } + PrepareAuraScript(spell_item_healing_touch_refund); - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_HEALING_TOUCH_MANA, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HEALING_TOUCH_MANA }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_healing_touch_refund_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(nullptr, SPELL_HEALING_TOUCH_MANA, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_healing_touch_refund_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_healing_touch_refund::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; enum Heartpierce @@ -1241,7 +1032,7 @@ class spell_item_heartpierce : public SpellScriptLoader return; } - caster->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + caster->CastSpell(nullptr, spellId, true, nullptr, aurEff); } void Register() override @@ -1257,36 +1048,25 @@ class spell_item_heartpierce : public SpellScriptLoader }; // 40971 - Bonus Healing (Crystal Spire of Karabor) -class spell_item_crystal_spire_of_karabor : public SpellScriptLoader +class spell_item_crystal_spire_of_karabor : public AuraScript { - public: - spell_item_crystal_spire_of_karabor() : SpellScriptLoader("spell_item_crystal_spire_of_karabor") { } - - class spell_item_crystal_spire_of_karabor_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_crystal_spire_of_karabor_AuraScript); - - bool CheckProc(ProcEventInfo& eventInfo) - { - int32 pct = GetSpellInfo()->Effects[EFFECT_0].BasePoints; - if (HealInfo* healInfo = eventInfo.GetHealInfo()) - if (Unit* healTarget = healInfo->GetTarget()) - if (healTarget->GetHealth() - healInfo->GetEffectiveHeal() <= healTarget->CountPctFromMaxHealth(pct)) - return true; + PrepareAuraScript(spell_item_crystal_spire_of_karabor); - return false; - } + bool CheckProc(ProcEventInfo& eventInfo) + { + int32 pct = GetSpellInfo()->Effects[EFFECT_0].BasePoints; + if (HealInfo* healInfo = eventInfo.GetHealInfo()) + if (Unit* healTarget = healInfo->GetTarget()) + if (healTarget->GetHealth() - healInfo->GetEffectiveHeal() <= healTarget->CountPctFromMaxHealth(pct)) + return true; - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_item_crystal_spire_of_karabor_AuraScript::CheckProc); - } - }; + return false; + } - AuraScript* GetAuraScript() const override - { - return new spell_item_crystal_spire_of_karabor_AuraScript(); - } + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_crystal_spire_of_karabor::CheckProc); + } }; // http://www.wowhead.com/item=27388 Mr. Pinchy @@ -1300,56 +1080,45 @@ enum MakeAWish SPELL_MR_PINCHYS_GIFT = 33064, }; -class spell_item_make_a_wish : public SpellScriptLoader +class spell_item_make_a_wish : public SpellScript { - public: - spell_item_make_a_wish() : SpellScriptLoader("spell_item_make_a_wish") { } + PrepareSpellScript(spell_item_make_a_wish); - class spell_item_make_a_wish_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_make_a_wish_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_MR_PINCHYS_BLESSING, - SPELL_SUMMON_MIGHTY_MR_PINCHY, - SPELL_SUMMON_FURIOUS_MR_PINCHY, - SPELL_TINY_MAGICAL_CRAWDAD, - SPELL_MR_PINCHYS_GIFT - }); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - uint32 spellId = SPELL_MR_PINCHYS_GIFT; - switch (urand(1, 5)) - { - case 1: spellId = SPELL_MR_PINCHYS_BLESSING; break; - case 2: spellId = SPELL_SUMMON_MIGHTY_MR_PINCHY; break; - case 3: spellId = SPELL_SUMMON_FURIOUS_MR_PINCHY; break; - case 4: spellId = SPELL_TINY_MAGICAL_CRAWDAD; break; - } - caster->CastSpell(caster, spellId, true, nullptr); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_make_a_wish_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_MR_PINCHYS_BLESSING, + SPELL_SUMMON_MIGHTY_MR_PINCHY, + SPELL_SUMMON_FURIOUS_MR_PINCHY, + SPELL_TINY_MAGICAL_CRAWDAD, + SPELL_MR_PINCHYS_GIFT + }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + uint32 spellId = SPELL_MR_PINCHYS_GIFT; + switch (urand(1, 5)) { - return new spell_item_make_a_wish_SpellScript(); + case 1: spellId = SPELL_MR_PINCHYS_BLESSING; break; + case 2: spellId = SPELL_SUMMON_MIGHTY_MR_PINCHY; break; + case 3: spellId = SPELL_SUMMON_FURIOUS_MR_PINCHY; break; + case 4: spellId = SPELL_TINY_MAGICAL_CRAWDAD; break; } + caster->CastSpell(caster, spellId, true, nullptr); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_make_a_wish::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum MarkOfConquest @@ -1360,97 +1129,75 @@ enum MarkOfConquest // Item - 27920: Mark of Conquest // Item - 27921: Mark of Conquest // 33510 - Health Restore -class spell_item_mark_of_conquest : public SpellScriptLoader +class spell_item_mark_of_conquest : public AuraScript { - public: - spell_item_mark_of_conquest() : SpellScriptLoader("spell_item_mark_of_conquest") { } - - class spell_item_mark_of_conquest_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_mark_of_conquest_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_MARK_OF_CONQUEST_ENERGIZE }); - } + PrepareAuraScript(spell_item_mark_of_conquest); - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - if (eventInfo.GetTypeMask() & (PROC_FLAG_DONE_RANGED_AUTO_ATTACK | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS)) - { - // in that case, do not cast heal spell - PreventDefaultAction(); - // but mana instead - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_MARK_OF_CONQUEST_ENERGIZE, true, nullptr, aurEff); - } - } - - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_mark_of_conquest_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_MARK_OF_CONQUEST_ENERGIZE }); + } - AuraScript* GetAuraScript() const override + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + if (eventInfo.GetTypeMask() & (PROC_FLAG_DONE_RANGED_AUTO_ATTACK | PROC_FLAG_DONE_SPELL_RANGED_DMG_CLASS)) { - return new spell_item_mark_of_conquest_AuraScript(); + // in that case, do not cast heal spell + PreventDefaultAction(); + // but mana instead + eventInfo.GetActor()->CastSpell(nullptr, SPELL_MARK_OF_CONQUEST_ENERGIZE, true, nullptr, aurEff); } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_mark_of_conquest::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } }; // http://www.wowhead.com/item=32686 Mingo's Fortune Giblets // 40802 Mingo's Fortune Generator -class spell_item_mingos_fortune_generator : public SpellScriptLoader +class spell_item_mingos_fortune_generator : public SpellScript { - public: - spell_item_mingos_fortune_generator() : SpellScriptLoader("spell_item_mingos_fortune_generator") { } - - class spell_item_mingos_fortune_generator_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_mingos_fortune_generator_SpellScript); - - void HandleDummy(SpellEffIndex effIndex) - { - // Selecting one from Bloodstained Fortune item - uint32 newitemid; - switch (urand(1, 20)) - { - case 1: newitemid = 32688; break; - case 2: newitemid = 32689; break; - case 3: newitemid = 32690; break; - case 4: newitemid = 32691; break; - case 5: newitemid = 32692; break; - case 6: newitemid = 32693; break; - case 7: newitemid = 32700; break; - case 8: newitemid = 32701; break; - case 9: newitemid = 32702; break; - case 10: newitemid = 32703; break; - case 11: newitemid = 32704; break; - case 12: newitemid = 32705; break; - case 13: newitemid = 32706; break; - case 14: newitemid = 32707; break; - case 15: newitemid = 32708; break; - case 16: newitemid = 32709; break; - case 17: newitemid = 32710; break; - case 18: newitemid = 32711; break; - case 19: newitemid = 32712; break; - case 20: newitemid = 32713; break; - default: - return; - } + PrepareSpellScript(spell_item_mingos_fortune_generator); - CreateItem(effIndex, newitemid); - } + void HandleDummy(SpellEffIndex effIndex) + { + // Selecting one from Bloodstained Fortune item + uint32 newitemid; + switch (urand(1, 20)) + { + case 1: newitemid = 32688; break; + case 2: newitemid = 32689; break; + case 3: newitemid = 32690; break; + case 4: newitemid = 32691; break; + case 5: newitemid = 32692; break; + case 6: newitemid = 32693; break; + case 7: newitemid = 32700; break; + case 8: newitemid = 32701; break; + case 9: newitemid = 32702; break; + case 10: newitemid = 32703; break; + case 11: newitemid = 32704; break; + case 12: newitemid = 32705; break; + case 13: newitemid = 32706; break; + case 14: newitemid = 32707; break; + case 15: newitemid = 32708; break; + case 16: newitemid = 32709; break; + case 17: newitemid = 32710; break; + case 18: newitemid = 32711; break; + case 19: newitemid = 32712; break; + case 20: newitemid = 32713; break; + default: + return; + } - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_mingos_fortune_generator_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + CreateItem(effIndex, newitemid); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_mingos_fortune_generator_SpellScript(); - } + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_mingos_fortune_generator::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 71875, 71877 - Item - Black Bruise: Necrotic Touch Proc @@ -1459,47 +1206,36 @@ enum NecroticTouch SPELL_ITEM_NECROTIC_TOUCH_PROC = 71879 }; -class spell_item_necrotic_touch : public SpellScriptLoader +class spell_item_necrotic_touch : public AuraScript { - public: - spell_item_necrotic_touch() : SpellScriptLoader("spell_item_necrotic_touch") { } - - class spell_item_necrotic_touch_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_necrotic_touch_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_ITEM_NECROTIC_TOUCH_PROC }); - } + PrepareAuraScript(spell_item_necrotic_touch); - bool CheckProc(ProcEventInfo& eventInfo) - { - return eventInfo.GetProcTarget() && eventInfo.GetProcTarget()->IsAlive(); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_ITEM_NECROTIC_TOUCH_PROC }); + } - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - DamageInfo* damageInfo = eventInfo.GetDamageInfo(); - if (!damageInfo || !damageInfo->GetDamage()) - return; + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetProcTarget() && eventInfo.GetProcTarget()->IsAlive(); + } - int32 bp = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); - GetTarget()->CastCustomSpell(SPELL_ITEM_NECROTIC_TOUCH_PROC, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetProcTarget(), true, nullptr, aurEff); - } + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_item_necrotic_touch_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_item_necrotic_touch_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + int32 bp = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_ITEM_NECROTIC_TOUCH_PROC, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetProcTarget(), true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_necrotic_touch_AuraScript(); - } + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_necrotic_touch::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_necrotic_touch::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; // http://www.wowhead.com/item=10720 Gnomish Net-o-Matic Projector @@ -1511,50 +1247,39 @@ enum NetOMaticSpells SPELL_NET_O_MATIC_TRIGGERED3 = 13099, }; -class spell_item_net_o_matic : public SpellScriptLoader +class spell_item_net_o_matic : public SpellScript { - public: - spell_item_net_o_matic() : SpellScriptLoader("spell_item_net_o_matic") { } + PrepareSpellScript(spell_item_net_o_matic); - class spell_item_net_o_matic_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_item_net_o_matic_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_NET_O_MATIC_TRIGGERED1, - SPELL_NET_O_MATIC_TRIGGERED2, - SPELL_NET_O_MATIC_TRIGGERED3 - }); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - if (Unit* target = GetHitUnit()) - { - uint32 spellId = SPELL_NET_O_MATIC_TRIGGERED3; - uint32 roll = urand(0, 99); - if (roll < 2) // 2% for 30 sec self root (off-like chance unknown) - spellId = SPELL_NET_O_MATIC_TRIGGERED1; - else if (roll < 4) // 2% for 20 sec root, charge to target (off-like chance unknown) - spellId = SPELL_NET_O_MATIC_TRIGGERED2; - - GetCaster()->CastSpell(target, spellId, true, nullptr); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_net_o_matic_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + SPELL_NET_O_MATIC_TRIGGERED1, + SPELL_NET_O_MATIC_TRIGGERED2, + SPELL_NET_O_MATIC_TRIGGERED3 + }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) { - return new spell_item_net_o_matic_SpellScript(); + uint32 spellId = SPELL_NET_O_MATIC_TRIGGERED3; + uint32 roll = urand(0, 99); + if (roll < 2) // 2% for 30 sec self root (off-like chance unknown) + spellId = SPELL_NET_O_MATIC_TRIGGERED1; + else if (roll < 4) // 2% for 20 sec root, charge to target (off-like chance unknown) + spellId = SPELL_NET_O_MATIC_TRIGGERED2; + + GetCaster()->CastSpell(target, spellId, true, nullptr); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_net_o_matic::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // http://www.wowhead.com/item=8529 Noggenfogger Elixir @@ -1566,83 +1291,61 @@ enum NoggenfoggerElixirSpells SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED3 = 16591, }; -class spell_item_noggenfogger_elixir : public SpellScriptLoader +class spell_item_noggenfogger_elixir : public SpellScript { - public: - spell_item_noggenfogger_elixir() : SpellScriptLoader("spell_item_noggenfogger_elixir") { } - - class spell_item_noggenfogger_elixir_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_noggenfogger_elixir_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + PrepareSpellScript(spell_item_noggenfogger_elixir); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED1, - SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED2, - SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED3 - }); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - uint32 spellId = SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED3; - switch (urand(1, 3)) - { - case 1: spellId = SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED1; break; - case 2: spellId = SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED2; break; - } - - caster->CastSpell(caster, spellId, true, nullptr); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_noggenfogger_elixir_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED1, + SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED2, + SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED3 + }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + uint32 spellId = SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED3; + switch (urand(1, 3)) { - return new spell_item_noggenfogger_elixir_SpellScript(); + case 1: spellId = SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED1; break; + case 2: spellId = SPELL_NOGGENFOGGER_ELIXIR_TRIGGERED2; break; } + + caster->CastSpell(caster, spellId, true, nullptr); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_noggenfogger_elixir::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 29601 - Enlightenment (Pendant of the Violet Eye) -class spell_item_pendant_of_the_violet_eye : public SpellScriptLoader +class spell_item_pendant_of_the_violet_eye : public AuraScript { - public: - spell_item_pendant_of_the_violet_eye() : SpellScriptLoader("spell_item_pendant_of_the_violet_eye") { } - - class spell_item_pendant_of_the_violet_eye_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_pendant_of_the_violet_eye_AuraScript); - - bool CheckProc(ProcEventInfo& eventInfo) - { - if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) - return spellInfo->PowerType == POWER_MANA || (spellInfo->ManaCost != 0 && spellInfo->ManaCostPercentage != 0 && spellInfo->ManaCostPerlevel != 0); + PrepareAuraScript(spell_item_pendant_of_the_violet_eye); - return false; - } + bool CheckProc(ProcEventInfo& eventInfo) + { + if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo()) + return spellInfo->PowerType == POWER_MANA || (spellInfo->ManaCost != 0 && spellInfo->ManaCostPercentage != 0 && spellInfo->ManaCostPerlevel != 0); - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_item_pendant_of_the_violet_eye_AuraScript::CheckProc); - } - }; + return false; + } - AuraScript* GetAuraScript() const override - { - return new spell_item_pendant_of_the_violet_eye_AuraScript(); - } + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_pendant_of_the_violet_eye::CheckProc); + } }; enum PersistentShieldMisc @@ -1651,50 +1354,39 @@ enum PersistentShieldMisc }; // 26467 - Persistent Shield -class spell_item_persistent_shield : public SpellScriptLoader +class spell_item_persistent_shield : public AuraScript { - public: - spell_item_persistent_shield() : SpellScriptLoader("spell_item_persistent_shield") { } - - class spell_item_persistent_shield_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_persistent_shield_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_PERSISTENT_SHIELD_TRIGGERED }); - } + PrepareAuraScript(spell_item_persistent_shield); - bool CheckProc(ProcEventInfo& eventInfo) - { - return eventInfo.GetHealInfo() && eventInfo.GetHealInfo()->GetHeal(); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PERSISTENT_SHIELD_TRIGGERED }); + } - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - Unit* caster = eventInfo.GetActor(); - Unit* target = eventInfo.GetProcTarget(); - int32 bp0 = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), 15); + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetHealInfo() && eventInfo.GetHealInfo()->GetHeal(); + } - // Scarab Brooch does not replace stronger shields - if (AuraEffect const* shield = target->GetAuraEffect(SPELL_PERSISTENT_SHIELD_TRIGGERED, EFFECT_0, caster->GetGUID())) - if (shield->GetAmount() > bp0) - return; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetProcTarget(); + int32 bp0 = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), 15); - caster->CastCustomSpell(SPELL_PERSISTENT_SHIELD_TRIGGERED, SPELLVALUE_BASE_POINT0, bp0, target, true, nullptr, aurEff); - } + // Scarab Brooch does not replace stronger shields + if (AuraEffect const* shield = target->GetAuraEffect(SPELL_PERSISTENT_SHIELD_TRIGGERED, EFFECT_0, caster->GetGUID())) + if (shield->GetAmount() > bp0) + return; - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_item_persistent_shield_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_item_persistent_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); - } - }; + caster->CastCustomSpell(SPELL_PERSISTENT_SHIELD_TRIGGERED, SPELLVALUE_BASE_POINT0, bp0, target, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_persistent_shield_AuraScript(); - } + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_persistent_shield::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_persistent_shield::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } }; enum PetHealing @@ -1705,71 +1397,49 @@ enum PetHealing // 37381 - Pet Healing // Hunter T5 2P Bonus // Warlock T5 2P Bonus -class spell_item_pet_healing : public SpellScriptLoader +class spell_item_pet_healing : public AuraScript { - public: - spell_item_pet_healing() : SpellScriptLoader("spell_item_pet_healing") { } - - class spell_item_pet_healing_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_pet_healing_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_HEALTH_LINK }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - DamageInfo* damageInfo = eventInfo.GetDamageInfo(); - if (!damageInfo || !damageInfo->GetDamage()) - return; + PrepareAuraScript(spell_item_pet_healing); - int32 bp = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); - Unit* caster = eventInfo.GetActor(); - caster->CastCustomSpell(SPELL_HEALTH_LINK, SPELLVALUE_BASE_POINT0, bp, (Unit*)nullptr, true, nullptr, aurEff); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HEALTH_LINK }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_pet_healing_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + DamageInfo* damageInfo = eventInfo.GetDamageInfo(); + if (!damageInfo || !damageInfo->GetDamage()) + return; + + int32 bp = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); + Unit* caster = eventInfo.GetActor(); + caster->CastCustomSpell(SPELL_HEALTH_LINK, SPELLVALUE_BASE_POINT0, bp, (Unit*)nullptr, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_pet_healing_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_pet_healing::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; // 17512 - Piccolo of the Flaming Fire -class spell_item_piccolo_of_the_flaming_fire : public SpellScriptLoader +class spell_item_piccolo_of_the_flaming_fire : public SpellScript { - public: - spell_item_piccolo_of_the_flaming_fire() : SpellScriptLoader("spell_item_piccolo_of_the_flaming_fire") { } - - class spell_item_piccolo_of_the_flaming_fire_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_piccolo_of_the_flaming_fire_SpellScript); + PrepareSpellScript(spell_item_piccolo_of_the_flaming_fire); - void HandleScript(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - if (Player* target = GetHitPlayer()) - target->HandleEmoteCommand(EMOTE_STATE_DANCE); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_piccolo_of_the_flaming_fire_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + if (Player* target = GetHitPlayer()) + target->HandleEmoteCommand(EMOTE_STATE_DANCE); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_piccolo_of_the_flaming_fire_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_piccolo_of_the_flaming_fire::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; // http://www.wowhead.com/item=6657 Savory Deviate Delight @@ -1782,55 +1452,44 @@ enum SavoryDeviateDelight SPELL_YAAARRRR_FEMALE = 8222, }; -class spell_item_savory_deviate_delight : public SpellScriptLoader +class spell_item_savory_deviate_delight : public SpellScript { - public: - spell_item_savory_deviate_delight() : SpellScriptLoader("spell_item_savory_deviate_delight") { } + PrepareSpellScript(spell_item_savory_deviate_delight); - class spell_item_savory_deviate_delight_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_savory_deviate_delight_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_FLIP_OUT_MALE, - SPELL_FLIP_OUT_FEMALE, - SPELL_YAAARRRR_MALE, - SPELL_YAAARRRR_FEMALE - }); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - uint32 spellId = 0; - switch (urand(1, 2)) - { - // Flip Out - ninja - case 1: spellId = (caster->getGender() == GENDER_MALE ? SPELL_FLIP_OUT_MALE : SPELL_FLIP_OUT_FEMALE); break; - // Yaaarrrr - pirate - case 2: spellId = (caster->getGender() == GENDER_MALE ? SPELL_YAAARRRR_MALE : SPELL_YAAARRRR_FEMALE); break; - } - caster->CastSpell(caster, spellId, true, nullptr); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_savory_deviate_delight_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_FLIP_OUT_MALE, + SPELL_FLIP_OUT_FEMALE, + SPELL_YAAARRRR_MALE, + SPELL_YAAARRRR_FEMALE + }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + uint32 spellId = 0; + switch (urand(1, 2)) { - return new spell_item_savory_deviate_delight_SpellScript(); + // Flip Out - ninja + case 1: spellId = (caster->getGender() == GENDER_MALE ? SPELL_FLIP_OUT_MALE : SPELL_FLIP_OUT_FEMALE); break; + // Yaaarrrr - pirate + case 2: spellId = (caster->getGender() == GENDER_MALE ? SPELL_YAAARRRR_MALE : SPELL_YAAARRRR_FEMALE); break; } + caster->CastSpell(caster, spellId, true, nullptr); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_savory_deviate_delight::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 48129 - Scroll of Recall @@ -1846,64 +1505,53 @@ enum ScrollOfRecall SPELL_SCROLL_OF_RECALL_FAIL_HORDE_1 = 60328, }; -class spell_item_scroll_of_recall : public SpellScriptLoader +class spell_item_scroll_of_recall : public SpellScript { - public: - spell_item_scroll_of_recall() : SpellScriptLoader("spell_item_scroll_of_recall") { } - - class spell_item_scroll_of_recall_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_scroll_of_recall_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + PrepareSpellScript(spell_item_scroll_of_recall); - void HandleScript(SpellEffIndex effIndex) - { - Unit* caster = GetCaster(); - uint8 maxSafeLevel = 0; - switch (GetSpellInfo()->Id) - { - case SPELL_SCROLL_OF_RECALL_I: // Scroll of Recall - maxSafeLevel = 40; - break; - case SPELL_SCROLL_OF_RECALL_II: // Scroll of Recall II - maxSafeLevel = 70; - break; - case SPELL_SCROLL_OF_RECALL_III: // Scroll of Recal III - maxSafeLevel = 80; - break; - default: - break; - } - - if (caster->getLevel() > maxSafeLevel) - { - caster->CastSpell(caster, SPELL_LOST, true); + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - // ALLIANCE from 60323 to 60330 - HORDE from 60328 to 60335 - uint32 spellId = SPELL_SCROLL_OF_RECALL_FAIL_ALLIANCE_1; - if (GetCaster()->ToPlayer()->GetTeam() == HORDE) - spellId = SPELL_SCROLL_OF_RECALL_FAIL_HORDE_1; + void HandleScript(SpellEffIndex effIndex) + { + Unit* caster = GetCaster(); + uint8 maxSafeLevel = 0; + switch (GetSpellInfo()->Id) + { + case SPELL_SCROLL_OF_RECALL_I: // Scroll of Recall + maxSafeLevel = 40; + break; + case SPELL_SCROLL_OF_RECALL_II: // Scroll of Recall II + maxSafeLevel = 70; + break; + case SPELL_SCROLL_OF_RECALL_III: // Scroll of Recal III + maxSafeLevel = 80; + break; + default: + break; + } - GetCaster()->CastSpell(GetCaster(), spellId + urand(0, 7), true); + if (caster->getLevel() > maxSafeLevel) + { + caster->CastSpell(caster, SPELL_LOST, true); - PreventHitDefaultEffect(effIndex); - } - } + // ALLIANCE from 60323 to 60330 - HORDE from 60328 to 60335 + uint32 spellId = SPELL_SCROLL_OF_RECALL_FAIL_ALLIANCE_1; + if (GetCaster()->ToPlayer()->GetTeam() == HORDE) + spellId = SPELL_SCROLL_OF_RECALL_FAIL_HORDE_1; - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_scroll_of_recall_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_TELEPORT_UNITS); - } - }; + GetCaster()->CastSpell(GetCaster(), spellId + urand(0, 7), true); - SpellScript* GetSpellScript() const override - { - return new spell_item_scroll_of_recall_SpellScript(); + PreventHitDefaultEffect(effIndex); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_scroll_of_recall::HandleScript, EFFECT_0, SPELL_EFFECT_TELEPORT_UNITS); + } }; // 71169 - Shadow's Fate (Shadowmourne questline) @@ -1913,71 +1561,49 @@ enum ShadowsFate NPC_SINDRAGOSA = 36853 }; -class spell_item_unsated_craving : public SpellScriptLoader +class spell_item_unsated_craving : public AuraScript { - public: - spell_item_unsated_craving() : SpellScriptLoader("spell_item_unsated_craving") { } - - class spell_item_unsated_craving_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_unsated_craving_AuraScript); + PrepareAuraScript(spell_item_unsated_craving); - bool CheckProc(ProcEventInfo& procInfo) - { - Unit* caster = procInfo.GetActor(); - if (!caster || caster->GetTypeId() != TYPEID_PLAYER) - return false; - - Unit* target = procInfo.GetActionTarget(); - if (!target || target->GetTypeId() != TYPEID_UNIT || target->IsCritter() || (target->GetEntry() != NPC_SINDRAGOSA && target->IsSummon())) - return false; + bool CheckProc(ProcEventInfo& procInfo) + { + Unit* caster = procInfo.GetActor(); + if (!caster || caster->GetTypeId() != TYPEID_PLAYER) + return false; - return true; - } + Unit* target = procInfo.GetActionTarget(); + if (!target || target->GetTypeId() != TYPEID_UNIT || target->IsCritter() || (target->GetEntry() != NPC_SINDRAGOSA && target->IsSummon())) + return false; - void Register() override - { - DoCheckProc += AuraCheckProcFn(spell_item_unsated_craving_AuraScript::CheckProc); - } - }; + return true; + } - AuraScript* GetAuraScript() const override - { - return new spell_item_unsated_craving_AuraScript(); - } + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_unsated_craving::CheckProc); + } }; -class spell_item_shadows_fate : public SpellScriptLoader +class spell_item_shadows_fate : public AuraScript { - public: - spell_item_shadows_fate() : SpellScriptLoader("spell_item_shadows_fate") { } - - class spell_item_shadows_fate_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_shadows_fate_AuraScript); + PrepareAuraScript(spell_item_shadows_fate); - void HandleProc(ProcEventInfo& procInfo) - { - PreventDefaultAction(); - - Unit* caster = procInfo.GetActor(); - Unit* target = GetCaster(); - if (!caster || !target) - return; + void HandleProc(ProcEventInfo& procInfo) + { + PreventDefaultAction(); - caster->CastSpell(target, SPELL_SOUL_FEAST, TRIGGERED_FULL_MASK); - } + Unit* caster = procInfo.GetActor(); + Unit* target = GetCaster(); + if (!caster || !target) + return; - void Register() override - { - OnProc += AuraProcFn(spell_item_shadows_fate_AuraScript::HandleProc); - } - }; + caster->CastSpell(target, SPELL_SOUL_FEAST, true); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_shadows_fate_AuraScript(); - } + void Register() override + { + OnProc += AuraProcFn(spell_item_shadows_fate::HandleProc); + } }; enum Shadowmourne @@ -1990,120 +1616,98 @@ enum Shadowmourne }; // 71903 - Item - Shadowmourne Legendary -class spell_item_shadowmourne : public SpellScriptLoader +class spell_item_shadowmourne : public AuraScript { - public: - spell_item_shadowmourne() : SpellScriptLoader("spell_item_shadowmourne") { } + PrepareAuraScript(spell_item_shadowmourne); - class spell_item_shadowmourne_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_item_shadowmourne_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE, - SPELL_SHADOWMOURNE_SOUL_FRAGMENT, - SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF - }); - } - - bool CheckProc(ProcEventInfo& eventInfo) - { - if (GetTarget()->HasAura(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF)) // cant collect shards while under effect of Chaos Bane buff - return false; - return eventInfo.GetProcTarget() && eventInfo.GetProcTarget()->IsAlive(); - } + SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE, + SPELL_SHADOWMOURNE_SOUL_FRAGMENT, + SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF + }); + } - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - GetTarget()->CastSpell(GetTarget(), SPELL_SHADOWMOURNE_SOUL_FRAGMENT, true, nullptr, aurEff); + bool CheckProc(ProcEventInfo& eventInfo) + { + if (GetTarget()->HasAura(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF)) // cant collect shards while under effect of Chaos Bane buff + return false; + return eventInfo.GetProcTarget() && eventInfo.GetProcTarget()->IsAlive(); + } - // this can't be handled in AuraScript of SoulFragments because we need to know victim - if (Aura* soulFragments = GetTarget()->GetAura(SPELL_SHADOWMOURNE_SOUL_FRAGMENT)) - { - if (soulFragments->GetStackAmount() >= 10) - { - GetTarget()->CastSpell(eventInfo.GetProcTarget(), SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE, true, nullptr, aurEff); - soulFragments->Remove(); - } - } - } + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_SHADOWMOURNE_SOUL_FRAGMENT, true, nullptr, aurEff); - void Register() override + // this can't be handled in AuraScript of SoulFragments because we need to know victim + if (Aura* soulFragments = GetTarget()->GetAura(SPELL_SHADOWMOURNE_SOUL_FRAGMENT)) + { + if (soulFragments->GetStackAmount() >= 10) { - DoCheckProc += AuraCheckProcFn(spell_item_shadowmourne_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_item_shadowmourne_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + GetTarget()->CastSpell(eventInfo.GetProcTarget(), SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE, true, nullptr, aurEff); + soulFragments->Remove(); } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_item_shadowmourne_AuraScript(); } + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_item_shadowmourne::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_shadowmourne::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; // 71905 - Soul Fragment -class spell_item_shadowmourne_soul_fragment : public SpellScriptLoader +class spell_item_shadowmourne_soul_fragment : public AuraScript { - public: - spell_item_shadowmourne_soul_fragment() : SpellScriptLoader("spell_item_shadowmourne_soul_fragment") { } + PrepareAuraScript(spell_item_shadowmourne_soul_fragment); - class spell_item_shadowmourne_soul_fragment_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareAuraScript(spell_item_shadowmourne_soul_fragment_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_SHADOWMOURNE_VISUAL_LOW, - SPELL_SHADOWMOURNE_VISUAL_HIGH, - SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF - }); - } - - void OnStackChange(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - switch (GetStackAmount()) - { - case 1: - target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_LOW, true); - break; - case 6: - target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW); - target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_HIGH, true); - break; - case 10: - target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH); - target->CastSpell(target, SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF, true); - break; - default: - break; - } - } + SPELL_SHADOWMOURNE_VISUAL_LOW, + SPELL_SHADOWMOURNE_VISUAL_HIGH, + SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF + }); + } - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); + void OnStackChange(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + switch (GetStackAmount()) + { + case 1: + target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_LOW, true); + break; + case 6: target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW); + target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_HIGH, true); + break; + case 10: target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH); - } + target->CastSpell(target, SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF, true); + break; + default: + break; + } + } - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_item_shadowmourne_soul_fragment_AuraScript::OnStackChange, EFFECT_0, SPELL_AURA_MOD_STAT, AuraEffectHandleModes(AURA_EFFECT_HANDLE_REAL | AURA_EFFECT_HANDLE_REAPPLY)); - AfterEffectRemove += AuraEffectRemoveFn(spell_item_shadowmourne_soul_fragment_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_STAT, AURA_EFFECT_HANDLE_REAL); - } - }; + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW); + target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_shadowmourne_soul_fragment_AuraScript(); - } + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_item_shadowmourne_soul_fragment::OnStackChange, EFFECT_0, SPELL_AURA_MOD_STAT, AuraEffectHandleModes(AURA_EFFECT_HANDLE_REAL | AURA_EFFECT_HANDLE_REAPPLY)); + AfterEffectRemove += AuraEffectRemoveFn(spell_item_shadowmourne_soul_fragment::OnRemove, EFFECT_0, SPELL_AURA_MOD_STAT, AURA_EFFECT_HANDLE_REAL); + } }; // http://www.wowhead.com/item=7734 Six Demon Bag @@ -2118,69 +1722,58 @@ enum SixDemonBagSpells SPELL_ENVELOPING_WINDS = 25189, }; -class spell_item_six_demon_bag : public SpellScriptLoader +class spell_item_six_demon_bag : public SpellScript { - public: - spell_item_six_demon_bag() : SpellScriptLoader("spell_item_six_demon_bag") { } - - class spell_item_six_demon_bag_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_six_demon_bag_SpellScript); + PrepareSpellScript(spell_item_six_demon_bag); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_FROSTBOLT, - SPELL_POLYMORPH, - SPELL_SUMMON_FELHOUND_MINION, - SPELL_FIREBALL, - SPELL_CHAIN_LIGHTNING, - SPELL_ENVELOPING_WINDS - }); - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( + { + SPELL_FROSTBOLT, + SPELL_POLYMORPH, + SPELL_SUMMON_FELHOUND_MINION, + SPELL_FIREBALL, + SPELL_CHAIN_LIGHTNING, + SPELL_ENVELOPING_WINDS + }); + } - void HandleDummy(SpellEffIndex /*effIndex*/) + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + if (Unit* target = GetHitUnit()) + { + uint32 spellId = 0; + uint32 rand = urand(0, 99); + if (rand < 25) // Fireball (25% chance) + spellId = SPELL_FIREBALL; + else if (rand < 50) // Frostball (25% chance) + spellId = SPELL_FROSTBOLT; + else if (rand < 70) // Chain Lighting (20% chance) + spellId = SPELL_CHAIN_LIGHTNING; + else if (rand < 80) // Polymorph (10% chance) { - Unit* caster = GetCaster(); - if (Unit* target = GetHitUnit()) - { - uint32 spellId = 0; - uint32 rand = urand(0, 99); - if (rand < 25) // Fireball (25% chance) - spellId = SPELL_FIREBALL; - else if (rand < 50) // Frostball (25% chance) - spellId = SPELL_FROSTBOLT; - else if (rand < 70) // Chain Lighting (20% chance) - spellId = SPELL_CHAIN_LIGHTNING; - else if (rand < 80) // Polymorph (10% chance) - { - spellId = SPELL_POLYMORPH; - if (urand(0, 100) <= 30) // 30% chance to self-cast - target = caster; - } - else if (rand < 95) // Enveloping Winds (15% chance) - spellId = SPELL_ENVELOPING_WINDS; - else // Summon Felhund minion (5% chance) - { - spellId = SPELL_SUMMON_FELHOUND_MINION; - target = caster; - } - - caster->CastSpell(target, spellId, true, GetCastItem()); - } + spellId = SPELL_POLYMORPH; + if (urand(0, 100) <= 30) // 30% chance to self-cast + target = caster; } - - void Register() override + else if (rand < 95) // Enveloping Winds (15% chance) + spellId = SPELL_ENVELOPING_WINDS; + else // Summon Felhund minion (5% chance) { - OnEffectHitTarget += SpellEffectFn(spell_item_six_demon_bag_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + spellId = SPELL_SUMMON_FELHOUND_MINION; + target = caster; } - }; - SpellScript* GetSpellScript() const override - { - return new spell_item_six_demon_bag_SpellScript(); + caster->CastSpell(target, spellId, true, GetCastItem()); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_six_demon_bag::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum SwiftHandJusticeMisc @@ -2189,39 +1782,28 @@ enum SwiftHandJusticeMisc }; // 59906 - Swift Hand of Justice Dummy -class spell_item_swift_hand_justice_dummy : public SpellScriptLoader +class spell_item_swift_hand_justice_dummy : public AuraScript { - public: - spell_item_swift_hand_justice_dummy() : SpellScriptLoader("spell_item_swift_hand_justice_dummy") { } - - class spell_item_swift_hand_justice_dummy_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_swift_hand_justice_dummy_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_SWIFT_HAND_OF_JUSTICE_HEAL }); - } + PrepareAuraScript(spell_item_swift_hand_justice_dummy); - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_SWIFT_HAND_OF_JUSTICE_HEAL }); + } - Unit* caster = eventInfo.GetActor(); - int32 amount = caster->CountPctFromMaxHealth(aurEff->GetAmount()); - caster->CastCustomSpell(SPELL_SWIFT_HAND_OF_JUSTICE_HEAL, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); - } + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_swift_hand_justice_dummy_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + Unit* caster = eventInfo.GetActor(); + int32 amount = caster->CountPctFromMaxHealth(aurEff->GetAmount()); + caster->CastCustomSpell(SPELL_SWIFT_HAND_OF_JUSTICE_HEAL, SPELLVALUE_BASE_POINT0, amount, (Unit*)nullptr, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_swift_hand_justice_dummy_AuraScript(); - } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_swift_hand_justice_dummy::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; enum TotemOfFlowingWater @@ -2231,66 +1813,43 @@ enum TotemOfFlowingWater // Item - 23005: Totem of Flowing Water // 28849 - Lesser Healing Wave -class spell_item_totem_of_flowing_water : public SpellScriptLoader +class spell_item_totem_of_flowing_water : public AuraScript { - public: - spell_item_totem_of_flowing_water() : SpellScriptLoader("spell_item_totem_of_flowing_water") { } - - class spell_item_totem_of_flowing_water_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_totem_of_flowing_water_AuraScript); + PrepareAuraScript(spell_item_totem_of_flowing_water); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_LESSER_HEALING_WAVE_MANA }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_LESSER_HEALING_WAVE_MANA, true, nullptr, aurEff); - } - - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_totem_of_flowing_water_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_LESSER_HEALING_WAVE_MANA }); + } - AuraScript* GetAuraScript() const override - { - return new spell_item_totem_of_flowing_water_AuraScript(); - } + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + eventInfo.GetActor()->CastSpell(nullptr, SPELL_LESSER_HEALING_WAVE_MANA, true, nullptr, aurEff); + } + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_totem_of_flowing_water::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } }; // 28862 - The Eye of Diminution -class spell_item_the_eye_of_diminution : public SpellScriptLoader +class spell_item_the_eye_of_diminution : public AuraScript { - public: - spell_item_the_eye_of_diminution() : SpellScriptLoader("spell_item_the_eye_of_diminution") { } - - class spell_item_the_eye_of_diminution_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_the_eye_of_diminution_AuraScript); + PrepareAuraScript(spell_item_the_eye_of_diminution); - void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) - { - int32 diff = GetUnitOwner()->getLevel() - 60; - if (diff > 0) - amount += diff; - } - - void Register() override - { - DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_the_eye_of_diminution_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT); - } - }; + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + int32 diff = GetUnitOwner()->getLevel() - 60; + if (diff > 0) + amount += diff; + } - AuraScript* GetAuraScript() const override - { - return new spell_item_the_eye_of_diminution_AuraScript(); - } + void Register() override + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_the_eye_of_diminution::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT); + } }; // http://www.wowhead.com/item=44012 Underbelly Elixir @@ -2302,102 +1861,80 @@ enum UnderbellyElixirSpells SPELL_UNDERBELLY_ELIXIR_TRIGGERED3 = 59843, }; -class spell_item_underbelly_elixir : public SpellScriptLoader +class spell_item_underbelly_elixir : public SpellScript { - public: - spell_item_underbelly_elixir() : SpellScriptLoader("spell_item_underbelly_elixir") { } + PrepareSpellScript(spell_item_underbelly_elixir); - class spell_item_underbelly_elixir_SpellScript : public SpellScript + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_item_underbelly_elixir_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_UNDERBELLY_ELIXIR_TRIGGERED1, - SPELL_UNDERBELLY_ELIXIR_TRIGGERED2, - SPELL_UNDERBELLY_ELIXIR_TRIGGERED3 - }); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Unit* caster = GetCaster(); - uint32 spellId = SPELL_UNDERBELLY_ELIXIR_TRIGGERED3; - switch (urand(1, 3)) - { - case 1: spellId = SPELL_UNDERBELLY_ELIXIR_TRIGGERED1; break; - case 2: spellId = SPELL_UNDERBELLY_ELIXIR_TRIGGERED2; break; - } - caster->CastSpell(caster, spellId, true, nullptr); - } - - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_underbelly_elixir_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + SPELL_UNDERBELLY_ELIXIR_TRIGGERED1, + SPELL_UNDERBELLY_ELIXIR_TRIGGERED2, + SPELL_UNDERBELLY_ELIXIR_TRIGGERED3 + }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + uint32 spellId = SPELL_UNDERBELLY_ELIXIR_TRIGGERED3; + switch (urand(1, 3)) { - return new spell_item_underbelly_elixir_SpellScript(); + case 1: spellId = SPELL_UNDERBELLY_ELIXIR_TRIGGERED1; break; + case 2: spellId = SPELL_UNDERBELLY_ELIXIR_TRIGGERED2; break; } + caster->CastSpell(caster, spellId, true, nullptr); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_underbelly_elixir::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 47776 - Roll 'dem Bones -class spell_item_worn_troll_dice : public SpellScriptLoader +class spell_item_worn_troll_dice : public SpellScript { - public: - spell_item_worn_troll_dice() : SpellScriptLoader("spell_item_worn_troll_dice") { } - - class spell_item_worn_troll_dice_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_worn_troll_dice_SpellScript); - - enum - { - TEXT_WORN_TROLL_DICE = 26152 - }; + PrepareSpellScript(spell_item_worn_troll_dice); - bool Validate(SpellInfo const* /*spellInfo*/) override - { - if (!sObjectMgr->GetBroadcastText(TEXT_WORN_TROLL_DICE)) - return false; - return true; - } + enum + { + TEXT_WORN_TROLL_DICE = 26152 + }; - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sObjectMgr->GetBroadcastText(TEXT_WORN_TROLL_DICE)) + return false; + return true; + } - void HandleScript(SpellEffIndex /*effIndex*/) - { - GetCaster()->TextEmote(TEXT_WORN_TROLL_DICE, GetHitUnit()); + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - static uint32 const minimum = 1; - static uint32 const maximum = 6; + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->TextEmote(TEXT_WORN_TROLL_DICE, GetHitUnit()); - // roll twice - GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum); - GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum); - } + static uint32 const minimum = 1; + static uint32 const maximum = 6; - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_worn_troll_dice_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + // roll twice + GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum); + GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_worn_troll_dice_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_worn_troll_dice::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum AirRifleSpells @@ -2407,52 +1944,41 @@ enum AirRifleSpells SPELL_AIR_RIFLE_SHOOT_SELF = 65577, }; -class spell_item_red_rider_air_rifle : public SpellScriptLoader +class spell_item_red_rider_air_rifle : public SpellScript { - public: - spell_item_red_rider_air_rifle() : SpellScriptLoader("spell_item_red_rider_air_rifle") { } + PrepareSpellScript(spell_item_red_rider_air_rifle); - class spell_item_red_rider_air_rifle_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_item_red_rider_air_rifle_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo( - { - SPELL_AIR_RIFLE_HOLD_VISUAL, - SPELL_AIR_RIFLE_SHOOT, - SPELL_AIR_RIFLE_SHOOT_SELF - }); - } - - void HandleScript(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - Unit* caster = GetCaster(); - if (Unit* target = GetHitUnit()) - { - caster->CastSpell(caster, SPELL_AIR_RIFLE_HOLD_VISUAL, true); - // needed because this spell shares GCD with its triggered spells (which must not be cast with triggered flag) - if (Player* player = caster->ToPlayer()) - player->GetSpellHistory()->CancelGlobalCooldown(GetSpellInfo()); - if (urand(0, 4)) - caster->CastSpell(target, SPELL_AIR_RIFLE_SHOOT, false); - else - caster->CastSpell(caster, SPELL_AIR_RIFLE_SHOOT_SELF, false); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_red_rider_air_rifle_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + SPELL_AIR_RIFLE_HOLD_VISUAL, + SPELL_AIR_RIFLE_SHOOT, + SPELL_AIR_RIFLE_SHOOT_SELF + }); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_red_rider_air_rifle_SpellScript(); + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + Unit* caster = GetCaster(); + if (Unit* target = GetHitUnit()) + { + caster->CastSpell(caster, SPELL_AIR_RIFLE_HOLD_VISUAL, true); + // needed because this spell shares GCD with its triggered spells (which must not be cast with triggered flag) + if (Player* player = caster->ToPlayer()) + player->GetSpellHistory()->CancelGlobalCooldown(GetSpellInfo()); + if (urand(0, 4)) + caster->CastSpell(target, SPELL_AIR_RIFLE_SHOOT, false); + else + caster->CastSpell(caster, SPELL_AIR_RIFLE_SHOOT_SELF, false); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_red_rider_air_rifle::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum GenericData @@ -2475,83 +2001,61 @@ enum CreateHeartCandy ITEM_HEART_CANDY_8 = 21820, }; -class spell_item_create_heart_candy : public SpellScriptLoader +class spell_item_create_heart_candy : public SpellScript { - public: - spell_item_create_heart_candy() : SpellScriptLoader("spell_item_create_heart_candy") { } - - class spell_item_create_heart_candy_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_create_heart_candy_SpellScript); + PrepareSpellScript(spell_item_create_heart_candy); - void HandleScript(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); - if (Player* target = GetHitPlayer()) - { - static const uint32 items[] = {ITEM_HEART_CANDY_1, ITEM_HEART_CANDY_2, ITEM_HEART_CANDY_3, ITEM_HEART_CANDY_4, ITEM_HEART_CANDY_5, ITEM_HEART_CANDY_6, ITEM_HEART_CANDY_7, ITEM_HEART_CANDY_8}; - target->AddItem(items[urand(0, 7)], 1); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_create_heart_candy_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; - - SpellScript* GetSpellScript() const override + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + if (Player* target = GetHitPlayer()) { - return new spell_item_create_heart_candy_SpellScript(); + static const uint32 items[] = {ITEM_HEART_CANDY_1, ITEM_HEART_CANDY_2, ITEM_HEART_CANDY_3, ITEM_HEART_CANDY_4, ITEM_HEART_CANDY_5, ITEM_HEART_CANDY_6, ITEM_HEART_CANDY_7, ITEM_HEART_CANDY_8}; + target->AddItem(items[urand(0, 7)], 1); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_create_heart_candy::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; -class spell_item_book_of_glyph_mastery : public SpellScriptLoader +class spell_item_book_of_glyph_mastery : public SpellScript { - public: - spell_item_book_of_glyph_mastery() : SpellScriptLoader("spell_item_book_of_glyph_mastery") { } - - class spell_item_book_of_glyph_mastery_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_book_of_glyph_mastery_SpellScript); + PrepareSpellScript(spell_item_book_of_glyph_mastery); - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - - SpellCastResult CheckRequirement() - { - if (HasDiscoveredAllSpells(GetSpellInfo()->Id, GetCaster()->ToPlayer())) - { - SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_LEARNED_EVERYTHING); - return SPELL_FAILED_CUSTOM_ERROR; - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - return SPELL_CAST_OK; - } + SpellCastResult CheckRequirement() + { + if (HasDiscoveredAllSpells(GetSpellInfo()->Id, GetCaster()->ToPlayer())) + { + SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_LEARNED_EVERYTHING); + return SPELL_FAILED_CUSTOM_ERROR; + } - void HandleScript(SpellEffIndex /*effIndex*/) - { - Player* caster = GetCaster()->ToPlayer(); - uint32 spellId = GetSpellInfo()->Id; + return SPELL_CAST_OK; + } - // learn random explicit discovery recipe (if any) - if (uint32 discoveredSpellId = GetExplicitDiscoverySpell(spellId, caster)) - caster->LearnSpell(discoveredSpellId, false); - } + void HandleScript(SpellEffIndex /*effIndex*/) + { + Player* caster = GetCaster()->ToPlayer(); + uint32 spellId = GetSpellInfo()->Id; - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_item_book_of_glyph_mastery_SpellScript::CheckRequirement); - OnEffectHitTarget += SpellEffectFn(spell_item_book_of_glyph_mastery_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - } - }; + // learn random explicit discovery recipe (if any) + if (uint32 discoveredSpellId = GetExplicitDiscoverySpell(spellId, caster)) + caster->LearnSpell(discoveredSpellId, false); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_book_of_glyph_mastery_SpellScript(); - } + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_item_book_of_glyph_mastery::CheckRequirement); + OnEffectHitTarget += SpellEffectFn(spell_item_book_of_glyph_mastery::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } }; enum GiftOfTheHarvester @@ -2560,38 +2064,27 @@ enum GiftOfTheHarvester MAX_GHOULS = 5, }; -class spell_item_gift_of_the_harvester : public SpellScriptLoader +class spell_item_gift_of_the_harvester : public SpellScript { - public: - spell_item_gift_of_the_harvester() : SpellScriptLoader("spell_item_gift_of_the_harvester") { } + PrepareSpellScript(spell_item_gift_of_the_harvester); - class spell_item_gift_of_the_harvester_SpellScript : public SpellScript + SpellCastResult CheckRequirement() + { + std::list<Creature*> ghouls; + GetCaster()->GetAllMinionsByEntry(ghouls, NPC_GHOUL); + if (ghouls.size() >= MAX_GHOULS) { - PrepareSpellScript(spell_item_gift_of_the_harvester_SpellScript); - - SpellCastResult CheckRequirement() - { - std::list<Creature*> ghouls; - GetCaster()->GetAllMinionsByEntry(ghouls, NPC_GHOUL); - if (ghouls.size() >= MAX_GHOULS) - { - SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_TOO_MANY_GHOULS); - return SPELL_FAILED_CUSTOM_ERROR; - } - - return SPELL_CAST_OK; - } + SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_TOO_MANY_GHOULS); + return SPELL_FAILED_CUSTOM_ERROR; + } - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_item_gift_of_the_harvester_SpellScript::CheckRequirement); - } - }; + return SPELL_CAST_OK; + } - SpellScript* GetSpellScript() const override - { - return new spell_item_gift_of_the_harvester_SpellScript(); - } + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_item_gift_of_the_harvester::CheckRequirement); + } }; enum Sinkholes @@ -2601,37 +2094,26 @@ enum Sinkholes NPC_NORTHWEST_SINKHOLE = 25666, }; -class spell_item_map_of_the_geyser_fields : public SpellScriptLoader +class spell_item_map_of_the_geyser_fields : public SpellScript { - public: - spell_item_map_of_the_geyser_fields() : SpellScriptLoader("spell_item_map_of_the_geyser_fields") { } + PrepareSpellScript(spell_item_map_of_the_geyser_fields); - class spell_item_map_of_the_geyser_fields_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_map_of_the_geyser_fields_SpellScript); - - SpellCastResult CheckSinkholes() - { - Unit* caster = GetCaster(); - if (caster->FindNearestCreature(NPC_SOUTH_SINKHOLE, 30.0f, true) || - caster->FindNearestCreature(NPC_NORTHEAST_SINKHOLE, 30.0f, true) || - caster->FindNearestCreature(NPC_NORTHWEST_SINKHOLE, 30.0f, true)) - return SPELL_CAST_OK; - - SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_BE_CLOSE_TO_SINKHOLE); - return SPELL_FAILED_CUSTOM_ERROR; - } - - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_item_map_of_the_geyser_fields_SpellScript::CheckSinkholes); - } - }; + SpellCastResult CheckSinkholes() + { + Unit* caster = GetCaster(); + if (caster->FindNearestCreature(NPC_SOUTH_SINKHOLE, 30.0f, true) || + caster->FindNearestCreature(NPC_NORTHEAST_SINKHOLE, 30.0f, true) || + caster->FindNearestCreature(NPC_NORTHWEST_SINKHOLE, 30.0f, true)) + return SPELL_CAST_OK; + + SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_BE_CLOSE_TO_SINKHOLE); + return SPELL_FAILED_CUSTOM_ERROR; + } - SpellScript* GetSpellScript() const override - { - return new spell_item_map_of_the_geyser_fields_SpellScript(); - } + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_item_map_of_the_geyser_fields::CheckSinkholes); + } }; enum VanquishedClutchesSpells @@ -2641,42 +2123,31 @@ enum VanquishedClutchesSpells SPELL_CORRUPTOR = 64984, }; -class spell_item_vanquished_clutches : public SpellScriptLoader +class spell_item_vanquished_clutches : public SpellScript { - public: - spell_item_vanquished_clutches() : SpellScriptLoader("spell_item_vanquished_clutches") { } + PrepareSpellScript(spell_item_vanquished_clutches); - class spell_item_vanquished_clutches_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_item_vanquished_clutches_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_CRUSHER, - SPELL_CONSTRICTOR, - SPELL_CORRUPTOR - }); - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - uint32 spellId = RAND(SPELL_CRUSHER, SPELL_CONSTRICTOR, SPELL_CORRUPTOR); - Unit* caster = GetCaster(); - caster->CastSpell(caster, spellId, true); - } + SPELL_CRUSHER, + SPELL_CONSTRICTOR, + SPELL_CORRUPTOR + }); + } - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_vanquished_clutches_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + uint32 spellId = RAND(SPELL_CRUSHER, SPELL_CONSTRICTOR, SPELL_CORRUPTOR); + Unit* caster = GetCaster(); + caster->CastSpell(caster, spellId, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_vanquished_clutches_SpellScript(); - } + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_vanquished_clutches::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum AshbringerSounds @@ -2695,43 +2166,32 @@ enum AshbringerSounds SOUND_ASHBRINGER_12 = 8928, // "Kill them all!" }; -class spell_item_ashbringer : public SpellScriptLoader +class spell_item_ashbringer : public SpellScript { - public: - spell_item_ashbringer() : SpellScriptLoader("spell_item_ashbringer") { } - - class spell_item_ashbringer_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_ashbringer_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + PrepareSpellScript(spell_item_ashbringer); - void OnDummyEffect(SpellEffIndex effIndex) - { - PreventHitDefaultEffect(effIndex); + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - Player* player = GetCaster()->ToPlayer(); - uint32 sound_id = RAND( SOUND_ASHBRINGER_1, SOUND_ASHBRINGER_2, SOUND_ASHBRINGER_3, SOUND_ASHBRINGER_4, SOUND_ASHBRINGER_5, SOUND_ASHBRINGER_6, - SOUND_ASHBRINGER_7, SOUND_ASHBRINGER_8, SOUND_ASHBRINGER_9, SOUND_ASHBRINGER_10, SOUND_ASHBRINGER_11, SOUND_ASHBRINGER_12 ); + void OnDummyEffect(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); - // Ashbringers effect (spellID 28441) retriggers every 5 seconds, with a chance of making it say one of the above 12 sounds - if (urand(0, 60) < 1) - player->PlayDirectSound(sound_id, player); - } + Player* player = GetCaster()->ToPlayer(); + uint32 sound_id = RAND( SOUND_ASHBRINGER_1, SOUND_ASHBRINGER_2, SOUND_ASHBRINGER_3, SOUND_ASHBRINGER_4, SOUND_ASHBRINGER_5, SOUND_ASHBRINGER_6, + SOUND_ASHBRINGER_7, SOUND_ASHBRINGER_8, SOUND_ASHBRINGER_9, SOUND_ASHBRINGER_10, SOUND_ASHBRINGER_11, SOUND_ASHBRINGER_12 ); - void Register() override - { - OnEffectHit += SpellEffectFn(spell_item_ashbringer_SpellScript::OnDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + // Ashbringers effect (spellID 28441) retriggers every 5 seconds, with a chance of making it say one of the above 12 sounds + if (urand(0, 60) < 1) + player->PlayDirectSound(sound_id, player); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_ashbringer_SpellScript(); - } + void Register() override + { + OnEffectHit += SpellEffectFn(spell_item_ashbringer::OnDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum MagicEater @@ -2744,79 +2204,57 @@ enum MagicEater SPELL_WELL_FED_5 = 57291, }; -class spell_magic_eater_food : public SpellScriptLoader +class spell_magic_eater_food : public AuraScript { - public: - spell_magic_eater_food() : SpellScriptLoader("spell_magic_eater_food") { } - - class spell_magic_eater_food_AuraScript : public AuraScript - { - PrepareAuraScript(spell_magic_eater_food_AuraScript); + PrepareAuraScript(spell_magic_eater_food); - void HandleTriggerSpell(AuraEffect const* /*aurEff*/) - { - PreventDefaultAction(); - Unit* target = GetTarget(); - switch (urand(0, 5)) - { - case 0: - target->CastSpell(target, SPELL_WILD_MAGIC, true); - break; - case 1: - target->CastSpell(target, SPELL_WELL_FED_1, true); - break; - case 2: - target->CastSpell(target, SPELL_WELL_FED_2, true); - break; - case 3: - target->CastSpell(target, SPELL_WELL_FED_3, true); - break; - case 4: - target->CastSpell(target, SPELL_WELL_FED_4, true); - break; - case 5: - target->CastSpell(target, SPELL_WELL_FED_5, true); - break; - } - } - - void Register() override - { - OnEffectPeriodic += AuraEffectPeriodicFn(spell_magic_eater_food_AuraScript::HandleTriggerSpell, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_magic_eater_food_AuraScript(); + void HandleTriggerSpell(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + Unit* target = GetTarget(); + switch (urand(0, 5)) + { + case 0: + target->CastSpell(target, SPELL_WILD_MAGIC, true); + break; + case 1: + target->CastSpell(target, SPELL_WELL_FED_1, true); + break; + case 2: + target->CastSpell(target, SPELL_WELL_FED_2, true); + break; + case 3: + target->CastSpell(target, SPELL_WELL_FED_3, true); + break; + case 4: + target->CastSpell(target, SPELL_WELL_FED_4, true); + break; + case 5: + target->CastSpell(target, SPELL_WELL_FED_5, true); + break; } + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_magic_eater_food::HandleTriggerSpell, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } }; -class spell_item_shimmering_vessel : public SpellScriptLoader +class spell_item_shimmering_vessel : public SpellScript { - public: - spell_item_shimmering_vessel() : SpellScriptLoader("spell_item_shimmering_vessel") { } - - class spell_item_shimmering_vessel_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_shimmering_vessel_SpellScript); - - void HandleDummy(SpellEffIndex /* effIndex */) - { - if (Creature* target = GetHitCreature()) - target->setDeathState(JUST_RESPAWNED); - } + PrepareSpellScript(spell_item_shimmering_vessel); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_shimmering_vessel_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /* effIndex */) + { + if (Creature* target = GetHitCreature()) + target->setDeathState(JUST_RESPAWNED); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_shimmering_vessel_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_shimmering_vessel::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum PurifyHelboarMeat @@ -2825,45 +2263,34 @@ enum PurifyHelboarMeat SPELL_SUMMON_TOXIC_HELBOAR_MEAT = 29278, }; -class spell_item_purify_helboar_meat : public SpellScriptLoader +class spell_item_purify_helboar_meat : public SpellScript { - public: - spell_item_purify_helboar_meat() : SpellScriptLoader("spell_item_purify_helboar_meat") { } - - class spell_item_purify_helboar_meat_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_purify_helboar_meat_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + PrepareSpellScript(spell_item_purify_helboar_meat); - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo( - { - SPELL_SUMMON_PURIFIED_HELBOAR_MEAT, - SPELL_SUMMON_TOXIC_HELBOAR_MEAT - }); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - caster->CastSpell(caster, roll_chance_i(50) ? SPELL_SUMMON_PURIFIED_HELBOAR_MEAT : SPELL_SUMMON_TOXIC_HELBOAR_MEAT, true, nullptr); - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( + { + SPELL_SUMMON_PURIFIED_HELBOAR_MEAT, + SPELL_SUMMON_TOXIC_HELBOAR_MEAT + }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_purify_helboar_meat_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + caster->CastSpell(caster, roll_chance_i(50) ? SPELL_SUMMON_PURIFIED_HELBOAR_MEAT : SPELL_SUMMON_TOXIC_HELBOAR_MEAT, true, nullptr); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_purify_helboar_meat_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_purify_helboar_meat::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum CrystalPrison @@ -2871,42 +2298,31 @@ enum CrystalPrison OBJECT_IMPRISONED_DOOMGUARD = 179644, }; -class spell_item_crystal_prison_dummy_dnd : public SpellScriptLoader +class spell_item_crystal_prison_dummy_dnd : public SpellScript { - public: - spell_item_crystal_prison_dummy_dnd() : SpellScriptLoader("spell_item_crystal_prison_dummy_dnd") { } - - class spell_item_crystal_prison_dummy_dnd_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_crystal_prison_dummy_dnd_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sObjectMgr->GetGameObjectTemplate(OBJECT_IMPRISONED_DOOMGUARD)) - return false; - return true; - } + PrepareSpellScript(spell_item_crystal_prison_dummy_dnd); - void HandleDummy(SpellEffIndex /* effIndex */) - { - if (Creature* target = GetHitCreature()) - if (target->isDead() && !target->IsPet()) - { - GetCaster()->SummonGameObject(OBJECT_IMPRISONED_DOOMGUARD, *target, QuaternionData(), uint32(target->GetRespawnTime()-time(nullptr))); - target->DespawnOrUnsummon(); - } - } + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sObjectMgr->GetGameObjectTemplate(OBJECT_IMPRISONED_DOOMGUARD)) + return false; + return true; + } - void Register() override + void HandleDummy(SpellEffIndex /* effIndex */) + { + if (Creature* target = GetHitCreature()) + if (target->isDead() && !target->IsPet()) { - OnEffectHitTarget += SpellEffectFn(spell_item_crystal_prison_dummy_dnd_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + GetCaster()->SummonGameObject(OBJECT_IMPRISONED_DOOMGUARD, *target, QuaternionData(), uint32(target->GetRespawnTime()-time(nullptr))); + target->DespawnOrUnsummon(); } - }; + } - SpellScript* GetSpellScript() const override - { - return new spell_item_crystal_prison_dummy_dnd_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_crystal_prison_dummy_dnd::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum ReindeerTransformation @@ -2918,65 +2334,55 @@ enum ReindeerTransformation SPELL_REINDEER_60 = 25858, }; -class spell_item_reindeer_transformation : public SpellScriptLoader -{ - public: - spell_item_reindeer_transformation() : SpellScriptLoader("spell_item_reindeer_transformation") { } - - class spell_item_reindeer_transformation_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_reindeer_transformation_SpellScript); - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo( - { - SPELL_FLYING_REINDEER_310, - SPELL_FLYING_REINDEER_280, - SPELL_FLYING_REINDEER_60, - SPELL_REINDEER_100, - SPELL_REINDEER_60 - }); - } +class spell_item_reindeer_transformation : public SpellScript +{ + PrepareSpellScript(spell_item_reindeer_transformation); - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - if (caster->HasAuraType(SPELL_AURA_MOUNTED)) - { - float flyspeed = caster->GetSpeedRate(MOVE_FLIGHT); - float speed = caster->GetSpeedRate(MOVE_RUN); - - caster->RemoveAurasByType(SPELL_AURA_MOUNTED); - //5 different spells used depending on mounted speed and if mount can fly or not - - if (flyspeed >= 4.1f) - // Flying Reindeer - caster->CastSpell(caster, SPELL_FLYING_REINDEER_310, true); //310% flying Reindeer - else if (flyspeed >= 3.8f) - // Flying Reindeer - caster->CastSpell(caster, SPELL_FLYING_REINDEER_280, true); //280% flying Reindeer - else if (flyspeed >= 1.6f) - // Flying Reindeer - caster->CastSpell(caster, SPELL_FLYING_REINDEER_60, true); //60% flying Reindeer - else if (speed >= 2.0f) - // Reindeer - caster->CastSpell(caster, SPELL_REINDEER_100, true); //100% ground Reindeer - else - // Reindeer - caster->CastSpell(caster, SPELL_REINDEER_60, true); //60% ground Reindeer - } - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( + { + SPELL_FLYING_REINDEER_310, + SPELL_FLYING_REINDEER_280, + SPELL_FLYING_REINDEER_60, + SPELL_REINDEER_100, + SPELL_REINDEER_60 + }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_reindeer_transformation_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + if (caster->HasAuraType(SPELL_AURA_MOUNTED)) + { + float flyspeed = caster->GetSpeedRate(MOVE_FLIGHT); + float speed = caster->GetSpeedRate(MOVE_RUN); + + caster->RemoveAurasByType(SPELL_AURA_MOUNTED); + //5 different spells used depending on mounted speed and if mount can fly or not + + if (flyspeed >= 4.1f) + // Flying Reindeer + caster->CastSpell(caster, SPELL_FLYING_REINDEER_310, true); //310% flying Reindeer + else if (flyspeed >= 3.8f) + // Flying Reindeer + caster->CastSpell(caster, SPELL_FLYING_REINDEER_280, true); //280% flying Reindeer + else if (flyspeed >= 1.6f) + // Flying Reindeer + caster->CastSpell(caster, SPELL_FLYING_REINDEER_60, true); //60% flying Reindeer + else if (speed >= 2.0f) + // Reindeer + caster->CastSpell(caster, SPELL_REINDEER_100, true); //100% ground Reindeer + else + // Reindeer + caster->CastSpell(caster, SPELL_REINDEER_60, true); //60% ground Reindeer + } + } - SpellScript* GetSpellScript() const override + void Register() override { - return new spell_item_reindeer_transformation_SpellScript(); + OnEffectHitTarget += SpellEffectFn(spell_item_reindeer_transformation::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; @@ -2986,42 +2392,31 @@ enum NighInvulnerability SPELL_COMPLETE_VULNERABILITY = 30457, }; -class spell_item_nigh_invulnerability : public SpellScriptLoader +class spell_item_nigh_invulnerability : public SpellScript { - public: - spell_item_nigh_invulnerability() : SpellScriptLoader("spell_item_nigh_invulnerability") { } - - class spell_item_nigh_invulnerability_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_nigh_invulnerability_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_NIGH_INVULNERABILITY, SPELL_COMPLETE_VULNERABILITY }); - } - - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - if (Item* castItem = GetCastItem()) - { - if (roll_chance_i(86)) // Nigh-Invulnerability - success - caster->CastSpell(caster, SPELL_NIGH_INVULNERABILITY, true, castItem); - else // Complete Vulnerability - backfire in 14% casts - caster->CastSpell(caster, SPELL_COMPLETE_VULNERABILITY, true, castItem); - } - } + PrepareSpellScript(spell_item_nigh_invulnerability); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_nigh_invulnerability_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_NIGH_INVULNERABILITY, SPELL_COMPLETE_VULNERABILITY }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + if (Item* castItem = GetCastItem()) { - return new spell_item_nigh_invulnerability_SpellScript(); + if (roll_chance_i(86)) // Nigh-Invulnerability - success + caster->CastSpell(caster, SPELL_NIGH_INVULNERABILITY, true, castItem); + else // Complete Vulnerability - backfire in 14% casts + caster->CastSpell(caster, SPELL_COMPLETE_VULNERABILITY, true, castItem); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_nigh_invulnerability::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum Poultryzer @@ -3030,36 +2425,25 @@ enum Poultryzer SPELL_POULTRYIZER_BACKFIRE = 30504, }; -class spell_item_poultryizer : public SpellScriptLoader +class spell_item_poultryizer : public SpellScript { - public: - spell_item_poultryizer() : SpellScriptLoader("spell_item_poultryizer") { } + PrepareSpellScript(spell_item_poultryizer); - class spell_item_poultryizer_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_poultryizer_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_POULTRYIZER_SUCCESS, SPELL_POULTRYIZER_BACKFIRE }); - } - - void HandleDummy(SpellEffIndex /* effIndex */) - { - if (GetCastItem() && GetHitUnit()) - GetCaster()->CastSpell(GetHitUnit(), roll_chance_i(80) ? SPELL_POULTRYIZER_SUCCESS : SPELL_POULTRYIZER_BACKFIRE, true, GetCastItem()); - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_POULTRYIZER_SUCCESS, SPELL_POULTRYIZER_BACKFIRE }); + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_poultryizer_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /* effIndex */) + { + if (GetCastItem() && GetHitUnit()) + GetCaster()->CastSpell(GetHitUnit(), roll_chance_i(80) ? SPELL_POULTRYIZER_SUCCESS : SPELL_POULTRYIZER_BACKFIRE, true, GetCastItem()); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_poultryizer_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_poultryizer::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum SocretharsStone @@ -3068,50 +2452,39 @@ enum SocretharsStone SPELL_SOCRETHAR_FROM_SEAT = 35744, }; -class spell_item_socrethars_stone : public SpellScriptLoader +class spell_item_socrethars_stone : public SpellScript { - public: - spell_item_socrethars_stone() : SpellScriptLoader("spell_item_socrethars_stone") { } - - class spell_item_socrethars_stone_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_socrethars_stone_SpellScript); - - bool Load() override - { - return (GetCaster()->GetAreaId() == 3900 || GetCaster()->GetAreaId() == 3742); - } - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_SOCRETHAR_TO_SEAT, SPELL_SOCRETHAR_FROM_SEAT }); - } - - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - switch (caster->GetAreaId()) - { - case 3900: - caster->CastSpell(caster, SPELL_SOCRETHAR_TO_SEAT, true); - break; - case 3742: - caster->CastSpell(caster, SPELL_SOCRETHAR_FROM_SEAT, true); - break; - default: - return; - } - } + PrepareSpellScript(spell_item_socrethars_stone); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_socrethars_stone_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Load() override + { + return (GetCaster()->GetAreaId() == 3900 || GetCaster()->GetAreaId() == 3742); + } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_SOCRETHAR_TO_SEAT, SPELL_SOCRETHAR_FROM_SEAT }); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_socrethars_stone_SpellScript(); + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + switch (caster->GetAreaId()) + { + case 3900: + caster->CastSpell(caster, SPELL_SOCRETHAR_TO_SEAT, true); + break; + case 3742: + caster->CastSpell(caster, SPELL_SOCRETHAR_FROM_SEAT, true); + break; + default: + return; } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_socrethars_stone::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum DemonBroiledSurprise @@ -3121,56 +2494,45 @@ enum DemonBroiledSurprise NPC_ABYSSAL_FLAMEBRINGER = 19973, }; -class spell_item_demon_broiled_surprise : public SpellScriptLoader +class spell_item_demon_broiled_surprise : public SpellScript { - public: - spell_item_demon_broiled_surprise() : SpellScriptLoader("spell_item_demon_broiled_surprise") { } - - class spell_item_demon_broiled_surprise_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_demon_broiled_surprise_SpellScript); + PrepareSpellScript(spell_item_demon_broiled_surprise); - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_CREATE_DEMON_BROILED_SURPRISE }) && - sObjectMgr->GetCreatureTemplate(NPC_ABYSSAL_FLAMEBRINGER) && - sObjectMgr->GetQuestTemplate(QUEST_SUPER_HOT_STEW); - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_CREATE_DEMON_BROILED_SURPRISE }) && + sObjectMgr->GetCreatureTemplate(NPC_ABYSSAL_FLAMEBRINGER) && + sObjectMgr->GetQuestTemplate(QUEST_SUPER_HOT_STEW); + } - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* player = GetCaster(); - player->CastSpell(player, SPELL_CREATE_DEMON_BROILED_SURPRISE, false); - } + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* player = GetCaster(); + player->CastSpell(player, SPELL_CREATE_DEMON_BROILED_SURPRISE, false); + } - SpellCastResult CheckRequirement() - { - Player* player = GetCaster()->ToPlayer(); - if (player->GetQuestStatus(QUEST_SUPER_HOT_STEW) != QUEST_STATUS_INCOMPLETE) - return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; - - if (Creature* creature = player->FindNearestCreature(NPC_ABYSSAL_FLAMEBRINGER, 10, false)) - if (creature->isDead()) - return SPELL_CAST_OK; - return SPELL_FAILED_NOT_HERE; - } + SpellCastResult CheckRequirement() + { + Player* player = GetCaster()->ToPlayer(); + if (player->GetQuestStatus(QUEST_SUPER_HOT_STEW) != QUEST_STATUS_INCOMPLETE) + return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_demon_broiled_surprise_SpellScript::HandleDummy, EFFECT_1, SPELL_EFFECT_DUMMY); - OnCheckCast += SpellCheckCastFn(spell_item_demon_broiled_surprise_SpellScript::CheckRequirement); - } - }; + if (Creature* creature = player->FindNearestCreature(NPC_ABYSSAL_FLAMEBRINGER, 10, false)) + if (creature->isDead()) + return SPELL_CAST_OK; + return SPELL_FAILED_NOT_HERE; + } - SpellScript* GetSpellScript() const override - { - return new spell_item_demon_broiled_surprise_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_demon_broiled_surprise::HandleDummy, EFFECT_1, SPELL_EFFECT_DUMMY); + OnCheckCast += SpellCheckCastFn(spell_item_demon_broiled_surprise::CheckRequirement); + } }; enum CompleteRaptorCapture @@ -3178,42 +2540,31 @@ enum CompleteRaptorCapture SPELL_RAPTOR_CAPTURE_CREDIT = 42337, }; -class spell_item_complete_raptor_capture : public SpellScriptLoader +class spell_item_complete_raptor_capture : public SpellScript { - public: - spell_item_complete_raptor_capture() : SpellScriptLoader("spell_item_complete_raptor_capture") { } - - class spell_item_complete_raptor_capture_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_complete_raptor_capture_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_RAPTOR_CAPTURE_CREDIT }); - } + PrepareSpellScript(spell_item_complete_raptor_capture); - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - if (GetHitCreature()) - { - GetHitCreature()->DespawnOrUnsummon(); - - //cast spell Raptor Capture Credit - caster->CastSpell(caster, SPELL_RAPTOR_CAPTURE_CREDIT, true, nullptr); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_complete_raptor_capture_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_RAPTOR_CAPTURE_CREDIT }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + if (GetHitCreature()) { - return new spell_item_complete_raptor_capture_SpellScript(); + GetHitCreature()->DespawnOrUnsummon(); + + //cast spell Raptor Capture Credit + caster->CastSpell(caster, SPELL_RAPTOR_CAPTURE_CREDIT, true, nullptr); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_complete_raptor_capture::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum ImpaleLeviroth @@ -3222,42 +2573,31 @@ enum ImpaleLeviroth SPELL_LEVIROTH_SELF_IMPALE = 49882 }; -class spell_item_impale_leviroth : public SpellScriptLoader +class spell_item_impale_leviroth : public SpellScript { - public: - spell_item_impale_leviroth() : SpellScriptLoader("spell_item_impale_leviroth") { } - - class spell_item_impale_leviroth_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_impale_leviroth_SpellScript); + PrepareSpellScript(spell_item_impale_leviroth); - bool Validate(SpellInfo const* /*spell*/) override - { - if (!sObjectMgr->GetCreatureTemplate(NPC_LEVIROTH)) - return false; - return true; - } - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - if (Creature* target = GetHitCreature()) - if (target->GetEntry() == NPC_LEVIROTH && !target->HealthBelowPct(95)) - { - target->CastSpell(target, SPELL_LEVIROTH_SELF_IMPALE, true); - target->ResetPlayerDamageReq(); - } - } + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sObjectMgr->GetCreatureTemplate(NPC_LEVIROTH)) + return false; + return true; + } - void Register() override + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Creature* target = GetHitCreature()) + if (target->GetEntry() == NPC_LEVIROTH && !target->HealthBelowPct(95)) { - OnEffectHitTarget += SpellEffectFn(spell_item_impale_leviroth_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + target->CastSpell(target, SPELL_LEVIROTH_SELF_IMPALE, true); + target->ResetPlayerDamageReq(); } - }; + } - SpellScript* GetSpellScript() const override - { - return new spell_item_impale_leviroth_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_impale_leviroth::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum BrewfestMountTransformation @@ -3270,111 +2610,130 @@ enum BrewfestMountTransformation SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE = 52845, }; -class spell_item_brewfest_mount_transformation : public SpellScriptLoader +class spell_item_brewfest_mount_transformation : public SpellScript { - public: - spell_item_brewfest_mount_transformation() : SpellScriptLoader("spell_item_brewfest_mount_transformation") { } + PrepareSpellScript(spell_item_brewfest_mount_transformation); - class spell_item_brewfest_mount_transformation_SpellScript : public SpellScript + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( { - PrepareSpellScript(spell_item_brewfest_mount_transformation_SpellScript); + SPELL_MOUNT_RAM_100, + SPELL_MOUNT_RAM_60, + SPELL_MOUNT_KODO_100, + SPELL_MOUNT_KODO_60 + }); + } - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo( - { - SPELL_MOUNT_RAM_100, - SPELL_MOUNT_RAM_60, - SPELL_MOUNT_KODO_100, - SPELL_MOUNT_KODO_60 - }); - } + void HandleDummy(SpellEffIndex /* effIndex */) + { + Player* caster = GetCaster()->ToPlayer(); + if (caster->HasAuraType(SPELL_AURA_MOUNTED)) + { + caster->RemoveAurasByType(SPELL_AURA_MOUNTED); + uint32 spell_id; - void HandleDummy(SpellEffIndex /* effIndex */) + switch (GetSpellInfo()->Id) { - Player* caster = GetCaster()->ToPlayer(); - if (caster->HasAuraType(SPELL_AURA_MOUNTED)) - { - caster->RemoveAurasByType(SPELL_AURA_MOUNTED); - uint32 spell_id; - - switch (GetSpellInfo()->Id) - { - case SPELL_BREWFEST_MOUNT_TRANSFORM: - if (caster->GetSpeedRate(MOVE_RUN) >= 2.0f) - spell_id = caster->GetTeam() == ALLIANCE ? SPELL_MOUNT_RAM_100 : SPELL_MOUNT_KODO_100; - else - spell_id = caster->GetTeam() == ALLIANCE ? SPELL_MOUNT_RAM_60 : SPELL_MOUNT_KODO_60; - break; - case SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE: - if (caster->GetSpeedRate(MOVE_RUN) >= 2.0f) - spell_id = caster->GetTeam() == HORDE ? SPELL_MOUNT_RAM_100 : SPELL_MOUNT_KODO_100; - else - spell_id = caster->GetTeam() == HORDE ? SPELL_MOUNT_RAM_60 : SPELL_MOUNT_KODO_60; - break; - default: - return; - } - caster->CastSpell(caster, spell_id, true); - } - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_brewfest_mount_transformation_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + case SPELL_BREWFEST_MOUNT_TRANSFORM: + if (caster->GetSpeedRate(MOVE_RUN) >= 2.0f) + spell_id = caster->GetTeam() == ALLIANCE ? SPELL_MOUNT_RAM_100 : SPELL_MOUNT_KODO_100; + else + spell_id = caster->GetTeam() == ALLIANCE ? SPELL_MOUNT_RAM_60 : SPELL_MOUNT_KODO_60; + break; + case SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE: + if (caster->GetSpeedRate(MOVE_RUN) >= 2.0f) + spell_id = caster->GetTeam() == HORDE ? SPELL_MOUNT_RAM_100 : SPELL_MOUNT_KODO_100; + else + spell_id = caster->GetTeam() == HORDE ? SPELL_MOUNT_RAM_60 : SPELL_MOUNT_KODO_60; + break; + default: + return; } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_item_brewfest_mount_transformation_SpellScript(); + caster->CastSpell(caster, spell_id, true); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_brewfest_mount_transformation::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -enum NitroBoots +enum NitroBoosts { - SPELL_NITRO_BOOTS_SUCCESS = 54861, - SPELL_NITRO_BOOTS_BACKFIRE = 46014, + SPELL_NITRO_BOOSTS_SUCCESS = 54861, + SPELL_NITRO_BOOSTS_BACKFIRE = 54621, + SPELL_NITRO_BOOSTS_PARACHUTE = 54649, }; -class spell_item_nitro_boots : public SpellScriptLoader +class spell_item_nitro_boosts : public SpellScript { - public: - spell_item_nitro_boots() : SpellScriptLoader("spell_item_nitro_boots") { } + PrepareSpellScript(spell_item_nitro_boosts); - class spell_item_nitro_boots_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_nitro_boots_SpellScript); + bool Load() override + { + if (!GetCastItem()) + return false; + return true; + } - bool Load() override - { - if (!GetCastItem()) - return false; - return true; - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_NITRO_BOOSTS_SUCCESS, SPELL_NITRO_BOOSTS_BACKFIRE }); + } - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_NITRO_BOOTS_SUCCESS, SPELL_NITRO_BOOTS_BACKFIRE }); - } + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(caster->GetAreaId()); + bool success = true; + if (areaEntry && areaEntry->IsFlyable() && !caster->GetMap()->IsDungeon()) + success = roll_chance_i(95); // nitro boosts can only fail in flying-enabled locations on 3.3.5 + caster->CastSpell(caster, success ? SPELL_NITRO_BOOSTS_SUCCESS : SPELL_NITRO_BOOSTS_BACKFIRE, true, GetCastItem()); + } - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - bool success = caster->GetMap()->IsDungeon() || roll_chance_i(95); - caster->CastSpell(caster, success ? SPELL_NITRO_BOOTS_SUCCESS : SPELL_NITRO_BOOTS_BACKFIRE, true, GetCastItem()); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_nitro_boosts::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_nitro_boots_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; +class spell_item_nitro_boosts_backfire : public AuraScript +{ + PrepareAuraScript(spell_item_nitro_boosts_backfire); - SpellScript* GetSpellScript() const override + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_NITRO_BOOSTS_PARACHUTE }); + } + + void HandleApply(AuraEffect const* /*effect*/, AuraEffectHandleModes /*mode*/) + { + lastZ = GetTarget()->GetPositionZ(); + } + + void HandlePeriodicDummy(AuraEffect const* effect) + { + PreventDefaultAction(); + float curZ = GetTarget()->GetPositionZ(); + if (curZ < lastZ) { - return new spell_item_nitro_boots_SpellScript(); + if (roll_chance_i(80)) // we don't have enough sniffs to verify this, guesstimate + GetTarget()->CastSpell(GetTarget(), SPELL_NITRO_BOOSTS_PARACHUTE, true, nullptr, effect); + GetAura()->Remove(); } + else + lastZ = curZ; + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_item_nitro_boosts_backfire::HandleApply, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_item_nitro_boosts_backfire::HandlePeriodicDummy, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + + float lastZ = INVALID_HEIGHT; }; enum TeachLanguage @@ -3383,43 +2742,32 @@ enum TeachLanguage SPELL_LEARN_GOBLIN_BINARY = 50246, }; -class spell_item_teach_language : public SpellScriptLoader +class spell_item_teach_language : public SpellScript { - public: - spell_item_teach_language() : SpellScriptLoader("spell_item_teach_language") { } - - class spell_item_teach_language_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_teach_language_SpellScript); + PrepareSpellScript(spell_item_teach_language); - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_LEARN_GNOMISH_BINARY, SPELL_LEARN_GOBLIN_BINARY }); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void HandleDummy(SpellEffIndex /* effIndex */) - { - Player* caster = GetCaster()->ToPlayer(); + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_LEARN_GNOMISH_BINARY, SPELL_LEARN_GOBLIN_BINARY }); + } - if (roll_chance_i(34)) - caster->CastSpell(caster, caster->GetTeam() == ALLIANCE ? SPELL_LEARN_GNOMISH_BINARY : SPELL_LEARN_GOBLIN_BINARY, true); - } + void HandleDummy(SpellEffIndex /* effIndex */) + { + Player* caster = GetCaster()->ToPlayer(); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_teach_language_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + if (roll_chance_i(34)) + caster->CastSpell(caster, caster->GetTeam() == ALLIANCE ? SPELL_LEARN_GNOMISH_BINARY : SPELL_LEARN_GOBLIN_BINARY, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_teach_language_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_teach_language::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum RocketBoots @@ -3427,53 +2775,42 @@ enum RocketBoots SPELL_ROCKET_BOOTS_PROC = 30452, }; -class spell_item_rocket_boots : public SpellScriptLoader +class spell_item_rocket_boots : public SpellScript { - public: - spell_item_rocket_boots() : SpellScriptLoader("spell_item_rocket_boots") { } - - class spell_item_rocket_boots_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_rocket_boots_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } + PrepareSpellScript(spell_item_rocket_boots); - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_ROCKET_BOOTS_PROC }); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void HandleDummy(SpellEffIndex /* effIndex */) - { - Player* caster = GetCaster()->ToPlayer(); - if (Battleground* bg = caster->GetBattleground()) - bg->EventPlayerDroppedFlag(caster); + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_ROCKET_BOOTS_PROC }); + } - caster->GetSpellHistory()->ResetCooldown(SPELL_ROCKET_BOOTS_PROC); - caster->CastSpell(caster, SPELL_ROCKET_BOOTS_PROC, true, nullptr); - } + void HandleDummy(SpellEffIndex /* effIndex */) + { + Player* caster = GetCaster()->ToPlayer(); + if (Battleground* bg = caster->GetBattleground()) + bg->EventPlayerDroppedFlag(caster); - SpellCastResult CheckCast() - { - if (GetCaster()->IsInWater()) - return SPELL_FAILED_ONLY_ABOVEWATER; - return SPELL_CAST_OK; - } + caster->GetSpellHistory()->ResetCooldown(SPELL_ROCKET_BOOTS_PROC); + caster->CastSpell(caster, SPELL_ROCKET_BOOTS_PROC, true, nullptr); + } - void Register() override - { - OnCheckCast += SpellCheckCastFn(spell_item_rocket_boots_SpellScript::CheckCast); - OnEffectHitTarget += SpellEffectFn(spell_item_rocket_boots_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + SpellCastResult CheckCast() + { + if (GetCaster()->IsInWater()) + return SPELL_FAILED_ONLY_ABOVEWATER; + return SPELL_CAST_OK; + } - SpellScript* GetSpellScript() const override - { - return new spell_item_rocket_boots_SpellScript(); - } + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_item_rocket_boots::CheckCast); + OnEffectHitTarget += SpellEffectFn(spell_item_rocket_boots::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum PygmyOil @@ -3482,75 +2819,53 @@ enum PygmyOil SPELL_PYGMY_OIL_SMALLER_AURA = 53805, }; -class spell_item_pygmy_oil : public SpellScriptLoader +class spell_item_pygmy_oil : public SpellScript { - public: - spell_item_pygmy_oil() : SpellScriptLoader("spell_item_pygmy_oil") { } + PrepareSpellScript(spell_item_pygmy_oil); - class spell_item_pygmy_oil_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_pygmy_oil_SpellScript); - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_PYGMY_OIL_PYGMY_AURA, SPELL_PYGMY_OIL_SMALLER_AURA }); - } - - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - if (Aura* aura = caster->GetAura(SPELL_PYGMY_OIL_PYGMY_AURA)) - aura->RefreshDuration(); - else - { - aura = caster->GetAura(SPELL_PYGMY_OIL_SMALLER_AURA); - if (!aura || aura->GetStackAmount() < 5 || !roll_chance_i(50)) - caster->CastSpell(caster, SPELL_PYGMY_OIL_SMALLER_AURA, true); - else - { - aura->Remove(); - caster->CastSpell(caster, SPELL_PYGMY_OIL_PYGMY_AURA, true); - } - } - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_PYGMY_OIL_PYGMY_AURA, SPELL_PYGMY_OIL_SMALLER_AURA }); + } - void Register() override + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + if (Aura* aura = caster->GetAura(SPELL_PYGMY_OIL_PYGMY_AURA)) + aura->RefreshDuration(); + else + { + aura = caster->GetAura(SPELL_PYGMY_OIL_SMALLER_AURA); + if (!aura || aura->GetStackAmount() < 5 || !roll_chance_i(50)) + caster->CastSpell(caster, SPELL_PYGMY_OIL_SMALLER_AURA, true); + else { - OnEffectHitTarget += SpellEffectFn(spell_item_pygmy_oil_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + aura->Remove(); + caster->CastSpell(caster, SPELL_PYGMY_OIL_PYGMY_AURA, true); } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_item_pygmy_oil_SpellScript(); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_pygmy_oil::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_item_unusual_compass : public SpellScriptLoader +class spell_item_unusual_compass : public SpellScript { - public: - spell_item_unusual_compass() : SpellScriptLoader("spell_item_unusual_compass") { } - - class spell_item_unusual_compass_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_unusual_compass_SpellScript); + PrepareSpellScript(spell_item_unusual_compass); - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - caster->SetFacingTo(frand(0.0f, 2.0f * float(M_PI))); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_unusual_compass_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + caster->SetFacingTo(frand(0.0f, 2.0f * float(M_PI))); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_unusual_compass_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_unusual_compass::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum ChickenCover @@ -3561,50 +2876,39 @@ enum ChickenCover QUEST_FLOWN_THE_COOP = 12532, }; -class spell_item_chicken_cover : public SpellScriptLoader +class spell_item_chicken_cover : public SpellScript { - public: - spell_item_chicken_cover() : SpellScriptLoader("spell_item_chicken_cover") { } + PrepareSpellScript(spell_item_chicken_cover); - class spell_item_chicken_cover_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_chicken_cover_SpellScript); - - bool Load() override - { - return GetCaster()->GetTypeId() == TYPEID_PLAYER; - } - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_CHICKEN_NET, SPELL_CAPTURE_CHICKEN_ESCAPE }) && - sObjectMgr->GetQuestTemplate(QUEST_CHICKEN_PARTY) && - sObjectMgr->GetQuestTemplate(QUEST_FLOWN_THE_COOP); - } + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } - void HandleDummy(SpellEffIndex /* effIndex */) - { - Player* caster = GetCaster()->ToPlayer(); - if (Unit* target = GetHitUnit()) - { - if (!target->HasAura(SPELL_CHICKEN_NET) && (caster->GetQuestStatus(QUEST_CHICKEN_PARTY) == QUEST_STATUS_INCOMPLETE || caster->GetQuestStatus(QUEST_FLOWN_THE_COOP) == QUEST_STATUS_INCOMPLETE)) - { - caster->CastSpell(caster, SPELL_CAPTURE_CHICKEN_ESCAPE, true); - target->KillSelf(); - } - } - } + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_CHICKEN_NET, SPELL_CAPTURE_CHICKEN_ESCAPE }) && + sObjectMgr->GetQuestTemplate(QUEST_CHICKEN_PARTY) && + sObjectMgr->GetQuestTemplate(QUEST_FLOWN_THE_COOP); + } - void Register() override + void HandleDummy(SpellEffIndex /* effIndex */) + { + Player* caster = GetCaster()->ToPlayer(); + if (Unit* target = GetHitUnit()) + { + if (!target->HasAura(SPELL_CHICKEN_NET) && (caster->GetQuestStatus(QUEST_CHICKEN_PARTY) == QUEST_STATUS_INCOMPLETE || caster->GetQuestStatus(QUEST_FLOWN_THE_COOP) == QUEST_STATUS_INCOMPLETE)) { - OnEffectHitTarget += SpellEffectFn(spell_item_chicken_cover_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + caster->CastSpell(caster, SPELL_CAPTURE_CHICKEN_ESCAPE, true); + target->KillSelf(); } - }; - - SpellScript* GetSpellScript() const override - { - return new spell_item_chicken_cover_SpellScript(); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_chicken_cover::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum Refocus @@ -3614,100 +2918,67 @@ enum Refocus SPELL_VOLLEY = 42243, }; -class spell_item_refocus : public SpellScriptLoader +class spell_item_refocus : public SpellScript { - public: - spell_item_refocus() : SpellScriptLoader("spell_item_refocus") { } - - class spell_item_refocus_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_refocus_SpellScript); - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - Player* caster = GetCaster()->ToPlayer(); + PrepareSpellScript(spell_item_refocus); - if (!caster || caster->getClass() != CLASS_HUNTER) - return; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Player* caster = GetCaster()->ToPlayer(); - if (caster->GetSpellHistory()->HasCooldown(SPELL_AIMED_SHOT)) - caster->GetSpellHistory()->ResetCooldown(SPELL_AIMED_SHOT, true); + if (!caster || caster->getClass() != CLASS_HUNTER) + return; - if (caster->GetSpellHistory()->HasCooldown(SPELL_MULTISHOT)) - caster->GetSpellHistory()->ResetCooldown(SPELL_MULTISHOT, true); + if (caster->GetSpellHistory()->HasCooldown(SPELL_AIMED_SHOT)) + caster->GetSpellHistory()->ResetCooldown(SPELL_AIMED_SHOT, true); - if (caster->GetSpellHistory()->HasCooldown(SPELL_VOLLEY)) - caster->GetSpellHistory()->ResetCooldown(SPELL_VOLLEY, true); - } + if (caster->GetSpellHistory()->HasCooldown(SPELL_MULTISHOT)) + caster->GetSpellHistory()->ResetCooldown(SPELL_MULTISHOT, true); - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_refocus_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + if (caster->GetSpellHistory()->HasCooldown(SPELL_VOLLEY)) + caster->GetSpellHistory()->ResetCooldown(SPELL_VOLLEY, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_refocus_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_refocus::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; -class spell_item_muisek_vessel : public SpellScriptLoader +class spell_item_muisek_vessel : public SpellScript { - public: - spell_item_muisek_vessel() : SpellScriptLoader("spell_item_muisek_vessel") { } - - class spell_item_muisek_vessel_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_muisek_vessel_SpellScript); + PrepareSpellScript(spell_item_muisek_vessel); - void HandleDummy(SpellEffIndex /*effIndex*/) - { - if (Creature* target = GetHitCreature()) - if (target->isDead()) - target->DespawnOrUnsummon(); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_muisek_vessel_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Creature* target = GetHitCreature()) + if (target->isDead()) + target->DespawnOrUnsummon(); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_muisek_vessel_SpellScript(); - } + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_muisek_vessel::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum GreatmothersSoulcather { SPELL_FORCE_CAST_SUMMON_GNOME_SOUL = 46486, }; -class spell_item_greatmothers_soulcatcher : public SpellScriptLoader +class spell_item_greatmothers_soulcatcher : public SpellScript { -public: - spell_item_greatmothers_soulcatcher() : SpellScriptLoader("spell_item_greatmothers_soulcatcher") { } + PrepareSpellScript(spell_item_greatmothers_soulcatcher); - class spell_item_greatmothers_soulcatcher_SpellScript : public SpellScript + void HandleDummy(SpellEffIndex /*effIndex*/) { - PrepareSpellScript(spell_item_greatmothers_soulcatcher_SpellScript); - - void HandleDummy(SpellEffIndex /*effIndex*/) - { - if (GetHitUnit()) - GetCaster()->CastSpell(GetCaster(), SPELL_FORCE_CAST_SUMMON_GNOME_SOUL); - } - - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_greatmothers_soulcatcher_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + if (GetHitUnit()) + GetCaster()->CastSpell(GetCaster(), SPELL_FORCE_CAST_SUMMON_GNOME_SOUL); + } - SpellScript* GetSpellScript() const override + void Register() override { - return new spell_item_greatmothers_soulcatcher_SpellScript(); + OnEffectHitTarget += SpellEffectFn(spell_item_greatmothers_soulcatcher::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; @@ -3778,60 +3049,49 @@ enum SoulPreserver SPELL_SOUL_PRESERVER_SHAMAN = 60515, }; -class spell_item_soul_preserver : public SpellScriptLoader +class spell_item_soul_preserver : public AuraScript { -public: - spell_item_soul_preserver() : SpellScriptLoader("spell_item_soul_preserver") { } + PrepareAuraScript(spell_item_soul_preserver); - class spell_item_soul_preserver_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override { - PrepareAuraScript(spell_item_soul_preserver_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_SOUL_PRESERVER_DRUID, - SPELL_SOUL_PRESERVER_PALADIN, - SPELL_SOUL_PRESERVER_PRIEST, - SPELL_SOUL_PRESERVER_SHAMAN - }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + return ValidateSpellInfo( { - PreventDefaultAction(); - - Unit* caster = eventInfo.GetActor(); - - switch (caster->getClass()) - { - case CLASS_DRUID: - caster->CastSpell(caster, SPELL_SOUL_PRESERVER_DRUID, true, nullptr, aurEff); - break; - case CLASS_PALADIN: - caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PALADIN, true, nullptr, aurEff); - break; - case CLASS_PRIEST: - caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PRIEST, true, nullptr, aurEff); - break; - case CLASS_SHAMAN: - caster->CastSpell(caster, SPELL_SOUL_PRESERVER_SHAMAN, true, nullptr, aurEff); - break; - default: - break; - } - } + SPELL_SOUL_PRESERVER_DRUID, + SPELL_SOUL_PRESERVER_PALADIN, + SPELL_SOUL_PRESERVER_PRIEST, + SPELL_SOUL_PRESERVER_SHAMAN + }); + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_soul_preserver_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + + switch (caster->getClass()) + { + case CLASS_DRUID: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_DRUID, true, nullptr, aurEff); + break; + case CLASS_PALADIN: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PALADIN, true, nullptr, aurEff); + break; + case CLASS_PRIEST: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PRIEST, true, nullptr, aurEff); + break; + case CLASS_SHAMAN: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_SHAMAN, true, nullptr, aurEff); + break; + default: + break; } - }; + } - AuraScript* GetAuraScript() const override + void Register() override { - return new spell_item_soul_preserver_AuraScript(); + OnEffectProc += AuraEffectProcFn(spell_item_soul_preserver::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } }; @@ -3916,40 +3176,29 @@ class spell_item_sunwell_neck : public SpellScriptLoader } }; -class spell_item_toy_train_set_pulse : public SpellScriptLoader +class spell_item_toy_train_set_pulse : public SpellScript { -public: - spell_item_toy_train_set_pulse() : SpellScriptLoader("spell_item_toy_train_set_pulse") { } + PrepareSpellScript(spell_item_toy_train_set_pulse); - class spell_item_toy_train_set_pulse_SpellScript : public SpellScript + void HandleDummy(SpellEffIndex /*index*/) { - PrepareSpellScript(spell_item_toy_train_set_pulse_SpellScript); - - void HandleDummy(SpellEffIndex /*index*/) + if (Player* target = GetHitUnit()->ToPlayer()) { - if (Player* target = GetHitUnit()->ToPlayer()) - { - target->HandleEmoteCommand(EMOTE_ONESHOT_TRAIN); - if (EmotesTextSoundEntry const* soundEntry = FindTextSoundEmoteFor(TEXT_EMOTE_TRAIN, target->getRace(), target->getGender())) - target->PlayDistanceSound(soundEntry->SoundId); - } - } - - void HandleTargets(std::list<WorldObject*>& targetList) - { - targetList.remove_if([](WorldObject const* obj) { return obj->GetTypeId() != TYPEID_PLAYER; }); + target->HandleEmoteCommand(EMOTE_ONESHOT_TRAIN); + if (EmotesTextSoundEntry const* soundEntry = FindTextSoundEmoteFor(TEXT_EMOTE_TRAIN, target->getRace(), target->getGender())) + target->PlayDistanceSound(soundEntry->SoundId); } + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_toy_train_set_pulse_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_item_toy_train_set_pulse_SpellScript::HandleTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ALLY); - } - }; + void HandleTargets(std::list<WorldObject*>& targetList) + { + targetList.remove_if([](WorldObject const* obj) { return obj->GetTypeId() != TYPEID_PLAYER; }); + } - SpellScript* GetSpellScript() const override + void Register() override { - return new spell_item_toy_train_set_pulse_SpellScript(); + OnEffectHitTarget += SpellEffectFn(spell_item_toy_train_set_pulse::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_item_toy_train_set_pulse::HandleTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ALLY); } }; @@ -3963,66 +3212,55 @@ enum DeathChoiceSpells SPELL_DEATH_CHOICE_HEROIC_STRENGTH = 67773 }; -class spell_item_death_choice : public SpellScriptLoader +class spell_item_death_choice : public AuraScript { -public: - spell_item_death_choice() : SpellScriptLoader("spell_item_death_choice") { } + PrepareAuraScript(spell_item_death_choice); - class spell_item_death_choice_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override { - PrepareAuraScript(spell_item_death_choice_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override + return ValidateSpellInfo( { - return ValidateSpellInfo( - { - SPELL_DEATH_CHOICE_NORMAL_STRENGTH, - SPELL_DEATH_CHOICE_NORMAL_AGILITY, - SPELL_DEATH_CHOICE_HEROIC_STRENGTH, - SPELL_DEATH_CHOICE_HEROIC_AGILITY - }); - } + SPELL_DEATH_CHOICE_NORMAL_STRENGTH, + SPELL_DEATH_CHOICE_NORMAL_AGILITY, + SPELL_DEATH_CHOICE_HEROIC_STRENGTH, + SPELL_DEATH_CHOICE_HEROIC_AGILITY + }); + } - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); - Unit* caster = eventInfo.GetActor(); - float str = caster->GetStat(STAT_STRENGTH); - float agi = caster->GetStat(STAT_AGILITY); + Unit* caster = eventInfo.GetActor(); + float str = caster->GetStat(STAT_STRENGTH); + float agi = caster->GetStat(STAT_AGILITY); - switch (aurEff->GetId()) + switch (aurEff->GetId()) + { + case SPELL_DEATH_CHOICE_NORMAL_AURA: { - case SPELL_DEATH_CHOICE_NORMAL_AURA: - { - if (str > agi) - caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_STRENGTH, true, nullptr, aurEff); - else - caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_AGILITY, true, nullptr, aurEff); - break; - } - case SPELL_DEATH_CHOICE_HEROIC_AURA: - { - if (str > agi) - caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_STRENGTH, true, nullptr, aurEff); - else - caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_AGILITY, true, nullptr, aurEff); - break; - } - default: - break; + if (str > agi) + caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_STRENGTH, true, nullptr, aurEff); + else + caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_AGILITY, true, nullptr, aurEff); + break; } + case SPELL_DEATH_CHOICE_HEROIC_AURA: + { + if (str > agi) + caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_STRENGTH, true, nullptr, aurEff); + else + caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_AGILITY, true, nullptr, aurEff); + break; + } + default: + break; } + } - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_death_choice_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); - } - }; - - AuraScript* GetAuraScript() const override + void Register() override { - return new spell_item_death_choice_AuraScript(); + OnEffectProc += AuraEffectProcFn(spell_item_death_choice::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } }; @@ -4113,75 +3351,64 @@ enum DarkmoonCardSpells SPELL_DARKMOON_CARD_SPIRIT = 60235, }; -class spell_item_darkmoon_card_greatness : public SpellScriptLoader +class spell_item_darkmoon_card_greatness : public AuraScript { -public: - spell_item_darkmoon_card_greatness() : SpellScriptLoader("spell_item_darkmoon_card_greatness") { } + PrepareAuraScript(spell_item_darkmoon_card_greatness); - class spell_item_darkmoon_card_greatness_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override { - PrepareAuraScript(spell_item_darkmoon_card_greatness_AuraScript); + return ValidateSpellInfo( + { + SPELL_DARKMOON_CARD_STRENGTH, + SPELL_DARKMOON_CARD_AGILITY, + SPELL_DARKMOON_CARD_INTELLECT, + SPELL_DARKMOON_CARD_SPIRIT + }); + } - bool Validate(SpellInfo const* /*spellInfo*/) override + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + float str = caster->GetStat(STAT_STRENGTH); + float agi = caster->GetStat(STAT_AGILITY); + float intl = caster->GetStat(STAT_INTELLECT); + float spi = caster->GetStat(STAT_SPIRIT); + float stat = 0.0f; + + uint32 spellTrigger = SPELL_DARKMOON_CARD_STRENGTH; + + if (str > stat) { - return ValidateSpellInfo( - { - SPELL_DARKMOON_CARD_STRENGTH, - SPELL_DARKMOON_CARD_AGILITY, - SPELL_DARKMOON_CARD_INTELLECT, - SPELL_DARKMOON_CARD_SPIRIT - }); + spellTrigger = SPELL_DARKMOON_CARD_STRENGTH; + stat = str; } - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + if (agi > stat) { - PreventDefaultAction(); + spellTrigger = SPELL_DARKMOON_CARD_AGILITY; + stat = agi; + } - Unit* caster = eventInfo.GetActor(); - float str = caster->GetStat(STAT_STRENGTH); - float agi = caster->GetStat(STAT_AGILITY); - float intl = caster->GetStat(STAT_INTELLECT); - float spi = caster->GetStat(STAT_SPIRIT); - float stat = 0.0f; - - uint32 spellTrigger = SPELL_DARKMOON_CARD_STRENGTH; - - if (str > stat) - { - spellTrigger = SPELL_DARKMOON_CARD_STRENGTH; - stat = str; - } - - if (agi > stat) - { - spellTrigger = SPELL_DARKMOON_CARD_AGILITY; - stat = agi; - } - - if (intl > stat) - { - spellTrigger = SPELL_DARKMOON_CARD_INTELLECT; - stat = intl; - } - - if (spi > stat) - { - spellTrigger = SPELL_DARKMOON_CARD_SPIRIT; - stat = spi; - } - - caster->CastSpell(caster, spellTrigger, true, nullptr, aurEff); + if (intl > stat) + { + spellTrigger = SPELL_DARKMOON_CARD_INTELLECT; + stat = intl; } - void Register() override + if (spi > stat) { - OnEffectProc += AuraEffectProcFn(spell_item_darkmoon_card_greatness_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + spellTrigger = SPELL_DARKMOON_CARD_SPIRIT; + stat = spi; } - }; - AuraScript* GetAuraScript() const override + caster->CastSpell(caster, spellTrigger, true, nullptr, aurEff); + } + + void Register() override { - return new spell_item_darkmoon_card_greatness_AuraScript(); + OnEffectProc += AuraEffectProcFn(spell_item_darkmoon_card_greatness::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } }; @@ -4191,43 +3418,32 @@ enum CharmWitchDoctor SPELL_CHARM_WITCH_DOCTOR_PROC = 43821 }; -class spell_item_charm_witch_doctor : public SpellScriptLoader +class spell_item_charm_witch_doctor : public AuraScript { -public: - spell_item_charm_witch_doctor() : SpellScriptLoader("spell_item_charm_witch_doctor") { } + PrepareAuraScript(spell_item_charm_witch_doctor); - class spell_item_charm_witch_doctor_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override { - PrepareAuraScript(spell_item_charm_witch_doctor_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_CHARM_WITCH_DOCTOR_PROC }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) - { - PreventDefaultAction(); + return ValidateSpellInfo({ SPELL_CHARM_WITCH_DOCTOR_PROC }); + } - Unit* caster = eventInfo.GetActor(); - Unit* target = eventInfo.GetActionTarget(); + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); - if (target) - { - int32 bp = CalculatePct(target->GetCreateHealth(),aurEff->GetSpellInfo()->Effects[1].CalcValue()); - caster->CastCustomSpell(target, SPELL_CHARM_WITCH_DOCTOR_PROC, &bp, nullptr, nullptr, true, nullptr, aurEff); - } - } + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetActionTarget(); - void Register() override + if (target) { - OnEffectProc += AuraEffectProcFn(spell_item_charm_witch_doctor_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + int32 bp = CalculatePct(target->GetCreateHealth(),aurEff->GetSpellInfo()->Effects[1].CalcValue()); + caster->CastCustomSpell(target, SPELL_CHARM_WITCH_DOCTOR_PROC, &bp, nullptr, nullptr, true, nullptr, aurEff); } - }; + } - AuraScript* GetAuraScript() const override + void Register() override { - return new spell_item_charm_witch_doctor_AuraScript(); + OnEffectProc += AuraEffectProcFn(spell_item_charm_witch_doctor::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } }; @@ -4238,47 +3454,36 @@ enum ManaDrainSpells SPELL_MANA_DRAIN_LEECH = 27526 }; -class spell_item_mana_drain : public SpellScriptLoader +class spell_item_mana_drain : public AuraScript { -public: - spell_item_mana_drain() : SpellScriptLoader("spell_item_mana_drain") { } + PrepareAuraScript(spell_item_mana_drain); - class spell_item_mana_drain_AuraScript : public AuraScript + bool Validate(SpellInfo const* /*spellInfo*/) override { - PrepareAuraScript(spell_item_mana_drain_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo( - { - SPELL_MANA_DRAIN_ENERGIZE, - SPELL_MANA_DRAIN_LEECH - }); - } - - void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + return ValidateSpellInfo( { - PreventDefaultAction(); + SPELL_MANA_DRAIN_ENERGIZE, + SPELL_MANA_DRAIN_LEECH + }); + } - Unit* caster = eventInfo.GetActor(); - Unit* target = eventInfo.GetActionTarget(); + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); - if (caster->IsAlive()) - caster->CastSpell(caster, SPELL_MANA_DRAIN_ENERGIZE, true, nullptr, aurEff); + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetActionTarget(); - if (target && target->IsAlive()) - caster->CastSpell(target, SPELL_MANA_DRAIN_LEECH, true, nullptr, aurEff); - } + if (caster->IsAlive()) + caster->CastSpell(caster, SPELL_MANA_DRAIN_ENERGIZE, true, nullptr, aurEff); - void Register() override - { - OnEffectProc += AuraEffectProcFn(spell_item_mana_drain_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); - } - }; + if (target && target->IsAlive()) + caster->CastSpell(target, SPELL_MANA_DRAIN_LEECH, true, nullptr, aurEff); + } - AuraScript* GetAuraScript() const override + void Register() override { - return new spell_item_mana_drain_AuraScript(); + OnEffectProc += AuraEffectProcFn(spell_item_mana_drain::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } }; @@ -4290,57 +3495,46 @@ enum TauntFlag }; // 51640 - Taunt Flag Targeting -class spell_item_taunt_flag_targeting : public SpellScriptLoader +class spell_item_taunt_flag_targeting : public SpellScript { - public: - spell_item_taunt_flag_targeting() : SpellScriptLoader("spell_item_taunt_flag_targeting") { } + PrepareSpellScript(spell_item_taunt_flag_targeting); - class spell_item_taunt_flag_targeting_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_taunt_flag_targeting_SpellScript); - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_TAUNT_FLAG }) && - sObjectMgr->GetBroadcastText(EMOTE_PLANTS_FLAG); - } - - void FilterTargets(std::list<WorldObject*>& targets) - { - targets.remove_if([](WorldObject* obj) -> bool - { - return obj->GetTypeId() != TYPEID_PLAYER && obj->GetTypeId() != TYPEID_CORPSE; - }); + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_TAUNT_FLAG }) && + sObjectMgr->GetBroadcastText(EMOTE_PLANTS_FLAG); + } - if (targets.empty()) - { - FinishCast(SPELL_FAILED_NO_VALID_TARGETS); - return; - } + void FilterTargets(std::list<WorldObject*>& targets) + { + targets.remove_if([](WorldObject* obj) -> bool + { + return obj->GetTypeId() != TYPEID_PLAYER && obj->GetTypeId() != TYPEID_CORPSE; + }); - Trinity::Containers::RandomResize(targets, 1); - } + if (targets.empty()) + { + FinishCast(SPELL_FAILED_NO_VALID_TARGETS); + return; + } - void HandleDummy(SpellEffIndex /*effIndex*/) - { - // we *really* want the unit implementation here - // it sends a packet like seen on sniff - GetCaster()->Unit::TextEmote(EMOTE_PLANTS_FLAG, GetHitUnit(), false); + Trinity::Containers::RandomResize(targets, 1); + } - GetCaster()->CastSpell(GetHitUnit(), SPELL_TAUNT_FLAG, true); - } + void HandleDummy(SpellEffIndex /*effIndex*/) + { + // we *really* want the unit implementation here + // it sends a packet like seen on sniff + GetCaster()->Unit::TextEmote(EMOTE_PLANTS_FLAG, GetHitUnit(), false); - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_item_taunt_flag_targeting_SpellScript::FilterTargets, EFFECT_0, TARGET_CORPSE_SRC_AREA_ENEMY); - OnEffectHitTarget += SpellEffectFn(spell_item_taunt_flag_targeting_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + GetCaster()->CastSpell(GetHitUnit(), SPELL_TAUNT_FLAG, true); + } - SpellScript* GetSpellScript() const override - { - return new spell_item_taunt_flag_targeting_SpellScript(); - } + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_item_taunt_flag_targeting::FilterTargets, EFFECT_0, TARGET_CORPSE_SRC_AREA_ENEMY); + OnEffectHitTarget += SpellEffectFn(spell_item_taunt_flag_targeting::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 13180 - Gnomish Mind Control Cap @@ -4352,49 +3546,38 @@ enum MindControlCap SPELL_DULLARD = 67809 }; -class spell_item_mind_control_cap : public SpellScriptLoader +class spell_item_mind_control_cap : public SpellScript { - public: - spell_item_mind_control_cap() : SpellScriptLoader("spell_item_mind_control_cap") { } - - class spell_item_mind_control_cap_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_mind_control_cap_SpellScript); - - bool Load() override - { - if (!GetCastItem()) - return false; - return true; - } - - bool Validate(SpellInfo const* /*spell*/) override - { - return ValidateSpellInfo({ SPELL_GNOMISH_MIND_CONTROL_CAP, SPELL_DULLARD }); - } + PrepareSpellScript(spell_item_mind_control_cap); - void HandleDummy(SpellEffIndex /* effIndex */) - { - Unit* caster = GetCaster(); - if (Unit* target = GetHitUnit()) - { - if (roll_chance_i(ROLL_CHANCE_NO_BACKFIRE)) - caster->CastSpell(target, roll_chance_i(ROLL_CHANCE_DULLARD) ? SPELL_DULLARD : SPELL_GNOMISH_MIND_CONTROL_CAP, true, GetCastItem()); - else - target->CastSpell(caster, SPELL_GNOMISH_MIND_CONTROL_CAP, true); // backfire - 5% chance - } - } + bool Load() override + { + if (!GetCastItem()) + return false; + return true; + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_mind_control_cap_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_GNOMISH_MIND_CONTROL_CAP, SPELL_DULLARD }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /* effIndex */) + { + Unit* caster = GetCaster(); + if (Unit* target = GetHitUnit()) { - return new spell_item_mind_control_cap_SpellScript(); + if (roll_chance_i(ROLL_CHANCE_NO_BACKFIRE)) + caster->CastSpell(target, roll_chance_i(ROLL_CHANCE_DULLARD) ? SPELL_DULLARD : SPELL_GNOMISH_MIND_CONTROL_CAP, true, GetCastItem()); + else + target->CastSpell(caster, SPELL_GNOMISH_MIND_CONTROL_CAP, true); // backfire - 5% chance } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_mind_control_cap::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; // 8344 - Universal Remote (Gnomish Universal Remote) @@ -4405,51 +3588,40 @@ enum UniversalRemote SPELL_TARGET_LOCK = 8347 }; -class spell_item_universal_remote : public SpellScriptLoader +class spell_item_universal_remote : public SpellScript { - public: - spell_item_universal_remote() : SpellScriptLoader("spell_item_universal_remote") { } - - class spell_item_universal_remote_SpellScript : public SpellScript - { - PrepareSpellScript(spell_item_universal_remote_SpellScript); - - bool Load() override - { - if (!GetCastItem()) - return false; - return true; - } - - bool Validate(SpellInfo const* /*spellInfo*/) override - { - return ValidateSpellInfo({ SPELL_CONTROL_MACHINE, SPELL_MOBILITY_MALFUNCTION, SPELL_TARGET_LOCK }); - } + PrepareSpellScript(spell_item_universal_remote); - void HandleDummy(SpellEffIndex /*effIndex*/) - { - if (Unit* target = GetHitUnit()) - { - uint8 chance = urand(0, 99); - if (chance < 15) - GetCaster()->CastSpell(target, SPELL_TARGET_LOCK, true, GetCastItem()); - else if (chance < 25) - GetCaster()->CastSpell(target, SPELL_MOBILITY_MALFUNCTION, true, GetCastItem()); - else - GetCaster()->CastSpell(target, SPELL_CONTROL_MACHINE, true, GetCastItem()); - } - } + bool Load() override + { + if (!GetCastItem()) + return false; + return true; + } - void Register() override - { - OnEffectHitTarget += SpellEffectFn(spell_item_universal_remote_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); - } - }; + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_CONTROL_MACHINE, SPELL_MOBILITY_MALFUNCTION, SPELL_TARGET_LOCK }); + } - SpellScript* GetSpellScript() const override + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) { - return new spell_item_universal_remote_SpellScript(); + uint8 chance = urand(0, 99); + if (chance < 15) + GetCaster()->CastSpell(target, SPELL_TARGET_LOCK, true, GetCastItem()); + else if (chance < 25) + GetCaster()->CastSpell(target, SPELL_MOBILITY_MALFUNCTION, true, GetCastItem()); + else + GetCaster()->CastSpell(target, SPELL_CONTROL_MACHINE, true, GetCastItem()); } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_universal_remote::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } }; enum ZandalarianCharms @@ -4514,137 +3686,113 @@ class spell_item_zandalarian_charm : public SpellScriptLoader }; // 45051 - Mad Alchemist's Potion (34440) -class spell_item_mad_alchemists_potion : public SpellScriptLoader +class spell_item_mad_alchemists_potion : public SpellScript { -public: - spell_item_mad_alchemists_potion() : SpellScriptLoader("spell_item_mad_alchemists_potion") {} + PrepareSpellScript(spell_item_mad_alchemists_potion); - class mad_alchemists_potion_SpellScript : public SpellScript + void SecondaryEffect() { - PrepareSpellScript(mad_alchemists_potion_SpellScript); - - void SecondaryEffect() - { - std::vector<uint32> availableElixirs = - { - // Battle Elixirs - 33720, // Onslaught Elixir (28102) - 54452, // Adept's Elixir (28103) - 33726, // Elixir of Mastery (28104) - 28490, // Elixir of Major Strength (22824) - 28491, // Elixir of Healing Power (22825) - 28493, // Elixir of Major Frost Power (22827) - 54494, // Elixir of Major Agility (22831) - 28501, // Elixir of Major Firepower (22833) - 28503,// Elixir of Major Shadow Power (22835) - 38954, // Fel Strength Elixir (31679) - // Guardian Elixirs - 39625, // Elixir of Major Fortitude (32062) - 39626, // Earthen Elixir (32063) - 39627, // Elixir of Draenic Wisdom (32067) - 39628, // Elixir of Ironskin (32068) - 28502, // Elixir of Major Defense (22834) - 28514, // Elixir of Empowerment (22848) - // Other - 28489, // Elixir of Camouflage (22823) - 28496 // Elixir of the Searching Eye (22830) - }; - - Unit* target = GetCaster(); - - if (target->getPowerType() == POWER_MANA) - availableElixirs.push_back(28509); // Elixir of Major Mageblood (22840) - - uint32 chosenElixir = Trinity::Containers::SelectRandomContainerElement(availableElixirs); - - bool useElixir = true; - - SpellGroup chosenSpellGroup = SPELL_GROUP_NONE; - if (sSpellMgr->IsSpellMemberOfSpellGroup(chosenElixir, SPELL_GROUP_ELIXIR_BATTLE)) - chosenSpellGroup = SPELL_GROUP_ELIXIR_BATTLE; - if (sSpellMgr->IsSpellMemberOfSpellGroup(chosenElixir, SPELL_GROUP_ELIXIR_GUARDIAN)) - chosenSpellGroup = SPELL_GROUP_ELIXIR_GUARDIAN; - // If another spell of the same group is already active the elixir should not be cast - if (chosenSpellGroup != SPELL_GROUP_NONE) - { - Unit::AuraApplicationMap const& auraMap = target->GetAppliedAuras(); - for (auto itr = auraMap.begin(); itr != auraMap.end(); ++itr) + std::vector<uint32> availableElixirs = + { + // Battle Elixirs + 33720, // Onslaught Elixir (28102) + 54452, // Adept's Elixir (28103) + 33726, // Elixir of Mastery (28104) + 28490, // Elixir of Major Strength (22824) + 28491, // Elixir of Healing Power (22825) + 28493, // Elixir of Major Frost Power (22827) + 54494, // Elixir of Major Agility (22831) + 28501, // Elixir of Major Firepower (22833) + 28503,// Elixir of Major Shadow Power (22835) + 38954, // Fel Strength Elixir (31679) + // Guardian Elixirs + 39625, // Elixir of Major Fortitude (32062) + 39626, // Earthen Elixir (32063) + 39627, // Elixir of Draenic Wisdom (32067) + 39628, // Elixir of Ironskin (32068) + 28502, // Elixir of Major Defense (22834) + 28514, // Elixir of Empowerment (22848) + // Other + 28489, // Elixir of Camouflage (22823) + 28496 // Elixir of the Searching Eye (22830) + }; + + Unit* target = GetCaster(); + + if (target->getPowerType() == POWER_MANA) + availableElixirs.push_back(28509); // Elixir of Major Mageblood (22840) + + uint32 chosenElixir = Trinity::Containers::SelectRandomContainerElement(availableElixirs); + + bool useElixir = true; + + SpellGroup chosenSpellGroup = SPELL_GROUP_NONE; + if (sSpellMgr->IsSpellMemberOfSpellGroup(chosenElixir, SPELL_GROUP_ELIXIR_BATTLE)) + chosenSpellGroup = SPELL_GROUP_ELIXIR_BATTLE; + if (sSpellMgr->IsSpellMemberOfSpellGroup(chosenElixir, SPELL_GROUP_ELIXIR_GUARDIAN)) + chosenSpellGroup = SPELL_GROUP_ELIXIR_GUARDIAN; + // If another spell of the same group is already active the elixir should not be cast + if (chosenSpellGroup != SPELL_GROUP_NONE) + { + Unit::AuraApplicationMap const& auraMap = target->GetAppliedAuras(); + for (auto itr = auraMap.begin(); itr != auraMap.end(); ++itr) + { + uint32 spellId = itr->second->GetBase()->GetId(); + if (sSpellMgr->IsSpellMemberOfSpellGroup(spellId, chosenSpellGroup) && spellId != chosenElixir) { - uint32 spellId = itr->second->GetBase()->GetId(); - if (sSpellMgr->IsSpellMemberOfSpellGroup(spellId, chosenSpellGroup) && spellId != chosenElixir) - { - useElixir = false; - break; - } + useElixir = false; + break; } } - - if (useElixir) - target->CastSpell(target, chosenElixir, true, GetCastItem()); - } - - void Register() override - { - AfterCast += SpellCastFn(mad_alchemists_potion_SpellScript::SecondaryEffect); } - }; + if (useElixir) + target->CastSpell(target, chosenElixir, true, GetCastItem()); + } - SpellScript* GetSpellScript() const override + void Register() override { - return new mad_alchemists_potion_SpellScript(); + AfterCast += SpellCastFn(spell_item_mad_alchemists_potion::SecondaryEffect); } }; // 53750 - Crazy Alchemist's Potion (40077) -class spell_item_crazy_alchemists_potion : public SpellScriptLoader +class spell_item_crazy_alchemists_potion : public SpellScript { -public: - spell_item_crazy_alchemists_potion() : SpellScriptLoader("spell_item_crazy_alchemists_potion") {} + PrepareSpellScript(spell_item_crazy_alchemists_potion); - class crazy_alchemists_potion_SpellScript : public SpellScript + void SecondaryEffect() { - PrepareSpellScript(crazy_alchemists_potion_SpellScript); - - void SecondaryEffect() + std::vector<uint32> availableElixirs = { - std::vector<uint32> availableElixirs = - { - 43185, // Runic Healing Potion (33447) - 53750, // Crazy Alchemist's Potion (40077) - 53761, // Powerful Rejuvenation Potion (40087) - 53762, // Indestructible Potion (40093) - 53908, // Potion of Speed (40211) - 53909, // Potion of Wild Magic (40212) - 53910, // Mighty Arcane Protection Potion (40213) - 53911, // Mighty Fire Protection Potion (40214) - 53913, // Mighty Frost Protection Potion (40215) - 53914, // Mighty Nature Protection Potion (40216) - 53915 // Mighty Shadow Protection Potion (40217) - }; - - Unit* target = GetCaster(); - - if (!target->IsInCombat()) - availableElixirs.push_back(53753); // Potion of Nightmares (40081) - if (target->getPowerType() == POWER_MANA) - availableElixirs.push_back(43186); // Runic Mana Potion(33448) - - uint32 chosenElixir = Trinity::Containers::SelectRandomContainerElement(availableElixirs); + 43185, // Runic Healing Potion (33447) + 53750, // Crazy Alchemist's Potion (40077) + 53761, // Powerful Rejuvenation Potion (40087) + 53762, // Indestructible Potion (40093) + 53908, // Potion of Speed (40211) + 53909, // Potion of Wild Magic (40212) + 53910, // Mighty Arcane Protection Potion (40213) + 53911, // Mighty Fire Protection Potion (40214) + 53913, // Mighty Frost Protection Potion (40215) + 53914, // Mighty Nature Protection Potion (40216) + 53915 // Mighty Shadow Protection Potion (40217) + }; - target->CastSpell(target, chosenElixir, true, GetCastItem()); - } + Unit* target = GetCaster(); - void Register() override - { - AfterCast += SpellCastFn(crazy_alchemists_potion_SpellScript::SecondaryEffect); - } + if (!target->IsInCombat()) + availableElixirs.push_back(53753); // Potion of Nightmares (40081) + if (target->getPowerType() == POWER_MANA) + availableElixirs.push_back(43186); // Runic Mana Potion(33448) - }; + uint32 chosenElixir = Trinity::Containers::SelectRandomContainerElement(availableElixirs); + + target->CastSpell(target, chosenElixir, true, GetCastItem()); + } - SpellScript* GetSpellScript() const override + void Register() override { - return new crazy_alchemists_potion_SpellScript(); + AfterCast += SpellCastFn(spell_item_crazy_alchemists_potion::SecondaryEffect); } }; @@ -4659,110 +3807,111 @@ void AddSC_item_spell_scripts() // 23075 Mithril Mechanical Dragonling new spell_item_trigger_spell("spell_item_mithril_mechanical_dragonling", SPELL_MITHRIL_MECHANICAL_DRAGONLING); - new spell_item_aegis_of_preservation(); - new spell_item_arcane_shroud(); - new spell_item_alchemists_stone(); + RegisterAuraScript(spell_item_aegis_of_preservation); + RegisterAuraScript(spell_item_alchemists_stone); new spell_item_anger_capacitor<8>("spell_item_tiny_abomination_in_a_jar"); new spell_item_anger_capacitor<7>("spell_item_tiny_abomination_in_a_jar_hero"); - new spell_item_aura_of_madness(); - new spell_item_dementia(); - new spell_item_blessing_of_ancient_kings(); - new spell_item_deadly_precision(); - new spell_item_deadly_precision_dummy(); + RegisterAuraScript(spell_item_arcane_shroud); + RegisterAuraScript(spell_item_aura_of_madness); + RegisterAuraScript(spell_item_dementia); + RegisterAuraScript(spell_item_blessing_of_ancient_kings); + RegisterAuraScript(spell_item_deadly_precision); + RegisterSpellScript(spell_item_deadly_precision_dummy); new spell_item_deathbringers_will<SPELL_STRENGTH_OF_THE_TAUNKA, SPELL_AGILITY_OF_THE_VRYKUL, SPELL_POWER_OF_THE_TAUNKA, SPELL_AIM_OF_THE_IRON_DWARVES, SPELL_SPEED_OF_THE_VRYKUL>("spell_item_deathbringers_will_normal"); new spell_item_deathbringers_will<SPELL_STRENGTH_OF_THE_TAUNKA_HERO, SPELL_AGILITY_OF_THE_VRYKUL_HERO, SPELL_POWER_OF_THE_TAUNKA_HERO, SPELL_AIM_OF_THE_IRON_DWARVES_HERO, SPELL_SPEED_OF_THE_VRYKUL_HERO>("spell_item_deathbringers_will_heroic"); - new spell_item_decahedral_dwarven_dice(); + RegisterSpellScript(spell_item_decahedral_dwarven_dice); new spell_item_defibrillate("spell_item_goblin_jumper_cables", 67, SPELL_GOBLIN_JUMPER_CABLES_FAIL); new spell_item_defibrillate("spell_item_goblin_jumper_cables_xl", 50, SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL); new spell_item_defibrillate("spell_item_gnomish_army_knife", 33); - new spell_item_desperate_defense(); - new spell_item_deviate_fish(); - new spell_item_discerning_eye_beast_dummy(); - new spell_item_echoes_of_light(); - new spell_item_fate_rune_of_unsurpassed_vigor(); - new spell_item_flask_of_the_north(); - new spell_item_frozen_shadoweave(); - new spell_item_gnomish_death_ray(); - new spell_item_harm_prevention_belt(); - new spell_item_healing_touch_refund(); + RegisterAuraScript(spell_item_desperate_defense); + RegisterSpellScript(spell_item_deviate_fish); + RegisterAuraScript(spell_item_discerning_eye_beast_dummy); + RegisterSpellScript(spell_item_echoes_of_light); + RegisterAuraScript(spell_item_fate_rune_of_unsurpassed_vigor); + RegisterSpellScript(spell_item_flask_of_the_north); + RegisterAuraScript(spell_item_frozen_shadoweave); + RegisterSpellScript(spell_item_gnomish_death_ray); + RegisterAuraScript(spell_item_harm_prevention_belt); + RegisterAuraScript(spell_item_healing_touch_refund); new spell_item_heartpierce<SPELL_INVIGORATION_ENERGY, SPELL_INVIGORATION_MANA, SPELL_INVIGORATION_RAGE, SPELL_INVIGORATION_RP>("spell_item_heartpierce"); new spell_item_heartpierce<SPELL_INVIGORATION_ENERGY_HERO, SPELL_INVIGORATION_MANA_HERO, SPELL_INVIGORATION_RAGE_HERO, SPELL_INVIGORATION_RP_HERO>("spell_item_heartpierce_hero"); - new spell_item_crystal_spire_of_karabor(); - new spell_item_make_a_wish(); - new spell_item_mark_of_conquest(); - new spell_item_mingos_fortune_generator(); - new spell_item_necrotic_touch(); - new spell_item_net_o_matic(); - new spell_item_noggenfogger_elixir(); - new spell_item_pendant_of_the_violet_eye(); - new spell_item_persistent_shield(); - new spell_item_pet_healing(); - new spell_item_piccolo_of_the_flaming_fire(); - new spell_item_savory_deviate_delight(); - new spell_item_scroll_of_recall(); - new spell_item_unsated_craving(); - new spell_item_shadows_fate(); - new spell_item_shadowmourne(); - new spell_item_shadowmourne_soul_fragment(); - new spell_item_six_demon_bag(); - new spell_item_swift_hand_justice_dummy(); - new spell_item_totem_of_flowing_water(); - new spell_item_the_eye_of_diminution(); - new spell_item_underbelly_elixir(); - new spell_item_worn_troll_dice(); - new spell_item_red_rider_air_rifle(); - - new spell_item_create_heart_candy(); - new spell_item_book_of_glyph_mastery(); - new spell_item_gift_of_the_harvester(); - new spell_item_map_of_the_geyser_fields(); - new spell_item_vanquished_clutches(); - - new spell_item_ashbringer(); - new spell_magic_eater_food(); - new spell_item_refocus(); - new spell_item_shimmering_vessel(); - new spell_item_purify_helboar_meat(); - new spell_item_crystal_prison_dummy_dnd(); - new spell_item_reindeer_transformation(); - new spell_item_nigh_invulnerability(); - new spell_item_poultryizer(); - new spell_item_socrethars_stone(); - new spell_item_demon_broiled_surprise(); - new spell_item_complete_raptor_capture(); - new spell_item_impale_leviroth(); - new spell_item_brewfest_mount_transformation(); - new spell_item_nitro_boots(); - new spell_item_teach_language(); - new spell_item_rocket_boots(); - new spell_item_pygmy_oil(); - new spell_item_unusual_compass(); - new spell_item_chicken_cover(); - new spell_item_muisek_vessel(); - new spell_item_greatmothers_soulcatcher(); + RegisterAuraScript(spell_item_crystal_spire_of_karabor); + RegisterSpellScript(spell_item_make_a_wish); + RegisterAuraScript(spell_item_mark_of_conquest); + RegisterSpellScript(spell_item_mingos_fortune_generator); + RegisterAuraScript(spell_item_necrotic_touch); + RegisterSpellScript(spell_item_net_o_matic); + RegisterSpellScript(spell_item_noggenfogger_elixir); + RegisterAuraScript(spell_item_pendant_of_the_violet_eye); + RegisterAuraScript(spell_item_persistent_shield); + RegisterAuraScript(spell_item_pet_healing); + RegisterSpellScript(spell_item_piccolo_of_the_flaming_fire); + RegisterSpellScript(spell_item_savory_deviate_delight); + RegisterSpellScript(spell_item_scroll_of_recall); + RegisterAuraScript(spell_item_unsated_craving); + RegisterAuraScript(spell_item_shadows_fate); + RegisterAuraScript(spell_item_shadowmourne); + RegisterAuraScript(spell_item_shadowmourne_soul_fragment); + RegisterSpellScript(spell_item_six_demon_bag); + RegisterAuraScript(spell_item_swift_hand_justice_dummy); + RegisterAuraScript(spell_item_totem_of_flowing_water); + RegisterAuraScript(spell_item_the_eye_of_diminution); + RegisterSpellScript(spell_item_underbelly_elixir); + RegisterSpellScript(spell_item_worn_troll_dice); + RegisterSpellScript(spell_item_red_rider_air_rifle); + + RegisterSpellScript(spell_item_create_heart_candy); + RegisterSpellScript(spell_item_book_of_glyph_mastery); + RegisterSpellScript(spell_item_gift_of_the_harvester); + RegisterSpellScript(spell_item_map_of_the_geyser_fields); + RegisterSpellScript(spell_item_vanquished_clutches); + + RegisterSpellScript(spell_item_ashbringer); + RegisterAuraScript(spell_magic_eater_food); + RegisterSpellScript(spell_item_shimmering_vessel); + RegisterSpellScript(spell_item_purify_helboar_meat); + RegisterSpellScript(spell_item_crystal_prison_dummy_dnd); + RegisterSpellScript(spell_item_reindeer_transformation); + RegisterSpellScript(spell_item_nigh_invulnerability); + RegisterSpellScript(spell_item_poultryizer); + RegisterSpellScript(spell_item_socrethars_stone); + RegisterSpellScript(spell_item_demon_broiled_surprise); + RegisterSpellScript(spell_item_complete_raptor_capture); + RegisterSpellScript(spell_item_impale_leviroth); + RegisterSpellScript(spell_item_brewfest_mount_transformation); + RegisterSpellScript(spell_item_nitro_boosts); + RegisterAuraScript(spell_item_nitro_boosts_backfire); + RegisterSpellScript(spell_item_teach_language); + RegisterSpellScript(spell_item_rocket_boots); + RegisterSpellScript(spell_item_pygmy_oil); + RegisterSpellScript(spell_item_unusual_compass); + RegisterSpellScript(spell_item_chicken_cover); + RegisterSpellScript(spell_item_refocus); + RegisterSpellScript(spell_item_muisek_vessel); + RegisterSpellScript(spell_item_greatmothers_soulcatcher); new spell_item_shard_of_the_scale<SPELL_PURIFIED_CAUTERIZING_HEAL, SPELL_PURIFIED_SEARING_FLAMES>("spell_item_purified_shard_of_the_scale"); new spell_item_shard_of_the_scale<SPELL_SHINY_CAUTERIZING_HEAL, SPELL_SHINY_SEARING_FLAMES>("spell_item_shiny_shard_of_the_scale"); - new spell_item_soul_preserver(); + RegisterAuraScript(spell_item_soul_preserver); new spell_item_sunwell_neck<SPELL_LIGHTS_WRATH, SPELL_ARCANE_BOLT>("spell_item_sunwell_exalted_caster_neck"); new spell_item_sunwell_neck<SPELL_LIGHTS_STRENGTH, SPELL_ARCANE_STRIKE>("spell_item_sunwell_exalted_melee_neck"); new spell_item_sunwell_neck<SPELL_LIGHTS_WARD, SPELL_ARCANE_INSIGHT>("spell_item_sunwell_exalted_tank_neck"); new spell_item_sunwell_neck<SPELL_LIGHTS_SALVATION, SPELL_ARCANE_SURGE>("spell_item_sunwell_exalted_healer_neck"); - new spell_item_toy_train_set_pulse(); - new spell_item_death_choice(); + RegisterSpellScript(spell_item_toy_train_set_pulse); + RegisterAuraScript(spell_item_death_choice); new spell_item_trinket_stack("spell_item_lightning_capacitor", SPELL_LIGHTNING_CAPACITOR_STACK, SPELL_LIGHTNING_CAPACITOR_TRIGGER); new spell_item_trinket_stack("spell_item_thunder_capacitor", SPELL_THUNDER_CAPACITOR_STACK, SPELL_THUNDER_CAPACITOR_TRIGGER); new spell_item_trinket_stack("spell_item_toc25_normal_caster_trinket", SPELL_TOC25_CASTER_TRINKET_NORMAL_STACK, SPELL_TOC25_CASTER_TRINKET_NORMAL_TRIGGER); new spell_item_trinket_stack("spell_item_toc25_heroic_caster_trinket", SPELL_TOC25_CASTER_TRINKET_HEROIC_STACK, SPELL_TOC25_CASTER_TRINKET_HEROIC_TRIGGER); - new spell_item_darkmoon_card_greatness(); - new spell_item_charm_witch_doctor(); - new spell_item_mana_drain(); - new spell_item_taunt_flag_targeting(); - new spell_item_mind_control_cap(); - new spell_item_universal_remote(); + RegisterAuraScript(spell_item_darkmoon_card_greatness); + RegisterAuraScript(spell_item_charm_witch_doctor); + RegisterAuraScript(spell_item_mana_drain); + RegisterSpellScript(spell_item_taunt_flag_targeting); + RegisterSpellScript(spell_item_mind_control_cap); + RegisterSpellScript(spell_item_universal_remote); new spell_item_zandalarian_charm("spell_item_unstable_power", SPELL_UNSTABLE_POWER_AURA_STACK); new spell_item_zandalarian_charm("spell_item_restless_strength", SPELL_RESTLESS_STRENGTH_AURA_STACK); - new spell_item_mad_alchemists_potion(); - new spell_item_crazy_alchemists_potion(); + RegisterSpellScript(spell_item_mad_alchemists_potion); + RegisterSpellScript(spell_item_crazy_alchemists_potion); } diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index 440c0ba4c5e..e45ed806ec2 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -74,7 +74,8 @@ enum MageSpellIcons { SPELL_ICON_MAGE_SHATTERED_BARRIER = 2945, SPELL_ICON_MAGE_PRESENCE_OF_MIND = 139, - SPELL_ICON_MAGE_CLEARCASTING = 212 + SPELL_ICON_MAGE_CLEARCASTING = 212, + SPELL_ICON_MAGE_LIVING_BOMB = 3000 }; // Incanter's Absorbtion @@ -357,7 +358,7 @@ class spell_mage_combustion : public SpellScriptLoader // Do not take charges, add a stack of crit buff if (!(eventInfo.GetHitMask() & PROC_HIT_CRITICAL)) { - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_MAGE_COMBUSTION_PROC, true); + eventInfo.GetActor()->CastSpell(nullptr, SPELL_MAGE_COMBUSTION_PROC, true); return false; } @@ -408,6 +409,26 @@ class spell_mage_combustion_proc : public SpellScriptLoader } }; +// -31661 - Dragon's Breath +class spell_mage_dragon_breath : public AuraScript +{ + PrepareAuraScript(spell_mage_dragon_breath); + + bool CheckProc(ProcEventInfo& eventInfo) + { + // Dont proc with Living Bomb explosion + SpellInfo const* spellInfo = eventInfo.GetSpellInfo(); + if (spellInfo && spellInfo->SpellIconID == SPELL_ICON_MAGE_LIVING_BOMB && spellInfo->SpellFamilyName == SPELLFAMILY_MAGE) + return false; + return true; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_mage_dragon_breath::CheckProc); + } +}; + // -11185 - Improved Blizzard class spell_mage_imp_blizzard : public SpellScriptLoader { @@ -461,7 +482,7 @@ class spell_mage_imp_mana_gems : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_MAGE_MANA_SURGE, true, nullptr, aurEff); + eventInfo.GetActor()->CastSpell(nullptr, SPELL_MAGE_MANA_SURGE, true, nullptr, aurEff); } void Register() override @@ -712,7 +733,7 @@ class spell_mage_gen_extra_effects : public SpellScriptLoader Unit* caster = eventInfo.GetActor(); if (caster->HasAura(SPELL_MAGE_T10_2P_BONUS)) - caster->CastSpell((Unit*)nullptr, SPELL_MAGE_T10_2P_BONUS_EFFECT, true); + caster->CastSpell(nullptr, SPELL_MAGE_T10_2P_BONUS_EFFECT, true); } void Register() override @@ -1295,6 +1316,7 @@ void AddSC_mage_spell_scripts() new spell_mage_cold_snap(); new spell_mage_combustion(); new spell_mage_combustion_proc(); + RegisterAuraScript(spell_mage_dragon_breath); new spell_mage_imp_blizzard(); new spell_mage_imp_mana_gems(); new spell_mage_empowered_fire(); diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 08ea6472a8d..28455808c01 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -1631,8 +1631,8 @@ class spell_pal_judgements_of_the_wise : public SpellScriptLoader PreventDefaultAction(); Unit* caster = eventInfo.GetActor(); - caster->CastSpell((Unit*)nullptr, SPELL_PALADIN_JUDGEMENTS_OF_THE_WISE_MANA, true, nullptr, aurEff); - caster->CastSpell((Unit*)nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); + caster->CastSpell(nullptr, SPELL_PALADIN_JUDGEMENTS_OF_THE_WISE_MANA, true, nullptr, aurEff); + caster->CastSpell(nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); } void Register() override @@ -2223,7 +2223,7 @@ class spell_pal_sheath_of_light : public SpellScriptLoader Unit* target = eventInfo.GetProcTarget(); SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_PALADIN_SHEATH_OF_LIGHT_HEAL); - int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); + int32 amount = CalculatePct(static_cast<int32>(healInfo->GetEffectiveHeal()), aurEff->GetAmount()); amount /= spellInfo->GetMaxTicks(); // Add remaining ticks to healing done amount += target->GetRemainingPeriodicAmount(caster->GetGUID(), SPELL_PALADIN_SHEATH_OF_LIGHT_HEAL, SPELL_AURA_PERIODIC_HEAL); diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index 4ba231b021c..74c728a6cb0 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -648,10 +648,10 @@ class spell_pri_item_t6_trinket : public SpellScriptLoader PreventDefaultAction(); Unit* caster = eventInfo.GetActor(); if (eventInfo.GetSpellTypeMask() & PROC_SPELL_TYPE_HEAL) - caster->CastSpell((Unit*)nullptr, SPELL_PRIEST_DIVINE_BLESSING, true, nullptr, aurEff); + caster->CastSpell(nullptr, SPELL_PRIEST_DIVINE_BLESSING, true, nullptr, aurEff); if (eventInfo.GetSpellTypeMask() & PROC_SPELL_TYPE_DAMAGE) - caster->CastSpell((Unit*)nullptr, SPELL_PRIEST_DIVINE_WRATH, true, nullptr, aurEff); + caster->CastSpell(nullptr, SPELL_PRIEST_DIVINE_WRATH, true, nullptr, aurEff); } void Register() override @@ -1287,7 +1287,7 @@ class spell_pri_vampiric_touch : public SpellScriptLoader void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); - eventInfo.GetActor()->CastSpell((Unit*)nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); + eventInfo.GetActor()->CastSpell(nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index e3ef1faee6f..7cc32340665 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -2149,7 +2149,7 @@ class spell_q12641_death_comes_from_on_high : public SpellScriptLoader return; } - GetCaster()->CastSpell((Unit*)nullptr, spellId, true); + GetCaster()->CastSpell(nullptr, spellId, true); } void Register() override diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp index 750b5073a7c..0476c4c09df 100644 --- a/src/server/scripts/Spells/spell_rogue.cpp +++ b/src/server/scripts/Spells/spell_rogue.cpp @@ -1022,7 +1022,7 @@ public: return; if (Player* player = caster->ToPlayer()) - player->CastSpell((Unit*)nullptr, SPELL_ROGUE_HONOR_AMONG_THIEVES_2, true); + player->CastSpell(nullptr, SPELL_ROGUE_HONOR_AMONG_THIEVES_2, true); } void Register() override @@ -1060,7 +1060,7 @@ class spell_rog_turn_the_tables : public SpellScriptLoader if (!caster) return; - caster->CastSpell((Unit*)nullptr, GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true, nullptr, aurEff); + caster->CastSpell(nullptr, GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index 72208d73653..ecde34a4b8e 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -455,7 +455,7 @@ class spell_sha_earthbind_totem : public SpellScriptLoader if (Player* owner = GetCaster()->GetCharmerOrOwnerPlayerOrPlayerItself()) if (AuraEffect* aur = owner->GetDummyAuraEffect(SPELLFAMILY_SHAMAN, 2289, 0)) if (roll_chance_i(aur->GetBaseAmount())) - GetTarget()->CastSpell((Unit*)nullptr, SPELL_SHAMAN_TOTEM_EARTHEN_POWER, true); + GetTarget()->CastSpell(nullptr, SPELL_SHAMAN_TOTEM_EARTHEN_POWER, true); } void Apply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) @@ -1075,7 +1075,7 @@ class spell_sha_imp_water_shield : public SpellScriptLoader return; uint32 spellId = waterShield->GetSpellInfo()->Effects[waterShield->GetEffIndex()].TriggerSpell; - caster->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + caster->CastSpell(nullptr, spellId, true, nullptr, aurEff); } void Register() override @@ -1308,7 +1308,7 @@ class spell_sha_item_t6_trinket : public SpellScriptLoader return; if (roll_chance_i(chance)) - eventInfo.GetActor()->CastSpell((Unit*)nullptr, spellId, true, nullptr, aurEff); + eventInfo.GetActor()->CastSpell(nullptr, spellId, true, nullptr, aurEff); } void Register() override @@ -1474,7 +1474,7 @@ class spell_sha_maelstrom_weapon : public SpellScriptLoader if (!aurEff || !roll_chance_i(aurEff->GetAmount())) return; - caster->CastSpell((Unit*)nullptr, SPELL_SHAMAN_MAELSTROM_POWER, true, nullptr, aurEff); + caster->CastSpell(nullptr, SPELL_SHAMAN_MAELSTROM_POWER, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index 4ff38244435..3ee5f8f4e7c 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -1334,11 +1334,11 @@ class spell_warl_soul_leech : public SpellScriptLoader uint32 selfSpellId = casterMana[impSoulLeechRank - 1]; uint32 petSpellId = petMana[impSoulLeechRank - 1]; - caster->CastSpell((Unit*)nullptr, selfSpellId, true, nullptr, aurEff); - caster->CastSpell((Unit*)nullptr, petSpellId, true, nullptr, aurEff); + caster->CastSpell(nullptr, selfSpellId, true, nullptr, aurEff); + caster->CastSpell(nullptr, petSpellId, true, nullptr, aurEff); if (roll_chance_i(impSoulLeech->GetAmount())) - caster->CastSpell((Unit*)nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); + caster->CastSpell(nullptr, SPELL_REPLENISHMENT, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index 4c859f5c8bc..3d6791a0391 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -444,13 +444,13 @@ class spell_warr_extra_proc : public SpellScriptLoader if (!roll_chance_i(bonusAurEff->GetAmount())) return; - target->CastSpell((Unit*)nullptr, SPELL_WARRIOR_EXTRA_CHARGE, true, nullptr, aurEff); + target->CastSpell(nullptr, SPELL_WARRIOR_EXTRA_CHARGE, true, nullptr, aurEff); SpellInfo const* auraInfo = aurEff->GetSpellInfo(); if (auraInfo->IsRankOf(sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_BLOODSURGE_R1))) - target->CastSpell((Unit*)nullptr, SPELL_WARRIOR_SLAM_GCD_REDUCED, true, nullptr, aurEff); + target->CastSpell(nullptr, SPELL_WARRIOR_SLAM_GCD_REDUCED, true, nullptr, aurEff); else if (auraInfo->IsRankOf(sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_SUDDEN_DEATH_R1))) - target->CastSpell((Unit*)nullptr, SPELL_WARRIOR_EXECUTE_GCD_REDUCED, true, nullptr, aurEff); + target->CastSpell(nullptr, SPELL_WARRIOR_EXECUTE_GCD_REDUCED, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/World/action_ip_logger.cpp b/src/server/scripts/World/action_ip_logger.cpp index 9dc3fba9896..9e894a52104 100644 --- a/src/server/scripts/World/action_ip_logger.cpp +++ b/src/server/scripts/World/action_ip_logger.cpp @@ -144,7 +144,7 @@ class AccountActionIpLogger : public AccountScript stmt->setUInt32(1, characterGuid); stmt->setUInt8(2, aType); stmt->setUInt32(3, playerGuid); - stmt->setString(4, systemNote.c_str()); + stmt->setString(4, systemNote); LoginDatabase.Execute(stmt); } else // ... but for failed login, we query last_attempt_ip from account table. Which we do with an unique query @@ -155,7 +155,7 @@ class AccountActionIpLogger : public AccountScript stmt->setUInt32(1, characterGuid); stmt->setUInt8(2, aType); stmt->setUInt32(3, playerGuid); - stmt->setString(4, systemNote.c_str()); + stmt->setString(4, systemNote); LoginDatabase.Execute(stmt); } return; @@ -236,8 +236,8 @@ class CharacterActionIpLogger : public PlayerScript stmt->setUInt32(0, playerGuid); stmt->setUInt32(1, characterGuid); stmt->setUInt8(2, aType); - stmt->setString(3, currentIp.c_str()); // We query the ip here. - stmt->setString(4, systemNote.c_str()); + stmt->setString(3, currentIp); // We query the ip here. + stmt->setString(4, systemNote); // Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now; // Rather, we let it be added with the SQL query. @@ -297,7 +297,7 @@ public: stmt2->setUInt32(1, characterGuid); stmt2->setUInt8(2, aType); stmt2->setUInt32(3, playerGuid); - stmt2->setString(4, systemNote.c_str()); + stmt2->setString(4, systemNote); // Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now; // Rather, we let it be added with the SQL query. diff --git a/src/server/scripts/World/areatrigger_scripts.cpp b/src/server/scripts/World/areatrigger_scripts.cpp index a7d730f185a..0e603e515e1 100644 --- a/src/server/scripts/World/areatrigger_scripts.cpp +++ b/src/server/scripts/World/areatrigger_scripts.cpp @@ -291,6 +291,38 @@ class AreaTrigger_at_nats_landing : public AreaTriggerScript }; /*###### +## at_sentry_point +######*/ + +enum SentryPoint +{ + SPELL_TELEPORT_VISUAL = 799, // TODO Find the correct spell + QUEST_MISSING_DIPLO_PT14 = 1265, + NPC_TERVOSH = 4967 +}; + +class AreaTrigger_at_sentry_point : public AreaTriggerScript +{ +public: + AreaTrigger_at_sentry_point() : AreaTriggerScript("at_sentry_point") { } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) + { + QuestStatus quest_status = player->GetQuestStatus(QUEST_MISSING_DIPLO_PT14); + if (!player->IsAlive() || quest_status == QUEST_STATUS_NONE || quest_status == QUEST_STATUS_REWARDED) + return false; + + if (!player->FindNearestCreature(NPC_TERVOSH, 100.0f)) + { + if (Creature* tervosh = player->SummonCreature(NPC_TERVOSH, -3476.51f, -4105.94f, 17.1f, 5.3816f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + tervosh->CastSpell(tervosh, SPELL_TELEPORT_VISUAL, true); + } + + return true; + } +}; + +/*###### ## at_brewfest ######*/ @@ -488,6 +520,7 @@ void AddSC_areatrigger_scripts() new AreaTrigger_at_last_rites(); new AreaTrigger_at_sholazar_waygate(); new AreaTrigger_at_nats_landing(); + new AreaTrigger_at_sentry_point(); new AreaTrigger_at_brewfest(); new AreaTrigger_at_area_52_entrance(); new AreaTrigger_at_frostgrips_hollow(); diff --git a/src/server/scripts/World/boss_emerald_dragons.cpp b/src/server/scripts/World/boss_emerald_dragons.cpp index f510b5c6839..96daefc124c 100644 --- a/src/server/scripts/World/boss_emerald_dragons.cpp +++ b/src/server/scripts/World/boss_emerald_dragons.cpp @@ -398,7 +398,7 @@ class boss_lethon : public CreatureScript switch (eventId) { case EVENT_SHADOW_BOLT_WHIRL: - me->CastSpell((Unit*)nullptr, SPELL_SHADOW_BOLT_WHIRL, false); + me->CastSpell(nullptr, SPELL_SHADOW_BOLT_WHIRL, false); events.ScheduleEvent(EVENT_SHADOW_BOLT_WHIRL, urand(15000, 30000)); break; default: @@ -438,7 +438,7 @@ class npc_spirit_shade : public CreatureScript { if (moveType == FOLLOW_MOTION_TYPE && data == _summonerGuid.GetCounter()) { - me->CastSpell((Unit*)nullptr, SPELL_DARK_OFFERING, false); + me->CastSpell(nullptr, SPELL_DARK_OFFERING, false); me->DespawnOrUnsummon(1000); } } diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index d9aa9358ee7..91b7bf30ad7 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -37,7 +37,6 @@ go_soulwell go_bashir_crystalforge go_soulwell go_dragonflayer_cage -go_tadpole_cage go_amberpine_outhouse go_hive_pod go_veil_skith_cage @@ -1232,49 +1231,6 @@ public: }; /*###### -## Quest 11560: Oh Noes, the Tadpoles! -## go_tadpole_cage -######*/ - -enum Tadpoles -{ - QUEST_OH_NOES_THE_TADPOLES = 11560, - NPC_WINTERFIN_TADPOLE = 25201 -}; - -class go_tadpole_cage : public GameObjectScript -{ -public: - go_tadpole_cage() : GameObjectScript("go_tadpole_cage") { } - - struct go_tadpole_cageAI : public GameObjectAI - { - go_tadpole_cageAI(GameObject* go) : GameObjectAI(go) { } - - bool GossipHello(Player* player) override - { - me->UseDoorOrButton(); - if (player->GetQuestStatus(QUEST_OH_NOES_THE_TADPOLES) == QUEST_STATUS_INCOMPLETE) - { - Creature* pTadpole = me->FindNearestCreature(NPC_WINTERFIN_TADPOLE, 1.0f); - if (pTadpole) - { - pTadpole->DisappearAndDie(); - player->KilledMonsterCredit(NPC_WINTERFIN_TADPOLE); - //FIX: Summon minion tadpole - } - } - return true; - } - }; - - GameObjectAI* GetAI(GameObject* go) const override - { - return new go_tadpole_cageAI(go); - } -}; - -/*###### ## go_amberpine_outhouse ######*/ @@ -1528,10 +1484,18 @@ public: enum MidsummerPoleRibbon { - SPELL_POLE_DANCE = 29726, - SPELL_BLUE_FIRE_RING = 46842, - NPC_POLE_RIBBON_BUNNY = 17066, - ACTION_COSMETIC_FIRES = 0 + SPELL_TEST_RIBBON_POLE_1 = 29705, + SPELL_TEST_RIBBON_POLE_2 = 29726, + SPELL_TEST_RIBBON_POLE_3 = 29727, + NPC_POLE_RIBBON_BUNNY = 17066, + ACTION_COSMETIC_FIRES = 0 +}; + +uint32 const RibbonPoleSpells[3] = +{ + SPELL_TEST_RIBBON_POLE_1, + SPELL_TEST_RIBBON_POLE_2, + SPELL_TEST_RIBBON_POLE_3 }; class go_midsummer_ribbon_pole : public GameObjectScript @@ -1548,7 +1512,7 @@ public: if (Creature* creature = me->FindNearestCreature(NPC_POLE_RIBBON_BUNNY, 10.0f)) { creature->GetAI()->DoAction(ACTION_COSMETIC_FIRES); - player->CastSpell(creature, SPELL_POLE_DANCE, true); + player->CastSpell(player, RibbonPoleSpells[urand(0, 2)], true); } return true; } @@ -1679,43 +1643,99 @@ public: case EVENT_BM_START_MUSIC: if (!IsHolidayActive(HOLIDAY_BREWFEST)) // Check if Brewfest is active break; - // Check if gob is correct area, play music, set time of music - if (me->GetAreaId() == SILVERMOON || me->GetAreaId() == UNDERCITY || me->GetAreaId() == ORGRIMMAR_1 || me->GetAreaId() == ORGRIMMAR_2 || me->GetAreaId() == THUNDERBLUFF || me->GetAreaId() == SHATTRATH) - { - if (rnd == 0) - { - me->PlayDirectMusic(EVENT_BREWFESTGOBLIN01); - musicTime = EVENT_BREWFESTGOBLIN01_TIME; - } - else if (rnd == 1) - { - me->PlayDirectMusic(EVENT_BREWFESTGOBLIN02); - musicTime = EVENT_BREWFESTGOBLIN02_TIME; - } - else - { - me->PlayDirectMusic(EVENT_BREWFESTGOBLIN03); - musicTime = EVENT_BREWFESTGOBLIN03_TIME; - } - } - if (me->GetAreaId() == IRONFORGE_1 || me->GetAreaId() == IRONFORGE_2 || me->GetAreaId() == STORMWIND || me->GetAreaId() == EXODAR || me->GetAreaId() == DARNASSUS || me->GetAreaId() == SHATTRATH) + + switch (me->GetAreaId()) { - if (rnd == 0) - { - me->PlayDirectMusic(EVENT_BREWFESTDWARF01); - musicTime = EVENT_BREWFESTDWARF01_TIME; - } - else if (rnd == 1) - { - me->PlayDirectMusic(EVENT_BREWFESTDWARF02); - musicTime = EVENT_BREWFESTDWARF02_TIME; - } - else - { - me->PlayDirectMusic(EVENT_BREWFESTDWARF03); - musicTime = EVENT_BREWFESTDWARF03_TIME; - } + // Horde + case SILVERMOON: + case UNDERCITY: + case ORGRIMMAR_1: + case ORGRIMMAR_2: + case THUNDERBLUFF: + if (rnd == 0) + { + me->PlayDirectMusic(EVENT_BREWFESTGOBLIN01); + musicTime = EVENT_BREWFESTGOBLIN01_TIME; + } + else if (rnd == 1) + { + me->PlayDirectMusic(EVENT_BREWFESTGOBLIN02); + musicTime = EVENT_BREWFESTGOBLIN02_TIME; + } + else + { + me->PlayDirectMusic(EVENT_BREWFESTGOBLIN03); + musicTime = EVENT_BREWFESTGOBLIN03_TIME; + } + break; + // Alliance + case IRONFORGE_1: + case IRONFORGE_2: + case STORMWIND: + case EXODAR: + case DARNASSUS: + if (rnd == 0) + { + me->PlayDirectMusic(EVENT_BREWFESTDWARF01); + musicTime = EVENT_BREWFESTDWARF01_TIME; + } + else if (rnd == 1) + { + me->PlayDirectMusic(EVENT_BREWFESTDWARF02); + musicTime = EVENT_BREWFESTDWARF02_TIME; + } + else + { + me->PlayDirectMusic(EVENT_BREWFESTDWARF03); + musicTime = EVENT_BREWFESTDWARF03_TIME; + } + break; + // Neurtal + case SHATTRATH: + std::vector<Player*> playersNearby; + me->GetPlayerListInGrid(playersNearby, me->GetVisibilityRange()); + for (Player* player : playersNearby) + { + if (player->GetTeamId() == TEAM_HORDE) + { + if (rnd == 0) + { + me->PlayDirectMusic(EVENT_BREWFESTGOBLIN01); + musicTime = EVENT_BREWFESTGOBLIN01_TIME; + } + else if (rnd == 1) + { + me->PlayDirectMusic(EVENT_BREWFESTGOBLIN02); + musicTime = EVENT_BREWFESTGOBLIN02_TIME; + } + else + { + me->PlayDirectMusic(EVENT_BREWFESTGOBLIN03); + musicTime = EVENT_BREWFESTGOBLIN03_TIME; + } + } + else + { + if (rnd == 0) + { + me->PlayDirectMusic(EVENT_BREWFESTDWARF01); + musicTime = EVENT_BREWFESTDWARF01_TIME; + } + else if (rnd == 1) + { + me->PlayDirectMusic(EVENT_BREWFESTDWARF02); + musicTime = EVENT_BREWFESTDWARF02_TIME; + } + else + { + me->PlayDirectMusic(EVENT_BREWFESTDWARF03); + musicTime = EVENT_BREWFESTDWARF03_TIME; + } + } + } + break; } + _events.ScheduleEvent(EVENT_BM_START_MUSIC, 5000); // Every 5 second's SMSG_PLAY_MUSIC packet (PlayDirectMusic) is pushed to the client break; default: @@ -2045,7 +2065,6 @@ void AddSC_go_scripts() new go_table_theka(); new go_inconspicuous_landmark(); new go_soulwell(); - new go_tadpole_cage(); new go_dragonflayer_cage(); new go_amberpine_outhouse(); new go_hive_pod(); diff --git a/src/server/scripts/World/item_scripts.cpp b/src/server/scripts/World/item_scripts.cpp index e5b3735a22e..ac231434c83 100644 --- a/src/server/scripts/World/item_scripts.cpp +++ b/src/server/scripts/World/item_scripts.cpp @@ -363,33 +363,6 @@ public: } }; -enum TheEmissary -{ - QUEST_THE_EMISSARY = 11626, - NPC_LEVIROTH = 26452 -}; - -class item_trident_of_nazjan : public ItemScript -{ -public: - item_trident_of_nazjan() : ItemScript("item_Trident_of_Nazjan") { } - - bool OnUse(Player* player, Item* item, SpellCastTargets const& /*targets*/) override - { - if (player->GetQuestStatus(QUEST_THE_EMISSARY) == QUEST_STATUS_INCOMPLETE) - { - if (Creature* pLeviroth = player->FindNearestCreature(NPC_LEVIROTH, 10.0f)) // spell range - { - pLeviroth->AI()->AttackStart(player); - return false; - } else - player->SendEquipError(EQUIP_ERR_OUT_OF_RANGE, item, nullptr); - } else - player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, item, nullptr); - return true; - } -}; - enum CapturedFrog { QUEST_THE_PERFECT_SPIES = 25444, @@ -451,7 +424,6 @@ void AddSC_item_scripts() new item_pile_fake_furs(); new item_petrov_cluster_bombs(); new item_dehta_trap_smasher(); - new item_trident_of_nazjan(); new item_captured_frog(); new item_generic_limit_chance_above_60(); } diff --git a/src/server/scripts/World/npc_innkeeper.cpp b/src/server/scripts/World/npc_innkeeper.cpp index 7db733c7f78..dd59d2c0aa8 100644 --- a/src/server/scripts/World/npc_innkeeper.cpp +++ b/src/server/scripts/World/npc_innkeeper.cpp @@ -42,7 +42,14 @@ enum Spells #define LOCALE_TRICK_OR_TREAT_6 "¡Truco o trato!" #define LOCALE_INNKEEPER_0 "Make this inn my home." +#define LOCALE_INNKEEPER_2 "Faites de cette auberge votre foyer." #define LOCALE_INNKEEPER_3 "Ich möchte dieses Gasthaus zu meinem Heimatort machen." +#define LOCALE_INNKEEPER_6 "Fija tu hogar en esta taberna." + +#define LOCALE_VENDOR_0 "I want to browse your goods." +#define LOCALE_VENDOR_2 "Je voudrais regarder vos articles." +#define LOCALE_VENDOR_3 "Ich sehe mich nur mal um." +#define LOCALE_VENDOR_6 "Quiero ver tus mercancías." class npc_innkeeper : public CreatureScript { @@ -72,14 +79,26 @@ public: player->PrepareQuestMenu(me->GetGUID()); if (me->IsVendor()) - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + { + char const* localizedEntry; + switch (player->GetSession()->GetSessionDbcLocale()) + { + case LOCALE_frFR: localizedEntry = LOCALE_VENDOR_2; break; + case LOCALE_deDE: localizedEntry = LOCALE_VENDOR_3; break; + case LOCALE_esES: localizedEntry = LOCALE_VENDOR_6; break; + case LOCALE_enUS: default: localizedEntry = LOCALE_VENDOR_0; + } + AddGossipItemFor(player, GOSSIP_ICON_VENDOR, localizedEntry, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + } if (me->IsInnkeeper()) { char const* localizedEntry; switch (player->GetSession()->GetSessionDbcLocale()) { + case LOCALE_frFR: localizedEntry = LOCALE_INNKEEPER_2; break; case LOCALE_deDE: localizedEntry = LOCALE_INNKEEPER_3; break; + case LOCALE_esES: localizedEntry = LOCALE_INNKEEPER_6; break; case LOCALE_enUS: default: localizedEntry = LOCALE_INNKEEPER_0; } AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, localizedEntry, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INN); diff --git a/src/server/scripts/World/npc_professions.cpp b/src/server/scripts/World/npc_professions.cpp index 9f1df957784..b68722b7ff8 100644 --- a/src/server/scripts/World/npc_professions.cpp +++ b/src/server/scripts/World/npc_professions.cpp @@ -64,15 +64,6 @@ A few notes for future developement: # gossip item and box texts ###*/ -#define GOSSIP_LEARN_POTION "Please teach me how to become a Master of Potions, Lauranna" -#define GOSSIP_UNLEARN_POTION "I wish to unlearn Potion Mastery" -#define GOSSIP_LEARN_TRANSMUTE "Please teach me how to become a Master of Transmutations, Zarevhi" -#define GOSSIP_UNLEARN_TRANSMUTE "I wish to unlearn Transmutation Mastery" -#define GOSSIP_LEARN_ELIXIR "Please teach me how to become a Master of Elixirs, Lorokeem" -#define GOSSIP_UNLEARN_ELIXIR "I wish to unlearn Elixir Mastery" - -#define BOX_UNLEARN_ALCHEMY_SPEC "Do you really want to unlearn your alchemy specialty and lose all associated recipes? \n Cost: " - #define GOSSIP_WEAPON_LEARN "Please teach me how to become a Weaponsmith" #define GOSSIP_WEAPON_UNLEARN "I wish to unlearn the art of Weaponsmithing" #define GOSSIP_ARMOR_LEARN "Please teach me how to become a Armorsmith" @@ -168,19 +159,7 @@ enum ProfessionSpells S_UNLEARN_SPELLFIRE = 41299, S_UNLEARN_MOONCLOTH = 41558, - S_UNLEARN_SHADOWEAVE = 41559, - - S_TRANSMUTE = 28672, - S_ELIXIR = 28677, - S_POTION = 28675, - - S_LEARN_TRANSMUTE = 28674, - S_LEARN_ELIXIR = 28678, - S_LEARN_POTION = 28676, - - S_UNLEARN_TRANSMUTE = 41565, - S_UNLEARN_ELIXIR = 41564, - S_UNLEARN_POTION = 41563, + S_UNLEARN_SHADOWEAVE = 41559 }; /*### @@ -188,11 +167,6 @@ enum ProfessionSpells ###*/ enum SpecializationTrainers { - /* Alchemy */ - N_TRAINER_TRANSMUTE = 22427, // Zarevhi - N_TRAINER_ELIXIR = 19052, // Lorokeem - N_TRAINER_POTION = 17909, // Lauranna Thar'well - /* Blacksmithing */ N_TRAINER_SMITHOMNI1 = 11145, // Myolor Sunderfury N_TRAINER_SMITHOMNI2 = 11176, // Krathok Moltenfist @@ -219,26 +193,15 @@ enum SpecializationTrainers }; /*### -# specialization quests -###*/ -enum SpecializationQuests -{ - /* Alchemy */ - Q_MASTER_TRANSMUTE = 10899, - Q_MASTER_ELIXIR = 10902, - Q_MASTER_POTION = 10897, -}; - -/*### # formulas to calculate unlearning cost ###*/ -int32 DoLearnCost(Player* /*player*/) //tailor, alchemy +int32 DoLearnCost(Player* /*player*/) //tailor { return 200000; } -int32 DoHighUnlearnCost(Player* /*player*/) //tailor, alchemy +int32 DoHighUnlearnCost(Player* /*player*/) //tailor { return 1500000; } @@ -441,183 +404,6 @@ void ProcessUnlearnAction(Player* player, Creature* creature, uint32 spellId, ui } /*### -# start menues alchemy -###*/ - -class npc_prof_alchemy : public CreatureScript -{ -public: - npc_prof_alchemy() : CreatureScript("npc_prof_alchemy") { } - - struct npc_prof_alchemyAI : public ScriptedAI - { - npc_prof_alchemyAI(Creature* creature) : ScriptedAI(creature) { } - - inline bool HasAlchemySpell(Player* player) - { - return (player->HasSpell(S_TRANSMUTE) || player->HasSpell(S_ELIXIR) || player->HasSpell(S_POTION)); - } - - bool GossipHello(Player* player) override - { - if (me->IsQuestGiver()) - player->PrepareQuestMenu(me->GetGUID()); - - if (me->IsVendor()) - AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - if (me->IsTrainer()) - AddGossipItemFor(player, GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN); - - if (player->HasSkill(SKILL_ALCHEMY) && player->GetBaseSkillValue(SKILL_ALCHEMY) >= 350 && player->getLevel() > 67) - { - if (player->GetQuestRewardStatus(Q_MASTER_TRANSMUTE) || player->GetQuestRewardStatus(Q_MASTER_ELIXIR) || player->GetQuestRewardStatus(Q_MASTER_POTION)) - { - switch (me->GetEntry()) - { - case N_TRAINER_TRANSMUTE: //Zarevhi - if (!HasAlchemySpell(player)) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LEARN_TRANSMUTE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 1); - if (player->HasSpell(S_TRANSMUTE)) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_TRANSMUTE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 4); - break; - case N_TRAINER_ELIXIR: //Lorokeem - if (!HasAlchemySpell(player)) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LEARN_ELIXIR, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 2); - if (player->HasSpell(S_ELIXIR)) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_ELIXIR, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 5); - break; - case N_TRAINER_POTION: //Lauranna Thar'well - if (!HasAlchemySpell(player)) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LEARN_POTION, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 3); - if (player->HasSpell(S_POTION)) - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_UNLEARN_POTION, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 6); - break; - } - } - } - - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - return true; - } - - void SendActionMenu(Player* player, uint32 action) - { - switch (action) - { - case GOSSIP_ACTION_TRADE: - player->GetSession()->SendListInventory(me->GetGUID()); - break; - case GOSSIP_ACTION_TRAIN: - player->GetSession()->SendTrainerList(me->GetGUID()); - break; - //Learn Alchemy - case GOSSIP_ACTION_INFO_DEF + 1: - ProcessCastaction(player, me, S_TRANSMUTE, S_LEARN_TRANSMUTE, DoLearnCost(player)); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - ProcessCastaction(player, me, S_ELIXIR, S_LEARN_ELIXIR, DoLearnCost(player)); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - ProcessCastaction(player, me, S_POTION, S_LEARN_POTION, DoLearnCost(player)); - break; - //Unlearn Alchemy - case GOSSIP_ACTION_INFO_DEF + 4: - ProcessCastaction(player, me, 0, S_UNLEARN_TRANSMUTE, DoHighUnlearnCost(player)); - break; - case GOSSIP_ACTION_INFO_DEF + 5: - ProcessCastaction(player, me, 0, S_UNLEARN_ELIXIR, DoHighUnlearnCost(player)); - break; - case GOSSIP_ACTION_INFO_DEF + 6: - ProcessCastaction(player, me, 0, S_UNLEARN_POTION, DoHighUnlearnCost(player)); - break; - } - } - - void SendConfirmLearn(Player* player, uint32 action) - { - if (action) - { - switch (me->GetEntry()) - { - case N_TRAINER_TRANSMUTE: - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LEARN_TRANSMUTE, GOSSIP_SENDER_CHECK, action); - //unknown textID () - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - break; - case N_TRAINER_ELIXIR: - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LEARN_ELIXIR, GOSSIP_SENDER_CHECK, action); - //unknown textID () - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - break; - case N_TRAINER_POTION: - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LEARN_POTION, GOSSIP_SENDER_CHECK, action); - //unknown textID () - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - break; - } - } - } - - void SendConfirmUnlearn(Player* player, uint32 action) - { - if (action) - { - switch (me->GetEntry()) - { - case N_TRAINER_TRANSMUTE: - AddGossipItemFor(player, 0, GOSSIP_UNLEARN_TRANSMUTE, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ALCHEMY_SPEC, DoHighUnlearnCost(player), false); - //unknown textID () - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - break; - case N_TRAINER_ELIXIR: - AddGossipItemFor(player, 0, GOSSIP_UNLEARN_ELIXIR, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ALCHEMY_SPEC, DoHighUnlearnCost(player), false); - //unknown textID () - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - break; - case N_TRAINER_POTION: - AddGossipItemFor(player, 0, GOSSIP_UNLEARN_POTION, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ALCHEMY_SPEC, DoHighUnlearnCost(player), false); - //unknown textID () - SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); - break; - } - } - } - - bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override - { - uint32 const sender = player->PlayerTalkClass->GetGossipOptionSender(gossipListId); - uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); - ClearGossipMenuFor(player); - switch (sender) - { - case GOSSIP_SENDER_MAIN: - SendActionMenu(player, action); - break; - - case GOSSIP_SENDER_LEARN: - SendConfirmLearn(player, action); - break; - - case GOSSIP_SENDER_UNLEARN: - SendConfirmUnlearn(player, action); - break; - - case GOSSIP_SENDER_CHECK: - SendActionMenu(player, action); - break; - } - return true; - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_prof_alchemyAI(creature); - } -}; - -/*### # start menues blacksmith ###*/ @@ -1365,7 +1151,6 @@ public: void AddSC_npc_professions() { - new npc_prof_alchemy(); new npc_prof_blacksmith(); new npc_engineering_tele_trinket(); new go_soothsaying_for_dummies(); diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index 03631c5b808..0e19b5f7bb4 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -457,8 +457,7 @@ public: enum TorchTossingTarget { - NPC_TORCH_TOSSING_TARGET_BUNNY = 25535, - SPELL_TARGET_INDICATOR = 45723 + SPELL_TORCH_TARGET_PICKER = 45907 }; class npc_torch_tossing_target_bunny_controller : public CreatureScript @@ -468,42 +467,28 @@ public: struct npc_torch_tossing_target_bunny_controllerAI : public ScriptedAI { - npc_torch_tossing_target_bunny_controllerAI(Creature* creature) : ScriptedAI(creature) - { - _targetTimer = 3000; - } + npc_torch_tossing_target_bunny_controllerAI(Creature* creature) : ScriptedAI(creature) { } - ObjectGuid DoSearchForTargets(ObjectGuid lastTargetGUID) + void Reset() override { - std::list<Creature*> targets; - me->GetCreatureListWithEntryInGrid(targets, NPC_TORCH_TOSSING_TARGET_BUNNY, 60.0f); - targets.remove_if([lastTargetGUID](Creature* creature) { return creature->GetGUID() == lastTargetGUID; }); - - if (!targets.empty()) + _scheduler.Schedule(Seconds(2), [this](TaskContext context) { - _lastTargetGUID = Trinity::Containers::SelectRandomContainerElement(targets)->GetGUID(); - - return _lastTargetGUID; - } - return ObjectGuid::Empty; + me->CastCustomSpell(SPELL_TORCH_TARGET_PICKER, SPELLVALUE_MAX_TARGETS, 1); + _scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) + { + me->CastCustomSpell(SPELL_TORCH_TARGET_PICKER, SPELLVALUE_MAX_TARGETS, 1); + }); + context.Repeat(Seconds(5)); + }); } void UpdateAI(uint32 diff) override { - if (_targetTimer < diff) - { - if (Unit* target = ObjectAccessor::GetUnit(*me, DoSearchForTargets(_lastTargetGUID))) - target->CastSpell(target, SPELL_TARGET_INDICATOR, true); - - _targetTimer = 3000; - } - else - _targetTimer -= diff; + _scheduler.Update(diff); } private: - uint32 _targetTimer; - ObjectGuid _lastTargetGUID; + TaskScheduler _scheduler; }; CreatureAI* GetAI(Creature* creature) const override @@ -1040,9 +1025,9 @@ class npc_garments_of_quests : public CreatureScript public: npc_garments_of_quests() : CreatureScript("npc_garments_of_quests") { } - struct npc_garments_of_questsAI : public npc_escortAI + struct npc_garments_of_questsAI : public EscortAI { - npc_garments_of_questsAI(Creature* creature) : npc_escortAI(creature) + npc_garments_of_questsAI(Creature* creature) : EscortAI(creature) { switch (me->GetEntry()) { @@ -1130,11 +1115,6 @@ public: } } - void WaypointReached(uint32 /*waypointId*/) override - { - - } - void UpdateAI(uint32 diff) override { if (CanRun && !me->IsInCombat()) @@ -1165,7 +1145,7 @@ public: RunAwayTimer -= diff; } - npc_escortAI::UpdateAI(diff); + EscortAI::UpdateAI(diff); } }; @@ -1603,7 +1583,7 @@ class npc_tournament_mount : public CreatureScript if (apply) { _pennantSpellId = GetPennantSpellId(player); - player->CastSpell((Unit*)nullptr, _pennantSpellId, true); + player->CastSpell(nullptr, _pennantSpellId, true); } else player->RemoveAurasDueToSpell(_pennantSpellId); diff --git a/src/server/shared/DataStores/DBCStore.cpp b/src/server/shared/DataStores/DBCStore.cpp index 30cae057f90..b81b82e87b9 100644 --- a/src/server/shared/DataStores/DBCStore.cpp +++ b/src/server/shared/DataStores/DBCStore.cpp @@ -18,7 +18,7 @@ #include "DBCStore.h" #include "DBCDatabaseLoader.h" -DBCStorageBase::DBCStorageBase(char const* fmt) : _fieldCount(0), _fileFormat(fmt), _dataTable(nullptr), _indexTableSize(0) +DBCStorageBase::DBCStorageBase(char const* fmt) : _fieldCount(0), _fileFormat(fmt), _dataTable(nullptr), _dataTableEx(nullptr), _indexTableSize(0) { } diff --git a/src/server/shared/Packets/ByteBuffer.cpp b/src/server/shared/Packets/ByteBuffer.cpp index a4ab6e1daf7..aeab874f635 100644 --- a/src/server/shared/Packets/ByteBuffer.cpp +++ b/src/server/shared/Packets/ByteBuffer.cpp @@ -169,7 +169,7 @@ void ByteBuffer::hexlike() const for (uint32 i = 0; i < size(); ++i) { char buf[3]; - snprintf(buf, 3, "%2X ", read<uint8>(i)); + snprintf(buf, 3, "%2X", read<uint8>(i)); if ((i == (j * 8)) && ((i != (k * 16)))) { o << "| "; diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index ba8a10c759d..78d18651724 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -16,6 +16,7 @@ # WARDEN SETTINGS # PLAYER INTERACTION # CREATURE SETTINGS +# SPAWN/RESPAWN SETTINGS # CHAT SETTINGS # GAME MASTER SETTINGS # VISIBILITY AND DISTANCES @@ -1104,6 +1105,34 @@ MaxGroupXPDistance = 74 MaxRecruitAFriendBonusDistance = 100 # +# MinQuestScaledXPRatio +# Description: Min ratio of experience that a quest can grant when player level scaling is factored. +# Example: 50 (No less than 50% experience granted from a lower leveled quests completion) +# 100 (Quests always grant full experience upon completion) +# Default: 0 - (Quests too low may grant no experience) + +MinQuestScaledXPRatio = 0 + +# +# MinCreatureScaledXPRatio +# Description: Min ratio of experience that a creature kill can grant when player level scaling is factored. This +# will also allow spell procs to trigger, such as Drain Soul, if > 0 and exp is grantable. +# Example: 50 (No less than 50% experience granted from a lower leveled creature kill) +# 100 (Creature kills always grant full experience upon kill) +# Default: 0 - (Creatures too low may grant no experience) + +MinCreatureScaledXPRatio = 0 + +# +# MinDiscoveredScaledXPRatio +# Description: Min ratio of experience that an area discovery event will grant when player level scaling is factored. +# Example: 50 (No less than 50% experience granted from discovering a new section of map) +# 100 (Map exploration always grant full experience upon discovery) +# Default: 0 - (No experience granted when discovered area is too low level) + +MinDiscoveredScaledXPRatio = 0 + +# # MailDeliveryDelay # Description: Time (in seconds) mail delivery is delayed when sending items. # Default: 3600 - (1 hour) @@ -1111,6 +1140,14 @@ MaxRecruitAFriendBonusDistance = 100 MailDeliveryDelay = 3600 # +# CleanOldMailTime +# Description: The hour at which old mails will be returned or deleted by the server. +# This can be any integer number from 0 to 23. 20 will be 8pm server time. +# Default: 4 - 4am + +CleanOldMailTime = 4 + +# # SkillChance.Prospecting # Description: Allow skill increase from prospecting. # Default: 0 - (Disabled) @@ -1710,6 +1747,130 @@ ListenRange.Yell = 300 Creature.MovingStopTimeForPlayer = 180000 +# MonsterSight +# Description: The maximum distance in yards that a "monster" creature can see +# regardless of level difference (through CreatureAI::IsVisible). +# Increases CONFIG_SIGHT_MONSTER to 50 yards. Used to be 20 yards. +# Default: 50.000000 + +MonsterSight = 50.000000 + +# +################################################################################################### + +################################################################################################### +# SPAWN/RESPAWN SETTINGS +# +# Respawn.MinCheckIntervalMS +# Description: Minimum time that needs to pass between respawn checks for any given map. +# Default: 5000 - 5 seconds + +Respawn.MinCheckIntervalMS = 5000 + +# +# Respawn.GuidWarnLevel +# Description: The point at which the highest guid for creatures or gameobjects in any map must reach +# before the warning logic is enabled. A restart will then be queued at the next quiet time +# The maximum guid per map is 16,777,216. So, it must be less than this value. +# Default: 12000000 - 12 million + +Respawn.GuidWarnLevel = 12000000 + +# +# Respawn.WarningMessage +# Description: This message will be periodically shown (Frequency specified by Respawn.WarningFrequency) to +# all users of the server, once the Respawn.GuidWarnLevel has been passed, and a restart scheduled. +# It's used to warn users that there will be an out of schedule server restart soon. +# Default: "There will be an unscheduled server restart at 03:00 server time. The server will be available again shortly after." + +Respawn.WarningMessage = "There will be an unscheduled server restart at 03:00. The server will be available again shortly after." + +# +# Respawn.WarningFrequency +# Description: The frequency (in seconds) that the warning message will be sent to users after a quiet time restart is triggered. +# The message will repeat each time this many seconds passed until the server is restarted. +# If set to 0, no warnings will be sent. +# Default: 1800 - (30 minutes) + +Respawn.WarningFrequency = 1800 + +# +# Respawn.GuidAlertLevel +# Description: The point at which the highest guid for creatures or gameobjects in any map must reach +# before the alert logic is enabled. A restart will then be triggered for 30 mins from that +# point. The maximum guid per map is 16,777,216. So, it must be less than this value. +# Default: 16000000 - 16 million + +Respawn.GuidAlertLevel = 16000000 + +# +# Respawn.AlertRestartReason +# Description: The shutdown reason given when the alert level is reached. The server will use a fixed time of +# 5 minutes and the reason for shutdown will be this message +# Default: "Urgent Maintenance" + +Respawn.AlertRestartReason = "Urgent Maintenance" + +# +# Respawn.RestartQuietTime +# Description: The hour at which the server will be restarted after the Respawn.GuidWarnLevel +# threshold has been reached. This can be between 0 and 23. 20 will be 8pm server time +# Default: 3 - 3am + +Respawn.RestartQuietTime = 3 + +# +# Respawn.DynamicMode +# Description: Select which mode (if any) should be used to adjust respawn of creatures. +# This will only affect creatures that have dynamic spawn rate scaling enabled in +# the spawn group table (by default, gathering nodes and quest targets with respawn time <30min +# 1 - Use number of players in zone +# Default: 0 - No dynamic respawn function + +Respawn.DynamicMode = 0 + +# +# Respawn.DynamicEscortNPC +# Description: This switch controls the dynamic respawn system for escort NPCs not in instancable maps (base maps only). +# This will cause the respawn timer to begin when an escort event begins, and potentially +# allow multiple instances of the NPC to be alive at the same time (when combined with Respawn.DynamicMode > 0) +# 1 - Enabled +# Default: 0 - Disabled + +Respawn.DynamicEscortNPC = 0 + +# +# Respawn.DynamicRateCreature +# Description: The rate at which the respawn time is adjusted for high player counts in a zone (for creatures). +# Up to this number of players, the respawn rate is unchanged. +# At double this number in players, you get twice as many respawns, at three times this number, three times the respawns, and so forth. +# Default: 10 + +Respawn.DynamicRateCreature = 10 + +# +# Respawn.DynamicMinimumCreature +# Description: The minimum respawn time for a creature under dynamic scaling. +# Default: 10 - (10 seconds) + +Respawn.DynamicMinimumCreature = 10 + +# +# Respawn.DynamicRateGameObject +# Description: The rate at which the respawn time is adjusted for high player counts in a zone (for gameobjects). +# Up to this number of players, the respawn rate is unchanged. +# At double this number in players, you get twice as many respawns, at three times this number, three times the respawns, and so forth. +# Default: 10 + +Respawn.DynamicRateGameObject = 10 + +# +# Respawn.DynamicMinimumGameObject +# Description: The minimum respawn time for a gameobject under dynamic scaling. +# Default: 10 - (10 seconds) + +Respawn.DynamicMinimumGameObject = 10 + # ################################################################################################### |