Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-11-16 13:15:00 +08:00
768 changed files with 5628 additions and 5394 deletions
+1 -9
View File
@@ -20,15 +20,7 @@
#include <boost/asio/deadline_timer.hpp>
#if BOOST_VERSION >= 107000
#define BasicDeadlineTimerThirdTemplateArg , boost::asio::io_context::executor_type
#elif BOOST_VERSION >= 106600
#define BasicDeadlineTimerThirdTemplateArg
#else
#define BasicDeadlineTimerThirdTemplateArg , boost::asio::deadline_timer_service<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime>>
#endif
#define DeadlineTimerBase boost::asio::basic_deadline_timer<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime> BasicDeadlineTimerThirdTemplateArg>
#define DeadlineTimerBase boost::asio::basic_deadline_timer<boost::posix_time::ptime, boost::asio::time_traits<boost::posix_time::ptime>, boost::asio::io_context::executor_type>
namespace Acore::Asio
{
-16
View File
@@ -20,16 +20,10 @@
#include <boost/version.hpp>
#if BOOST_VERSION >= 106600
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#define IoContextBaseNamespace boost::asio
#define IoContextBase io_context
#else
#include <boost/asio/io_service.hpp>
#define IoContextBaseNamespace boost::asio
#define IoContextBase io_service
#endif
namespace Acore::Asio
{
@@ -45,9 +39,7 @@ namespace Acore::Asio
std::size_t run() { return _impl.run(); }
void stop() { _impl.stop(); }
#if BOOST_VERSION >= 106600
boost::asio::io_context::executor_type get_executor() noexcept { return _impl.get_executor(); }
#endif
private:
IoContextBaseNamespace::IoContextBase _impl;
@@ -56,21 +48,13 @@ namespace Acore::Asio
template<typename T>
inline decltype(auto) post(IoContextBaseNamespace::IoContextBase& ioContext, T&& t)
{
#if BOOST_VERSION >= 106600
return boost::asio::post(ioContext, std::forward<T>(t));
#else
return ioContext.post(std::forward<T>(t));
#endif
}
template<typename T>
inline decltype(auto) get_io_context(T&& ioObject)
{
#if BOOST_VERSION >= 106600
return ioObject.get_executor().context();
#else
return ioObject.get_io_service();
#endif
}
}
-12
View File
@@ -23,21 +23,9 @@
namespace Acore::Net
{
#if BOOST_VERSION >= 106600
using boost::asio::ip::make_address;
using boost::asio::ip::make_address_v4;
inline uint32 address_to_uint(boost::asio::ip::address_v4 const& address) { return address.to_uint(); }
#else
inline boost::asio::ip::address make_address(char const* str) { return boost::asio::ip::address::from_string(str); }
inline boost::asio::ip::address make_address(char const* str, boost::system::error_code& ec) { return boost::asio::ip::address::from_string(str, ec); }
inline boost::asio::ip::address make_address(std::string const& str) { return boost::asio::ip::address::from_string(str); }
inline boost::asio::ip::address make_address(std::string const& str, boost::system::error_code& ec) { return boost::asio::ip::address::from_string(str, ec); }
inline boost::asio::ip::address_v4 make_address_v4(char const* str) { return boost::asio::ip::address_v4::from_string(str); }
inline boost::asio::ip::address_v4 make_address_v4(char const* str, boost::system::error_code& ec) { return boost::asio::ip::address_v4::from_string(str, ec); }
inline boost::asio::ip::address_v4 make_address_v4(std::string const& str) { return boost::asio::ip::address_v4::from_string(str); }
inline boost::asio::ip::address_v4 make_address_v4(std::string const& str, boost::system::error_code& ec) { return boost::asio::ip::address_v4::from_string(str, ec); }
inline uint32 address_to_uint(boost::asio::ip::address_v4 const& address) { return address.to_ulong(); }
#endif
}
#endif // IpAddress_h__
-15
View File
@@ -20,24 +20,16 @@
#include "Define.h"
#include "IpAddress.h"
#include <boost/version.hpp>
#if BOOST_VERSION >= 106600
#include <boost/asio/ip/network_v4.hpp>
#include <boost/asio/ip/network_v6.hpp>
#endif
namespace Acore::Net
{
inline bool IsInNetwork(boost::asio::ip::address_v4 const& networkAddress, boost::asio::ip::address_v4 const& mask, boost::asio::ip::address_v4 const& clientAddress)
{
#if BOOST_VERSION >= 106600
boost::asio::ip::network_v4 network = boost::asio::ip::make_network_v4(networkAddress, mask);
boost::asio::ip::address_v4_range hosts = network.hosts();
return hosts.find(clientAddress) != hosts.end();
#else
return (clientAddress.to_ulong() & mask.to_ulong()) == (networkAddress.to_ulong() & mask.to_ulong());
#endif
}
inline boost::asio::ip::address_v4 GetDefaultNetmaskV4(boost::asio::ip::address_v4 const& networkAddress)
@@ -59,16 +51,9 @@ namespace Acore::Net
inline bool IsInNetwork(boost::asio::ip::address_v6 const& networkAddress, uint16 prefixLength, boost::asio::ip::address_v6 const& clientAddress)
{
#if BOOST_VERSION >= 106600
boost::asio::ip::network_v6 network = boost::asio::ip::make_network_v6(networkAddress, prefixLength);
boost::asio::ip::address_v6_range hosts = network.hosts();
return hosts.find(clientAddress) != hosts.end();
#else
(void)networkAddress;
(void)prefixLength;
(void)clientAddress;
return false;
#endif
}
}
-11
View File
@@ -35,23 +35,12 @@ namespace Acore::Asio
Optional<boost::asio::ip::tcp::endpoint> Resolve(boost::asio::ip::tcp const& protocol, std::string const& host, std::string const& service)
{
boost::system::error_code ec;
#if BOOST_VERSION >= 106600
boost::asio::ip::resolver_base::flags flagsResolver = boost::asio::ip::resolver_base::all_matching;
boost::asio::ip::tcp::resolver::results_type results = _impl.resolve(protocol, host, service, flagsResolver, ec);
if (results.begin() == results.end() || ec)
return {};
return results.begin()->endpoint();
#else
boost::asio::ip::resolver_query_base::flags flagsQuery = boost::asio::ip::tcp::resolver::query::all_matching;
boost::asio::ip::tcp::resolver::query query(std::move(protocol), std::move(host), std::move(service), flagsQuery);
boost::asio::ip::tcp::resolver::iterator itr = _impl.resolve(query, ec);
boost::asio::ip::tcp::resolver::iterator end;
if (itr == end || ec)
return {};
return itr->endpoint();
#endif
}
private:
+1 -12
View File
@@ -19,11 +19,8 @@
#define Strand_h__
#include "IoContext.h"
#include <boost/asio/strand.hpp>
#if BOOST_VERSION >= 106600
#include <boost/asio/bind_executor.hpp>
#endif
#include <boost/asio/strand.hpp>
namespace Acore::Asio
{
@@ -36,15 +33,7 @@ namespace Acore::Asio
Strand(IoContext& ioContext) : IoContextBaseNamespace::IoContextBase::strand(ioContext) { }
};
#if BOOST_VERSION >= 106600
using boost::asio::bind_executor;
#else
template<typename T>
inline decltype(auto) bind_executor(Strand& strand, T&& t)
{
return strand.wrap(std::forward<T>(t));
}
#endif
}
#endif // Strand_h__
@@ -22,6 +22,7 @@
#include "Duration.h"
#include "Log.h"
#include "MotdMgr.h"
#include "QueryResult.h"
#include "SRP6.h"
#include "Util.h"
#include "World.h"
@@ -781,6 +781,7 @@ Logger.spells.scripts=2,Console Errors
#Logger.spells=4,Console Server
#Logger.sql.dev=4,Console Server
#Logger.sql.driver=4,Console Server
#Logger.sql.updates=4,Console Server
#Logger.vehicles=4,Console Server
#Logger.warden=4,Console Server
#Logger.weather=4,Console Server
@@ -194,6 +194,7 @@ ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature),
{
_isHeroic = me->GetMap()->IsHeroic();
_difficulty = Difficulty(me->GetMap()->GetSpawnMode());
_invincible = false;
}
void ScriptedAI::AttackStartNoMove(Unit* who)
@@ -222,6 +223,12 @@ void ScriptedAI::UpdateAI(uint32 /*diff*/)
DoMeleeAttackIfReady();
}
void ScriptedAI::DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/)
{
if (IsInvincible() && damage >= me->GetHealth())
damage = me->GetHealth() - 1;
}
void ScriptedAI::DoStartMovement(Unit* victim, float distance, float angle)
{
if (victim)
@@ -732,8 +739,10 @@ void BossAI::UpdateAI(uint32 diff)
DoMeleeAttackIfReady();
}
void BossAI::DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/)
void BossAI::DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask)
{
ScriptedAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask);
if (_nextHealthCheck._valid)
if (me->HealthBelowPctDamaged(_nextHealthCheck._healthPct, damage))
{
@@ -198,7 +198,7 @@ struct ScriptedAI : public CreatureAI
void AttackStartNoMove(Unit* target);
// Called at any Damage from any attacker (before damage apply)
void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override {}
void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override;
//Called at World update tick
void UpdateAI(uint32 diff) override;
@@ -438,9 +438,14 @@ struct ScriptedAI : public CreatureAI
Player* SelectTargetFromPlayerList(float maxdist, uint32 excludeAura = 0, bool mustBeInLOS = false) const;
// Allows dropping to 1 HP but prevents creature from dying.
void SetInvincibility(bool apply) { _invincible = apply; };
[[nodiscard]] bool IsInvincible() const { return _invincible; };
private:
Difficulty _difficulty;
bool _isHeroic;
bool _invincible;
std::unordered_set<uint32> _uniqueTimedEvents;
};
@@ -35,6 +35,7 @@
#include "SmartAI.h"
#include "SpellMgr.h"
#include "Vehicle.h"
#include "WorldState.h"
/// @todo: this import is not necessary for compilation and marked as unused by the IDE
// however, for some reasons removing it would cause a damn linking issue
@@ -696,6 +697,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (isWithinLOSInMap && isWithinMeleeRange && isRangedAttack && isTargetRooted && canCastSpell && !me->IsVehicle())
{
failedSpellCast = true; // Mark spellcast as failed so we can retry it later
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
continue;
float minDistance = std::max(meleeRange, spellMinRange) - distanceToTarget + NOMINAL_MELEE_RANGE;
CAST_AI(SmartAI, me->AI())->MoveAway(std::min(minDistance, spellMaxRange));
continue;
@@ -705,12 +710,20 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (distanceToTarget > spellMaxRange && isWithinLOSInMap)
{
failedSpellCast = true;
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
continue;
CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
continue;
}
else if (distanceToTarget < spellMinRange || !(isWithinLOSInMap || isSpellIgnoreLOS))
{
failedSpellCast = true;
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
continue;
CAST_AI(SmartAI, me->AI())->SetCombatMove(true);
continue;
}
@@ -3257,6 +3270,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
break;
}
case SMART_ACTION_WORLD_SCRIPT:
{
sWorldState->HandleExternalEvent(static_cast<WorldStateEvent>(e.action.worldStateScript.eventId), e.action.worldStateScript.param);
break;
}
default:
LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Unhandled Action type {}", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
break;
@@ -5061,7 +5079,7 @@ void SmartScript::GetScript()
if (CreatureTemplate const* cInfo = me->GetCreatureTemplate())
{
if (cInfo->HasFlagsExtra(CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI))
if (cInfo->HasFlagsExtra(CREATURE_FLAG_EXTRA_DONT_OVERRIDE_ENTRY_SAI))
{
e = sSmartScriptMgr->GetScript((int32)me->GetEntry(), mScriptType);
FillScript(e, me, nullptr);
@@ -18,9 +18,7 @@
#ifndef ACORE_SMARTSCRIPT_H
#define ACORE_SMARTSCRIPT_H
#include "Common.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "GridNotifiers.h"
#include "SmartScriptMgr.h"
#include "Spell.h"
@@ -777,6 +777,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_MOVEMENT_STOP: return NO_PARAMS;
case SMART_ACTION_MOVEMENT_PAUSE: return sizeof(SmartAction::move);
case SMART_ACTION_MOVEMENT_RESUME: return sizeof(SmartAction::move);
case SMART_ACTION_WORLD_SCRIPT: return sizeof(SmartAction::worldStateScript);
default:
LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an action with no unused params specified in SmartAIMgr::CheckUnusedActionParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
@@ -1960,6 +1961,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_MOVEMENT_STOP:
case SMART_ACTION_MOVEMENT_PAUSE:
case SMART_ACTION_MOVEMENT_RESUME:
case SMART_ACTION_WORLD_SCRIPT:
break;
default:
LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type({}), event_type({}), Entry {} SourceType {} Event {}, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
@@ -20,12 +20,11 @@
#include "Common.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "DBCStores.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Optional.h"
#include "Spell.h"
#include "SpellMgr.h"
#include "Unit.h"
#include <limits>
typedef uint32 SAIBool;
@@ -727,8 +726,9 @@ enum SMART_ACTION
SMART_ACTION_MOVEMENT_STOP = 234, //
SMART_ACTION_MOVEMENT_PAUSE = 235, // timer
SMART_ACTION_MOVEMENT_RESUME = 236, // timerOverride
SMART_ACTION_WORLD_SCRIPT = 237, // eventId, param
SMART_ACTION_AC_END = 237, // placeholder
SMART_ACTION_AC_END = 238, // placeholder
};
enum class SmartActionSummonCreatureFlags
@@ -1472,6 +1472,12 @@ struct SmartAction
{
uint32 timer;
} move;
struct
{
uint32 eventId;
uint32 param;
} worldStateScript;
//! Note for any new future actions
//! All parameters must have type uint32
@@ -20,7 +20,6 @@
#include "Battlefield.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "World.h"
class Group;
@@ -19,9 +19,10 @@
#define __BATTLEGROUNDQUEUE_H
#include "Battleground.h"
#include "Common.h"
#include "DBCEnums.h"
#include "EventProcessor.h"
#include "ObjectGuid.h"
#include "SharedDefines.h"
#include <array>
constexpr auto COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME = 10;
-1
View File
@@ -18,7 +18,6 @@
#ifndef _CHANNEL_H
#define _CHANNEL_H
#include "Common.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include <string>
@@ -506,6 +506,9 @@ bool Acore::Impl::ChatCommands::ChatCommandNode::IsInvokerVisible(ChatHandler co
if (!_invoker)
return false;
if (!sScriptMgr->OnBeforeIsInvokerVisible(_name, _permission, who))
return true;
if (who.IsConsole() && (_permission.AllowConsole == Acore::ChatCommands::Console::No))
return false;
+1
View File
@@ -23,6 +23,7 @@
#include "SharedDefines.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "World.h"
using namespace Acore::Hyperlinks;
@@ -28,6 +28,7 @@
#include "Spell.h"
#include "SpellAuras.h"
#include "SpellMgr.h"
#include "WorldState.h"
// Checks if object meets the condition
// Can have CONDITION_SOURCE_TYPE_NONE && !mReferenceId if called from a special event (ie: eventAI)
@@ -570,6 +571,11 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo)
condMeets = unit->IsCharmed();
break;
}
case CONDITION_WORLD_SCRIPT:
{
condMeets = sWorldState->IsConditionFulfilled(static_cast<WorldStateCondition>(ConditionValue1), static_cast<WorldStateConditionState>(ConditionValue2));
break;
}
default:
condMeets = false;
break;
@@ -770,6 +776,9 @@ uint32 Condition::GetSearcherTypeMaskForCondition()
case CONDITION_CHARMED:
mask |= GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER;
break;
case CONDITION_WORLD_SCRIPT:
mask |= GRID_MAP_TYPE_MASK_ALL;
break;
default:
ASSERT(false && "Condition::GetSearcherTypeMaskForCondition - missing condition handling!");
break;
+3 -1
View File
@@ -86,7 +86,9 @@ enum ConditionTypes
CONDITION_AC_START = 100,
CONDITION_QUEST_SATISFY_EXCLUSIVE = 101, // quest_id 0 0 true if satisfied exclusive group
CONDITION_HAS_AURA_TYPE = 102, // aura_type 0 0 true if has aura type
CONDITION_AC_END = 103 // placeholder
CONDITION_WORLD_SCRIPT = 103, // conditionId state 0 true if WorldState::IsConditionFulfilled returns true
CONDITION_AC_END = 104 // placeholder
};
/*! Documentation on implementing a new ConditionSourceType:
@@ -18,8 +18,6 @@
#ifndef _LFGQUEUE_H
#define _LFGQUEUE_H
#include <utility>
#include "LFG.h"
namespace lfg
@@ -22,10 +22,8 @@
#include "CharmInfo.h"
#include "Common.h"
#include "CreatureData.h"
#include "DatabaseEnv.h"
#include "LootMgr.h"
#include "Unit.h"
#include "World.h"
#include <list>
class SpellInfo;
@@ -69,7 +69,7 @@ enum CreatureFlagsExtra : uint32
CREATURE_FLAG_EXTRA_MODULE = 0x01000000,
CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE = 0x02000000, // Prevent creatures from calling for assistance on initial aggro
CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS = 0x04000000, // Prevents creature from responding to assistance calls
CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI = 0x08000000, // Load both ENTRY and GUID specific SAI
CREATURE_FLAG_EXTRA_DONT_OVERRIDE_ENTRY_SAI = 0x08000000, // Load both ENTRY and GUID specific SAI
CREATURE_FLAG_EXTRA_DUNGEON_BOSS = 0x10000000, // creature is a dungeon boss (SET DYNAMICALLY, DO NOT ADD IN DB)
CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING = 0x20000000, // creature ignore pathfinding
CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK = 0x40000000, // creature is immune to knockback effects
@@ -21,6 +21,7 @@
#include "Log.h"
#include "MoveSplineInit.h"
#include "ObjectMgr.h"
#include "QueryResult.h"
#include "WaypointMgr.h"
FormationMgr::~FormationMgr()
@@ -19,6 +19,7 @@
#define _FORMATIONS_H
#include "Define.h"
#include "ObjectGuid.h"
#include "Unit.h"
#include <map>
#include <unordered_map>
@@ -20,8 +20,9 @@
#include "Common.h"
#include "NPCHandler.h"
#include "Object.h"
#include "ObjectGuid.h"
#include "QuestDef.h"
#include <map>
class WorldSession;
class ObjectGuid;
@@ -58,7 +58,7 @@ AC_API_EXPORT EnumText EnumUtils<CreatureFlagsExtra>::ToString(CreatureFlagsExtr
case CREATURE_FLAG_EXTRA_MODULE: return { "CREATURE_FLAG_EXTRA_MODULE", "CREATURE_FLAG_EXTRA_MODULE", "" };
case CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE: return { "CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE", "CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE", "Prevent creatures from calling for assistance on initial aggro" };
case CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS: return { "CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS", "CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS", "Prevents creature from responding to assistance calls" };
case CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI: return { "CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI", "CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI", "Load both ENTRY and GUID specific SAI" };
case CREATURE_FLAG_EXTRA_DONT_OVERRIDE_ENTRY_SAI: return { "CREATURE_FLAG_EXTRA_DONT_OVERRIDE_ENTRY_SAI", "CREATURE_FLAG_EXTRA_DONT_OVERRIDE_ENTRY_SAI", "Load both ENTRY and GUID specific SAI" };
case CREATURE_FLAG_EXTRA_DUNGEON_BOSS: return { "CREATURE_FLAG_EXTRA_DUNGEON_BOSS", "CREATURE_FLAG_EXTRA_DUNGEON_BOSS", "creature is a dungeon boss (SET DYNAMICALLY, DO NOT ADD IN DB)" };
case CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING: return { "CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING", "CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING", "creature ignore pathfinding" };
case CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK: return { "CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK", "CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK", "creature is immune to knockback effects" };
@@ -102,7 +102,7 @@ AC_API_EXPORT CreatureFlagsExtra EnumUtils<CreatureFlagsExtra>::FromIndex(std::s
case 24: return CREATURE_FLAG_EXTRA_MODULE;
case 25: return CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE;
case 26: return CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS;
case 27: return CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI;
case 27: return CREATURE_FLAG_EXTRA_DONT_OVERRIDE_ENTRY_SAI;
case 28: return CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
case 29: return CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING;
case 30: return CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK;
@@ -143,7 +143,7 @@ AC_API_EXPORT std::size_t EnumUtils<CreatureFlagsExtra>::ToIndex(CreatureFlagsEx
case CREATURE_FLAG_EXTRA_MODULE: return 24;
case CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE: return 25;
case CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS: return 26;
case CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI: return 27;
case CREATURE_FLAG_EXTRA_DONT_OVERRIDE_ENTRY_SAI: return 27;
case CREATURE_FLAG_EXTRA_DUNGEON_BOSS: return 28;
case CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING: return 29;
case CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK: return 30;
@@ -19,7 +19,6 @@
#define AZEROTHCORE_GAMEOBJECT_H
#include "Common.h"
#include "DatabaseEnv.h"
#include "G3D/Quat.h"
#include "GameObjectData.h"
#include "LootMgr.h"
@@ -19,9 +19,9 @@
#define GAMEOBJECTDATA_H
#include "SharedDefines.h"
#include "WorldPacket.h"
#include <string>
#include <array>
#include <vector>
#include <string>
#define MAX_GAMEOBJECT_QUEST_ITEMS 6
#define MAX_GO_STATE 3
@@ -20,6 +20,7 @@
#include "DatabaseEnv.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "QueryResult.h"
#include "Util.h"
#include <cmath>
#include <functional>
@@ -19,6 +19,7 @@
#include "ByteBuffer.h"
#include "Geometry.h"
#include "GridDefines.h"
#include "Random.h"
#include <G3D/g3dmath.h>
#include <sstream>
@@ -19,7 +19,9 @@
#define CinematicMgr_h__
#include "Define.h"
#include "Object.h"
#include "Position.h"
#include "TemporarySummon.h"
#include <vector>
constexpr auto CINEMATIC_UPDATEDIFF = 500;
constexpr auto CINEMATIC_LOOKAHEAD = 2000;
+5 -1
View File
@@ -83,6 +83,7 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "WorldState.h"
#include <cmath>
/// @todo: this import is not necessary for compilation and marked as unused by the IDE
@@ -1722,6 +1723,7 @@ void Player::RemoveFromWorld()
m_session->DoLootRelease(lguid);
sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
sWorldState->HandlePlayerLeaveZone(this, static_cast<WorldStateZoneId>(m_zoneUpdateId));
}
// Remove items from world before self - player must be found in Item::RemoveFromObjectUpdate
@@ -6930,7 +6932,7 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt
float maxDamage = proto->Damage[i].DamageMax;
// If set dpsMod in ScalingStatValue use it for min (70% from average), max (130% from average) damage
if (ssv)
if (ssv && i == 0) // scaling stats only for first damage
{
int32 extraDPS = ssv->getDPSMod(ScalingStatValue);
if (extraDPS)
@@ -6945,6 +6947,8 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt
if (apply)
{
sScriptMgr->OnApplyWeaponDamage(this, slot, proto, minDamage, maxDamage, i);
if (minDamage > 0.f)
{
SetBaseWeaponDamage(WeaponAttackType(attType), MINDAMAGE, minDamage, i);
+1
View File
@@ -1423,6 +1423,7 @@ public:
void CompleteQuest(uint32 quest_id);
void IncompleteQuest(uint32 quest_id);
void RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce = true, bool isLFGReward = false);
void SetRewardedQuest(uint32 quest_id);
void FailQuest(uint32 quest_id);
bool SatisfyQuestSkill(Quest const* qInfo, bool msg) const;
bool SatisfyQuestLevel(Quest const* qInfo, bool msg) const;
@@ -819,8 +819,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
SetSeasonalQuestStatus(quest_id);
RemoveActiveQuest(quest_id, false);
m_RewardedQuests.insert(quest_id);
m_RewardedQuestsSave[quest_id] = true;
SetRewardedQuest(quest_id);
if (announce)
SendQuestReward(quest, XP);
@@ -877,6 +876,12 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
sScriptMgr->OnPlayerCompleteQuest(this, quest);
}
void Player::SetRewardedQuest(uint32 quest_id)
{
m_RewardedQuests.insert(quest_id);
m_RewardedQuestsSave[quest_id] = true;
}
void Player::FailQuest(uint32 questId)
{
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
@@ -38,6 +38,7 @@
#include "Vehicle.h"
#include "Weather.h"
#include "WeatherMgr.h"
#include "WorldState.h"
#include "WorldStatePackets.h"
/// @todo: this import is not necessary for compilation and marked as unused by the IDE
@@ -1216,6 +1217,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
{
sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone);
sWorldState->HandlePlayerLeaveZone(this, static_cast<WorldStateZoneId>(m_zoneUpdateId));
sWorldState->HandlePlayerEnterZone(this, static_cast<WorldStateZoneId>(newZone));
sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
sBattlefieldMgr->HandlePlayerEnterZone(this, newZone);
SendInitWorldStates(newZone,
+2 -1
View File
@@ -18,7 +18,8 @@
#ifndef _CHARMINFO_H
#define _CHARMINFO_H
#include "Object.h"
#include "Define.h"
#include "ObjectGuid.h"
#define MAX_SPELL_CHARM 4
#define MAX_SPELL_VEHICLE 6
+2
View File
@@ -180,6 +180,7 @@ enum eScriptFlags
// Playsound flags
SF_PLAYSOUND_TARGET_PLAYER = 0x1,
SF_PLAYSOUND_DISTANCE_SOUND = 0x2,
SF_PLAYSOUND_DISTANCE_RADIUS = 0x4,
// Orientation flags
SF_ORIENTATION_FACE_TARGET = 0x1,
@@ -307,6 +308,7 @@ struct ScriptInfo
{
uint32 SoundID; // datalong
uint32 Flags; // datalong2
int32 Radius; // dataint
} Playsound;
struct // SCRIPT_COMMAND_CREATE_ITEM (17)
-1
View File
@@ -24,7 +24,6 @@
#include "Grid.h"
#include "GridReference.h"
#include "Timer.h"
#include "Util.h"
template
<
-1
View File
@@ -22,7 +22,6 @@
#include "ObjectMgr.h"
#include "Optional.h"
#include "Player.h"
#include "WorldPacket.h"
#include <set>
#include <unordered_map>
+27 -1
View File
@@ -59,6 +59,9 @@ void InstanceScript::OnCreatureCreate(Creature* creature)
{
AddObject(creature);
AddMinion(creature);
if (creature->IsSummon())
SetSummoner(creature);
}
void InstanceScript::OnCreatureRemove(Creature* creature)
@@ -194,6 +197,15 @@ void InstanceScript::LoadObjectData(ObjectData const* data, ObjectInfoMap& objec
}
}
void InstanceScript::LoadSummonData(ObjectData const* data)
{
while (data->entry)
{
_summonInfo[data->entry] = data->type;
++data;
}
}
void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state)
{
switch (state)
@@ -348,6 +360,16 @@ void InstanceScript::RemoveMinion(Creature* minion)
AddMinion(minion, false);
}
void InstanceScript::SetSummoner(Creature* creature)
{
auto const& summonData = _summonInfo.find(creature->GetEntry());
if (summonData != _summonInfo.end())
if (Creature* summoner = GetCreature(summonData->second))
if (summoner->IsAIEnabled)
summoner->AI()->JustSummoned(creature);
}
bool InstanceScript::SetBossState(uint32 id, EncounterState state)
{
if (id < bosses.size())
@@ -393,7 +415,11 @@ void InstanceScript::StorePersistentData(uint32 index, uint32 data)
return;
}
persistentData[index] = data;
if (persistentData[index] != data)
{
persistentData[index] = data;
SaveToDB();
}
}
void InstanceScript::DoForAllMinions(uint32 id, std::function<void(Creature*)> exec)
@@ -288,6 +288,11 @@ protected:
void LoadDoorData(DoorData const* data);
void LoadMinionData(MinionData const* data);
void LoadObjectData(ObjectData const* creatureData, ObjectData const* gameObjectData);
// Allows setting another creature as summoner for a creature.
// This is used to handle summons that are not directly controlled by the summoner.
// Summoner creature must be loaded in the instance data (LoadObjectData).
void LoadSummonData(ObjectData const* data);
void SetSummoner(Creature* creature);
void AddObject(Creature* obj, bool add = true);
void RemoveObject(Creature* obj);
@@ -324,6 +329,7 @@ private:
MinionInfoMap minions;
ObjectInfoMap _creatureInfo;
ObjectInfoMap _gameObjectInfo;
ObjectInfoMap _summonInfo;
ObjectGuidMap _objectGuids;
ObjectStateMap _objectStateMap;
uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets
+1
View File
@@ -19,6 +19,7 @@
#include "DatabaseEnv.h"
#include "ObjectMgr.h"
#include "PreparedStatement.h"
#include "QueryResult.h"
LootItemStorage::LootItemStorage()
{
+1
View File
@@ -31,6 +31,7 @@
#include "MapInstanced.h"
#include "Metric.h"
#include "MiscPackets.h"
#include "MMapFactory.h"
#include "Object.h"
#include "ObjectAccessor.h"
#include "ObjectGridLoader.h"
+1
View File
@@ -19,6 +19,7 @@
#include "InstanceScript.h"
#include "MapMgr.h"
#include "MoveSpline.h"
#include "QueryResult.h"
#include "Transport.h"
TransportTemplate::~TransportTemplate()
+1 -1
View File
@@ -18,7 +18,7 @@
#ifndef __DYNAMICVISIBILITY_H
#define __DYNAMICVISIBILITY_H
#include "Common.h"
#include "Define.h"
struct VisibilitySettingData
{
+3 -1
View File
@@ -19,7 +19,9 @@
#define ACORE_MOTIONMASTER_H
#include "Common.h"
#include "Object.h"
#include "ObjectGuid.h"
#include "PathGenerator.h"
#include "Position.h"
#include "SharedDefines.h"
#include "Spline/MoveSpline.h"
#include <optional>
@@ -18,7 +18,9 @@
#ifndef ACORE_FLEEINGMOVEMENTGENERATOR_H
#define ACORE_FLEEINGMOVEMENTGENERATOR_H
#include "Creature.h"
#include "MovementGenerator.h"
#include "Timer.h"
template<class T>
class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovementGenerator<T> >
@@ -20,7 +20,6 @@
#include "DetourNavMesh.h"
#include "DetourNavMeshQuery.h"
#include "MMapFactory.h"
#include "MMapMgr.h"
#include "MapDefines.h"
#include "MoveSplineInitArgs.h"
@@ -18,7 +18,7 @@
#ifndef ACORE_POINTMOVEMENTGENERATOR_H
#define ACORE_POINTMOVEMENTGENERATOR_H
#include "FollowerReference.h"
#include "Creature.h"
#include "MovementGenerator.h"
template<class T>
@@ -25,6 +25,7 @@
#include "ObjectAccessor.h"
#include "Spell.h"
#include "Util.h"
#include "World.h"
template<class T>
RandomMovementGenerator<T>::~RandomMovementGenerator() { }
@@ -20,6 +20,7 @@
#include "MovementGenerator.h"
#include "PathGenerator.h"
#include "Timer.h"
#define RANDOM_POINTS_NUMBER 12
#define RANDOM_LINKS_COUNT 7
@@ -18,7 +18,7 @@
#ifndef ACORE_WAYPOINTMANAGER_H
#define ACORE_WAYPOINTMANAGER_H
#include "Common.h"
#include "Define.h"
#include <optional>
#include <unordered_map>
#include <vector>
+1
View File
@@ -20,6 +20,7 @@
#include "Log.h"
#include "MapMgr.h"
#include "ObjectMgr.h"
#include "QueryResult.h"
#include "Transport.h"
////////////////////////////////////////////////////////////
+1 -2
View File
@@ -18,10 +18,9 @@
#ifndef __ACORE_REPUTATION_MGR_H
#define __ACORE_REPUTATION_MGR_H
#include "Common.h"
#include "DatabaseEnvFwd.h"
#include "DBCStructure.h"
#include "Language.h"
#include "QueryResult.h"
#include "SharedDefines.h"
#include <map>
+4 -4
View File
@@ -20,14 +20,12 @@
#include "GridNotifiers.h"
#include "Map.h"
#include "MapMgr.h"
#include "MapRefMgr.h"
#include "ObjectMgr.h"
#include "Pet.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "Transport.h"
#include "WaypointMgr.h"
#include "World.h"
/// Put scripts in the execution queue
void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target)
@@ -730,8 +728,10 @@ void Map::ScriptsProcess()
break;
}
// Playsound.Flags bitmask: 0/2=without/with distance dependent
if (step.script->Playsound.Flags & SF_PLAYSOUND_DISTANCE_SOUND)
// Playsound.Flags bitmask: 0/2/4=without/with distance dependent/radius
if (step.script->Playsound.Flags & SF_PLAYSOUND_DISTANCE_RADIUS)
object->PlayRadiusSound(step.script->Playsound.SoundID, step.script->Playsound.Radius);
else if (step.script->Playsound.Flags & SF_PLAYSOUND_DISTANCE_SOUND)
object->PlayDistanceSound(step.script->Playsound.SoundID, player);
else
object->PlayDirectSound(step.script->Playsound.SoundID, player);
@@ -16,6 +16,7 @@
*/
#include "AllCommandScript.h"
#include "ChatCommand.h"
#include "ScriptMgr.h"
#include "ScriptMgrMacros.h"
@@ -29,6 +30,11 @@ bool ScriptMgr::OnTryExecuteCommand(ChatHandler& handler, std::string_view cmdSt
CALL_ENABLED_BOOLEAN_HOOKS(AllCommandScript, ALLCOMMANDHOOK_ON_TRY_EXECUTE_COMMAND, !script->OnTryExecuteCommand(handler, cmdStr));
}
bool ScriptMgr::OnBeforeIsInvokerVisible(std::string name, Acore::Impl::ChatCommands::CommandPermissions permissions, ChatHandler const& who)
{
CALL_ENABLED_BOOLEAN_HOOKS(AllCommandScript, ALLCOMMANDHOOK_ON_BEFORE_IS_INVOKER_VISIBLE, !script->OnBeforeIsInvokerVisible(name, permissions, who));
}
AllCommandScript::AllCommandScript(const char* name, std::vector<uint16> enabledHooks)
: ScriptObject(name, ALLCOMMANDHOOK_END)
{
@@ -19,12 +19,14 @@
#define SCRIPT_OBJECT_ALL_COMMAND_SCRIPT_H_
#include "ScriptObject.h"
#include "ChatCommand.h"
#include <vector>
enum AllCommandHook
{
ALLCOMMANDHOOK_ON_HANDLE_DEV_COMMAND,
ALLCOMMANDHOOK_ON_TRY_EXECUTE_COMMAND,
ALLCOMMANDHOOK_ON_BEFORE_IS_INVOKER_VISIBLE,
ALLCOMMANDHOOK_END
};
@@ -45,6 +47,8 @@ public:
* @param cmdStr Contains information about the command name
*/
[[nodiscard]] virtual bool OnTryExecuteCommand(ChatHandler& /*handler*/, std::string_view /*cmdStr*/) { return true; }
[[nodiscard]] virtual bool OnBeforeIsInvokerVisible(std::string /*name*/, Acore::Impl::ChatCommands::CommandPermissions /*permissions*/, ChatHandler const& /*who*/) { return true; }
};
// Compatibility for old scripts
@@ -18,6 +18,7 @@
#ifndef SCRIPT_OBJECT_GLOBAL_SCRIPT_H_
#define SCRIPT_OBJECT_GLOBAL_SCRIPT_H_
#include "DatabaseEnvFwd.h"
#include "DBCEnums.h"
#include "ObjectGuid.h"
#include "ScriptObject.h"
@@ -652,6 +652,11 @@ void ScriptMgr::OnApplyEnchantmentItemModsBefore(Player* player, Item* item, Enc
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_APPLY_ENCHANTMENT_ITEM_MODS_BEFORE, script->OnApplyEnchantmentItemModsBefore(player, item, slot, apply, enchant_spell_id, enchant_amount));
}
void ScriptMgr::OnApplyWeaponDamage(Player* player, uint8 slot, ItemTemplate const* proto, float& minDamage, float& maxDamage, uint8 damageIndex)
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_APPLY_WEAPON_DAMAGE, script->OnApplyWeaponDamage(player, slot, proto, minDamage, maxDamage, damageIndex));
}
bool ScriptMgr::CanArmorDamageModifier(Player* player)
{
CALL_ENABLED_BOOLEAN_HOOKS(PlayerScript, PLAYERHOOK_CAN_ARMOR_DAMAGE_MODIFIER, !script->CanArmorDamageModifier(player));
@@ -150,6 +150,7 @@ enum PlayerHook
PLAYERHOOK_ON_CUSTOM_SCALING_STAT_VALUE,
PLAYERHOOK_ON_APPLY_ITEM_MODS_BEFORE,
PLAYERHOOK_ON_APPLY_ENCHANTMENT_ITEM_MODS_BEFORE,
PLAYERHOOK_ON_APPLY_WEAPON_DAMAGE,
PLAYERHOOK_CAN_ARMOR_DAMAGE_MODIFIER,
PLAYERHOOK_ON_GET_FERAL_AP_BONUS,
PLAYERHOOK_CAN_APPLY_WEAPON_DEPENDENT_AURA_DAMAGE_MOD,
@@ -548,6 +549,8 @@ public:
virtual void OnApplyEnchantmentItemModsBefore(Player* /*player*/, Item* /*item*/, EnchantmentSlot /*slot*/, bool /*apply*/, uint32 /*enchant_spell_id*/, uint32& /*enchant_amount*/) { }
virtual void OnApplyWeaponDamage(Player* /*player*/, uint8 /*slot*/, ItemTemplate const* /*proto*/, float& /*minDamage*/, float& /*maxDamage*/, uint8 /*damageIndex*/) { }
[[nodiscard]] virtual bool CanArmorDamageModifier(Player* /*player*/) { return true; }
virtual void OnGetFeralApBonus(Player* /*player*/, int32& /*feral_bonus*/, int32 /*dpsMod*/, ItemTemplate const* /*proto*/, ScalingStatValuesEntry const* /*ssv*/) { }
@@ -20,6 +20,7 @@
#include "ScriptObject.h"
#include <vector>
#include <memory> // NOTE: this import is NEEDED (even though some IDEs report it as unused)
enum ServerHook
{
+3
View File
@@ -22,6 +22,7 @@
#include "ArenaTeam.h"
#include "AuctionHouseMgr.h"
#include "Battleground.h"
#include "ChatCommand.h"
#include "Common.h"
#include "DBCStores.h"
#include "DynamicObject.h"
@@ -444,6 +445,7 @@ public: /* PlayerScript */
void OnCustomScalingStatValue(Player* player, ItemTemplate const* proto, uint32& statType, int32& val, uint8 itemProtoStatNumber, uint32 ScalingStatValue, ScalingStatValuesEntry const* ssv);
void OnApplyItemModsBefore(Player* player, uint8 slot, bool apply, uint8 itemProtoStatNumber, uint32 statType, int32& val);
void OnApplyEnchantmentItemModsBefore(Player* player, Item* item, EnchantmentSlot slot, bool apply, uint32 enchant_spell_id, uint32& enchant_amount);
void OnApplyWeaponDamage(Player* player, uint8 slot, ItemTemplate const* proto, float& minDamage, float& maxDamage, uint8 damageIndex);
bool CanArmorDamageModifier(Player* player);
void OnGetFeralApBonus(Player* player, int32& feral_bonus, int32 dpsMod, ItemTemplate const* proto, ScalingStatValuesEntry const* ssv);
bool CanApplyWeaponDependentAuraDamageMod(Player* player, Item* item, WeaponAttackType attackType, AuraEffect const* aura, bool apply);
@@ -704,6 +706,7 @@ public: /* CommandSC */
void OnHandleDevCommand(Player* player, bool& enable);
bool OnTryExecuteCommand(ChatHandler& handler, std::string_view cmdStr);
bool OnBeforeIsInvokerVisible(std::string name, Acore::Impl::ChatCommands::CommandPermissions permissions, ChatHandler const& who);
public: /* DatabaseScript */
+1 -1
View File
@@ -18,8 +18,8 @@
#ifndef _SCRIPT_OBJECT_H_
#define _SCRIPT_OBJECT_H_
#include "DatabaseEnvFwd.h"
#include "ScriptObjectFwd.h"
#include <string>
//#include "Duration.h"
//#include "LFG.h"
+2
View File
@@ -49,6 +49,7 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSocket.h"
#include "WorldState.h"
#include <zlib.h>
#include "BanMgr.h"
@@ -641,6 +642,7 @@ void WorldSession::LogoutPlayer(bool save)
_player->RepopAtGraveyard();
sOutdoorPvPMgr->HandlePlayerLeaveZone(_player, _player->GetZoneId());
sWorldState->HandlePlayerLeaveZone(_player, static_cast<WorldStateZoneId>(_player->GetZoneId()));
// pussywizard: remove from battleground queues on logout
for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
+3 -1
View File
@@ -18,11 +18,13 @@
#ifndef __SPELL_H
#define __SPELL_H
#include "ConditionMgr.h"
#include "GridDefines.h"
#include "ObjectMgr.h"
#include "LootMgr.h"
#include "PathGenerator.h"
#include "SharedDefines.h"
#include "SpellInfo.h"
#include "Unit.h"
class Unit;
class Player;
+36 -8
View File
@@ -1152,13 +1152,29 @@ void Spell::EffectJumpDest(SpellEffIndex effIndex)
void Spell::CalculateJumpSpeeds(uint8 i, float dist, float& speedXY, float& speedZ)
{
if (m_spellInfo->Effects[i].MiscValue)
speedZ = float(m_spellInfo->Effects[i].MiscValue) / 10;
else if (m_spellInfo->Effects[i].MiscValueB)
speedZ = float(m_spellInfo->Effects[i].MiscValueB) / 10;
float runSpeed = m_caster->IsControlledByPlayer() ? playerBaseMoveSpeed[MOVE_RUN] : baseMoveSpeed[MOVE_RUN];
if (Creature* creature = m_caster->ToCreature())
runSpeed *= creature->GetCreatureTemplate()->speed_run;
float multiplier = m_spellInfo->Effects[i].ValueMultiplier;
if (multiplier <= 0.0f)
multiplier = 1.0f;
speedXY = std::min(runSpeed * 3.0f * multiplier, std::max(28.0f, m_caster->GetSpeed(MOVE_RUN) * 4.0f));
float duration = dist / speedXY;
float durationSqr = duration * duration;
float minHeight = m_spellInfo->Effects[i].MiscValue ? m_spellInfo->Effects[i].MiscValue / 10.0f : 0.5f; // Lower bound is blizzlike
float maxHeight = m_spellInfo->Effects[i].MiscValueB ? m_spellInfo->Effects[i].MiscValueB / 10.0f : 1000.0f; // Upper bound is unknown
float height;
if (durationSqr < minHeight * 8 / Movement::gravity)
height = minHeight;
else if (durationSqr > maxHeight * 8 / Movement::gravity)
height = maxHeight;
else
speedZ = 10.0f;
speedXY = dist * 10.0f / speedZ;
height = Movement::gravity * durationSqr / 8;
speedZ = std::sqrt(2 * Movement::gravity * height);
}
void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/)
@@ -1869,6 +1885,12 @@ void Spell::EffectEnergize(SpellEffIndex effIndex)
if (!unitTarget->IsAlive())
return;
if (unitTarget->HasUnitState(UNIT_STATE_ISOLATED))
{
m_caster->SendSpellDamageImmune(unitTarget, GetSpellInfo()->Id);
return;
}
if (m_spellInfo->Effects[effIndex].MiscValue < 0 || m_spellInfo->Effects[effIndex].MiscValue >= int8(MAX_POWERS))
return;
@@ -3663,6 +3685,12 @@ void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/)
if (!unitTarget || !unitTarget->IsAlive())
return;
if (unitTarget->HasUnitState(UNIT_STATE_ISOLATED))
{
m_caster->SendSpellDamageImmune(unitTarget, GetSpellInfo()->Id);
return;
}
int32 addhealth = 0;
// damage == 0 - heal for caster max health
@@ -4743,8 +4771,8 @@ void Spell::EffectQuestComplete(SpellEffIndex effIndex)
uint16 logSlot = player->FindQuestSlot(questId);
if (logSlot < MAX_QUEST_LOG_SIZE)
player->AreaExploredOrEventHappens(questId);
else if (player->CanTakeQuest(quest, false)) // never rewarded before
player->CompleteQuest(questId); // quest not in log - for internal use
else if (player->CanTakeQuest(quest, false)) // Check if the quest has already been turned in.
player->SetRewardedQuest(questId); // If not, set status to rewarded without broadcasting it to client.
}
}
@@ -1389,18 +1389,6 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(13); // 50000yd
});
// A'dal's Song of Battle
ApplySpellFix({ 39953 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_SRC_CASTER);
spellInfo->Effects[EFFECT_1].TargetA = SpellImplicitTargetInfo(TARGET_SRC_CASTER);
spellInfo->Effects[EFFECT_2].TargetA = SpellImplicitTargetInfo(TARGET_SRC_CASTER);
spellInfo->Effects[EFFECT_0].TargetB = SpellImplicitTargetInfo(TARGET_UNIT_SRC_AREA_ALLY);
spellInfo->Effects[EFFECT_1].TargetB = SpellImplicitTargetInfo(TARGET_UNIT_SRC_AREA_ALLY);
spellInfo->Effects[EFFECT_2].TargetB = SpellImplicitTargetInfo(TARGET_UNIT_SRC_AREA_ALLY);
spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(367); // 2 Hours
});
ApplySpellFix({
57607, // WintergraspCatapult - Spell Plague Barrel - EffectRadiusIndex
57619, // WintergraspDemolisher - Spell Hourl Boulder - EffectRadiusIndex
@@ -4856,6 +4844,12 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx3 |= SPELL_ATTR3_SUPPRESS_CASTER_PROCS;
});
// Claw Rage
ApplySpellFix({ 43149 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].TriggerSpell = 0;
});
// Black Qiraji Battle Tank
ApplySpellFix({ 26655, 26656 }, [](SpellInfo* spellInfo)
{
-1
View File
@@ -21,7 +21,6 @@
#include "Define.h"
#include "Duration.h"
#include <array>
#include <string>
constexpr auto AVG_DIFF_COUNT = 500;
@@ -19,6 +19,7 @@
#include "DBCStores.h"
#include "Database/DatabaseEnv.h"
#include "Log.h"
#include "QueryResult.h"
#include "SpellMgr.h"
#include "World.h"
-1
View File
@@ -19,7 +19,6 @@
#define _PLAYER_DUMP_H
#include "ObjectGuid.h"
#include <map>
#include <set>
enum DumpTableType
@@ -18,6 +18,7 @@
#include "WardenCheckMgr.h"
#include "Database/DatabaseEnv.h"
#include "Log.h"
#include "QueryResult.h"
#include "Util.h"
#include "Warden.h"
#include "WorldSession.h"
-1
View File
@@ -26,7 +26,6 @@
#include "Player.h"
#include "Weather.h"
#include "WorldSession.h"
#include <memory>
namespace WeatherMgr
{
+6
View File
@@ -93,6 +93,7 @@
#include "WhoListCacheMgr.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "WorldState.h"
#include <boost/asio/ip/address.hpp>
#include <cmath>
@@ -2414,6 +2415,11 @@ void World::Update(uint32 diff)
sOutdoorPvPMgr->Update(diff);
}
{
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update worldstate"));
sWorldState->Update(diff);
}
{
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update battlefields"));
sBattlefieldMgr->Update(diff);
+1 -1
View File
@@ -22,10 +22,10 @@
#ifndef __WORLD_H
#define __WORLD_H
#include "DatabaseEnvFwd.h"
#include "IWorld.h"
#include "LockedQueue.h"
#include "ObjectGuid.h"
#include "QueryResult.h"
#include "SharedDefines.h"
#include "Timer.h"
#include <atomic>
+293
View File
@@ -0,0 +1,293 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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/>.
*/
#include "MapMgr.h"
#include "Player.h"
#include "SharedDefines.h"
#include "WorldState.h"
WorldState* WorldState::instance()
{
static WorldState instance;
return &instance;
}
WorldState::WorldState() : _isMagtheridonHeadSpawnedHorde(false), _isMagtheridonHeadSpawnedAlliance(false)
{
_transportStates[WORLD_STATE_CONDITION_THE_IRON_EAGLE] = WORLD_STATE_CONDITION_STATE_NONE;
_transportStates[WORLD_STATE_CONDITION_THE_PURPLE_PRINCESS] = WORLD_STATE_CONDITION_STATE_NONE;
_transportStates[WORLD_STATE_CONDITION_THE_THUNDERCALLER] = WORLD_STATE_CONDITION_STATE_NONE;
}
WorldState::~WorldState()
{
}
bool WorldState::IsConditionFulfilled(WorldStateCondition conditionId, WorldStateConditionState state) const
{
switch (conditionId)
{
case WORLD_STATE_CONDITION_TROLLBANES_COMMAND:
return _isMagtheridonHeadSpawnedAlliance;
case WORLD_STATE_CONDITION_NAZGRELS_FAVOR:
return _isMagtheridonHeadSpawnedHorde;
case WORLD_STATE_CONDITION_THE_IRON_EAGLE:
case WORLD_STATE_CONDITION_THE_PURPLE_PRINCESS:
case WORLD_STATE_CONDITION_THE_THUNDERCALLER:
return _transportStates.at(conditionId) == state;
default:
LOG_ERROR("scripts", "WorldState::IsConditionFulfilled: Unhandled WorldStateCondition {}", conditionId);
return false;
}
}
void WorldState::HandleConditionStateChange(WorldStateCondition conditionId, WorldStateConditionState state)
{
_transportStates[conditionId] = state;
}
void WorldState::HandleExternalEvent(WorldStateEvent eventId, uint32 param)
{
std::lock_guard<std::mutex> guard(_mutex);
switch (eventId)
{
case WORLD_STATE_CUSTOM_EVENT_ON_ADALS_SONG_OF_BATTLE:
if (!_adalSongOfBattleTimer)
{
_adalSongOfBattleTimer = 120 * MINUTE * IN_MILLISECONDS;
BuffAdalsSongOfBattle();
}
break;
case WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_SPAWN:
if (param == TEAM_ALLIANCE)
{
_isMagtheridonHeadSpawnedAlliance = true;
BuffMagtheridonTeam(TEAM_ALLIANCE);
}
else
{
_isMagtheridonHeadSpawnedHorde = true;
BuffMagtheridonTeam(TEAM_HORDE);
}
break;
case WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_DESPAWN:
if (param == TEAM_ALLIANCE)
{
_isMagtheridonHeadSpawnedAlliance = false;
DispelMagtheridonTeam(TEAM_ALLIANCE);
}
else
{
_isMagtheridonHeadSpawnedHorde = false;
DispelMagtheridonTeam(TEAM_HORDE);
}
break;
default:
break;
}
}
void WorldState::Update(uint32 diff)
{
if (_adalSongOfBattleTimer)
{
if (_adalSongOfBattleTimer <= diff)
{
_adalSongOfBattleTimer = 0;
DispelAdalsSongOfBattle();
}
else
{
_adalSongOfBattleTimer -= diff;
}
}
}
void WorldState::HandlePlayerEnterZone(Player* player, WorldStateZoneId zoneId)
{
std::lock_guard<std::mutex> guard(_mutex);
switch (zoneId)
{
case ZONEID_SHATTRATH:
case ZONEID_BOTANICA:
case ZONEID_MECHANAR:
case ZONEID_ARCATRAZ:
if (_adalSongOfBattleTimer)
player->CastSpell(player, SPELL_ADAL_SONG_OF_BATTLE, true);
break;
case ZONEID_HELLFIRE_PENINSULA:
case ZONEID_HELLFIRE_RAMPARTS:
case ZONEID_HELLFIRE_CITADEL:
case ZONEID_BLOOD_FURNACE:
case ZONEID_SHATTERED_HALLS:
case ZONEID_MAGTHERIDON_LAIR:
if (_isMagtheridonHeadSpawnedAlliance && player->GetTeamId() == TEAM_ALLIANCE)
player->CastSpell(player, SPELL_TROLLBANES_COMMAND, true);
else if (_isMagtheridonHeadSpawnedHorde && player->GetTeamId() == TEAM_HORDE)
player->CastSpell(player, SPELL_NAZGRELS_FAVOR, true);
break;
default:
break;
}
};
void WorldState::HandlePlayerLeaveZone(Player* player, WorldStateZoneId zoneId)
{
std::lock_guard<std::mutex> guard(_mutex);
switch (zoneId)
{
case ZONEID_SHATTRATH:
case ZONEID_BOTANICA:
case ZONEID_MECHANAR:
case ZONEID_ARCATRAZ:
if (!_adalSongOfBattleTimer)
player->RemoveAurasDueToSpell(SPELL_ADAL_SONG_OF_BATTLE);
break;
case ZONEID_HELLFIRE_PENINSULA:
case ZONEID_HELLFIRE_RAMPARTS:
case ZONEID_HELLFIRE_CITADEL:
case ZONEID_BLOOD_FURNACE:
case ZONEID_SHATTERED_HALLS:
case ZONEID_MAGTHERIDON_LAIR:
if (player->GetTeamId() == TEAM_ALLIANCE)
player->RemoveAurasDueToSpell(SPELL_TROLLBANES_COMMAND);
else if (player->GetTeamId() == TEAM_HORDE)
player->RemoveAurasDueToSpell(SPELL_NAZGRELS_FAVOR);
break;
default:
break;
}
};
void WorldState::BuffMagtheridonTeam(TeamId team)
{
sMapMgr->DoForAllMaps([&](Map* map) -> void
{
switch (map->GetId())
{
case 530: // Outland
map->DoForAllPlayers([&](Player* player)
{
if (player->GetZoneId() == ZONEID_HELLFIRE_PENINSULA && player->GetTeamId() == TEAM_ALLIANCE && team == TEAM_ALLIANCE)
player->CastSpell(player, SPELL_TROLLBANES_COMMAND, true);
else if (player->GetZoneId() == ZONEID_HELLFIRE_PENINSULA && player->GetTeamId() == TEAM_HORDE && team == TEAM_HORDE)
player->CastSpell(player, SPELL_NAZGRELS_FAVOR, true);
});
break;
case 540: // The Shattered Halls
case 542: // The Blood Furnace
case 543: // Ramparts
case 544: // Magtheridon's Lair
map->DoForAllPlayers([&](Player* player)
{
if (player->GetTeamId() == TEAM_ALLIANCE && team == TEAM_ALLIANCE)
player->CastSpell(player, SPELL_TROLLBANES_COMMAND, true);
else if (player->GetTeamId() == TEAM_HORDE && team == TEAM_HORDE)
player->CastSpell(player, SPELL_NAZGRELS_FAVOR, true);
});
break;
default:
break;
}
});
}
void WorldState::DispelMagtheridonTeam(TeamId team)
{
sMapMgr->DoForAllMaps([&](Map* map) -> void
{
switch (map->GetId())
{
case 530: // Outland
map->DoForAllPlayers([&](Player* player)
{
if (player->GetZoneId() == ZONEID_HELLFIRE_PENINSULA && player->GetTeamId() == TEAM_ALLIANCE && team == TEAM_ALLIANCE)
player->RemoveAurasDueToSpell(SPELL_TROLLBANES_COMMAND);
else if (player->GetZoneId() == ZONEID_HELLFIRE_PENINSULA && player->GetTeamId() == TEAM_HORDE && team == TEAM_HORDE)
player->RemoveAurasDueToSpell(SPELL_NAZGRELS_FAVOR);
});
break;
case 540: // The Shattered Halls
case 542: // The Blood Furnace
case 543: // Ramparts
case 544: // Magtheridon's Lair
map->DoForAllPlayers([&](Player* player)
{
if (player->GetTeamId() == TEAM_ALLIANCE && team == TEAM_ALLIANCE)
player->RemoveAurasDueToSpell(SPELL_TROLLBANES_COMMAND);
else if (player->GetTeamId() == TEAM_HORDE && team == TEAM_HORDE)
player->RemoveAurasDueToSpell(SPELL_NAZGRELS_FAVOR);
});
break;
default:
break;
}
});
}
void WorldState::BuffAdalsSongOfBattle()
{
sMapMgr->DoForAllMaps([&](Map* map) -> void
{
switch (map->GetId())
{
case 530: // Outland
map->DoForAllPlayers([&](Player* player)
{
if (player->GetZoneId() == ZONEID_SHATTRATH)
player->CastSpell(player, SPELL_ADAL_SONG_OF_BATTLE, true);
});
break;
case 552: // Arcatraz
case 553: // Botanica
case 554: // Mechanar
map->DoForAllPlayers([&](Player* player)
{
player->CastSpell(player, SPELL_ADAL_SONG_OF_BATTLE, true);
});
break;
default:
break;
}
});
}
void WorldState::DispelAdalsSongOfBattle()
{
sMapMgr->DoForAllMaps([&](Map* map) -> void
{
switch (map->GetId())
{
case 530: // Outland
map->DoForAllPlayers([&](Player* player)
{
if (player->GetZoneId() == ZONEID_SHATTRATH)
player->RemoveAurasDueToSpell(SPELL_ADAL_SONG_OF_BATTLE);
});
break;
case 552: // Arcatraz
case 553: // Botanica
case 554: // Mechanar
map->DoForAllPlayers([&](Player* player)
{
player->RemoveAurasDueToSpell(SPELL_ADAL_SONG_OF_BATTLE);
});
break;
default:
break;
}
});
}
+95
View File
@@ -0,0 +1,95 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 WORLD_STATE_H
#define WORLD_STATE_H
#include "Player.h"
#include <atomic>
enum WorldStateCondition
{
WORLD_STATE_CONDITION_TROLLBANES_COMMAND = 39911,
WORLD_STATE_CONDITION_NAZGRELS_FAVOR = 39913,
// Zeppelins
WORLD_STATE_CONDITION_THE_THUNDERCALLER = 164871,
WORLD_STATE_CONDITION_THE_IRON_EAGLE = 175080,
WORLD_STATE_CONDITION_THE_PURPLE_PRINCESS = 176495,
};
enum WorldStateConditionState
{
WORLD_STATE_CONDITION_STATE_NONE = 0,
};
enum WorldStateEvent
{
WORLD_STATE_CUSTOM_EVENT_ON_ADALS_SONG_OF_BATTLE = 39953,
WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_SPAWN = 184640,
WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_DESPAWN = 184641,
};
enum WorldStateZoneId
{
ZONEID_SHATTRATH = 3703,
ZONEID_BOTANICA = 3847,
ZONEID_ARCATRAZ = 3848,
ZONEID_MECHANAR = 3849,
ZONEID_HELLFIRE_PENINSULA = 3483,
ZONEID_HELLFIRE_RAMPARTS = 3562,
ZONEID_HELLFIRE_CITADEL = 3563,
ZONEID_BLOOD_FURNACE = 3713,
ZONEID_SHATTERED_HALLS = 3714,
ZONEID_MAGTHERIDON_LAIR = 3836,
};
enum WorldStateSpells
{
SPELL_ADAL_SONG_OF_BATTLE = 39953,
SPELL_TROLLBANES_COMMAND = 39911,
SPELL_NAZGRELS_FAVOR = 39913,
};
// Intended for implementing server wide scripts, note: all behaviour must be safeguarded towards multithreading
class WorldState
{
public:
WorldState();
virtual ~WorldState();
static WorldState* instance();
void HandlePlayerEnterZone(Player* player, WorldStateZoneId zoneId);
void HandlePlayerLeaveZone(Player* player, WorldStateZoneId zoneId);
bool IsConditionFulfilled(WorldStateCondition conditionId, WorldStateConditionState state = WORLD_STATE_CONDITION_STATE_NONE) const;
void HandleConditionStateChange(WorldStateCondition conditionId, WorldStateConditionState state);
void HandleExternalEvent(WorldStateEvent eventId, uint32 param);
void Update(uint32 diff);
private:
void BuffAdalsSongOfBattle();
void DispelAdalsSongOfBattle();
uint32 _adalSongOfBattleTimer;
void BuffMagtheridonTeam(TeamId team);
void DispelMagtheridonTeam(TeamId team);
bool _isMagtheridonHeadSpawnedHorde;
bool _isMagtheridonHeadSpawnedAlliance;
std::map<WorldStateCondition, std::atomic<WorldStateConditionState>> _transportStates; // atomic to avoid having to lock
std::mutex _mutex; // all World State operations are threat unsafe
};
#define sWorldState WorldState::instance()
#endif
-1
View File
@@ -32,7 +32,6 @@ EndScriptData */
#include "MapMgr.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "Random.h"
#include "SpellAuraEffects.h"
using namespace Acore::ChatCommands;
+2 -5
View File
@@ -34,6 +34,7 @@
#include "Language.h"
#include "MapMgr.h"
#include "MiscPackets.h"
#include "MMapFactory.h"
#include "MovementGenerator.h"
#include "ObjectAccessor.h"
#include "Pet.h"
@@ -1157,19 +1158,15 @@ public:
static bool HandleReviveCommand(ChatHandler* handler, Optional<PlayerIdentifier> target)
{
if (!target)
{
target = PlayerIdentifier::FromTargetOrSelf(handler);
}
if (!target)
{
return false;
}
if (target->IsConnected())
{
auto targetPlayer = target->GetConnectedPlayer();
targetPlayer->RemoveAurasDueToSpell(27827); // Spirit of Redemption
targetPlayer->ResurrectPlayer(!AccountMgr::IsPlayerAccount(targetPlayer->GetSession()->GetSecurity()) ? 1.0f : 0.5f);
targetPlayer->SpawnCorpseBones();
targetPlayer->SaveToDB(false, false);
+3
View File
@@ -484,6 +484,9 @@ public:
if (!player)
return false;
if (!player->GetSelectedUnit())
return false;
Creature* creature = player->GetSelectedUnit()->ToCreature();
if (!creature)
@@ -62,13 +62,20 @@ public:
void Reset() override
{
_summonedRend = false;
if (instance->GetBossState(DATA_GYTH) == IN_PROGRESS)
{
instance->SetBossState(DATA_GYTH, NOT_STARTED);
summons.DespawnAll();
me->DespawnOrUnsummon();
}
SetInvincibility(true); // Don't let boss die before summoning Rend.
ScheduleHealthCheckEvent(25, [&] {
DoCastAOE(SPELL_SUMMON_REND, true);
me->RemoveAura(SPELL_REND_MOUNTS);
SetInvincibility(false);
});
}
void JustEngagedWith(Unit* /*who*/) override
@@ -104,21 +111,6 @@ public:
instance->SetBossState(DATA_GYTH, DONE);
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*type*/, SpellSchoolMask /*school*/) override
{
if (!_summonedRend && me->HealthBelowPctDamaged(25, damage))
{
if (damage >= me->GetHealth())
{
// Let creature fall to 1 HP but prevent it from dying before boss is summoned.
damage = me->GetHealth() - 1;
}
DoCast(me, SPELL_SUMMON_REND, true);
me->RemoveAura(SPELL_REND_MOUNTS);
_summonedRend = true;
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
@@ -173,9 +165,6 @@ public:
}
DoMeleeAttackIfReady();
}
private:
bool _summonedRend;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -37,8 +37,7 @@ enum Says
enum Spells
{
// Phase 1
SPELL_FIREBALL_N = 44189,
SPELL_FIREBALL_H = 46164,
SPELL_FIREBALL = 44189,
SPELL_FLAMESTRIKE_SUMMON = 44192,
SPELL_PHOENIX = 44194,
SPELL_SHOCK_BARRIER = 46165,
@@ -119,11 +118,10 @@ struct boss_felblood_kaelthas : public ScriptedAI
void JustDied(Unit*) override
{
instance->SetBossState(DATA_KAELTHAS, DONE);
summons.DespawnAll();
if (GameObject* orb = instance->GetGameObject(DATA_ESCAPE_ORB))
{
orb->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
}
}
void JustEngagedWith(Unit* /*who*/) override
@@ -218,7 +216,7 @@ struct boss_felblood_kaelthas : public ScriptedAI
switch (uint32 eventId = events.ExecuteEvent())
{
case EVENT_SPELL_FIREBALL:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_FIREBALL_N, SPELL_FIREBALL_H), false);
me->CastSpell(me->GetVictim(), SPELL_FIREBALL, false);
events.ScheduleEvent(EVENT_SPELL_FIREBALL, urand(3000, 4500));
break;
case EVENT_SPELL_FLAMESTRIKE:
@@ -32,12 +32,9 @@ enum Spells
SPELL_MEDALION_OF_IMMUNITY = 46227,
SPELL_DISPEL_MAGIC = 27609,
SPELL_FLASH_HEAL = 17843,
SPELL_SHADOW_WORD_PAIN_N = 14032,
SPELL_SHADOW_WORD_PAIN_H = 15654,
SPELL_POWER_WORD_SHIELD_N = 44291,
SPELL_POWER_WORD_SHIELD_H = 46193,
SPELL_RENEW_N = 44174,
SPELL_RENEW_H = 46192,
SPELL_SHADOW_WORD_PAIN = 14032,
SPELL_POWER_WORD_SHIELD = 44291,
SPELL_RENEW = 44174
};
enum Misc
@@ -196,14 +193,14 @@ struct boss_priestess_delrissa : public ScriptedAI
break;
case EVENT_SPELL_RENEW:
if (Unit* target = DoSelectLowestHpFriendly(40.0f, 1000))
me->CastSpell(target, DUNGEON_MODE(SPELL_RENEW_N, SPELL_RENEW_H), false);
me->CastSpell(target, SPELL_RENEW, false);
events.ScheduleEvent(EVENT_SPELL_RENEW, 7000);
break;
case EVENT_SPELL_PW_SHIELD:
{
std::list<Creature*> cList = DoFindFriendlyMissingBuff(40.0f, DUNGEON_MODE(SPELL_POWER_WORD_SHIELD_N, SPELL_POWER_WORD_SHIELD_H));
std::list<Creature*> cList = DoFindFriendlyMissingBuff(40.0f, SPELL_POWER_WORD_SHIELD);
if (Unit* target = Acore::Containers::SelectRandomContainerElement(cList))
me->CastSpell(target, DUNGEON_MODE(SPELL_POWER_WORD_SHIELD_N, SPELL_POWER_WORD_SHIELD_H), false);
me->CastSpell(target, SPELL_POWER_WORD_SHIELD, false);
events.ScheduleEvent(EVENT_SPELL_PW_SHIELD, 10000);
break;
}
@@ -239,7 +236,7 @@ struct boss_priestess_delrissa : public ScriptedAI
break;
case EVENT_SPELL_SW_PAIN:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 30.0f))
me->CastSpell(target, DUNGEON_MODE(SPELL_SHADOW_WORD_PAIN_N, SPELL_SHADOW_WORD_PAIN_H), false);
me->CastSpell(target, SPELL_SHADOW_WORD_PAIN, false);
events.ScheduleEvent(EVENT_SPELL_SW_PAIN, 10000);
break;
}
@@ -397,10 +394,8 @@ enum RogueEnum
SPELL_VANISH = 44290,
SPELL_GOUGE = 12540,
SPELL_KICK = 27613,
SPELL_BACKSTAB_N = 15657,
SPELL_BACKSTAB_H = 15582,
SPELL_EVISCERATE_N = 27611,
SPELL_EVISCERATE_H = 46189,
SPELL_BACKSTAB = 15657,
SPELL_EVISCERATE = 27611,
EVENT_SPELL_GOUGE = 1,
EVENT_SPELL_KICK = 2,
@@ -461,13 +456,13 @@ struct boss_kagani_nightstrike : public boss_priestess_lackey_commonAI
events.ScheduleEvent(EVENT_SPELL_KICK, 1000);
break;
case EVENT_SPELL_EVISCERATE:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_EVISCERATE_N, SPELL_EVISCERATE_H), false);
me->CastSpell(me->GetVictim(), SPELL_EVISCERATE, false);
events.ScheduleEvent(EVENT_SPELL_EVISCERATE, 10000);
break;
case EVENT_SPELL_BACKSTAB:
if (!me->GetVictim()->HasInArc(static_cast<float>(M_PI), me))
{
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_BACKSTAB_N, SPELL_BACKSTAB_H), false);
me->CastSpell(me->GetVictim(), SPELL_BACKSTAB, false);
events.ScheduleEvent(EVENT_SPELL_BACKSTAB, 5000);
}
else
@@ -481,12 +476,9 @@ struct boss_kagani_nightstrike : public boss_priestess_lackey_commonAI
enum WarlockEnum
{
SPELL_IMMOLATE_N = 44267,
SPELL_IMMOLATE_H = 46191,
SPELL_SHADOW_BOLT_N = 12471,
SPELL_SHADOW_BOLT_H = 15232,
SPELL_CURSE_OF_AGONY_N = 14875,
SPELL_CURSE_OF_AGONY_H = 46190,
SPELL_IMMOLATE = 44267,
SPELL_SHADOW_BOLT = 12471,
SPELL_CURSE_OF_AGONY = 14875,
SPELL_SEED_OF_CORRUPTION = 44141,
SPELL_FEAR = 38595,
SPELL_SUMMON_IMP = 44163,
@@ -525,11 +517,11 @@ struct boss_ellris_duskhallow : public boss_priestess_lackey_commonAI
{
case EVENT_SPELL_IMMOLATE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 30.0f))
me->CastSpell(target, DUNGEON_MODE(SPELL_IMMOLATE_N, SPELL_IMMOLATE_H), false);
me->CastSpell(target, SPELL_IMMOLATE, false);
events.ScheduleEvent(EVENT_SPELL_IMMOLATE, 12000);
break;
case EVENT_SPELL_SHADOW_BOLT:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_SHADOW_BOLT_N, SPELL_SHADOW_BOLT_H), false);
me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT, false);
events.ScheduleEvent(EVENT_SPELL_SHADOW_BOLT, 5000);
break;
case EVENT_SPELL_SEED_OF_CORRUPTION:
@@ -539,7 +531,7 @@ struct boss_ellris_duskhallow : public boss_priestess_lackey_commonAI
break;
case EVENT_SPELL_CURSE_OF_AGONY:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 30.0f))
me->CastSpell(target, DUNGEON_MODE(SPELL_CURSE_OF_AGONY_N, SPELL_CURSE_OF_AGONY_H), false);
me->CastSpell(target, SPELL_CURSE_OF_AGONY, false);
events.ScheduleEvent(EVENT_SPELL_CURSE_OF_AGONY, 13000);
break;
case EVENT_SPELL_FEAR:
@@ -555,8 +547,7 @@ struct boss_ellris_duskhallow : public boss_priestess_lackey_commonAI
enum MonkEnum
{
SPELL_KNOCKDOWN_N = 11428,
SPELL_KNOCKDOWN_H = 46183,
SPELL_KNOCKDOWN = 11428,
SPELL_SNAP_KICK = 46182,
SPELL_FISTS_OF_ARCANE = 44120,
@@ -588,7 +579,7 @@ struct boss_eramas_brightblaze : public boss_priestess_lackey_commonAI
switch (actualEventId)
{
case EVENT_SPELL_KNOCKDOWN:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_KNOCKDOWN_N, SPELL_KNOCKDOWN_H), false);
me->CastSpell(me->GetVictim(), SPELL_KNOCKDOWN, false);
events.ScheduleEvent(EVENT_SPELL_KNOCKDOWN, 10000);
break;
case EVENT_SPELL_SNAP_KICK:
@@ -609,14 +600,10 @@ enum MageEnum
{
SPELL_POLYMORPH = 13323,
SPELL_ICE_BLOCK = 27619,
SPELL_BLIZZARD_N = 44178,
SPELL_BLIZZARD_H = 46195,
SPELL_ICE_LANCE_N = 44176,
SPELL_ICE_LANCE_H = 46194,
SPELL_CONE_OF_COLD_N = 38384,
SPELL_CONE_OF_COLD_H = 12611,
SPELL_FROSTBOLT_N = 15043,
SPELL_FROSTBOLT_H = 15530,
SPELL_BLIZZARD = 44178,
SPELL_ICE_LANCE = 44176,
SPELL_CONE_OF_COLD = 12611,
SPELL_FROSTBOLT = 15043,
SPELL_BLINK = 14514,
EVENT_SPELL_POLYMORPH = 1,
@@ -669,19 +656,19 @@ struct boss_yazzai : public boss_priestess_lackey_commonAI
break;
case EVENT_SPELL_BLIZZARD:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
me->CastSpell(target, DUNGEON_MODE(SPELL_BLIZZARD_N, SPELL_BLIZZARD_H), false);
me->CastSpell(target, SPELL_BLIZZARD, false);
events.ScheduleEvent(EVENT_SPELL_BLIZZARD, 20000);
break;
case EVENT_SPELL_ICE_LANCE:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_ICE_LANCE_N, SPELL_ICE_LANCE_H), false);
me->CastSpell(me->GetVictim(), SPELL_ICE_LANCE, false);
events.ScheduleEvent(EVENT_SPELL_ICE_LANCE, 12000);
break;
case EVENT_SPELL_COC:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_CONE_OF_COLD_N, SPELL_CONE_OF_COLD_H), false);
me->CastSpell(me->GetVictim(), SPELL_CONE_OF_COLD, false);
events.ScheduleEvent(EVENT_SPELL_COC, 10000);
break;
case EVENT_SPELL_FROSTBOLT:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_FROSTBOLT_N, SPELL_FROSTBOLT_H), false);
me->CastSpell(me->GetVictim(), SPELL_FROSTBOLT, false);
events.ScheduleEvent(EVENT_SPELL_FROSTBOLT, 8000);
break;
case EVENT_SPELL_BLINK:
@@ -784,8 +771,7 @@ struct boss_warlord_salaris : public boss_priestess_lackey_commonAI
enum HunterEnum
{
SPELL_AIMED_SHOT = 44271,
SPELL_SHOOT_N = 15620,
SPELL_SHOOT_H = 22907,
SPELL_SHOOT = 15620,
SPELL_CONCUSSIVE_SHOT = 27634,
SPELL_MULTI_SHOT = 31942,
SPELL_WING_CLIP = 44286,
@@ -848,7 +834,7 @@ struct boss_garaxxas : public boss_priestess_lackey_commonAI
events.ScheduleEvent(EVENT_SPELL_MULTI_SHOT, 10000);
break;
case EVENT_SPELL_SHOOT:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_SHOOT_N, SPELL_SHOOT_H), false);
me->CastSpell(me->GetVictim(), SPELL_SHOOT, false);
events.ScheduleEvent(EVENT_SPELL_SHOOT, 2500);
break;
}
@@ -864,10 +850,8 @@ enum ShamanEnum
SPELL_EARTHBIND_TOTEM = 15786,
SPELL_WAR_STOMP = 46026,
SPELL_PURGE = 27626,
SPELL_LESSER_HEALING_WAVE_N = 44256,
SPELL_LESSER_HEALING_WAVE_H = 46181,
SPELL_FROST_SHOCK_N = 21401,
SPELL_FROST_SHOCK_H = 46180,
SPELL_LESSER_HEALING_WAVE = 44256,
SPELL_FROST_SHOCK = 21401,
EVENT_SPELL_TOTEM1 = 1,
EVENT_SPELL_TOTEM2 = 2,
@@ -933,12 +917,12 @@ struct boss_apoko : public boss_priestess_lackey_commonAI
events.ScheduleEvent(EVENT_SPELL_PURGE, 15000);
break;
case EVENT_SPELL_FROST_SHOCK:
me->CastSpell(me, DUNGEON_MODE(SPELL_FROST_SHOCK_N, SPELL_FROST_SHOCK_H), false);
me->CastSpell(me, SPELL_FROST_SHOCK, false);
events.ScheduleEvent(EVENT_SPELL_FROST_SHOCK, 12000);
break;
case EVENT_SPELL_HEALING_WAVE:
if (Unit* target = DoSelectLowestHpFriendly(40.0f, 1000))
me->CastSpell(target, DUNGEON_MODE(SPELL_LESSER_HEALING_WAVE_N, SPELL_LESSER_HEALING_WAVE_H), false);
me->CastSpell(target, SPELL_LESSER_HEALING_WAVE, false);
events.ScheduleEvent(EVENT_SPELL_HEALING_WAVE, 12000);
break;
}
@@ -949,12 +933,9 @@ struct boss_apoko : public boss_priestess_lackey_commonAI
enum EngineerEnum
{
SPELL_GOBLIN_DRAGON_GUN_N = 44272,
SPELL_GOBLIN_DRAGON_GUN_H = 46186,
SPELL_ROCKET_LAUNCH_N = 44137,
SPELL_ROCKET_LAUNCH_H = 46187,
SPELL_FEL_IRON_BOMB_N = 46024,
SPELL_FEL_IRON_BOMB_H = 46184,
SPELL_GOBLIN_DRAGON_GUN = 44272,
SPELL_ROCKET_LAUNCH = 44137,
SPELL_FEL_IRON_BOMB = 46024,
SPELL_RECOMBOBULATE = 44274,
SPELL_HIGH_EXPLOSIVE_SHEEP = 44276,
@@ -990,16 +971,16 @@ struct boss_zelfan : public boss_priestess_lackey_commonAI
switch (actualEventId)
{
case EVENT_SPELL_DRAGON_GUN:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_GOBLIN_DRAGON_GUN_N, SPELL_GOBLIN_DRAGON_GUN_H), false);
me->CastSpell(me->GetVictim(), SPELL_GOBLIN_DRAGON_GUN, false);
events.ScheduleEvent(EVENT_SPELL_DRAGON_GUN, 20000);
break;
case EVENT_SPELL_ROCKET_LAUNCH:
me->CastSpell(me->GetVictim(), DUNGEON_MODE(SPELL_ROCKET_LAUNCH_N, SPELL_ROCKET_LAUNCH_H), false);
me->CastSpell(me->GetVictim(), SPELL_ROCKET_LAUNCH, false);
events.ScheduleEvent(EVENT_SPELL_ROCKET_LAUNCH, 20000);
break;
case EVENT_SPELL_IRON_BOMB:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 15.0f))
me->CastSpell(target, DUNGEON_MODE(SPELL_FEL_IRON_BOMB_N, SPELL_FEL_IRON_BOMB_H), false);
me->CastSpell(target, SPELL_FEL_IRON_BOMB, false);
events.ScheduleEvent(EVENT_SPELL_IRON_BOMB, 20000);
break;
case EVENT_SPELL_RECOMBOBULATE:
@@ -36,8 +36,7 @@ enum Spells
SPELL_MANA_RAGE_TRIGGER = 44321,
//Selin's spells
SPELL_DRAIN_LIFE_N = 44294,
SPELL_DRAIN_LIFE_H = 46155,
SPELL_DRAIN_LIFE = 44294,
SPELL_FEL_EXPLOSION = 44314,
SPELL_DRAIN_MANA = 46153
};
@@ -181,7 +180,7 @@ struct boss_selin_fireheart : public ScriptedAI
{
case EVENT_SPELL_DRAIN_LIFE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
me->CastSpell(target, DUNGEON_MODE(SPELL_DRAIN_LIFE_N, SPELL_DRAIN_LIFE_H), false);
me->CastSpell(target, SPELL_DRAIN_LIFE, false);
events.ScheduleEvent(EVENT_SPELL_DRAIN_LIFE, 10000, 1);
return;
case EVENT_SPELL_DRAIN_MANA:
@@ -35,11 +35,9 @@ enum Spells
SPELL_ENERGY_FEEDBACK = 44335,
// Vexallus spell info
SPELL_CHAIN_LIGHTNING_N = 44318,
SPELL_CHAIN_LIGHTNING_H = 46380,
SPELL_CHAIN_LIGHTNING = 44318,
SPELL_OVERLOAD = 44352,
SPELL_ARCANE_SHOCK_N = 44319,
SPELL_ARCANE_SHOCK_H = 46381,
SPELL_ARCANE_SHOCK = 44319,
SPELL_SUMMON_PURE_ENERGY_N = 44322,
SPELL_SUMMON_PURE_ENERGY_H1 = 46154,
@@ -93,12 +91,12 @@ struct boss_vexallus : public BossAI
ScheduleTimedEvent(8s, [&]
{
DoCastRandomTarget(DUNGEON_MODE(SPELL_CHAIN_LIGHTNING_N, SPELL_CHAIN_LIGHTNING_H));
DoCastRandomTarget(SPELL_CHAIN_LIGHTNING);
}, 8s, 8s);
ScheduleTimedEvent(5s, [&]
{
DoCastRandomTarget(DUNGEON_MODE(SPELL_ARCANE_SHOCK_N, SPELL_ARCANE_SHOCK_H));
DoCastRandomTarget(SPELL_ARCANE_SHOCK);
}, 8s, 8s);
}
@@ -23,6 +23,7 @@
ObjectData const creatureData[] =
{
{ NPC_KAEL_THAS, DATA_KAELTHAS },
{ NPC_KALECGOS, DATA_KALECGOS },
{ 0, 0 }
};
@@ -59,27 +60,19 @@ public:
LoadDoorData(doorData);
}
ObjectGuid EscapeOrbGUID;
ObjectGuid DelrissaGUID;
ObjectGuid KaelGUID;
void ProcessEvent(WorldObject* /*obj*/, uint32 eventId) override
{
if (eventId == EVENT_SPAWN_KALECGOS)
{
if (!GetCreature(DATA_KALECGOS) && !scheduler.IsGroupScheduled(DATA_KALECGOS))
{
scheduler.Schedule(1min, 1min, DATA_KALECGOS,[this](TaskContext)
{
if (Creature* kalecgos = instance->SummonCreature(NPC_KALECGOS, KalecgosSpawnPos))
{
kalecgos->GetMotionMaster()->MovePath(PATH_KALECGOS_FLIGHT, false);
kalecgos->AI()->Talk(SAY_KALECGOS_SPAWN);
}
});
}
}
}
void OnCreatureCreate(Creature* creature) override
@@ -89,12 +82,9 @@ public:
case NPC_DELRISSA:
DelrissaGUID = creature->GetGUID();
break;
case NPC_KAEL_THAS:
KaelGUID = creature->GetGUID();
break;
case NPC_PHOENIX:
case NPC_PHOENIX_EGG:
if (Creature* kael = instance->GetCreature(KaelGUID))
if (Creature* kael = GetCreature(DATA_KAELTHAS))
kael->AI()->JustSummoned(creature);
break;
}
@@ -105,9 +95,7 @@ public:
ObjectGuid GetGuidData(uint32 identifier) const override
{
if (identifier == NPC_DELRISSA)
{
return DelrissaGUID;
}
return ObjectGuid::Empty;
}
@@ -119,53 +107,7 @@ public:
}
};
enum Spells
{
SPELL_KALECGOS_TRANSFORM = 44670,
SPELL_TRANSFORM_VISUAL = 24085,
SPELL_CAMERA_SHAKE = 44762,
SPELL_ORB_KILL_CREDIT = 46307
};
enum MovementPoints
{
POINT_ID_PREPARE_LANDING = 6
};
struct npc_kalecgos : public ScriptedAI
{
npc_kalecgos(Creature* creature) : ScriptedAI(creature) { }
void MovementInform(uint32 type, uint32 pointId) override
{
if (type != WAYPOINT_MOTION_TYPE)
return;
if (pointId == POINT_ID_PREPARE_LANDING)
{
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
me->SetHover(false);
me->m_Events.AddEventAtOffset([this]()
{
DoCastAOE(SPELL_CAMERA_SHAKE);
me->SetObjectScale(0.6f);
me->m_Events.AddEventAtOffset([this]()
{
DoCastSelf(SPELL_ORB_KILL_CREDIT, true);
DoCastSelf(SPELL_TRANSFORM_VISUAL);
DoCastSelf(SPELL_KALECGOS_TRANSFORM);
me->UpdateEntry(NPC_HUMAN_KALECGOS);
}, 1s);
}, 2s);
}
}
};
void AddSC_instance_magisters_terrace()
{
new instance_magisters_terrace();
RegisterMagistersTerraceCreatureAI(npc_kalecgos);
}
@@ -42,8 +42,7 @@ enum MTCreatures
NPC_KAEL_THAS = 24664,
NPC_PHOENIX = 21362,
NPC_PHOENIX_EGG = 21364,
NPC_KALECGOS = 24844,
NPC_HUMAN_KALECGOS = 24848
NPC_KALECGOS = 24844
};
enum MTGameObjects
@@ -61,11 +60,6 @@ enum InstanceEventIds
EVENT_SPAWN_KALECGOS = 16547
};
enum InstanceText
{
SAY_KALECGOS_SPAWN = 0
};
enum MovementData
{
PATH_KALECGOS_FLIGHT = 248440
@@ -63,106 +63,66 @@ enum Misc
ACTION_SPAWN_FELMYST = 2
};
class boss_brutallus : public CreatureScript
struct boss_brutallus : public BossAI
{
public:
boss_brutallus() : CreatureScript("boss_brutallus") { }
boss_brutallus(Creature* creature) : BossAI(creature, DATA_BRUTALLUS) { }
struct boss_brutallusAI : public BossAI
void Reset() override
{
boss_brutallusAI(Creature* creature) : BossAI(creature, DATA_BRUTALLUS) { }
BossAI::Reset();
DoCastSelf(SPELL_DUAL_WIELD, true);
me->m_Events.KillAllEvents(false);
}
void Reset() override
{
BossAI::Reset();
me->CastSpell(me, SPELL_DUAL_WIELD, true);
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (me->GetReactState() == REACT_PASSIVE && (!who || who->GetEntry() != NPC_MADRIGOSA))
{
if (who)
Unit::Kill(me, who);
damage = 0;
}
}
void JustEngagedWith(Unit* who) override
{
if (who->GetEntry() == NPC_MADRIGOSA)
return;
Talk(YELL_AGGRO);
BossAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_SPELL_SLASH, 11000);
events.ScheduleEvent(EVENT_SPELL_STOMP, 30000);
events.ScheduleEvent(EVENT_SPELL_BURN, 45000);
events.ScheduleEvent(EVENT_SPELL_BERSERK, 360000);
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && roll_chance_i(50))
Talk(YELL_KILL);
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(YELL_DEATH);
me->CastSpell(me, SPELL_SUMMON_BRUTALLUS_DEATH_CLOUD, true);
if (Creature* madrigosa = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_MADRIGOSA)))
madrigosa->AI()->DoAction(ACTION_SPAWN_FELMYST);
}
void AttackStart(Unit* who) override
{
if (who->GetEntry() == NPC_MADRIGOSA)
return;
BossAI::AttackStart(who);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_SLASH:
me->CastSpell(me->GetVictim(), SPELL_METEOR_SLASH, false);
events.ScheduleEvent(EVENT_SPELL_SLASH, 10000);
break;
case EVENT_SPELL_STOMP:
me->CastSpell(me->GetVictim(), SPELL_STOMP, false);
Talk(YELL_LOVE);
events.ScheduleEvent(EVENT_SPELL_STOMP, 30000);
break;
case EVENT_SPELL_BURN:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true, true, -SPELL_BURN_DAMAGE))
me->CastSpell(target, SPELL_BURN, false);
events.ScheduleEvent(EVENT_SPELL_BURN, 60000);
break;
case EVENT_SPELL_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
Talk(YELL_BERSERK);
break;
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
void JustEngagedWith(Unit* who) override
{
return GetSunwellPlateauAI<boss_brutallusAI>(creature);
if (who->GetEntry() == NPC_MADRIGOSA)
return;
Talk(YELL_AGGRO);
BossAI::JustEngagedWith(who);
ScheduleTimedEvent(11s, [&] {
DoCastVictim(SPELL_METEOR_SLASH);
}, 10s);
ScheduleTimedEvent(30s, [&] {
DoCastVictim(SPELL_STOMP);
Talk(YELL_LOVE);
}, 30s);
ScheduleTimedEvent(45s, [&] {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true, true, -SPELL_BURN_DAMAGE))
DoCast(target, SPELL_BURN);
}, 1min);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_BERSERK, true);
Talk(YELL_BERSERK);
}, 6min);
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && roll_chance_i(50))
Talk(YELL_KILL);
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(YELL_DEATH);
DoCastAOE(SPELL_SUMMON_BRUTALLUS_DEATH_CLOUD, true);
if (Creature* madrigosa = instance->GetCreature(DATA_MADRIGOSA))
madrigosa->AI()->DoAction(ACTION_SPAWN_FELMYST);
}
void AttackStart(Unit* who) override
{
if (who->GetEntry() == NPC_MADRIGOSA)
return;
BossAI::AttackStart(who);
}
};
@@ -210,203 +170,193 @@ enum eMadrigosa
SPELL_BRUTALLUS_BREAK_ICE = 46637,
};
class npc_madrigosa : public CreatureScript
struct npc_madrigosa : public NullCreatureAI
{
public:
npc_madrigosa() : CreatureScript("npc_madrigosa") { }
struct npc_madrigosaAI : public NullCreatureAI
npc_madrigosa(Creature* creature) : NullCreatureAI(creature)
{
npc_madrigosaAI(Creature* creature) : NullCreatureAI(creature)
{
instance = creature->GetInstanceScript();
bool appear = instance->GetBossState(DATA_BRUTALLUS) != DONE && instance->GetBossState(DATA_MADRIGOSA) == DONE;
creature->SetVisible(appear);
creature->SetStandState(UNIT_STAND_STATE_DEAD);
creature->SetDynamicFlag(UNIT_DYNFLAG_DEAD);
}
instance = creature->GetInstanceScript();
creature->SetStandState(UNIT_STAND_STATE_DEAD);
creature->SetDynamicFlag(UNIT_DYNFLAG_DEAD);
EventMap events;
InstanceScript* instance;
if (instance->IsBossDone(DATA_BRUTALLUS))
creature->SetVisible(false);
}
void DoAction(int32 param) override
{
if (param == ACTION_START_EVENT)
{
me->SetDisableGravity(true);
me->SetStandState(UNIT_STAND_STATE_STAND);
me->RemoveDynamicFlag(UNIT_DYNFLAG_DEAD);
me->NearTeleportTo(1570.97f, 725.51f, 79.77f, 3.82f);
events.ScheduleEvent(EVENT_MAD_1, 2000);
}
else if (param == ACTION_SPAWN_FELMYST)
events.ScheduleEvent(EVENT_SPAWN_FELMYST, 60000);
}
EventMap events;
InstanceScript* instance;
void UpdateAI(uint32 diff) override
{
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_MAD_1:
me->SetVisible(true);
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
{
me->SetTarget(brutallus->GetGUID());
brutallus->SetReactState(REACT_PASSIVE);
brutallus->setActive(true);
}
me->GetMotionMaster()->MovePoint(1, 1477.94f, 643.22f, 21.21f);
me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD);
events.ScheduleEvent(EVENT_MAD_2, 6000);
break;
case EVENT_MAD_2:
Talk(SAY_MAD_1);
me->CastSpell(me, SPELL_MADRIGOSA_FREEZE, false);
events.ScheduleEvent(EVENT_MAD_2_1, 1000);
break;
case EVENT_MAD_2_1:
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
me->CastSpell(me, SPELL_MADRIGOSA_FROST_BREATH, false);
events.ScheduleEvent(EVENT_MAD_3, 7000);
break;
case EVENT_MAD_3:
Talk(SAY_MAD_2);
events.ScheduleEvent(EVENT_MAD_4, 7000);
break;
case EVENT_MAD_4:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
brutallus->AI()->Talk(YELL_INTRO);
events.ScheduleEvent(EVENT_MAD_5, 5000);
break;
case EVENT_MAD_5:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
{
brutallus->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK1H);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK1H);
}
events.ScheduleEvent(EVENT_MAD_6, 10000);
break;
case EVENT_MAD_6:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
{
brutallus->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
}
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
events.ScheduleEvent(EVENT_MAD_7, 4000);
break;
case EVENT_MAD_7:
Talk(SAY_MAD_3);
me->CastSpell(me, SPELL_MADRIGOSA_FROST_BLAST, false);
events.ScheduleEvent(EVENT_MAD_8, 3000);
events.ScheduleEvent(EVENT_MAD_8, 5000);
events.ScheduleEvent(EVENT_MAD_8, 6500);
events.ScheduleEvent(EVENT_MAD_8, 7500);
events.ScheduleEvent(EVENT_MAD_8, 8500);
events.ScheduleEvent(EVENT_MAD_8, 9500);
events.ScheduleEvent(EVENT_MAD_9, 11000);
events.ScheduleEvent(EVENT_MAD_8, 12000);
events.ScheduleEvent(EVENT_MAD_8, 14000);
break;
case EVENT_MAD_8:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
me->CastSpell(brutallus, SPELL_MADRIGOSA_FROSTBOLT, false);
break;
case EVENT_MAD_9:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
{
brutallus->CastSpell(brutallus, SPELL_BRUTALLUS_FLAME_RING, true);
brutallus->RemoveAllAuras();
brutallus->CastSpell(brutallus, SPELL_BRUTALLUS_FEL_FIREBALL, false);
brutallus->AI()->Talk(YELL_INTRO_BREAK_ICE);
}
events.ScheduleEvent(EVENT_MAD_11, 6000);
break;
//case EVENT_MAD_10:
case EVENT_MAD_11:
me->SetDisableGravity(false);
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
events.ScheduleEvent(EVENT_MAD_13, 2500);
break;
case EVENT_MAD_13:
Talk(SAY_MAD_4);
me->RemoveAllAuras();
me->CastSpell(me, SPELL_MADRIGOSA_ENCAPSULATE, false);
events.ScheduleEvent(EVENT_MAD_14, 2000);
break;
case EVENT_MAD_14:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
{
brutallus->SetDisableGravity(true);
brutallus->GetMotionMaster()->MovePoint(0, brutallus->GetPositionX(), brutallus->GetPositionY() - 30.0f, brutallus->GetPositionZ() + 15.0f, false, true);
}
events.ScheduleEvent(EVENT_MAD_15, 10000);
break;
case EVENT_MAD_15:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
{
brutallus->RemoveAllAuras();
brutallus->SetDisableGravity(false);
brutallus->GetMotionMaster()->MoveFall();
brutallus->AI()->Talk(YELL_INTRO_CHARGE);
}
events.ScheduleEvent(EVENT_MAD_16, 1400);
break;
case EVENT_MAD_16:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
brutallus->CastSpell(me, SPELL_BRUTALLUS_CHARGE, true);
events.ScheduleEvent(EVENT_MAD_17, 1200);
break;
case EVENT_MAD_17:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
brutallus->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK1H);
events.ScheduleEvent(EVENT_MAD_18, 500);
break;
case EVENT_MAD_18:
Talk(SAY_MAD_5);
me->SetDynamicFlag(UNIT_DYNFLAG_DEAD);
me->SetStandState(UNIT_STAND_STATE_DEAD);
events.ScheduleEvent(EVENT_MAD_19, 6000);
break;
case EVENT_MAD_19:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
brutallus->AI()->Talk(YELL_INTRO_KILL_MADRIGOSA);
events.ScheduleEvent(EVENT_MAD_20, 7000);
break;
case EVENT_MAD_20:
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetFaction(FACTION_FRIENDLY);
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
{
brutallus->AI()->Talk(YELL_INTRO_TAUNT);
brutallus->CastSpell(brutallus, SPELL_BRUTALLUS_BREAK_ICE, false);
}
events.ScheduleEvent(EVENT_MAD_21, 4000);
break;
case EVENT_MAD_21:
if (Creature* brutallus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BRUTALLUS)))
{
brutallus->SetReactState(REACT_AGGRESSIVE);
brutallus->SetHealth(brutallus->GetMaxHealth());
brutallus->AI()->EnterEvadeMode();
brutallus->setActive(false);
}
break;
case EVENT_SPAWN_FELMYST:
me->DespawnOrUnsummon(1);
if (Creature* felmyst = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_FELMYST)))
felmyst->AI()->DoAction(ACTION_START_EVENT);
break;
}
}
};
CreatureAI* GetAI(Creature* creature) const override
void DoAction(int32 param) override
{
return GetSunwellPlateauAI<npc_madrigosaAI>(creature);
if (param == ACTION_START_EVENT)
{
me->SetDisableGravity(true);
me->SetStandState(UNIT_STAND_STATE_STAND);
me->RemoveDynamicFlag(UNIT_DYNFLAG_DEAD);
me->NearTeleportTo(1570.97f, 725.51f, 79.77f, 3.82f);
events.ScheduleEvent(EVENT_MAD_1, 2000);
}
else if (param == ACTION_SPAWN_FELMYST)
events.ScheduleEvent(EVENT_SPAWN_FELMYST, 60000);
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_MAD_1:
me->SetVisible(true);
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
{
me->SetTarget(brutallus->GetGUID());
brutallus->SetReactState(REACT_PASSIVE);
brutallus->setActive(true);
}
me->GetMotionMaster()->MovePoint(1, 1477.94f, 643.22f, 21.21f);
me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD);
events.ScheduleEvent(EVENT_MAD_2, 6000);
break;
case EVENT_MAD_2:
Talk(SAY_MAD_1);
me->CastSpell(me, SPELL_MADRIGOSA_FREEZE, false);
events.ScheduleEvent(EVENT_MAD_2_1, 1000);
break;
case EVENT_MAD_2_1:
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
me->CastSpell(me, SPELL_MADRIGOSA_FROST_BREATH, false);
events.ScheduleEvent(EVENT_MAD_3, 7000);
break;
case EVENT_MAD_3:
Talk(SAY_MAD_2);
events.ScheduleEvent(EVENT_MAD_4, 7000);
break;
case EVENT_MAD_4:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
brutallus->AI()->Talk(YELL_INTRO);
events.ScheduleEvent(EVENT_MAD_5, 5000);
break;
case EVENT_MAD_5:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
{
brutallus->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK1H);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACK1H);
}
events.ScheduleEvent(EVENT_MAD_6, 10000);
break;
case EVENT_MAD_6:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
{
brutallus->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
}
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
events.ScheduleEvent(EVENT_MAD_7, 4000);
break;
case EVENT_MAD_7:
Talk(SAY_MAD_3);
me->CastSpell(me, SPELL_MADRIGOSA_FROST_BLAST, false);
events.ScheduleEvent(EVENT_MAD_8, 3000);
events.ScheduleEvent(EVENT_MAD_8, 5000);
events.ScheduleEvent(EVENT_MAD_8, 6500);
events.ScheduleEvent(EVENT_MAD_8, 7500);
events.ScheduleEvent(EVENT_MAD_8, 8500);
events.ScheduleEvent(EVENT_MAD_8, 9500);
events.ScheduleEvent(EVENT_MAD_9, 11000);
events.ScheduleEvent(EVENT_MAD_8, 12000);
events.ScheduleEvent(EVENT_MAD_8, 14000);
break;
case EVENT_MAD_8:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
me->CastSpell(brutallus, SPELL_MADRIGOSA_FROSTBOLT, false);
break;
case EVENT_MAD_9:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
{
brutallus->CastSpell(brutallus, SPELL_BRUTALLUS_FLAME_RING, true);
brutallus->RemoveAllAuras();
brutallus->CastSpell(brutallus, SPELL_BRUTALLUS_FEL_FIREBALL, false);
brutallus->AI()->Talk(YELL_INTRO_BREAK_ICE);
}
events.ScheduleEvent(EVENT_MAD_11, 6000);
break;
//case EVENT_MAD_10:
case EVENT_MAD_11:
me->SetDisableGravity(false);
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
events.ScheduleEvent(EVENT_MAD_13, 2500);
break;
case EVENT_MAD_13:
Talk(SAY_MAD_4);
me->RemoveAllAuras();
me->CastSpell(me, SPELL_MADRIGOSA_ENCAPSULATE, false);
events.ScheduleEvent(EVENT_MAD_14, 2000);
break;
case EVENT_MAD_14:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
{
brutallus->SetDisableGravity(true);
brutallus->GetMotionMaster()->MovePoint(0, brutallus->GetPositionX(), brutallus->GetPositionY() - 30.0f, brutallus->GetPositionZ() + 15.0f, false, true);
}
events.ScheduleEvent(EVENT_MAD_15, 10000);
break;
case EVENT_MAD_15:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
{
brutallus->RemoveAllAuras();
brutallus->SetDisableGravity(false);
brutallus->GetMotionMaster()->MoveFall();
brutallus->AI()->Talk(YELL_INTRO_CHARGE);
}
events.ScheduleEvent(EVENT_MAD_16, 1400);
break;
case EVENT_MAD_16:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
brutallus->CastSpell(me, SPELL_BRUTALLUS_CHARGE, true);
events.ScheduleEvent(EVENT_MAD_17, 1200);
break;
case EVENT_MAD_17:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
brutallus->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK1H);
events.ScheduleEvent(EVENT_MAD_18, 500);
break;
case EVENT_MAD_18:
Talk(SAY_MAD_5);
me->SetDynamicFlag(UNIT_DYNFLAG_DEAD);
me->SetStandState(UNIT_STAND_STATE_DEAD);
events.ScheduleEvent(EVENT_MAD_19, 6000);
break;
case EVENT_MAD_19:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
brutallus->AI()->Talk(YELL_INTRO_KILL_MADRIGOSA);
events.ScheduleEvent(EVENT_MAD_20, 7000);
break;
case EVENT_MAD_20:
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetFaction(FACTION_FRIENDLY);
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
{
brutallus->AI()->Talk(YELL_INTRO_TAUNT);
brutallus->CastSpell(brutallus, SPELL_BRUTALLUS_BREAK_ICE, false);
}
events.ScheduleEvent(EVENT_MAD_21, 4000);
break;
case EVENT_MAD_21:
if (Creature* brutallus = instance->GetCreature(DATA_BRUTALLUS))
{
brutallus->SetReactState(REACT_AGGRESSIVE);
brutallus->SetHealth(brutallus->GetMaxHealth());
brutallus->AI()->EnterEvadeMode();
brutallus->setActive(false);
}
break;
case EVENT_SPAWN_FELMYST:
me->DespawnOrUnsummon(1);
if (Creature* felmyst = instance->GetCreature(DATA_FELMYST))
felmyst->AI()->DoAction(ACTION_START_EVENT);
break;
}
}
};
@@ -421,18 +371,13 @@ class spell_madrigosa_activate_barrier : public SpellScript
{
go->SetGoState(GO_STATE_READY);
if (Map* map = go->GetMap())
{
Map::PlayerList const& PlayerList = map->GetPlayers();
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (i->GetSource())
{
UpdateData data;
WorldPacket pkt;
go->BuildValuesUpdateBlockForPlayer(&data, i->GetSource());
data.BuildPacket(pkt);
i->GetSource()->GetSession()->SendPacket(&pkt);
}
}
map->DoForAllPlayers([&](Player* player) {
UpdateData data;
WorldPacket pkt;
go->BuildValuesUpdateBlockForPlayer(&data, player);
data.BuildPacket(pkt);
player->GetSession()->SendPacket(&pkt);
});
}
}
@@ -453,18 +398,13 @@ class spell_madrigosa_deactivate_barrier : public SpellScript
{
go->SetGoState(GO_STATE_ACTIVE);
if (Map* map = go->GetMap())
{
Map::PlayerList const& PlayerList = map->GetPlayers();
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (i->GetSource())
{
UpdateData data;
WorldPacket pkt;
go->BuildValuesUpdateBlockForPlayer(&data, i->GetSource());
data.BuildPacket(pkt);
i->GetSource()->GetSession()->SendPacket(&pkt);
}
}
map->DoForAllPlayers([&](Player* player) {
UpdateData data;
WorldPacket pkt;
go->BuildValuesUpdateBlockForPlayer(&data, player);
data.BuildPacket(pkt);
player->GetSession()->SendPacket(&pkt);
});
}
}
@@ -497,21 +437,17 @@ class spell_brutallus_burn : public SpellScript
}
};
class AreaTrigger_at_sunwell_madrigosa : public AreaTriggerScript
class at_sunwell_madrigosa : public OnlyOnceAreaTriggerScript
{
public:
AreaTrigger_at_sunwell_madrigosa() : AreaTriggerScript("at_sunwell_madrigosa") {}
at_sunwell_madrigosa() : OnlyOnceAreaTriggerScript("at_sunwell_madrigosa") {}
bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
bool _OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
{
if (InstanceScript* instance = player->GetInstanceScript())
if (instance->GetBossState(DATA_MADRIGOSA) != DONE)
{
instance->SetBossState(DATA_MADRIGOSA, NOT_STARTED);
instance->SetBossState(DATA_MADRIGOSA, DONE);
if (Creature* creature = ObjectAccessor::GetCreature(*player, instance->GetGuidData(NPC_MADRIGOSA)))
if (!instance->IsBossDone(DATA_BRUTALLUS))
if (Creature* creature = instance->GetCreature(DATA_MADRIGOSA))
creature->AI()->DoAction(ACTION_START_EVENT);
}
return true;
}
@@ -519,10 +455,10 @@ public:
void AddSC_boss_brutallus()
{
new boss_brutallus();
new npc_madrigosa();
RegisterSunwellPlateauCreatureAI(boss_brutallus);
RegisterSunwellPlateauCreatureAI(npc_madrigosa);
RegisterSpellScript(spell_madrigosa_activate_barrier);
RegisterSpellScript(spell_madrigosa_deactivate_barrier);
RegisterSpellScript(spell_brutallus_burn);
new AreaTrigger_at_sunwell_madrigosa();
new at_sunwell_madrigosa();
}
@@ -70,344 +70,248 @@ enum Spells
enum Misc
{
ACTION_SISTER_DIED = 1,
EVENT_SPELL_SHADOW_BLADES = 1,
EVENT_SPELL_SHADOW_NOVA = 2,
EVENT_SPELL_CONFOUNDING_BLOW = 3,
EVENT_SHADOW_IMAGE = 4,
EVENT_SPELL_ENRAGE = 5,
EVENT_SPELL_CONFLAGRATION = 6,
EVENT_SPELL_BLAZE = 7,
EVENT_SPELL_PYROGENICS = 8,
EVENT_SPELL_FLAME_SEAR = 9
GROUP_SPECIAL_ABILITY = 1
};
class boss_sacrolash : public CreatureScript
struct boss_sacrolash : public BossAI
{
boss_sacrolash(Creature* creature) : BossAI(creature, DATA_EREDAR_TWINS), _isSisterDead(false) {}
void Reset() override
{
DoCastSelf(SPELL_SHADOWFORM, true);
_isSisterDead = false;
BossAI::Reset();
me->SetLootMode(0);
me->m_Events.KillAllEvents(false);
}
void DoAction(int32 param) override
{
if (param == ACTION_SISTER_DIED)
{
me->ResetLootMode();
_isSisterDead = true;
Talk(YELL_SISTER_ALYTHESS_DEAD);
me->CastSpell(me, SPELL_EMPOWER, true);
scheduler.CancelGroup(GROUP_SPECIAL_ABILITY);
ScheduleTimedEvent(20s, [&] {
Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 100.0f);
if (!target)
target = me->GetVictim();
me->CastSpell(target, SPELL_CONFLAGRATION, false);
}, 30s, 35s);
}
}
void EnterEvadeMode(EvadeReason why) override
{
BossAI::EnterEvadeMode(why);
if (Creature* alythess = instance->GetCreature(DATA_ALYTHESS))
{
if (!alythess->IsAlive())
alythess->Respawn(true);
else if (!alythess->IsInEvadeMode())
alythess->AI()->EnterEvadeMode(why);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (Creature* alythess = instance->GetCreature(DATA_ALYTHESS))
if (alythess->IsAlive() && !alythess->IsInCombat())
alythess->AI()->AttackStart(who);
me->m_Events.AddEventAtOffset([&] {
Talk(YELL_BERSERK);
DoCastSelf(SPELL_ENRAGE, true);
}, 6min);
ScheduleTimedEvent(10s, [&] {
DoCastSelf(SPELL_SHADOW_BLADES);
}, 10s);
ScheduleTimedEvent(25s, [&] {
DoCastVictim(SPELL_CONFOUNDING_BLOW);
}, 20s, 25s);
ScheduleTimedEvent(20s, [&] {
me->SummonCreature(NPC_SHADOW_IMAGE, me->GetPosition(), TEMPSUMMON_TIMED_DESPAWN, 12000);
}, 6s);
scheduler.Schedule(36s, GROUP_SPECIAL_ABILITY, [this](TaskContext context) {
Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 100.0f);
if (!target)
target = me->GetVictim();
Talk(EMOTE_SHADOW_NOVA, target);
Talk(YELL_SHADOW_NOVA);
DoCast(target, SPELL_SHADOW_NOVA);
context.Repeat(30s, 35s);
});
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && urand(0, 1))
Talk(YELL_SAC_KILL);
}
void JustDied(Unit* /*killer*/) override
{
summons.DespawnAll();
if (_isSisterDead)
{
Talk(YELL_SAC_DEAD);
instance->SetBossState(DATA_EREDAR_TWINS, DONE);
}
else if (Creature* alythess = instance->GetCreature(DATA_ALYTHESS))
alythess->AI()->DoAction(ACTION_SISTER_DIED);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
{
summon->AI()->AttackStart(target);
summon->AddThreat(target, 10000000);
}
}
private:
bool _isSisterDead;
};
struct boss_alythess : public BossAI
{
boss_alythess(Creature* creature) : BossAI(creature, DATA_EREDAR_TWINS), _isSisterDead(false) { }
void Reset() override
{
DoCastSelf(SPELL_FIREFORM, true);
_isSisterDead = false;
BossAI::Reset();
me->SetLootMode(0);
me->m_Events.KillAllEvents(false);
}
void DoAction(int32 param) override
{
if (param == ACTION_SISTER_DIED)
{
me->ResetLootMode();
_isSisterDead = true;
Talk(YELL_SISTER_SACROLASH_DEAD);
me->CastSpell(me, SPELL_EMPOWER, true);
scheduler.CancelGroup(GROUP_SPECIAL_ABILITY);
ScheduleTimedEvent(20s, [&] {
Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 100.0f);
if (!target)
target = me->GetVictim();
DoCast(target, SPELL_SHADOW_NOVA);
}, 30s, 35s);
}
}
void EnterEvadeMode(EvadeReason why) override
{
BossAI::EnterEvadeMode(why);
if (Creature* sacrolash = instance->GetCreature(DATA_SACROLASH))
{
if (!sacrolash->IsAlive())
sacrolash->Respawn(true);
else if (!sacrolash->IsInEvadeMode())
sacrolash->AI()->EnterEvadeMode(why);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (Creature* sacrolash = instance->GetCreature(DATA_SACROLASH))
if (sacrolash->IsAlive() && !sacrolash->IsInCombat())
sacrolash->AI()->AttackStart(who);
me->m_Events.AddEventAtOffset([&] {
Talk(YELL_BERSERK);
DoCastSelf(SPELL_ENRAGE, true);
}, 6min);
ScheduleTimedEvent(1s, [&] {
DoCastVictim(SPELL_BLAZE);
}, 3800ms);
ScheduleTimedEvent(15s, [&] {
DoCastSelf(SPELL_PYROGENICS);
}, 15s);
ScheduleTimedEvent(20s, [&] {
me->CastCustomSpell(SPELL_FLAME_SEAR, SPELLVALUE_MAX_TARGETS, 5, me, TRIGGERED_NONE);
}, 15s);
scheduler.Schedule(20s, GROUP_SPECIAL_ABILITY, [this](TaskContext context) {
Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 100.0f);
if (!target)
target = me->GetVictim();
Talk(EMOTE_CONFLAGRATION, target);
Talk(YELL_CANFLAGRATION);
DoCast(target, SPELL_CONFLAGRATION);
context.Repeat(30s, 35s);
});
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && urand(0, 1))
Talk(YELL_SAC_KILL);
}
void JustDied(Unit* /*killer*/) override
{
summons.DespawnAll();
if (_isSisterDead)
{
Talk(YELL_SAC_DEAD);
instance->SetBossState(DATA_EREDAR_TWINS, DONE);
}
else if (Creature* sacrolash = instance->GetCreature(DATA_SACROLASH))
sacrolash->AI()->DoAction(ACTION_SISTER_DIED);
}
private:
bool _isSisterDead;
};
class spell_eredar_twins_apply_touch : public SpellScript
{
PrepareSpellScript(spell_eredar_twins_apply_touch);
public:
boss_sacrolash() : CreatureScript("boss_sacrolash") { }
struct boss_sacrolashAI : public BossAI
{
boss_sacrolashAI(Creature* creature) : BossAI(creature, DATA_EREDAR_TWINS) {}
bool sisterDied;
void Reset() override
{
me->CastSpell(me, SPELL_SHADOWFORM, true);
sisterDied = false;
BossAI::Reset();
me->SetLootMode(0);
}
void DoAction(int32 param) override
{
if (param == ACTION_SISTER_DIED)
{
me->ResetLootMode();
sisterDied = true;
Talk(YELL_SISTER_ALYTHESS_DEAD);
me->CastSpell(me, SPELL_EMPOWER, true);
uint32 timer = events.GetNextEventTime(EVENT_SPELL_SHADOW_NOVA);
events.CancelEvent(EVENT_SPELL_SHADOW_NOVA);
events.ScheduleEvent(EVENT_SPELL_CONFLAGRATION, timer - events.GetTimer());
}
}
void EnterEvadeMode(EvadeReason why) override
{
BossAI::EnterEvadeMode(why);
if (Creature* alythess = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GRAND_WARLOCK_ALYTHESS)))
{
if (!alythess->IsAlive())
alythess->Respawn(true);
else if (!alythess->IsInEvadeMode())
alythess->AI()->EnterEvadeMode(why);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (Creature* alythess = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GRAND_WARLOCK_ALYTHESS)))
if (alythess->IsAlive() && !alythess->IsInCombat())
alythess->AI()->AttackStart(who);
events.ScheduleEvent(EVENT_SPELL_SHADOW_BLADES, 10000);
events.ScheduleEvent(EVENT_SPELL_SHADOW_NOVA, 36000);
events.ScheduleEvent(EVENT_SPELL_CONFOUNDING_BLOW, 25000);
events.ScheduleEvent(EVENT_SHADOW_IMAGE, 20000);
events.ScheduleEvent(EVENT_SPELL_ENRAGE, 360000);
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && urand(0, 1))
Talk(YELL_SAC_KILL);
}
void JustDied(Unit* /*killer*/) override
{
events.Reset();
summons.DespawnAll();
if (sisterDied)
{
Talk(YELL_SAC_DEAD);
instance->SetBossState(DATA_EREDAR_TWINS, DONE);
}
else if (Creature* alythess = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GRAND_WARLOCK_ALYTHESS)))
alythess->AI()->DoAction(ACTION_SISTER_DIED);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
{
summon->AI()->AttackStart(target);
summon->AddThreat(target, 10000000);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_ENRAGE:
Talk(YELL_ENRAGE);
me->CastSpell(me, SPELL_ENRAGE, true);
break;
case EVENT_SPELL_CONFOUNDING_BLOW:
me->CastSpell(me->GetVictim(), SPELL_CONFOUNDING_BLOW, false);
events.ScheduleEvent(EVENT_SPELL_CONFOUNDING_BLOW, urand(20000, 25000));
break;
case EVENT_SPELL_SHADOW_BLADES:
me->CastSpell(me, SPELL_SHADOW_BLADES, false);
events.ScheduleEvent(EVENT_SPELL_SHADOW_BLADES, 10000);
break;
case EVENT_SPELL_SHADOW_NOVA:
{
Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 100.0f);
if (!target)
target = me->GetVictim();
Talk(EMOTE_SHADOW_NOVA, target);
Talk(YELL_SHADOW_NOVA);
me->CastSpell(target, SPELL_SHADOW_NOVA, false);
events.ScheduleEvent(EVENT_SPELL_SHADOW_NOVA, urand(30000, 35000));
break;
}
case EVENT_SHADOW_IMAGE:
me->SummonCreature(NPC_SHADOW_IMAGE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 12000);
events.ScheduleEvent(EVENT_SHADOW_IMAGE, 6000);
break;
case EVENT_SPELL_CONFLAGRATION:
{
Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 100.0f);
if (!target)
target = me->GetVictim();
me->CastSpell(target, SPELL_CONFLAGRATION, false);
events.ScheduleEvent(EVENT_SPELL_CONFLAGRATION, urand(30000, 35000));
break;
}
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI<boss_sacrolashAI>(creature);
};
};
class boss_alythess : public CreatureScript
{
public:
boss_alythess() : CreatureScript("boss_alythess") { }
struct boss_alythessAI : public BossAI
{
boss_alythessAI(Creature* creature) : BossAI(creature, DATA_EREDAR_TWINS) { }
bool sisterDied;
void Reset() override
{
me->CastSpell(me, SPELL_FIREFORM, true);
sisterDied = false;
BossAI::Reset();
me->SetLootMode(0);
}
void DoAction(int32 param) override
{
if (param == ACTION_SISTER_DIED)
{
me->ResetLootMode();
sisterDied = true;
Talk(YELL_SISTER_SACROLASH_DEAD);
me->CastSpell(me, SPELL_EMPOWER, true);
uint32 timer = events.GetNextEventTime(EVENT_SPELL_CONFLAGRATION);
events.CancelEvent(EVENT_SPELL_CONFLAGRATION);
events.ScheduleEvent(EVENT_SPELL_SHADOW_NOVA, timer - events.GetTimer());
}
}
void EnterEvadeMode(EvadeReason why) override
{
BossAI::EnterEvadeMode(why);
if (Creature* scorlash = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_LADY_SACROLASH)))
{
if (!scorlash->IsAlive())
scorlash->Respawn(true);
else if (!scorlash->IsInEvadeMode())
scorlash->AI()->EnterEvadeMode(why);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
if (Creature* scorlash = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_LADY_SACROLASH)))
if (scorlash->IsAlive() && !scorlash->IsInCombat())
scorlash->AI()->AttackStart(who);
events.ScheduleEvent(EVENT_SPELL_BLAZE, 100);
events.ScheduleEvent(EVENT_SPELL_PYROGENICS, 15000);
events.ScheduleEvent(EVENT_SPELL_FLAME_SEAR, 20000);
events.ScheduleEvent(EVENT_SPELL_CONFLAGRATION, 30000);
events.ScheduleEvent(EVENT_SPELL_ENRAGE, 360000);
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && urand(0, 1))
Talk(YELL_SAC_KILL);
}
void JustDied(Unit* /*killer*/) override
{
events.Reset();
summons.DespawnAll();
if (sisterDied)
{
Talk(YELL_SAC_DEAD);
instance->SetBossState(DATA_EREDAR_TWINS, DONE);
}
else if (Creature* scorlash = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_LADY_SACROLASH)))
scorlash->AI()->DoAction(ACTION_SISTER_DIED);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_ENRAGE:
Talk(YELL_BERSERK);
me->CastSpell(me, SPELL_ENRAGE, true);
break;
case EVENT_SPELL_PYROGENICS:
me->CastSpell(me, SPELL_PYROGENICS, false);
events.ScheduleEvent(EVENT_SPELL_PYROGENICS, 15000);
break;
case EVENT_SPELL_FLAME_SEAR:
me->CastCustomSpell(SPELL_FLAME_SEAR, SPELLVALUE_MAX_TARGETS, 5, me, TRIGGERED_NONE);
events.ScheduleEvent(EVENT_SPELL_FLAME_SEAR, 15000);
break;
case EVENT_SPELL_BLAZE:
me->CastSpell(me->GetVictim(), SPELL_BLAZE, false);
events.ScheduleEvent(EVENT_SPELL_BLAZE, 3800);
break;
case EVENT_SPELL_SHADOW_NOVA:
{
Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 100.0f);
if (!target)
target = me->GetVictim();
me->CastSpell(target, SPELL_SHADOW_NOVA, false);
events.ScheduleEvent(EVENT_SPELL_SHADOW_NOVA, urand(30000, 35000));
break;
}
case EVENT_SPELL_CONFLAGRATION:
{
Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 100.0f);
if (!target)
target = me->GetVictim();
Talk(EMOTE_CONFLAGRATION, target);
Talk(YELL_CANFLAGRATION);
me->CastSpell(target, SPELL_CONFLAGRATION, false);
events.ScheduleEvent(EVENT_SPELL_CONFLAGRATION, urand(30000, 35000));
break;
}
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI<boss_alythessAI>(creature);
};
};
class spell_eredar_twins_apply_dark_touched : public SpellScript
{
PrepareSpellScript(spell_eredar_twins_apply_dark_touched);
spell_eredar_twins_apply_touch(uint32 touchSpell) : SpellScript(), _touchSpell(touchSpell) { }
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_DARK_TOUCHED });
return ValidateSpellInfo({ _touchSpell });
}
void HandleApplyTouch()
{
if (Player* target = GetHitPlayer())
target->CastSpell(target, SPELL_DARK_TOUCHED, true);
target->CastSpell(target, _touchSpell, true);
}
void Register() override
{
AfterHit += SpellHitFn(spell_eredar_twins_apply_dark_touched::HandleApplyTouch);
}
};
class spell_eredar_twins_apply_flame_touched : public SpellScript
{
PrepareSpellScript(spell_eredar_twins_apply_flame_touched);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_FLAME_TOUCHED });
AfterHit += SpellHitFn(spell_eredar_twins_apply_touch::HandleApplyTouch);
}
void HandleApplyTouch()
{
if (Player* target = GetHitPlayer())
target->CastSpell(target, SPELL_FLAME_TOUCHED, true);
}
void Register() override
{
AfterHit += SpellHitFn(spell_eredar_twins_apply_flame_touched::HandleApplyTouch);
}
private:
uint32 _touchSpell;
};
class spell_eredar_twins_handle_touch : public SpellScript
@@ -473,22 +377,20 @@ class spell_eredar_twins_blaze : public SpellScript
}
};
class AreaTrigger_at_sunwell_eredar_twins : public AreaTriggerScript
class at_sunwell_eredar_twins : public OnlyOnceAreaTriggerScript
{
public:
AreaTrigger_at_sunwell_eredar_twins() : AreaTriggerScript("at_sunwell_eredar_twins") {}
at_sunwell_eredar_twins() : OnlyOnceAreaTriggerScript("at_sunwell_eredar_twins") {}
bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
bool _OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override
{
if (InstanceScript* instance = player->GetInstanceScript())
if (instance->GetBossState(DATA_EREDAR_TWINS_INTRO) != DONE)
{
instance->SetBossState(DATA_EREDAR_TWINS_INTRO, DONE);
if (Creature* creature = ObjectAccessor::GetCreature(*player, instance->GetGuidData(NPC_LADY_SACROLASH)))
creature->AI()->Talk(YELL_INTRO_SAC);
if (Creature* creature = ObjectAccessor::GetCreature(*player, instance->GetGuidData(NPC_GRAND_WARLOCK_ALYTHESS)))
creature->AI()->Talk(YELL_INTRO_ALY);
}
{
if (Creature* creature = instance->GetCreature(DATA_SACROLASH))
creature->AI()->Talk(YELL_INTRO_SAC);
if (Creature* creature = instance->GetCreature(DATA_ALYTHESS))
creature->AI()->Talk(YELL_INTRO_ALY);
}
return true;
}
@@ -496,11 +398,11 @@ public:
void AddSC_boss_eredar_twins()
{
new boss_sacrolash();
new boss_alythess();
RegisterSpellScript(spell_eredar_twins_apply_dark_touched);
RegisterSpellScript(spell_eredar_twins_apply_flame_touched);
RegisterSunwellPlateauCreatureAI(boss_sacrolash);
RegisterSunwellPlateauCreatureAI(boss_alythess);
RegisterSpellScriptWithArgs(spell_eredar_twins_apply_touch, "spell_eredar_twins_apply_dark_touched", SPELL_DARK_TOUCHED);
RegisterSpellScriptWithArgs(spell_eredar_twins_apply_touch, "spell_eredar_twins_apply_flame_touched", SPELL_FLAME_TOUCHED);
RegisterSpellScript(spell_eredar_twins_handle_touch);
RegisterSpellScript(spell_eredar_twins_blaze);
new AreaTrigger_at_sunwell_eredar_twins();
new at_sunwell_eredar_twins();
}
@@ -126,356 +126,323 @@ private:
Unit* _caster;
};
class boss_felmyst : public CreatureScript
struct boss_felmyst : public BossAI
{
public:
boss_felmyst() : CreatureScript("boss_felmyst") { }
struct boss_felmystAI : public BossAI
boss_felmyst(Creature* creature) : BossAI(creature, DATA_FELMYST)
{
boss_felmystAI(Creature* creature) : BossAI(creature, DATA_FELMYST)
{
bool appear = instance->GetBossState(DATA_BRUTALLUS) == DONE;
creature->SetVisible(appear);
creature->SetStandState(UNIT_STAND_STATE_SLEEP);
creature->SetReactState(REACT_PASSIVE);
}
bool appear = instance->GetBossState(DATA_BRUTALLUS) == DONE;
creature->SetVisible(appear);
creature->SetStandState(UNIT_STAND_STATE_SLEEP);
creature->SetReactState(REACT_PASSIVE);
}
EventMap events2;
EventMap events2;
void DoAction(int32 param) override
void DoAction(int32 param) override
{
if (param == ACTION_START_EVENT)
{
if (param == ACTION_START_EVENT)
{
me->SetVisible(true);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
events2.ScheduleEvent(EVENT_INTRO_1, 3000);
}
}
void Reset() override
{
BossAI::Reset();
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_PASSIVE);
me->SetDisableGravity(false);
events2.Reset();
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_FOG_OF_CORRUPTION_CHARM);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetVisible(true);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
if (events.Empty() && events2.Empty())
events2.ScheduleEvent(EVENT_INTRO_2, 3000);
events2.ScheduleEvent(EVENT_INTRO_1, 3000);
}
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && roll_chance_i(50))
Talk(YELL_KILL);
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(YELL_DEATH);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_FOG_OF_CORRUPTION_CHARM);
// Summon Kalecgos (human form of kalecgos fight)
me->SummonCreature(NPC_KALEC, 1526.28f, 700.10f, 60.0f, 4.33f);
}
void MovementInform(uint32 type, uint32 point) override
{
if (type != POINT_MOTION_TYPE)
return;
if (point == POINT_GROUND)
{
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
me->SendMovementFlagUpdate();
events.ScheduleEvent(EVENT_RESTORE_COMBAT, 0);
events.ScheduleEvent(EVENT_RESTORE_COMBAT2, 1);
events.ScheduleEvent(EVENT_SPELL_CLEAVE, 7500, 1);
events.ScheduleEvent(EVENT_SPELL_CORROSION, 12000, 1);
events.ScheduleEvent(EVENT_SPELL_GAS_NOVA, 18000, 1);
events.ScheduleEvent(EVENT_SPELL_ENCAPSULATE, 25000, 1);
events.ScheduleEvent(EVENT_FLIGHT, 60000, 1);
}
else if (point == POINT_AIR_BREATH_START1)
{
me->SetTarget();
me->SetFacingTo(4.71f);
events.ScheduleEvent(EVENT_FLIGHT_EMOTE, 2000);
events.ScheduleEvent(EVENT_CORRUPT_TRIGGERS, 5000);
events.ScheduleEvent(EVENT_FLIGHT_FLYOVER1, 5000);
}
else if (point == POINT_AIR_BREATH_END1)
{
me->RemoveAurasDueToSpell(SPELL_FELMYST_SPEED_BURST);
me->SetFacingTo(1.57f);
if (events.GetNextEventTime(EVENT_FLIGHT_BREATH1) != 0)
events.ScheduleEvent(EVENT_FLIGHT_BREATH2, 2000);
}
else if (point == POINT_AIR_BREATH_START2)
{
me->SetTarget();
me->SetFacingTo(1.57f);
events.ScheduleEvent(EVENT_FLIGHT_EMOTE, 2000);
events.ScheduleEvent(EVENT_CORRUPT_TRIGGERS, 5000);
events.ScheduleEvent(EVENT_FLIGHT_FLYOVER2, 5000);
}
else if (point == POINT_AIR_BREATH_END2)
{
me->RemoveAurasDueToSpell(SPELL_FELMYST_SPEED_BURST);
me->SetFacingTo(4.71f);
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
}
void UpdateAI(uint32 diff) override
{
events2.Update(diff);
switch (events2.ExecuteEvent())
{
case EVENT_INTRO_1:
me->SetStandState(UNIT_STAND_STATE_STAND);
events2.ScheduleEvent(EVENT_INTRO_2, 4000);
break;
case EVENT_INTRO_2:
Talk(YELL_BIRTH);
me->SetDisableGravity(true);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SendMovementFlagUpdate();
events2.ScheduleEvent(EVENT_INTRO_3, 1500);
break;
case EVENT_INTRO_3:
me->GetMotionMaster()->MovePoint(POINT_AIR, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 10.0f, false, true);
events2.ScheduleEvent(EVENT_INTRO_4, 2000);
break;
case EVENT_INTRO_4:
events.ScheduleEvent(EVENT_LAND, 3000, 1);
events.ScheduleEvent(EVENT_SPELL_BERSERK, 600000);
me->SetInCombatWithZone();
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->CastSpell(me, SPELL_NOXIOUS_FUMES, true);
me->GetMotionMaster()->MovePoint(POINT_MISC, 1472.18f, 603.38f, 34.0f, false, true);
break;
}
if (!events2.Empty())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_RESTORE_COMBAT:
me->SetReactState(REACT_AGGRESSIVE);
break;
case EVENT_RESTORE_COMBAT2:
me->SetTarget(me->GetVictim()->GetGUID());
me->GetMotionMaster()->MoveChase(me->GetVictim());
break;
case EVENT_LAND:
me->GetMotionMaster()->MovePoint(POINT_GROUND, me->GetPositionX(), me->GetPositionY(), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()), false, true);
break;
case EVENT_SPELL_BERSERK:
Talk(YELL_BERSERK);
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_SPELL_CLEAVE:
me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false);
events.ScheduleEvent(EVENT_SPELL_CLEAVE, 7500, 1);
break;
case EVENT_SPELL_CORROSION:
me->CastSpell(me->GetVictim(), SPELL_CORROSION, false);
events.ScheduleEvent(EVENT_SPELL_CORROSION, 20000, 1);
break;
case EVENT_SPELL_GAS_NOVA:
DoCast(me, SPELL_GAS_NOVA, false);
events.ScheduleEvent(EVENT_SPELL_GAS_NOVA, 20000, 1);
break;
case EVENT_SPELL_ENCAPSULATE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
me->CastSpell(target, SPELL_ENCAPSULATE_CHANNEL, false);
events.ScheduleEvent(EVENT_SPELL_ENCAPSULATE, 25000, 1);
break;
case EVENT_FLIGHT:
events.CancelEventGroup(1);
events.ScheduleEvent(EVENT_FLIGHT_SEQ, 1000);
me->SetReactState(REACT_PASSIVE);
me->StopMoving();
me->GetMotionMaster()->Clear();
break;
case EVENT_FLIGHT_SEQ:
Talk(YELL_TAKEOFF);
me->SetTarget();
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
me->SendMovementFlagUpdate();
events.ScheduleEvent(EVENT_FLIGHT_MOVE_UP, 2000);
events.ScheduleEvent(EVENT_FLIGHT_VAPOR, 8000);
events.ScheduleEvent(EVENT_FLIGHT_VAPOR, 21000);
events.ScheduleEvent(EVENT_FLIGHT_BREATH1, 35000);
events.ScheduleEvent(EVENT_FLIGHT_BREATH1, 72000);
events.ScheduleEvent(EVENT_LAND_FIGHT, 86000);
break;
case EVENT_FLIGHT_MOVE_UP:
me->GetMotionMaster()->MovePoint(POINT_AIR, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 15.0f, false, true);
break;
case EVENT_FLIGHT_VAPOR:
me->CastCustomSpell(SPELL_SUMMON_DEMONIC_VAPOR, SPELLVALUE_MAX_TARGETS, 1, me, true);
break;
case EVENT_FLIGHT_BREATH1:
{
Position pos = {1447.0f + urand(0, 2) * 25.0f, 705.0f, 50.0f, 4.71f};
me->GetMotionMaster()->MovePoint(POINT_AIR_BREATH_START1, pos, false, true);
break;
}
case EVENT_FLIGHT_BREATH2:
{
Position pos = {1447.0f + urand(0, 2) * 25.0f, 515.0f, 50.0f, 1.57f};
me->GetMotionMaster()->MovePoint(POINT_AIR_BREATH_START2, pos, false, true);
break;
}
case EVENT_FLIGHT_EMOTE:
Talk(EMOTE_BREATH);
break;
case EVENT_CORRUPT_TRIGGERS:
Talk(YELL_BREATH);
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(0));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(500));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(1000));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(1500));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(2000));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(2500));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(3000));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(3500));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(4000));
break;
case EVENT_FLIGHT_FLYOVER1:
me->CastSpell(me, SPELL_FELMYST_SPEED_BURST, true);
me->GetMotionMaster()->MovePoint(POINT_AIR_BREATH_END1, me->GetPositionX(), me->GetPositionY() - 200.0f, me->GetPositionZ() + 5.0f, false, true);
break;
case EVENT_FLIGHT_FLYOVER2:
me->CastSpell(me, SPELL_FELMYST_SPEED_BURST, true);
me->GetMotionMaster()->MovePoint(POINT_AIR_BREATH_END2, me->GetPositionX(), me->GetPositionY() + 200.0f, me->GetPositionZ() + 5.0f, false, true);
break;
case EVENT_LAND_FIGHT:
me->GetMotionMaster()->MovePoint(POINT_GROUND, 1500.0f, 552.8f, 26.52f, false, true);
break;
}
if (!me->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
void Reset() override
{
return GetSunwellPlateauAI<boss_felmystAI>(creature);
BossAI::Reset();
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_PASSIVE);
me->SetDisableGravity(false);
events2.Reset();
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_FOG_OF_CORRUPTION_CHARM);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
if (events.Empty() && events2.Empty())
events2.ScheduleEvent(EVENT_INTRO_2, 3000);
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && roll_chance_i(50))
Talk(YELL_KILL);
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
Talk(YELL_DEATH);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_FOG_OF_CORRUPTION_CHARM);
// Summon Kalecgos (human form of kalecgos fight)
me->SummonCreature(NPC_KALEC, 1526.28f, 700.10f, 60.0f, 4.33f);
}
void MovementInform(uint32 type, uint32 point) override
{
if (type != POINT_MOTION_TYPE)
return;
if (point == POINT_GROUND)
{
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
me->SendMovementFlagUpdate();
events.ScheduleEvent(EVENT_RESTORE_COMBAT, 0);
events.ScheduleEvent(EVENT_RESTORE_COMBAT2, 1);
events.ScheduleEvent(EVENT_SPELL_CLEAVE, 7500, 1);
events.ScheduleEvent(EVENT_SPELL_CORROSION, 12000, 1);
events.ScheduleEvent(EVENT_SPELL_GAS_NOVA, 18000, 1);
events.ScheduleEvent(EVENT_SPELL_ENCAPSULATE, 25000, 1);
events.ScheduleEvent(EVENT_FLIGHT, 60000, 1);
}
else if (point == POINT_AIR_BREATH_START1)
{
me->SetTarget();
me->SetFacingTo(4.71f);
events.ScheduleEvent(EVENT_FLIGHT_EMOTE, 2000);
events.ScheduleEvent(EVENT_CORRUPT_TRIGGERS, 5000);
events.ScheduleEvent(EVENT_FLIGHT_FLYOVER1, 5000);
}
else if (point == POINT_AIR_BREATH_END1)
{
me->RemoveAurasDueToSpell(SPELL_FELMYST_SPEED_BURST);
me->SetFacingTo(1.57f);
if (events.GetNextEventTime(EVENT_FLIGHT_BREATH1) != 0)
events.ScheduleEvent(EVENT_FLIGHT_BREATH2, 2000);
}
else if (point == POINT_AIR_BREATH_START2)
{
me->SetTarget();
me->SetFacingTo(1.57f);
events.ScheduleEvent(EVENT_FLIGHT_EMOTE, 2000);
events.ScheduleEvent(EVENT_CORRUPT_TRIGGERS, 5000);
events.ScheduleEvent(EVENT_FLIGHT_FLYOVER2, 5000);
}
else if (point == POINT_AIR_BREATH_END2)
{
me->RemoveAurasDueToSpell(SPELL_FELMYST_SPEED_BURST);
me->SetFacingTo(4.71f);
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
}
void UpdateAI(uint32 diff) override
{
events2.Update(diff);
switch (events2.ExecuteEvent())
{
case EVENT_INTRO_1:
me->SetStandState(UNIT_STAND_STATE_STAND);
events2.ScheduleEvent(EVENT_INTRO_2, 4000);
break;
case EVENT_INTRO_2:
Talk(YELL_BIRTH);
me->SetDisableGravity(true);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SendMovementFlagUpdate();
events2.ScheduleEvent(EVENT_INTRO_3, 1500);
break;
case EVENT_INTRO_3:
me->GetMotionMaster()->MovePoint(POINT_AIR, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 10.0f, false, true);
events2.ScheduleEvent(EVENT_INTRO_4, 2000);
break;
case EVENT_INTRO_4:
events.ScheduleEvent(EVENT_LAND, 3000, 1);
events.ScheduleEvent(EVENT_SPELL_BERSERK, 600000);
me->SetInCombatWithZone();
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->CastSpell(me, SPELL_NOXIOUS_FUMES, true);
me->GetMotionMaster()->MovePoint(POINT_MISC, 1472.18f, 603.38f, 34.0f, false, true);
break;
}
if (!events2.Empty())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_RESTORE_COMBAT:
me->SetReactState(REACT_AGGRESSIVE);
break;
case EVENT_RESTORE_COMBAT2:
me->SetTarget(me->GetVictim()->GetGUID());
me->GetMotionMaster()->MoveChase(me->GetVictim());
break;
case EVENT_LAND:
me->GetMotionMaster()->MovePoint(POINT_GROUND, me->GetPositionX(), me->GetPositionY(), me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()), false, true);
break;
case EVENT_SPELL_BERSERK:
Talk(YELL_BERSERK);
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_SPELL_CLEAVE:
me->CastSpell(me->GetVictim(), SPELL_CLEAVE, false);
events.ScheduleEvent(EVENT_SPELL_CLEAVE, 7500, 1);
break;
case EVENT_SPELL_CORROSION:
me->CastSpell(me->GetVictim(), SPELL_CORROSION, false);
events.ScheduleEvent(EVENT_SPELL_CORROSION, 20000, 1);
break;
case EVENT_SPELL_GAS_NOVA:
DoCast(me, SPELL_GAS_NOVA, false);
events.ScheduleEvent(EVENT_SPELL_GAS_NOVA, 20000, 1);
break;
case EVENT_SPELL_ENCAPSULATE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
me->CastSpell(target, SPELL_ENCAPSULATE_CHANNEL, false);
events.ScheduleEvent(EVENT_SPELL_ENCAPSULATE, 25000, 1);
break;
case EVENT_FLIGHT:
events.CancelEventGroup(1);
events.ScheduleEvent(EVENT_FLIGHT_SEQ, 1000);
me->SetReactState(REACT_PASSIVE);
me->StopMoving();
me->GetMotionMaster()->Clear();
break;
case EVENT_FLIGHT_SEQ:
Talk(YELL_TAKEOFF);
me->SetTarget();
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
me->SendMovementFlagUpdate();
events.ScheduleEvent(EVENT_FLIGHT_MOVE_UP, 2000);
events.ScheduleEvent(EVENT_FLIGHT_VAPOR, 8000);
events.ScheduleEvent(EVENT_FLIGHT_VAPOR, 21000);
events.ScheduleEvent(EVENT_FLIGHT_BREATH1, 35000);
events.ScheduleEvent(EVENT_FLIGHT_BREATH1, 72000);
events.ScheduleEvent(EVENT_LAND_FIGHT, 86000);
break;
case EVENT_FLIGHT_MOVE_UP:
me->GetMotionMaster()->MovePoint(POINT_AIR, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 15.0f, false, true);
break;
case EVENT_FLIGHT_VAPOR:
me->CastCustomSpell(SPELL_SUMMON_DEMONIC_VAPOR, SPELLVALUE_MAX_TARGETS, 1, me, true);
break;
case EVENT_FLIGHT_BREATH1:
{
Position pos = { 1447.0f + urand(0, 2) * 25.0f, 705.0f, 50.0f, 4.71f };
me->GetMotionMaster()->MovePoint(POINT_AIR_BREATH_START1, pos, false, true);
break;
}
case EVENT_FLIGHT_BREATH2:
{
Position pos = { 1447.0f + urand(0, 2) * 25.0f, 515.0f, 50.0f, 1.57f };
me->GetMotionMaster()->MovePoint(POINT_AIR_BREATH_START2, pos, false, true);
break;
}
case EVENT_FLIGHT_EMOTE:
Talk(EMOTE_BREATH);
break;
case EVENT_CORRUPT_TRIGGERS:
Talk(YELL_BREATH);
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(0));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(500));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(1000));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(1500));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(2000));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(2500));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(3000));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(3500));
me->m_Events.AddEvent(new CorruptTriggers(me), me->m_Events.CalculateTime(4000));
break;
case EVENT_FLIGHT_FLYOVER1:
me->CastSpell(me, SPELL_FELMYST_SPEED_BURST, true);
me->GetMotionMaster()->MovePoint(POINT_AIR_BREATH_END1, me->GetPositionX(), me->GetPositionY() - 200.0f, me->GetPositionZ() + 5.0f, false, true);
break;
case EVENT_FLIGHT_FLYOVER2:
me->CastSpell(me, SPELL_FELMYST_SPEED_BURST, true);
me->GetMotionMaster()->MovePoint(POINT_AIR_BREATH_END2, me->GetPositionX(), me->GetPositionY() + 200.0f, me->GetPositionZ() + 5.0f, false, true);
break;
case EVENT_LAND_FIGHT:
me->GetMotionMaster()->MovePoint(POINT_GROUND, 1500.0f, 552.8f, 26.52f, false, true);
break;
}
if (!me->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))
DoMeleeAttackIfReady();
}
};
class npc_demonic_vapor : public CreatureScript
struct npc_demonic_vapor : public NullCreatureAI
{
public:
npc_demonic_vapor() : CreatureScript("npc_demonic_vapor") { }
npc_demonic_vapor(Creature* creature) : NullCreatureAI(creature) { }
CreatureAI* GetAI(Creature* creature) const override
void Reset() override
{
return GetSunwellPlateauAI<npc_demonic_vaporAI>(creature);
me->CastSpell(me, SPELL_DEMONIC_VAPOR_SPAWN_TRIGGER, true);
me->CastSpell(me, SPELL_DEMONIC_VAPOR_PERIODIC, true);
}
struct npc_demonic_vaporAI : public NullCreatureAI
void UpdateAI(uint32 /*diff*/) override
{
npc_demonic_vaporAI(Creature* creature) : NullCreatureAI(creature) { }
void Reset() override
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE)
{
me->CastSpell(me, SPELL_DEMONIC_VAPOR_SPAWN_TRIGGER, true);
me->CastSpell(me, SPELL_DEMONIC_VAPOR_PERIODIC, true);
}
void UpdateAI(uint32 /*diff*/) override
{
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE)
{
Map::PlayerList const& players = me->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
if (me->GetDistance2d(itr->GetSource()) < 20.0f && itr->GetSource()->IsAlive())
{
me->GetMotionMaster()->MoveFollow(itr->GetSource(), 0.0f, 0.0f, MOTION_SLOT_CONTROLLED);
break;
}
}
}
};
};
class npc_demonic_vapor_trail : public CreatureScript
{
public:
npc_demonic_vapor_trail() : CreatureScript("npc_demonic_vapor_trail") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI<npc_demonic_vapor_trailAI>(creature);
}
struct npc_demonic_vapor_trailAI : public NullCreatureAI
{
npc_demonic_vapor_trailAI(Creature* creature) : NullCreatureAI(creature)
{
timer = 1;
}
uint32 timer;
void Reset() override
{
me->CastSpell(me, SPELL_DEMONIC_VAPOR_TRAIL_PERIODIC, true);
}
void SpellHitTarget(Unit*, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_DEMONIC_VAPOR)
me->CastSpell(me, SPELL_SUMMON_BLAZING_DEAD, true);
}
void UpdateAI(uint32 diff) override
{
if (timer)
{
timer += diff;
if (timer >= 6000)
Map::PlayerList const& players = me->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
if (me->GetDistance2d(itr->GetSource()) < 20.0f && itr->GetSource()->IsAlive())
{
timer = 0;
me->CastSpell(me, SPELL_SUMMON_BLAZING_DEAD, true);
me->GetMotionMaster()->MoveFollow(itr->GetSource(), 0.0f, 0.0f, MOTION_SLOT_CONTROLLED);
break;
}
}
}
};
struct npc_demonic_vapor_trail : public NullCreatureAI
{
npc_demonic_vapor_trail(Creature* creature) : NullCreatureAI(creature)
{
timer = 1;
}
uint32 timer;
void Reset() override
{
me->CastSpell(me, SPELL_DEMONIC_VAPOR_TRAIL_PERIODIC, true);
}
void SpellHitTarget(Unit*, SpellInfo const* spellInfo) override
{
if (spellInfo->Id == SPELL_DEMONIC_VAPOR)
me->CastSpell(me, SPELL_SUMMON_BLAZING_DEAD, true);
}
void UpdateAI(uint32 diff) override
{
if (timer)
{
timer += diff;
if (timer >= 6000)
{
timer = 0;
me->CastSpell(me, SPELL_SUMMON_BLAZING_DEAD, true);
}
}
}
void JustSummoned(Creature* summon) override
{
summon->SetInCombatWithZone();
summon->AI()->AttackStart(summon->AI()->SelectTarget(SelectTargetMethod::Random, 0, 100.0f));
}
};
void JustSummoned(Creature* summon) override
{
summon->SetInCombatWithZone();
summon->AI()->AttackStart(summon->AI()->SelectTarget(SelectTargetMethod::Random, 0, 100.0f));
}
};
class spell_felmyst_fog_of_corruption : public SpellScript
@@ -558,9 +525,9 @@ class spell_felmyst_open_brutallus_back_doors : public SpellScript
void AddSC_boss_felmyst()
{
new boss_felmyst();
new npc_demonic_vapor();
new npc_demonic_vapor_trail();
RegisterSunwellPlateauCreatureAI(boss_felmyst);
RegisterSunwellPlateauCreatureAI(npc_demonic_vapor);
RegisterSunwellPlateauCreatureAI(npc_demonic_vapor_trail);
RegisterSpellScript(spell_felmyst_fog_of_corruption);
RegisterSpellScript(spell_felmyst_fog_of_corruption_charm_aura);
RegisterSpellScript(spell_felmyst_open_brutallus_back_doors);
@@ -114,250 +114,239 @@ enum kalEvents
#define DRAGON_REALM_Z 53.079f
class boss_kalecgos : public CreatureScript
struct boss_kalecgos : public BossAI
{
public:
boss_kalecgos() : CreatureScript("boss_kalecgos") { }
struct boss_kalecgosAI : public BossAI
boss_kalecgos(Creature* creature) : BossAI(creature, DATA_KALECGOS)
{
boss_kalecgosAI(Creature* creature) : BossAI(creature, DATA_KALECGOS)
}
bool sathBanished;
EventMap events2;
bool CanAIAttack(Unit const* target) const override
{
return target->GetPositionZ() > 50.0f;
}
void JustReachedHome() override
{
BossAI::JustReachedHome();
me->SetVisible(true);
}
void Reset() override
{
BossAI::Reset();
me->SetHealth(me->GetMaxHealth());
me->SetStandState(UNIT_STAND_STATE_SLEEP);
me->SetDisableGravity(false);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
events2.Reset();
sathBanished = false;
ClearPlayerAuras();
}
void ClearPlayerAuras() const
{
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_CURSE_OF_BOUNDLESS_AGONY);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_CURSE_OF_BOUNDLESS_AGONY_PLR);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SPECTRAL_REALM);
}
void DoAction(int32 param) override
{
if (param == ACTION_ENRAGE || param == ACTION_ENRAGE_OTHER)
{
Talk(param == ACTION_ENRAGE ? SAY_KALEC_ENRAGE_SATH : SAY_SATH_ENRAGE_ME);
me->CastSpell(me, SPELL_CRAZED_RAGE, true);
events.CancelEvent(EVENT_CHECK_HEALTH);
return;
}
bool sathBanished;
EventMap events2;
bool CanAIAttack(Unit const* target) const override
else if (param == ACTION_BANISH)
{
return target->GetPositionZ() > 50.0f;
me->CastSpell(me, SPELL_BANISH, true);
events.Reset();
}
void JustReachedHome() override
else if (param == ACTION_SATH_BANISH)
sathBanished = true;
else if (param == ACTION_KALEC_DIED)
{
BossAI::JustReachedHome();
me->SetVisible(true);
}
void Reset() override
{
BossAI::Reset();
me->SetHealth(me->GetMaxHealth());
me->SetStandState(UNIT_STAND_STATE_SLEEP);
me->SetDisableGravity(false);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
events2.Reset();
sathBanished = false;
events.Reset();
events2.ScheduleEvent(EVENT_TALK_BAD_1, 0);
ClearPlayerAuras();
return;
}
void ClearPlayerAuras() const
if (me->HasAura(SPELL_BANISH) && sathBanished)
{
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_CURSE_OF_BOUNDLESS_AGONY);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_CURSE_OF_BOUNDLESS_AGONY_PLR);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SPECTRAL_REALM);
}
void DoAction(int32 param) override
{
if (param == ACTION_ENRAGE || param == ACTION_ENRAGE_OTHER)
events.Reset();
events2.ScheduleEvent(EVENT_TALK_GOOD_1, 1000);
ClearPlayerAuras();
if (Creature* Sath = instance->GetCreature(DATA_SATHROVARR))
{
Talk(param == ACTION_ENRAGE ? SAY_KALEC_ENRAGE_SATH : SAY_SATH_ENRAGE_ME);
me->CastSpell(me, SPELL_CRAZED_RAGE, true);
events.CancelEvent(EVENT_CHECK_HEALTH);
return;
}
else if (param == ACTION_BANISH)
{
me->CastSpell(me, SPELL_BANISH, true);
events.Reset();
}
else if (param == ACTION_SATH_BANISH)
sathBanished = true;
else if (param == ACTION_KALEC_DIED)
{
events.Reset();
events2.ScheduleEvent(EVENT_TALK_BAD_1, 0);
ClearPlayerAuras();
return;
}
if (me->HasAura(SPELL_BANISH) && sathBanished)
{
events.Reset();
events2.ScheduleEvent(EVENT_TALK_GOOD_1, 1000);
ClearPlayerAuras();
if (Creature* Sath = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_SATHROVARR)))
{
Sath->RemoveAllAuras();
Sath->GetMotionMaster()->MovementExpired();
Sath->SetReactState(REACT_PASSIVE);
Sath->NearTeleportTo(1696.20f, 915.0f, DRAGON_REALM_Z, Sath->GetOrientation());
}
Sath->RemoveAllAuras();
Sath->GetMotionMaster()->MovementExpired();
Sath->SetReactState(REACT_PASSIVE);
Sath->NearTeleportTo(1696.20f, 915.0f, DRAGON_REALM_Z, Sath->GetOrientation());
}
}
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_ARCANE_BUFFET, 6000);
events.ScheduleEvent(EVENT_FROST_BREATH, 15000);
events.ScheduleEvent(EVENT_WILD_MAGIC, 10000);
events.ScheduleEvent(EVENT_TAIL_LASH, 25000);
events.ScheduleEvent(EVENT_SPECTRAL_BLAST, 20000);
events.ScheduleEvent(EVENT_CHECK_POS, 5000);
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
events.ScheduleEvent(EVENT_SPAWN_SPECTRALS, 16000);
me->SetStandState(UNIT_STAND_STATE_STAND);
Talk(SAY_EVIL_AGGRO);
}
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= me->GetHealth() && attacker != me)
damage = 0;
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && roll_chance_i(50))
Talk(SAY_EVIL_SLAY);
}
void UpdateAI(uint32 diff) override
{
events2.Update(diff);
switch (events2.ExecuteEvent())
{
case EVENT_TALK_GOOD_1:
me->SetRegeneratingHealth(false);
me->RemoveAllAuras();
me->SetReactState(REACT_PASSIVE);
me->CombatStop();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetFaction(FACTION_FRIENDLY);
events2.ScheduleEvent(EVENT_TALK_GOOD_2, 1000);
break;
case EVENT_TALK_GOOD_2:
if (Creature* Sath = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_SATHROVARR)))
{
summons.Despawn(Sath);
Unit::Kill(me, Sath);
}
events2.ScheduleEvent(EVENT_TALK_GOOD_3, 8000);
break;
case EVENT_TALK_GOOD_3:
Talk(SAY_GOOD_PLRWIN);
events2.ScheduleEvent(EVENT_TALK_GOOD_4, 10000);
break;
case EVENT_TALK_GOOD_4:
me->SetDisableGravity(true);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
events2.ScheduleEvent(EVENT_TALK_GOOD_5, 10000);
break;
case EVENT_TALK_GOOD_5:
me->SetVisible(false);
me->KillSelf();
break;
case EVENT_TALK_BAD_1:
me->SetReactState(REACT_PASSIVE);
me->CombatStop();
me->RemoveAllAuras();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
Talk(SAY_EVIL_ENRAGE);
events2.ScheduleEvent(EVENT_TALK_BAD_2, 3000);
break;
case EVENT_TALK_BAD_2:
me->SetDisableGravity(true);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
events2.ScheduleEvent(EVENT_TALK_BAD_3, 15000);
break;
case EVENT_TALK_BAD_3:
me->SetVisible(false);
EnterEvadeMode();
break;
}
if (!events2.Empty())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPAWN_SPECTRALS:
me->SummonCreature(NPC_KALEC, 1702.21f, 931.7f, -74.56f, 5.07f, TEMPSUMMON_MANUAL_DESPAWN);
me->SummonCreature(NPC_SATHROVARR, 1704.62f, 927.78f, -73.9f, 2.0f, TEMPSUMMON_MANUAL_DESPAWN);
break;
case EVENT_ARCANE_BUFFET:
me->CastSpell(me, SPELL_ARCANE_BUFFET, false);
events.ScheduleEvent(EVENT_ARCANE_BUFFET, 8000);
break;
case EVENT_FROST_BREATH:
me->CastSpell(me->GetVictim(), SPELL_FROST_BREATH, false);
events.ScheduleEvent(EVENT_FROST_BREATH, 15000);
break;
case EVENT_TAIL_LASH:
me->CastSpell(me->GetVictim(), SPELL_TAIL_LASH, false);
events.ScheduleEvent(EVENT_TAIL_LASH, 15000);
break;
case EVENT_WILD_MAGIC:
me->CastCustomSpell(RAND(44978, 45001, 45002, 45004, 45006, 45010), SPELLVALUE_MAX_TARGETS, 1, me, false);
events.ScheduleEvent(EVENT_WILD_MAGIC, 20000);
break;
case EVENT_SPECTRAL_BLAST:
me->CastSpell(me, SPELL_SPECTRAL_BLAST, false);
events.ScheduleEvent(EVENT_SPECTRAL_BLAST, urand(15000, 25000));
break;
case EVENT_CHECK_POS:
if (me->GetDistance(me->GetHomePosition()) > 50.0f)
{
EnterEvadeMode();
return;
}
events.ScheduleEvent(EVENT_CHECK_POS, 5000);
break;
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(10))
{
if (Creature* Sath = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_SATHROVARR)))
Sath->AI()->DoAction(ACTION_ENRAGE_OTHER);
DoAction(ACTION_ENRAGE);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
break;
case EVENT_CHECK_HEALTH2:
if (me->HealthBelowPct(1))
{
DoAction(ACTION_BANISH);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
break;
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
void JustDied(Unit* killer) override
{
return GetSunwellPlateauAI<boss_kalecgosAI>(creature);
BossAI::JustDied(killer);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_ARCANE_BUFFET, 6000);
events.ScheduleEvent(EVENT_FROST_BREATH, 15000);
events.ScheduleEvent(EVENT_WILD_MAGIC, 10000);
events.ScheduleEvent(EVENT_TAIL_LASH, 25000);
events.ScheduleEvent(EVENT_SPECTRAL_BLAST, 20000);
events.ScheduleEvent(EVENT_CHECK_POS, 5000);
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
events.ScheduleEvent(EVENT_SPAWN_SPECTRALS, 16000);
me->SetStandState(UNIT_STAND_STATE_STAND);
Talk(SAY_EVIL_AGGRO);
}
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= me->GetHealth() && attacker != me)
damage = 0;
}
void KilledUnit(Unit* victim) override
{
if (victim->IsPlayer() && roll_chance_i(50))
Talk(SAY_EVIL_SLAY);
}
void UpdateAI(uint32 diff) override
{
events2.Update(diff);
switch (events2.ExecuteEvent())
{
case EVENT_TALK_GOOD_1:
me->SetRegeneratingHealth(false);
me->RemoveAllAuras();
me->SetReactState(REACT_PASSIVE);
me->CombatStop();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetFaction(FACTION_FRIENDLY);
events2.ScheduleEvent(EVENT_TALK_GOOD_2, 1000);
break;
case EVENT_TALK_GOOD_2:
if (Creature* Sath = instance->GetCreature(DATA_SATHROVARR))
{
summons.Despawn(Sath);
Unit::Kill(me, Sath);
}
events2.ScheduleEvent(EVENT_TALK_GOOD_3, 8000);
break;
case EVENT_TALK_GOOD_3:
Talk(SAY_GOOD_PLRWIN);
events2.ScheduleEvent(EVENT_TALK_GOOD_4, 10000);
break;
case EVENT_TALK_GOOD_4:
me->SetDisableGravity(true);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
events2.ScheduleEvent(EVENT_TALK_GOOD_5, 10000);
break;
case EVENT_TALK_GOOD_5:
me->SetVisible(false);
me->KillSelf();
break;
case EVENT_TALK_BAD_1:
me->SetReactState(REACT_PASSIVE);
me->CombatStop();
me->RemoveAllAuras();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
Talk(SAY_EVIL_ENRAGE);
events2.ScheduleEvent(EVENT_TALK_BAD_2, 3000);
break;
case EVENT_TALK_BAD_2:
me->SetDisableGravity(true);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
events2.ScheduleEvent(EVENT_TALK_BAD_3, 15000);
break;
case EVENT_TALK_BAD_3:
me->SetVisible(false);
EnterEvadeMode();
break;
}
if (!events2.Empty())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPAWN_SPECTRALS:
me->SummonCreature(NPC_KALEC, 1702.21f, 931.7f, -74.56f, 5.07f, TEMPSUMMON_MANUAL_DESPAWN);
me->SummonCreature(NPC_SATHROVARR, 1704.62f, 927.78f, -73.9f, 2.0f, TEMPSUMMON_MANUAL_DESPAWN);
break;
case EVENT_ARCANE_BUFFET:
me->CastSpell(me, SPELL_ARCANE_BUFFET, false);
events.ScheduleEvent(EVENT_ARCANE_BUFFET, 8000);
break;
case EVENT_FROST_BREATH:
me->CastSpell(me->GetVictim(), SPELL_FROST_BREATH, false);
events.ScheduleEvent(EVENT_FROST_BREATH, 15000);
break;
case EVENT_TAIL_LASH:
me->CastSpell(me->GetVictim(), SPELL_TAIL_LASH, false);
events.ScheduleEvent(EVENT_TAIL_LASH, 15000);
break;
case EVENT_WILD_MAGIC:
me->CastCustomSpell(RAND(44978, 45001, 45002, 45004, 45006, 45010), SPELLVALUE_MAX_TARGETS, 1, me, false);
events.ScheduleEvent(EVENT_WILD_MAGIC, 20000);
break;
case EVENT_SPECTRAL_BLAST:
me->CastSpell(me, SPELL_SPECTRAL_BLAST, false);
events.ScheduleEvent(EVENT_SPECTRAL_BLAST, urand(15000, 25000));
break;
case EVENT_CHECK_POS:
if (me->GetDistance(me->GetHomePosition()) > 50.0f)
{
EnterEvadeMode();
return;
}
events.ScheduleEvent(EVENT_CHECK_POS, 5000);
break;
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(10))
{
if (Creature* Sath = instance->GetCreature(DATA_SATHROVARR))
Sath->AI()->DoAction(ACTION_ENRAGE_OTHER);
DoAction(ACTION_ENRAGE);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
break;
case EVENT_CHECK_HEALTH2:
if (me->HealthBelowPct(1))
{
DoAction(ACTION_BANISH);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
break;
}
DoMeleeAttackIfReady();
}
};
@@ -371,255 +360,233 @@ enum Kalec
EVENT_KALEC_SCENE_3 = 103
};
class boss_kalec : public CreatureScript
struct boss_kalec : public ScriptedAI
{
public:
boss_kalec() : CreatureScript("boss_kalec") { }
boss_kalec(Creature* creature) : ScriptedAI(creature) { }
CreatureAI* GetAI(Creature* creature) const override
EventMap events;
EventMap events2;
void Reset() override
{
return GetSunwellPlateauAI<boss_kalecAI>(creature);
events.Reset();
events2.Reset();
if (me->GetPositionY() < 750.0f)
{
me->SetSpeed(MOVE_RUN, 2.4f);
me->SetDisplayId(MODEL_KALECGOS_DRAGON);
me->SetDisableGravity(true);
me->GetMotionMaster()->MovePoint(0, 1483.30f, 657.99f, 28.0f, false, true);
events2.ScheduleEvent(EVENT_KALEC_SCENE_1, 9000);
events2.ScheduleEvent(EVENT_KALEC_SCENE_2, 16000);
events2.ScheduleEvent(EVENT_KALEC_SCENE_3, 22000);
}
else
me->CastSpell(me, SPELL_SPECTRAL_INVISIBILITY, true);
}
struct boss_kalecAI : public ScriptedAI
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
boss_kalecAI(Creature* creature) : ScriptedAI(creature) { }
if (!who || who->GetEntry() != NPC_SATHROVARR)
damage = 0;
}
EventMap events;
EventMap events2;
void JustEngagedWith(Unit*) override
{
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
events.ScheduleEvent(EVENT_SPELL_REVITALIZE, 5000);
events.ScheduleEvent(EVENT_SPELL_HEROIC_STRIKE, 3000);
Talk(SAY_GOOD_AGGRO);
}
void Reset() override
void JustDied(Unit*) override
{
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS))
kalecgos->AI()->DoAction(ACTION_KALEC_DIED);
}
void UpdateAI(uint32 diff) override
{
events2.Update(diff);
switch (events2.ExecuteEvent())
{
events.Reset();
events2.Reset();
if (me->GetPositionY() < 750.0f)
case EVENT_KALEC_SCENE_1:
Talk(SAY_GOOD_MADRIGOSA);
me->GetMotionMaster()->MovePoint(0, 1509.0f, 560.0f, 30.0f, false, true);
break;
case EVENT_KALEC_SCENE_2:
me->CastSpell(me, SPELL_OPEN_BRUTALLUS_BACK_DOOR, true);
me->GetInstanceScript()->SetBossState(DATA_FELMYST_DOORS, NOT_STARTED);
me->GetInstanceScript()->SetBossState(DATA_FELMYST_DOORS, DONE);
break;
case EVENT_KALEC_SCENE_3:
me->GetMotionMaster()->MovePoint(0, 1400.0f, 630.0f, 90.0f, false, true);
me->DespawnOrUnsummon(6000);
break;
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(50))
{
me->SetSpeed(MOVE_RUN, 2.4f);
me->SetDisplayId(MODEL_KALECGOS_DRAGON);
me->SetDisableGravity(true);
me->GetMotionMaster()->MovePoint(0, 1483.30f, 657.99f, 28.0f, false, true);
events2.ScheduleEvent(EVENT_KALEC_SCENE_1, 9000);
events2.ScheduleEvent(EVENT_KALEC_SCENE_2, 16000);
events2.ScheduleEvent(EVENT_KALEC_SCENE_3, 22000);
Talk(SAY_GOOD_NEAR_DEATH);
break;
}
else
me->CastSpell(me, SPELL_SPECTRAL_INVISIBILITY, true);
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (!who || who->GetEntry() != NPC_SATHROVARR)
damage = 0;
}
void JustEngagedWith(Unit*) override
{
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
break;
case EVENT_CHECK_HEALTH2:
if (me->HealthBelowPct(10))
{
Talk(SAY_GOOD_NEAR_DEATH2);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
events.ScheduleEvent(EVENT_SPELL_REVITALIZE, 5000);
events.ScheduleEvent(EVENT_SPELL_HEROIC_STRIKE, 3000);
Talk(SAY_GOOD_AGGRO);
break;
case EVENT_SPELL_REVITALIZE:
me->CastSpell(me, SPELL_REVITALIZE, false);
events.ScheduleEvent(EVENT_SPELL_REVITALIZE, 10000);
break;
case EVENT_SPELL_HEROIC_STRIKE:
me->CastSpell(me->GetVictim(), SPELL_HEROIC_STRIKE, false);
events.ScheduleEvent(EVENT_SPELL_HEROIC_STRIKE, 5000);
break;
}
void JustDied(Unit*) override
{
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* kalecgos = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_KALECGOS)))
kalecgos->AI()->DoAction(ACTION_KALEC_DIED);
}
void UpdateAI(uint32 diff) override
{
events2.Update(diff);
switch (events2.ExecuteEvent())
{
case EVENT_KALEC_SCENE_1:
Talk(SAY_GOOD_MADRIGOSA);
me->GetMotionMaster()->MovePoint(0, 1509.0f, 560.0f, 30.0f, false, true);
break;
case EVENT_KALEC_SCENE_2:
me->CastSpell(me, SPELL_OPEN_BRUTALLUS_BACK_DOOR, true);
me->GetInstanceScript()->SetBossState(DATA_FELMYST_DOORS, NOT_STARTED);
me->GetInstanceScript()->SetBossState(DATA_FELMYST_DOORS, DONE);
break;
case EVENT_KALEC_SCENE_3:
me->GetMotionMaster()->MovePoint(0, 1400.0f, 630.0f, 90.0f, false, true);
me->DespawnOrUnsummon(6000);
break;
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(50))
{
Talk(SAY_GOOD_NEAR_DEATH);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
break;
case EVENT_CHECK_HEALTH2:
if (me->HealthBelowPct(10))
{
Talk(SAY_GOOD_NEAR_DEATH2);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
break;
case EVENT_SPELL_REVITALIZE:
me->CastSpell(me, SPELL_REVITALIZE, false);
events.ScheduleEvent(EVENT_SPELL_REVITALIZE, 10000);
break;
case EVENT_SPELL_HEROIC_STRIKE:
me->CastSpell(me->GetVictim(), SPELL_HEROIC_STRIKE, false);
events.ScheduleEvent(EVENT_SPELL_HEROIC_STRIKE, 5000);
break;
}
DoMeleeAttackIfReady();
}
};
DoMeleeAttackIfReady();
}
};
class boss_sathrovarr : public CreatureScript
struct boss_sathrovarr : public ScriptedAI
{
public:
boss_sathrovarr() : CreatureScript("boss_sathrovarr") { }
CreatureAI* GetAI(Creature* creature) const override
boss_sathrovarr(Creature* creature) : ScriptedAI(creature)
{
return GetSunwellPlateauAI<boss_sathrovarrAI>(creature);
instance = creature->GetInstanceScript();
}
struct boss_sathrovarrAI : public ScriptedAI
InstanceScript* instance;
EventMap events;
bool CanAIAttack(Unit const* target) const override
{
boss_sathrovarrAI(Creature* creature) : ScriptedAI(creature)
return target->GetPositionZ() < 50.0f;
}
void Reset() override
{
events.Reset();
me->CastSpell(me, SPELL_DEMONIC_VISUAL, true);
me->CastSpell(me, SPELL_SPECTRAL_INVISIBILITY, true);
events.ScheduleEvent(EVENT_SHADOW_BOLT, 7000);
events.ScheduleEvent(EVENT_AGONY_CURSE, 20000);
events.ScheduleEvent(EVENT_CORRUPTION_STRIKE, 13000);
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
}
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_SATH_AGGRO);
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= me->GetHealth() && who != me)
damage = 0;
}
void KilledUnit(Unit* target) override
{
if (target->IsPlayer())
Talk(SAY_SATH_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_SATH_DEATH);
}
void DoAction(int32 param) override
{
if (param == ACTION_ENRAGE || param == ACTION_ENRAGE_OTHER)
{
instance = creature->GetInstanceScript();
me->CastSpell(me, SPELL_CRAZED_RAGE, true);
events.CancelEvent(EVENT_CHECK_HEALTH);
}
InstanceScript* instance;
EventMap events;
bool CanAIAttack(Unit const* target) const override
{
return target->GetPositionZ() < 50.0f;
}
void Reset() override
else if (param == ACTION_BANISH)
{
me->CastSpell(me, SPELL_BANISH, true);
events.Reset();
me->CastSpell(me, SPELL_DEMONIC_VISUAL, true);
me->CastSpell(me, SPELL_SPECTRAL_INVISIBILITY, true);
}
}
events.ScheduleEvent(EVENT_SHADOW_BOLT, 7000);
events.ScheduleEvent(EVENT_AGONY_CURSE, 20000);
events.ScheduleEvent(EVENT_CORRUPTION_STRIKE, 13000);
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SHADOW_BOLT:
if (roll_chance_i(20))
Talk(SAY_SATH_SPELL1);
me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT, false);
events.ScheduleEvent(EVENT_SHADOW_BOLT, 9000);
break;
case EVENT_AGONY_CURSE:
me->CastCustomSpell(SPELL_CURSE_OF_BOUNDLESS_AGONY, SPELLVALUE_MAX_TARGETS, 1, me, false);
events.ScheduleEvent(EVENT_AGONY_CURSE, 30000);
break;
case EVENT_CORRUPTION_STRIKE:
if (roll_chance_i(20))
Talk(SAY_SATH_SPELL2);
me->CastSpell(me->GetVictim(), SPELL_CORRUPTION_STRIKE, false);
events.ScheduleEvent(EVENT_CORRUPTION_STRIKE, 9000);
break;
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(10))
{
if (InstanceScript* instanceScript = me->GetInstanceScript())
{
if (Creature* kalecgos = ObjectAccessor::GetCreature(*me, instanceScript->GetGuidData(
NPC_KALECGOS)))
{
kalecgos->AI()->DoAction(ACTION_ENRAGE_OTHER);
}
}
DoAction(ACTION_ENRAGE);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
break;
case EVENT_CHECK_HEALTH2:
if (me->HealthBelowPct(1))
{
if (Creature* kalecgos = instance->GetCreature(DATA_KALECGOS))
kalecgos->AI()->DoAction(ACTION_SATH_BANISH);
DoAction(ACTION_BANISH);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
break;
}
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_SATH_AGGRO);
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= me->GetHealth() && who != me)
damage = 0;
}
void KilledUnit(Unit* target) override
{
if (target->IsPlayer())
Talk(SAY_SATH_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_SATH_DEATH);
}
void DoAction(int32 param) override
{
if (param == ACTION_ENRAGE || param == ACTION_ENRAGE_OTHER)
{
me->CastSpell(me, SPELL_CRAZED_RAGE, true);
events.CancelEvent(EVENT_CHECK_HEALTH);
}
else if (param == ACTION_BANISH)
{
me->CastSpell(me, SPELL_BANISH, true);
events.Reset();
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SHADOW_BOLT:
if (roll_chance_i(20))
Talk(SAY_SATH_SPELL1);
me->CastSpell(me->GetVictim(), SPELL_SHADOW_BOLT, false);
events.ScheduleEvent(EVENT_SHADOW_BOLT, 9000);
break;
case EVENT_AGONY_CURSE:
me->CastCustomSpell(SPELL_CURSE_OF_BOUNDLESS_AGONY, SPELLVALUE_MAX_TARGETS, 1, me, false);
events.ScheduleEvent(EVENT_AGONY_CURSE, 30000);
break;
case EVENT_CORRUPTION_STRIKE:
if (roll_chance_i(20))
Talk(SAY_SATH_SPELL2);
me->CastSpell(me->GetVictim(), SPELL_CORRUPTION_STRIKE, false);
events.ScheduleEvent(EVENT_CORRUPTION_STRIKE, 9000);
break;
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(10))
{
if (InstanceScript* instanceScript = me->GetInstanceScript())
{
if (Creature *kalecgos = ObjectAccessor::GetCreature(*me, instanceScript->GetGuidData(
NPC_KALECGOS)))
{
kalecgos->AI()->DoAction(ACTION_ENRAGE_OTHER);
}
}
DoAction(ACTION_ENRAGE);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
break;
case EVENT_CHECK_HEALTH2:
if (me->HealthBelowPct(1))
{
if (Creature* kalecgos = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_KALECGOS)))
kalecgos->AI()->DoAction(ACTION_SATH_BANISH);
DoAction(ACTION_BANISH);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
break;
}
DoMeleeAttackIfReady();
}
};
DoMeleeAttackIfReady();
}
};
class SpectralBlastCheck
@@ -750,9 +717,9 @@ class spell_kalecgos_spectral_realm_aura : public AuraScript
void AddSC_boss_kalecgos()
{
new boss_kalecgos();
new boss_sathrovarr();
new boss_kalec();
RegisterSunwellPlateauCreatureAI(boss_kalecgos);
RegisterSunwellPlateauCreatureAI(boss_sathrovarr);
RegisterSunwellPlateauCreatureAI(boss_kalec);
RegisterSpellScript(spell_kalecgos_spectral_blast_dummy);
RegisterSpellScript(spell_kalecgos_curse_of_boundless_agony_aura);
RegisterSpellScript(spell_kalecgos_spectral_realm_dummy);
File diff suppressed because it is too large Load Diff
@@ -42,7 +42,6 @@ enum Spells
// Entropius's spells
SPELL_ENTROPIUS_COSMETIC_SPAWN = 46223,
SPELL_NEGATIVE_ENERGY_PERIODIC = 46284,
SPELL_NEGATIVE_ENERGY_CHAIN = 46285,
SPELL_BLACK_HOLE = 46282,
SPELL_DARKNESS = 46268,
SPELL_SUMMON_DARK_FIEND_ENTROPIUS = 46263,
@@ -55,277 +54,159 @@ enum Spells
SPELL_BLACK_HOLE_EFFECT = 46230
};
enum Misc
struct boss_muru : public BossAI
{
EVENT_SPELL_ENRAGE = 1,
EVENT_SUMMON_ENTROPIUS = 2,
EVENT_SET_INVISIBLE = 3,
EVENT_SPAWN_BLACK_HOLE = 4,
EVENT_SPAWN_DARKNESS = 5,
EVENT_START_BLACK_HOLE = 6,
EVENT_SWITCH_BLACK_HOLE_TARGET = 7,
EVENT_ENTROPIUS_AURAS = 8,
EVENT_ENTROPIUS_COMBAT = 9,
EVENT_SINGULARITY_DEATH = 10,
boss_muru(Creature* creature) : BossAI(creature, DATA_MURU) { }
DATA_ENRAGE_TIMER = 1,
DATA_NEGATIVE_ENERGY_TARGETS = 2
};
class boss_muru : public CreatureScript
{
public:
boss_muru() : CreatureScript("boss_muru") { }
struct boss_muruAI : public BossAI
void Reset() override
{
boss_muruAI(Creature* creature) : BossAI(creature, DATA_MURU) { }
BossAI::Reset();
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetVisible(true);
me->m_Events.KillAllEvents(false);
}
void Reset() override
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
DoCastSelf(SPELL_NEGATIVE_ENERGY, true);
DoCastSelf(SPELL_SUMMON_BLOOD_ELVES_PERIODIC, true);
DoCastSelf(SPELL_OPEN_PORTAL_PERIODIC, true);
DoCastSelf(SPELL_DARKNESS_PERIODIC, true);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_ENRAGE, true);
if (Creature* entropius = summons.GetCreatureWithEntry(NPC_ENTROPIUS))
entropius->CastSpell(entropius, SPELL_ENRAGE, true);
}, 10min);
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= me->GetHealth())
{
BossAI::Reset();
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetVisible(true);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
me->CastSpell(me, SPELL_NEGATIVE_ENERGY, true);
me->CastSpell(me, SPELL_SUMMON_BLOOD_ELVES_PERIODIC, true);
me->CastSpell(me, SPELL_OPEN_PORTAL_PERIODIC, true);
me->CastSpell(me, SPELL_DARKNESS_PERIODIC, true);
events.ScheduleEvent(EVENT_SPELL_ENRAGE, 600000);
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage >= me->GetHealth())
damage = me->GetHealth() - 1;
if (!me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
{
damage = 0;
if (!me->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAllAuras();
DoCastSelf(SPELL_OPEN_ALL_PORTALS, true);
me->m_Events.AddEventAtOffset([&]
{
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAllAuras();
me->CastSpell(me, SPELL_OPEN_ALL_PORTALS, true);
events.ScheduleEvent(EVENT_SUMMON_ENTROPIUS, 7000);
}
}
}
DoCastAOE(SPELL_SUMMON_ENTROPIUS);
}, 7s);
void JustSummoned(Creature* summon) override
{
if (summon->GetEntry() == NPC_ENTROPIUS)
summon->AI()->SetData(DATA_ENRAGE_TIMER, events.GetNextEventTime(EVENT_SPELL_ENRAGE));
else
{
if (!summon->IsTrigger())
summon->SetInCombatWithZone();
summons.Summon(summon);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_ENRAGE:
me->CastSpell(me, SPELL_ENRAGE, true);
break;
case EVENT_SUMMON_ENTROPIUS:
me->CastSpell(me, SPELL_SUMMON_ENTROPIUS, false);
events.ScheduleEvent(EVENT_SET_INVISIBLE, 1000);
break;
case EVENT_SET_INVISIBLE:
me->m_Events.AddEventAtOffset([&]
{
me->SetVisible(false);
break;
}, 8s);
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetSunwellPlateauAI<boss_muruAI>(creature);
}
};
class boss_entropius : public CreatureScript
struct boss_entropius : public ScriptedAI
{
public:
boss_entropius() : CreatureScript("boss_entropius") { }
boss_entropius(Creature* creature) : ScriptedAI(creature) { }
struct boss_entropiusAI : public ScriptedAI
void Reset() override
{
boss_entropiusAI(Creature* creature) : ScriptedAI(creature) { }
scheduler.CancelAll();
EventMap events;
EventMap events2;
DoCastSelf(SPELL_ENTROPIUS_COSMETIC_SPAWN);
DoCastSelf(SPELL_NEGATIVE_ENERGY_PERIODIC, true);
void Reset() override
{
events.Reset();
events2.Reset();
events2.ScheduleEvent(EVENT_ENTROPIUS_AURAS, 0);
events2.ScheduleEvent(EVENT_ENTROPIUS_COMBAT, 3000);
me->SetReactState(REACT_PASSIVE);
}
me->SetReactState(REACT_PASSIVE);
void EnterEvadeMode(EvadeReason why) override
{
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* muru = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_MURU)))
if (!muru->IsInEvadeMode())
muru->AI()->EnterEvadeMode(why);
me->m_Events.AddEventAtOffset([&] {
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
AttackStart(SelectTargetFromPlayerList(50.0f));
}, 3s);
}
me->DespawnOrUnsummon();
}
void JustEngagedWith(Unit* /*who*/) override
{
events.ScheduleEvent(EVENT_SPAWN_BLACK_HOLE, 15000);
events.ScheduleEvent(EVENT_SPAWN_DARKNESS, 10000);
}
void SetData(uint32 type, uint32 data) override
{
if (type == DATA_ENRAGE_TIMER)
events.ScheduleEvent(EVENT_SPELL_ENRAGE, data);
}
uint32 GetData(uint32 type) const override
{
if (type == DATA_NEGATIVE_ENERGY_TARGETS)
return 1 + uint32(events.GetTimer() / 12000);
return 0;
}
void JustDied(Unit* /*killer*/) override
{
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* muru = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_MURU)))
Unit::Kill(muru, muru);
}
void UpdateAI(uint32 diff) override
{
events2.Update(diff);
switch (events2.ExecuteEvent())
{
case EVENT_ENTROPIUS_AURAS:
me->CastSpell(me, SPELL_ENTROPIUS_COSMETIC_SPAWN, false);
me->CastSpell(me, SPELL_NEGATIVE_ENERGY_PERIODIC, true);
break;
case EVENT_ENTROPIUS_COMBAT:
me->SetReactState(REACT_AGGRESSIVE);
me->SetInCombatWithZone();
AttackStart(SelectTargetFromPlayerList(50.0f));
break;
}
if (!events2.Empty())
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_ENRAGE:
me->CastSpell(me, SPELL_ENRAGE, true);
break;
case EVENT_SPAWN_DARKNESS:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
me->CastSpell(target, SPELL_DARKNESS, true);
events.ScheduleEvent(EVENT_SPAWN_DARKNESS, 15000);
break;
case EVENT_SPAWN_BLACK_HOLE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
me->CastSpell(target, SPELL_BLACK_HOLE, true);
events.ScheduleEvent(EVENT_SPAWN_BLACK_HOLE, 15000);
break;
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
void EnterEvadeMode(EvadeReason why) override
{
return GetSunwellPlateauAI<boss_entropiusAI>(creature);
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* muru = instance->GetCreature(DATA_MURU))
if (!muru->IsInEvadeMode())
muru->AI()->EnterEvadeMode(why);
me->DespawnOrUnsummon();
}
void JustEngagedWith(Unit* /*who*/) override
{
ScheduleTimedEvent(10s, [&] {
DoCastRandomTarget(SPELL_DARKNESS, 0, 50.0f, true, true);
}, 15s);
ScheduleTimedEvent(15s, [&] {
DoCastRandomTarget(SPELL_BLACK_HOLE, 0, 50.0f, true, true);
}, 15s);
}
void JustDied(Unit* /*killer*/) override
{
if (InstanceScript* instance = me->GetInstanceScript())
if (Creature* muru = instance->GetCreature(DATA_MURU))
muru->KillSelf();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
}
};
class npc_singularity : public CreatureScript
struct npc_singularity : public NullCreatureAI
{
public:
npc_singularity() : CreatureScript("npc_singularity") { }
npc_singularity(Creature* creature) : NullCreatureAI(creature) { }
CreatureAI* GetAI(Creature* creature) const override
void Reset() override
{
return GetSunwellPlateauAI<npc_singularityAI>(creature);
}
me->DespawnOrUnsummon(18000);
DoCastSelf(SPELL_BLACK_HOLE_SUMMON_VISUAL, true);
DoCastSelf(SPELL_BLACK_HOLE_SUMMON_VISUAL2, true);
struct npc_singularityAI : public NullCreatureAI
{
npc_singularityAI(Creature* creature) : NullCreatureAI(creature)
me->m_Events.AddEventAtOffset([&] {
me->KillSelf();
}, 17s);
me->m_Events.AddEventAtOffset([&] {
me->RemoveAurasDueToSpell(SPELL_BLACK_HOLE_SUMMON_VISUAL2);
DoCastSelf(SPELL_BLACK_HOLE_VISUAL2, true);
DoCastSelf(SPELL_BLACK_HOLE_PASSIVE, true);
}, 3500ms);
scheduler.Schedule(5s, [this](TaskContext context)
{
}
EventMap events;
void Reset() override
{
me->DespawnOrUnsummon(18000);
me->CastSpell(me, SPELL_BLACK_HOLE_SUMMON_VISUAL, true);
me->CastSpell(me, SPELL_BLACK_HOLE_SUMMON_VISUAL2, true);
events.ScheduleEvent(EVENT_START_BLACK_HOLE, 3500);
events.ScheduleEvent(EVENT_SWITCH_BLACK_HOLE_TARGET, 5000);
events.ScheduleEvent(EVENT_SINGULARITY_DEATH, 17000);
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
switch (events.ExecuteEvent())
auto const& playerList = me->GetMap()->GetPlayers();
for (auto const& playerRef : playerList)
{
case EVENT_SINGULARITY_DEATH:
me->KillSelf();
break;
case EVENT_START_BLACK_HOLE:
me->RemoveAurasDueToSpell(SPELL_BLACK_HOLE_SUMMON_VISUAL2);
me->CastSpell(me, SPELL_BLACK_HOLE_VISUAL2, true);
me->CastSpell(me, SPELL_BLACK_HOLE_PASSIVE, true);
break;
case EVENT_SWITCH_BLACK_HOLE_TARGET:
if (Player* player = playerRef.GetSource())
if (me->GetDistance2d(player) < 15.0f && player->GetPositionZ() < 72.0f && player->IsAlive() && !player->HasAura(SPELL_BLACK_HOLE_EFFECT))
{
Map::PlayerList const& players = me->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
if (Player* player = itr->GetSource())
if (me->GetDistance2d(player) < 15.0f && player->GetPositionZ() < 72.0f && player->IsAlive() && !player->HasAura(SPELL_BLACK_HOLE_EFFECT))
{
me->GetMotionMaster()->MovePoint(0, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), false, true);
events.ScheduleEvent(EVENT_SWITCH_BLACK_HOLE_TARGET, 5000);
return;
}
events.ScheduleEvent(EVENT_SWITCH_BLACK_HOLE_TARGET, 500);
break;
me->GetMotionMaster()->MovePoint(0, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), false, true);
context.Repeat();
return;
}
}
}
};
context.Repeat(1s);
});
}
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
}
};
class spell_muru_summon_blood_elves_periodic_aura : public AuraScript
@@ -382,39 +263,6 @@ class spell_muru_darkness_aura : public AuraScript
}
};
class spell_entropius_negative_energy : public SpellScript
{
PrepareSpellScript(spell_entropius_negative_energy);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_NEGATIVE_ENERGY_CHAIN });
}
bool Load() override
{
return GetCaster()->IsCreature();
}
void FilterTargets(std::list<WorldObject*>& targets)
{
Acore::Containers::RandomResize(targets, GetCaster()->GetAI()->GetData(DATA_NEGATIVE_ENERGY_TARGETS));
}
void HandleScriptEffect(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
if (Unit* target = GetHitUnit())
GetCaster()->CastSpell(target, SPELL_NEGATIVE_ENERGY_CHAIN, true);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_entropius_negative_energy::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
OnEffectHitTarget += SpellEffectFn(spell_entropius_negative_energy::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
class spell_entropius_void_zone_visual_aura : public AuraScript
{
PrepareAuraScript(spell_entropius_void_zone_visual_aura);
@@ -473,15 +321,38 @@ class spell_entropius_black_hole_effect : public SpellScript
}
};
// 46284 - Negative Energy Periodic
class spell_entropius_negative_energy_periodic : public AuraScript
{
PrepareAuraScript(spell_entropius_negative_energy_periodic);
bool Validate(SpellInfo const* spellInfo) override
{
return ValidateSpellInfo({ spellInfo->Effects[EFFECT_0].TriggerSpell });
}
void PeriodicTick(AuraEffect const* aurEff)
{
PreventDefaultAction();
uint32 targetCount = aurEff->GetTickNumber() > 12 ? 1 : aurEff->GetTickNumber() / 12;
GetTarget()->CastCustomSpell(aurEff->GetSpellInfo()->Effects[EFFECT_0].TriggerSpell, SPELLVALUE_MAX_TARGETS, targetCount);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_entropius_negative_energy_periodic::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
void AddSC_boss_muru()
{
new boss_muru();
new boss_entropius();
new npc_singularity();
RegisterSunwellPlateauCreatureAI(boss_muru);
RegisterSunwellPlateauCreatureAI(boss_entropius);
RegisterSunwellPlateauCreatureAI(npc_singularity);
RegisterSpellScript(spell_muru_summon_blood_elves_periodic_aura);
RegisterSpellScript(spell_muru_darkness_aura);
RegisterSpellScript(spell_entropius_negative_energy);
RegisterSpellScript(spell_entropius_void_zone_visual_aura);
RegisterSpellScript(spell_entropius_black_hole_effect);
RegisterSpellScript(spell_entropius_negative_energy_periodic);
}
@@ -33,6 +33,32 @@ DoorData const doorData[] =
{ 0, 0, DOOR_TYPE_ROOM } // END
};
ObjectData const creatureData[] =
{
{ NPC_KALECGOS, DATA_KALECGOS },
{ NPC_BRUTALLUS, DATA_BRUTALLUS },
{ NPC_FELMYST, DATA_FELMYST },
{ NPC_MURU, DATA_MURU },
{ NPC_LADY_SACROLASH, DATA_SACROLASH },
{ NPC_GRAND_WARLOCK_ALYTHESS, DATA_ALYTHESS },
{ NPC_MADRIGOSA, DATA_MADRIGOSA },
{ NPC_SATHROVARR, DATA_SATHROVARR },
{ NPC_KILJAEDEN_CONTROLLER, DATA_KJ_CONTROLLER },
{ NPC_ANVEENA, DATA_ANVEENA },
{ NPC_KALECGOS_KJ, DATA_KALECGOS_KJ },
{ 0, 0, }
};
ObjectData const gameObjectData[] =
{
{ GO_ICE_BARRIER, DATA_ICEBARRIER },
{ GO_ORB_OF_THE_BLUE_DRAGONFLIGHT1, DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_1 },
{ GO_ORB_OF_THE_BLUE_DRAGONFLIGHT2, DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_2 },
{ GO_ORB_OF_THE_BLUE_DRAGONFLIGHT3, DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_3 },
{ GO_ORB_OF_THE_BLUE_DRAGONFLIGHT4, DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_4 },
{ 0, 0 }
};
class instance_sunwell_plateau : public InstanceMapScript
{
public:
@@ -45,35 +71,17 @@ public:
SetHeaders(DataHeader);
SetBossNumber(MAX_ENCOUNTERS);
LoadDoorData(doorData);
LoadObjectData(creatureData, gameObjectData);
}
void OnPlayerEnter(Player* player) override
{
instance->LoadGrid(1477.94f, 643.22f);
instance->LoadGrid(1641.45f, 988.08f);
if (GameObject* gobj = instance->GetGameObject(IceBarrierGUID))
if (GameObject* gobj = GetGameObject(DATA_ICEBARRIER))
gobj->SendUpdateToPlayer(player);
}
Player const* GetPlayerInMap() const
{
Map::PlayerList const& players = instance->GetPlayers();
if (!players.IsEmpty())
{
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
Player* player = itr->GetSource();
if (player && !player->HasAura(45839))
return player;
}
}
//else
// LOG_DEBUG("scripts", "Instance Sunwell Plateau: GetPlayerInMap, but PlayerList is empty!");
return nullptr;
}
void OnCreatureCreate(Creature* creature) override
{
if (creature->GetSpawnId() > 0 || !creature->GetOwnerGUID().IsPlayer())
@@ -81,177 +89,32 @@ public:
switch (creature->GetEntry())
{
case NPC_KALECGOS:
KalecgosDragonGUID = creature->GetGUID();
break;
case NPC_SATHROVARR:
SathrovarrGUID = creature->GetGUID();
break;
case NPC_BRUTALLUS:
BrutallusGUID = creature->GetGUID();
break;
case NPC_MADRIGOSA:
MadrigosaGUID = creature->GetGUID();
break;
case NPC_FELMYST:
FelmystGUID = creature->GetGUID();
break;
case NPC_GRAND_WARLOCK_ALYTHESS:
AlythessGUID = creature->GetGUID();
break;
case NPC_LADY_SACROLASH:
SacrolashGUID = creature->GetGUID();
break;
case NPC_MURU:
MuruGUID = creature->GetGUID();
break;
case NPC_KILJAEDEN:
KilJaedenGUID = creature->GetGUID();
break;
case NPC_KILJAEDEN_CONTROLLER:
KilJaedenControllerGUID = creature->GetGUID();
break;
case NPC_ANVEENA:
AnveenaGUID = creature->GetGUID();
break;
case NPC_KALECGOS_KJ:
KalecgosKjGUID = creature->GetGUID();
break;
// Xinef: Felmyst encounter
case NPC_DEMONIC_VAPOR_TRAIL:
case NPC_UNYIELDING_DEAD:
if (Creature* felmyst = instance->GetCreature(FelmystGUID))
if (Creature* felmyst = GetCreature(DATA_FELMYST))
felmyst->AI()->JustSummoned(creature);
break;
// Xinef: M'uru encounter
case NPC_DARKNESS:
case NPC_VOID_SENTINEL:
case NPC_VOID_SPAWN:
if (Creature* muru = instance->GetCreature(MuruGUID))
if (Creature* muru = GetCreature(DATA_MURU))
muru->AI()->JustSummoned(creature);
break;
// Xinef: Kil'jaeden encounter
case NPC_FELFIRE_PORTAL:
case NPC_VOLATILE_FELFIRE_FIEND:
case NPC_SHIELD_ORB:
case NPC_SINISTER_REFLECTION:
if (Creature* kiljaedenC = instance->GetCreature(KilJaedenControllerGUID))
if (Creature* kiljaedenC = GetCreature(DATA_KJ_CONTROLLER))
kiljaedenC->AI()->JustSummoned(creature);
break;
default:
break;
}
InstanceScript::OnCreatureCreate(creature);
}
void OnGameObjectCreate(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_FORCE_FIELD:
case GO_BOSS_COLLISION_1:
case GO_BOSS_COLLISION_2:
case GO_FIRE_BARRIER:
case GO_MURUS_GATE_1:
case GO_MURUS_GATE_2:
AddDoor(go);
break;
case GO_ICE_BARRIER:
IceBarrierGUID = go->GetGUID();
go->setActive(true);
break;
case GO_ORB_OF_THE_BLUE_DRAGONFLIGHT1:
blueFlightOrbGUID[0] = go->GetGUID();
break;
case GO_ORB_OF_THE_BLUE_DRAGONFLIGHT2:
blueFlightOrbGUID[1] = go->GetGUID();
break;
case GO_ORB_OF_THE_BLUE_DRAGONFLIGHT3:
blueFlightOrbGUID[2] = go->GetGUID();
break;
case GO_ORB_OF_THE_BLUE_DRAGONFLIGHT4:
blueFlightOrbGUID[3] = go->GetGUID();
break;
default:
break;
}
}
void OnGameObjectRemove(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_FIRE_BARRIER:
case GO_MURUS_GATE_1:
case GO_MURUS_GATE_2:
case GO_BOSS_COLLISION_1:
case GO_BOSS_COLLISION_2:
case GO_FORCE_FIELD:
RemoveDoor(go);
break;
default:
break;
}
}
ObjectGuid GetGuidData(uint32 id) const override
{
switch (id)
{
case NPC_KALECGOS:
return KalecgosDragonGUID;
case NPC_SATHROVARR:
return SathrovarrGUID;
case NPC_BRUTALLUS:
return BrutallusGUID;
case NPC_MADRIGOSA:
return MadrigosaGUID;
case NPC_FELMYST:
return FelmystGUID;
case NPC_GRAND_WARLOCK_ALYTHESS:
return AlythessGUID;
case NPC_LADY_SACROLASH:
return SacrolashGUID;
case NPC_MURU:
return MuruGUID;
case NPC_ANVEENA:
return AnveenaGUID;
case NPC_KALECGOS_KJ:
return KalecgosKjGUID;
case NPC_KILJAEDEN_CONTROLLER:
return KilJaedenControllerGUID;
case NPC_KILJAEDEN:
return KilJaedenGUID;
// Orbs
case DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_1:
case DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_2:
case DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_3:
case DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_4:
return blueFlightOrbGUID[id - DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_1];
}
return ObjectGuid::Empty;
}
protected:
ObjectGuid KalecgosDragonGUID;
ObjectGuid SathrovarrGUID;
ObjectGuid BrutallusGUID;
ObjectGuid MadrigosaGUID;
ObjectGuid FelmystGUID;
ObjectGuid AlythessGUID;
ObjectGuid SacrolashGUID;
ObjectGuid MuruGUID;
ObjectGuid KilJaedenGUID;
ObjectGuid KilJaedenControllerGUID;
ObjectGuid AnveenaGUID;
ObjectGuid KalecgosKjGUID;
ObjectGuid IceBarrierGUID;
ObjectGuid blueFlightOrbGUID[4];
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override
@@ -24,24 +24,36 @@
#define SWPScriptName "instance_sunwell_plateau"
enum DataTypes
enum BossIds
{
DATA_KALECGOS = 0,
DATA_MADRIGOSA = 1,
DATA_BRUTALLUS = 2,
DATA_FELMYST = 3,
DATA_FELMYST_DOORS = 4,
DATA_EREDAR_TWINS_INTRO = 5,
DATA_EREDAR_TWINS = 6,
DATA_MURU = 7,
DATA_KILJAEDEN = 8,
MAX_ENCOUNTERS = 9,
DATA_BRUTALLUS = 1,
DATA_FELMYST = 2,
DATA_FELMYST_DOORS = 3,
DATA_EREDAR_TWINS = 4,
DATA_MURU = 5,
DATA_KILJAEDEN = 6,
MAX_ENCOUNTERS
};
// GameObject GUIDs
DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_1 = 10,
DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_2 = 11,
DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_3 = 12,
DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_4 = 13
enum DataTypes
{
DATA_SACROLASH = 7,
DATA_ALYTHESS = 8,
DATA_MADRIGOSA = 9,
DATA_SATHROVARR = 10,
DATA_KJ_CONTROLLER = 11,
DATA_ANVEENA = 12,
DATA_KALECGOS_KJ = 13
};
enum DataTypesObjects
{
DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_1 = 0,
DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_2 = 1,
DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_3 = 2,
DATA_ORB_OF_THE_BLUE_DRAGONFLIGHT_4 = 3,
DATA_ICEBARRIER = 4,
};
enum CreatureIds
@@ -105,4 +117,6 @@ inline AI* GetSunwellPlateauAI(T* obj)
return GetInstanceAI<AI>(obj, SWPScriptName);
}
#define RegisterSunwellPlateauCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetSunwellPlateauAI)
#endif // SUNWELL_PLATEAU_H
@@ -15,20 +15,13 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: boss_Akilzon
SD%Complete: 75%
SDComment: Missing timer for Call Lightning and Sound ID's
SQLUpdate:
#Temporary fix for Soaring Eagles
EndScriptData */
#include "Cell.h"
#include "CreatureScript.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "Weather.h"
#include "zulaman.h"
@@ -39,6 +32,7 @@ enum Spells
SPELL_CALL_LIGHTNING = 43661, // Missing timer
SPELL_GUST_OF_WIND = 43621,
SPELL_ELECTRICAL_STORM = 43648,
SPELL_ELECTRICAL_STORM_AREA = 44007, // Safe within the eye of the storm
SPELL_BERSERK = 45078,
SPELL_ELECTRICAL_OVERLOAD = 43658,
SPELL_EAGLE_SWOOP = 44732,
@@ -56,394 +50,310 @@ enum Says
SAY_DEATH = 5
};
enum Misc
{
ACTION_INCREASE_STORM_COUNT = 1
};
constexpr auto NPC_SOARING_EAGLE = 24858;
//enum Misc
//{
// SE_LOC_X_MAX = 400,
// SE_LOC_X_MIN = 335,
// SE_LOC_Y_MAX = 1435,
// SE_LOC_Y_MIN = 1370
//};
enum Events
struct boss_akilzon : public BossAI
{
EVENT_STATIC_DISRUPTION = 1,
EVENT_GUST_OF_WIND = 2,
EVENT_CALL_LIGHTNING = 3,
EVENT_ELECTRICAL_STORM = 4,
EVENT_RAIN = 5,
EVENT_SUMMON_EAGLES = 6,
EVENT_STORM_SEQUENCE = 7,
EVENT_ENRAGE = 8
};
boss_akilzon(Creature* creature) : BossAI(creature, DATA_AKILZON), _stormCount(0), _isRaining(false) { }
class boss_akilzon : public CreatureScript
{
public:
boss_akilzon() : CreatureScript("boss_akilzon") { }
struct boss_akilzonAI : public BossAI
void Reset() override
{
boss_akilzonAI(Creature* creature) : BossAI(creature, DATA_AKILZON)
{
}
_Reset();
void Reset() override
{
_Reset();
_targetGUID.Clear();
_cloudGUID.Clear();
_cycloneGUID.Clear();
TargetGUID.Clear();
CloudGUID.Clear();
CycloneGUID.Clear();
_stormCount = 0;
_isRaining = false;
for (uint8 i = 0; i < 8; ++i)
BirdGUIDs[i].Clear();
SetWeather(WEATHER_STATE_FINE, 0.0f);
StormCount = 0;
isRaining = false;
SetWeather(WEATHER_STATE_FINE, 0.0f);
}
void JustEngagedWith(Unit* /*who*/) override
{
events.ScheduleEvent(EVENT_STATIC_DISRUPTION, urand(10000, 20000)); // 10 to 20 seconds (bosskillers)
events.ScheduleEvent(EVENT_GUST_OF_WIND, urand(20000, 30000)); // 20 to 30 seconds(bosskillers)
events.ScheduleEvent(EVENT_CALL_LIGHTNING, urand(10000, 20000)); // totaly random timer. can't find any info on this
events.ScheduleEvent(EVENT_ELECTRICAL_STORM, 60000); // 60 seconds(bosskillers)
events.ScheduleEvent(EVENT_RAIN, urand(47000, 52000));
events.ScheduleEvent(EVENT_ENRAGE, 10 * MINUTE * IN_MILLISECONDS); // 10 minutes till enrage(bosskillers)
Talk(SAY_AGGRO);
//DoZoneInCombat();
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
_JustDied();
}
void KilledUnit(Unit* who) override
{
if (who->IsPlayer())
Talk(SAY_KILL);
}
void SetWeather(uint32 weather, float grade)
{
Map* map = me->GetMap();
if (!map->IsDungeon())
return;
WorldPacket data(SMSG_WEATHER, (4 + 4 + 4));
data << uint32(weather) << float(grade) << uint8(0);
map->SendToPlayers(&data);
}
void HandleStormSequence(Unit* Cloud) // 1: begin, 2-9: tick, 10: end
{
if (StormCount < 10 && StormCount > 1)
{
// deal damage
int32 bp0 = 800;
for (uint8 i = 2; i < StormCount; ++i)
bp0 *= 2;
std::list<Unit*> tempUnitMap;
Acore::AnyAoETargetUnitInObjectRangeCheck u_check(me, me, SIZE_OF_GRIDS);
Acore::UnitListSearcher<Acore::AnyAoETargetUnitInObjectRangeCheck> searcher(me, tempUnitMap, u_check);
Cell::VisitAllObjects(me, searcher, SIZE_OF_GRIDS);
// deal damage
for (std::list<Unit*>::const_iterator i = tempUnitMap.begin(); i != tempUnitMap.end(); ++i)
{
if (Unit* target = (*i))
{
if (Cloud && !Cloud->IsWithinDist(target, 6, false))
Cloud->CastCustomSpell(target, SPELL_ZAP, &bp0, nullptr, nullptr, true, 0, 0, me->GetGUID());
}
}
// visual
float x, y, z;
z = me->GetPositionZ();
for (uint8 i = 0; i < 5 + rand() % 5; ++i)
{
x = 343.0f + rand() % 60;
y = 1380.0f + rand() % 60;
if (Unit* trigger = me->SummonTrigger(x, y, z, 0, 2000))
{
trigger->SetFaction(FACTION_FRIENDLY);
trigger->SetMaxHealth(100000);
trigger->SetHealth(100000);
trigger->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
if (Cloud)
Cloud->CastCustomSpell(trigger, /*43661*/SPELL_ZAP, &bp0, nullptr, nullptr, true, 0, 0, Cloud->GetGUID());
}
}
}
++StormCount;
if (StormCount > 10)
{
StormCount = 0; // finish
events.ScheduleEvent(EVENT_SUMMON_EAGLES, 5000);
me->InterruptNonMeleeSpells(false);
CloudGUID.Clear();
if (Cloud)
Unit::DealDamage(Cloud, Cloud, Cloud->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false);
SetWeather(WEATHER_STATE_FINE, 0.0f);
isRaining = false;
}
events.ScheduleEvent(EVENT_STORM_SEQUENCE, 1000);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_STATIC_DISRUPTION:
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 1);
if (!target)
target = me->GetVictim();
if (target)
{
TargetGUID = target->GetGUID();
DoCast(target, SPELL_STATIC_DISRUPTION, false);
me->SetInFront(me->GetVictim());
}
/*if (float dist = me->IsWithinDist3d(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 5.0f) dist = 5.0f;
SDisruptAOEVisual_Timer = 1000 + std::floor(dist / 30 * 1000.0f);*/
events.ScheduleEvent(EVENT_STATIC_DISRUPTION, urand(10000, 18000));
break;
}
case EVENT_GUST_OF_WIND:
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 1);
if (!target)
target = me->GetVictim();
if (target)
DoCast(target, SPELL_GUST_OF_WIND);
events.ScheduleEvent(EVENT_GUST_OF_WIND, urand(20000, 30000));
break;
}
case EVENT_CALL_LIGHTNING:
DoCastVictim(SPELL_CALL_LIGHTNING);
events.ScheduleEvent(EVENT_CALL_LIGHTNING, urand(12000, 17000)); // totaly random timer. can't find any info on this
break;
case EVENT_ELECTRICAL_STORM:
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50, true);
if (!target)
{
EnterEvadeMode();
return;
}
target->CastSpell(target, 44007, true); // cloud visual
DoCast(target, SPELL_ELECTRICAL_STORM, false); // storm cyclon + visual
float x, y, z;
target->GetPosition(x, y, z);
/// @todo: fix it in correct way, that causes player to can fly until logout
/*
if (target)
{
target->SetDisableGravity(true);
target->MonsterMoveWithSpeed(x, y, me->GetPositionZ()+15, 0);
}
*/
Unit* Cloud = me->SummonTrigger(x, y, me->GetPositionZ() + 16, 0, 15000);
if (Cloud)
{
CloudGUID = Cloud->GetGUID();
Cloud->SetDisableGravity(true);
Cloud->StopMoving();
Cloud->SetObjectScale(1.0f);
Cloud->SetFaction(FACTION_FRIENDLY);
Cloud->SetMaxHealth(9999999);
Cloud->SetHealth(9999999);
Cloud->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
StormCount = 1;
events.ScheduleEvent(EVENT_ELECTRICAL_STORM, 60000); // 60 seconds(bosskillers)
events.ScheduleEvent(EVENT_RAIN, urand(47000, 52000));
break;
}
case EVENT_RAIN:
if (!isRaining)
{
SetWeather(WEATHER_STATE_HEAVY_RAIN, 0.9999f);
isRaining = true;
}
else
events.ScheduleEvent(EVENT_RAIN, 1000);
break;
case EVENT_STORM_SEQUENCE:
{
Unit* target = ObjectAccessor::GetUnit(*me, CloudGUID);
if (!target || !target->IsAlive())
{
EnterEvadeMode();
return;
}
else if (Unit* Cyclone = ObjectAccessor::GetUnit(*me, CycloneGUID))
Cyclone->CastSpell(target, SPELL_SAND_STORM, true); // keep casting or...
HandleStormSequence(target);
break;
}
case EVENT_SUMMON_EAGLES:
Talk(SAY_SUMMON);
float x, y, z;
me->GetPosition(x, y, z);
for (uint8 i = 0; i < 8; ++i)
{
Unit* bird = ObjectAccessor::GetUnit(*me, BirdGUIDs[i]);
if (!bird) //they despawned on die
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
x = target->GetPositionX() + irand(-10, 10);
y = target->GetPositionY() + irand(-10, 10);
z = target->GetPositionZ() + urand(16, 20);
if (z > 95)
z = 95.0f - urand(0, 5);
}
Creature* creature = me->SummonCreature(NPC_SOARING_EAGLE, x, y, z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
if (creature)
{
creature->AddThreat(me->GetVictim(), 1.0f);
creature->AI()->AttackStart(me->GetVictim());
BirdGUIDs[i] = creature->GetGUID();
}
}
}
break;
case EVENT_ENRAGE:
Talk(SAY_ENRAGE);
DoCast(me, SPELL_BERSERK, true);
events.ScheduleEvent(EVENT_ENRAGE, 600000);
break;
default:
break;
}
}
DoMeleeAttackIfReady();
}
private:
ObjectGuid BirdGUIDs[8];
ObjectGuid TargetGUID;
ObjectGuid CycloneGUID;
ObjectGuid CloudGUID;
uint8 StormCount;
bool isRaining;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetZulAmanAI<boss_akilzonAI>(creature);
me->m_Events.KillAllEvents(false);
}
};
class npc_akilzon_eagle : public CreatureScript
{
public:
npc_akilzon_eagle() : CreatureScript("npc_akilzon_eagle") { }
struct npc_akilzon_eagleAI : public ScriptedAI
void JustEngagedWith(Unit* /*who*/) override
{
npc_akilzon_eagleAI(Creature* creature) : ScriptedAI(creature) { }
_JustEngagedWith();
uint32 EagleSwoop_Timer;
bool arrived;
ObjectGuid TargetGUID;
void Reset() override
{
EagleSwoop_Timer = urand(5000, 10000);
arrived = true;
TargetGUID.Clear();
me->SetDisableGravity(true);
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
}
void MoveInLineOfSight(Unit* /*who*/) override { }
void MovementInform(uint32, uint32) override
{
arrived = true;
if (TargetGUID)
ScheduleTimedEvent(10s, 20s, [&] {
Unit* target = SelectTarget(SelectTargetMethod::Random, 1);
if (!target)
target = me->GetVictim();
if (target)
{
if (Unit* target = ObjectAccessor::GetUnit(*me, TargetGUID))
DoCast(target, SPELL_EAGLE_SWOOP, true);
TargetGUID.Clear();
me->SetSpeed(MOVE_RUN, 1.2f);
EagleSwoop_Timer = urand(5000, 10000);
_targetGUID = target->GetGUID();
DoCast(target, SPELL_STATIC_DISRUPTION, false);
me->SetInFront(me->GetVictim());
}
}, 10s, 18s);
ScheduleTimedEvent(20s, 30s, [&] {
Unit* target = SelectTarget(SelectTargetMethod::Random, 1);
if (!target)
target = me->GetVictim();
if (target)
DoCast(target, SPELL_GUST_OF_WIND);
}, 20s, 30s);
ScheduleTimedEvent(10s, 20s, [&] {
DoCastVictim(SPELL_CALL_LIGHTNING);
}, 12s, 17s);
ScheduleTimedEvent(1min, [&] {
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50, true);
if (!target)
{
EnterEvadeMode();
return;
}
target->CastSpell(target, SPELL_ELECTRICAL_STORM_AREA, true); // cloud visual
DoCast(target, SPELL_ELECTRICAL_STORM); // storm cyclon + visual
float x, y, z;
target->GetPosition(x, y, z);
Unit* Cloud = me->SummonTrigger(x, y, me->GetPositionZ() + 16, 0, 15000);
if (Cloud)
{
target->GetMotionMaster()->MoveJump(Cloud->GetPosition(), 1.0f, 1.0f);
_cloudGUID = Cloud->GetGUID();
Cloud->SetDisableGravity(true);
Cloud->StopMoving();
Cloud->SetObjectScale(1.0f);
Cloud->SetFaction(FACTION_FRIENDLY);
Cloud->SetMaxHealth(9999999);
Cloud->SetHealth(9999999);
Cloud->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->m_Events.AddEventAtOffset([&] {
HandleStormSequence();
}, 3s);
}
_stormCount = 1;
me->m_Events.AddEventAtOffset([&] {
if (!_isRaining)
{
SetWeather(WEATHER_STATE_HEAVY_RAIN, 0.9999f);
_isRaining = true;
}
}, Seconds(urand(47, 52)));
}, 1min);
ScheduleTimedEvent(47s, 52s, [&] {
if (!_isRaining)
{
SetWeather(WEATHER_STATE_HEAVY_RAIN, 0.9999f);
_isRaining = true;
}
}, 47s, 52s);
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_ENRAGE);
DoCastSelf(SPELL_BERSERK, true);
}, 10min);
Talk(SAY_AGGRO);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
_JustDied();
me->m_Events.KillAllEvents(false);
}
void KilledUnit(Unit* who) override
{
if (who->IsPlayer())
Talk(SAY_KILL);
}
void SetWeather(uint32 weather, float grade)
{
me->GetMap()->SetZoneWeather(me->GetZoneId(), WeatherState(weather), grade);
}
void HandleStormSequence() // 1: begin, 2-9: tick, 10: end
{
Creature* Cloud = ObjectAccessor::GetCreature(*me, _cloudGUID);
if (!Cloud)
return;
if (_stormCount > 10)
{
_stormCount = 0; // finish
me->m_Events.AddEventAtOffset([&] {
SummonEagles();
}, 5s);
me->InterruptNonMeleeSpells(false);
_cloudGUID.Clear();
if (Cloud)
Cloud->KillSelf();
SetWeather(WEATHER_STATE_FINE, 0.0f);
_isRaining = false;
}
void UpdateAI(uint32 diff) override
{
if (EagleSwoop_Timer <= diff)
EagleSwoop_Timer = 0;
else
EagleSwoop_Timer -= diff;
me->m_Events.AddEventAtOffset([&] {
Unit* target = ObjectAccessor::GetUnit(*me, _cloudGUID);
if (!target || !target->IsAlive())
return;
else if (Unit* Cyclone = ObjectAccessor::GetUnit(*me, _cycloneGUID))
Cyclone->CastSpell(target, SPELL_SAND_STORM, true); // keep casting or...
HandleStormSequence();
}, 1s);
}
if (arrived)
void DoAction(int32 actionId) override
{
if (actionId == ACTION_INCREASE_STORM_COUNT)
++_stormCount;
}
void SummonEagles()
{
Talk(SAY_SUMMON);
float x, y, z;
me->GetPosition(x, y, z);
for (uint8 i = 0; i < 8; ++i)
{
Unit* bird = ObjectAccessor::GetUnit(*me, _birdGUIDs[i]);
if (!bird) //they despawned on die
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
float x, y, z;
if (EagleSwoop_Timer)
{
x = target->GetPositionX() + irand(-10, 10);
y = target->GetPositionY() + irand(-10, 10);
z = target->GetPositionZ() + urand(10, 15);
if (z > 95)
z = 95.0f - urand(0, 5);
}
else
{
target->GetContactPoint(me, x, y, z);
z += 2;
me->SetSpeed(MOVE_RUN, 5.0f);
TargetGUID = target->GetGUID();
}
me->GetMotionMaster()->MovePoint(0, x, y, z);
arrived = false;
x = target->GetPositionX() + irand(-10, 10);
y = target->GetPositionY() + irand(-10, 10);
z = target->GetPositionZ() + urand(16, 20);
if (z > 95)
z = 95.0f - urand(0, 5);
}
;
if (Creature* creature = me->SummonCreature(NPC_SOARING_EAGLE, x, y, z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0))
{
creature->AddThreat(me->GetVictim(), 1.0f);
creature->AI()->AttackStart(me->GetVictim());
_birdGUIDs[i] = creature->GetGUID();
}
}
}
};
}
CreatureAI* GetAI(Creature* creature) const override
private:
ObjectGuid _birdGUIDs[8];
ObjectGuid _targetGUID;
ObjectGuid _cycloneGUID;
ObjectGuid _cloudGUID;
uint8 _stormCount;
bool _isRaining;
};
struct npc_akilzon_eagle : public ScriptedAI
{
npc_akilzon_eagle(Creature* creature) : ScriptedAI(creature) { }
uint32 EagleSwoop_Timer;
bool arrived;
ObjectGuid TargetGUID;
void Reset() override
{
return GetZulAmanAI<npc_akilzon_eagleAI>(creature);
EagleSwoop_Timer = urand(5000, 10000);
arrived = true;
TargetGUID.Clear();
me->SetDisableGravity(true);
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
}
void MoveInLineOfSight(Unit* /*who*/) override { }
void MovementInform(uint32, uint32) override
{
arrived = true;
if (TargetGUID)
{
if (Unit* target = ObjectAccessor::GetUnit(*me, TargetGUID))
DoCast(target, SPELL_EAGLE_SWOOP, true);
TargetGUID.Clear();
me->SetSpeed(MOVE_RUN, 1.2f);
EagleSwoop_Timer = urand(5000, 10000);
}
}
void UpdateAI(uint32 diff) override
{
if (EagleSwoop_Timer <= diff)
EagleSwoop_Timer = 0;
else
EagleSwoop_Timer -= diff;
if (arrived)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
float x, y, z;
if (EagleSwoop_Timer)
{
x = target->GetPositionX() + irand(-10, 10);
y = target->GetPositionY() + irand(-10, 10);
z = target->GetPositionZ() + urand(10, 15);
if (z > 95)
z = 95.0f - urand(0, 5);
}
else
{
target->GetContactPoint(me, x, y, z);
z += 2;
me->SetSpeed(MOVE_RUN, 5.0f);
TargetGUID = target->GetGUID();
}
me->GetMotionMaster()->MovePoint(0, x, y, z);
arrived = false;
}
}
}
};
// 43648 - Electrical Storm
class spell_electrial_storm : public AuraScript
{
PrepareAuraScript(spell_electrial_storm);
bool Load() override
{
return GetCaster() && GetCaster()->IsCreature();
}
void OnPeriodic(AuraEffect const* /*aurEff*/)
{
if (GetCaster())
GetCaster()->ToCreature()->AI()->DoAction(ACTION_INCREASE_STORM_COUNT);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_electrial_storm::OnPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
}
};
void AddSC_boss_akilzon()
{
new boss_akilzon();
new npc_akilzon_eagle();
RegisterZulAmanCreatureAI(boss_akilzon);
RegisterZulAmanCreatureAI(npc_akilzon_eagle);
RegisterSpellScript(spell_electrial_storm);
}
@@ -74,6 +74,11 @@ enum Groups
GROUP_MERGE = 2
};
enum Actions
{
ACTION_MERGE = 0
};
struct boss_halazzi : public BossAI
{
boss_halazzi(Creature* creature) : BossAI(creature, DATA_HALAZZI)
@@ -118,33 +123,19 @@ struct boss_halazzi : public BossAI
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override
{
if (damage >= me->GetHealth() && _phase != PHASE_ENRAGE)
{
damage = 0;
}
else
{
if (_phase == PHASE_LYNX || _phase == PHASE_ENRAGE)
{
_healthCheckPercentage = 25 * (3 - _transformCount);
if (!HealthAbovePct(_healthCheckPercentage))
{
EnterPhase(PHASE_SPLIT);
}
}
else if (_phase == PHASE_HUMAN)
{
if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX))
{
if (!HealthAbovePct(20) || !lynx->HealthAbovePct(20))
{
EnterPhase(PHASE_MERGE);
}
}
else
{
//should not really happen
EnterEvadeMode();
}
if (!HealthAbovePct(20))
EnterPhase(PHASE_MERGE);
}
}
}
@@ -152,17 +143,13 @@ struct boss_halazzi : public BossAI
void SpellHit(Unit*, SpellInfo const* spell) override
{
if (spell->Id == SPELL_TRANSFORM_SPLIT2)
{
EnterPhase(PHASE_HUMAN);
}
}
void AttackStart(Unit* who) override
{
if (_phase != PHASE_MERGE)
{
BossAI::AttackStart(who);
}
}
void EnterPhase(PhaseHalazzi nextPhase)
@@ -175,7 +162,7 @@ struct boss_halazzi : public BossAI
{
DoCastSelf(SPELL_TRANSFORM_MERGE, true);
me->RemoveAurasDueToSpell(SPELL_TRANSFORM_SPLIT2);
me->GetMotionMaster()->MoveChase(me->GetVictim());
me->ResumeChasingVictim();
}
summons.DespawnAll();
me->SetMaxHealth(_lynxFormHealth);
@@ -207,13 +194,9 @@ struct boss_halazzi : public BossAI
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
if (target->IsNonMeleeSpellCast(false))
{
DoCast(target, SPELL_EARTHSHOCK);
}
else
{
DoCast(target, SPELL_FLAMESHOCK);
}
}
context.Repeat(10s, 15s);
}).Schedule(12s, GROUP_HUMAN, [this](TaskContext context)
@@ -236,19 +219,13 @@ struct boss_halazzi : public BossAI
scheduler.Schedule(2s, GROUP_MERGE, [this](TaskContext context)
{
if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX))
{
if (me->IsWithinDistInMap(lynx, 6.0f))
{
if (_transformCount < 3)
{
EnterPhase(PHASE_LYNX);
}
else
{
EnterPhase(PHASE_ENRAGE);
}
}
}
context.Repeat(2s);
});
}
@@ -263,9 +240,13 @@ struct boss_halazzi : public BossAI
{
BossAI::KilledUnit(victim);
if (victim->IsPlayer())
{
Talk(SAY_KILL);
}
}
void DoAction(int32 actionId) override
{
if (actionId == ACTION_MERGE)
EnterPhase(PHASE_MERGE);
}
void JustDied(Unit* killer) override
@@ -281,60 +262,8 @@ private:
uint32 _healthCheckPercentage;
PhaseHalazzi _phase;
};
// Spirits Lynx AI
struct npc_halazzi_lynx : public ScriptedAI
{
npc_halazzi_lynx(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
scheduler.CancelAll();
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override
{
if (damage >= me->GetHealth())
{
damage = 0;
}
}
void AttackStart(Unit* who) override
{
if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE))
{
ScriptedAI::AttackStart(who);
}
}
void JustEngagedWith(Unit* who) override
{
ScriptedAI::JustEngagedWith(who);
ScheduleTimedEvent(30s, 50s, [&]
{
DoCastSelf(SPELL_LYNX_FRENZY);
}, 30s, 50s);
ScheduleTimedEvent(4s, [&]{
DoCastVictim(SPELL_SHRED_ARMOR);
}, 4s);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
{
return;
}
scheduler.Update(diff);
DoMeleeAttackIfReady();
}
};
void AddSC_boss_halazzi()
{
RegisterZulAmanCreatureAI(boss_halazzi);
RegisterZulAmanCreatureAI(npc_halazzi_lynx);
}
@@ -22,16 +22,10 @@
#include "Player.h"
#include "SpellScriptLoader.h"
#include "zulaman.h"
/* ScriptData
SDName: Boss_Hex_Lord_Malacrass
SD%Complete:
SDComment:
SDCategory: Zul'Aman
EndScriptData */
enum Says
{
SAY_AGGRO = 0,
SAY_AGGRO,
SAY_KILL_ONE,
SAY_KILL_TWO,
SAY_DRAIN_POWER,
@@ -117,23 +111,23 @@ enum Spells
// Death Knight
SPELL_DK_PLAGUE_STRIKE = 57599,
SPELL_DK_DEATH_AND_DECAY = 43265,
SPELL_DK_BLOOD_WORMS = 97630,
SPELL_DK_BLOOD_WORMS = 97630
};
const Position addPosition[4] =
const Position AddPosition[4] =
{
{112.8827f, 921.2795f, 33.8883f, 1.5696f},
{107.8827f, 921.2795f, 33.8883f, 1.5696f},
{122.8827f, 921.2795f, 33.8883f, 1.5696f},
{127.8827f, 921.2795f, 33.8883f, 1.5696f}
{ 128.48448f, 923.04285f, 33.97255f, 1.588249564170837402f },
{ 122.60526f, 923.24536f, 33.97256f, 1.570796370506286621f },
{ 111.69282f, 923.15314f, 33.972576f, 1.570796370506286621f },
{ 105.40299f, 923.3421f, 33.972588f, 1.553343057632446289f }
};
static uint32 addEntrySets[4][2] =
static uint32 AddEntrySets[4][2] =
{
{NPC_THURG, NPC_ALYSON_ANTILLE },
{NPC_LORD_RADAAN, NPC_SLITHER },
{NPC_GAZAKROTH, NPC_FENSTALKER },
{NPC_DARKHEART, NPC_KORAGG }
{ NPC_THURG, NPC_ALYSON_ANTILLE },
{ NPC_LORD_RADAAN, NPC_SLITHER },
{ NPC_GAZAKROTH, NPC_FENSTALKER },
{ NPC_DARKHEART, NPC_KORAGG }
};
enum Misc
@@ -145,11 +139,11 @@ enum Misc
enum AbilityTarget
{
ABILITY_TARGET_SELF = 0,
ABILITY_TARGET_VICTIM = 1,
ABILITY_TARGET_ENEMY = 2,
ABILITY_TARGET_HEAL = 3,
ABILITY_TARGET_BUFF = 4,
ABILITY_TARGET_SELF = 0,
ABILITY_TARGET_VICTIM = 1,
ABILITY_TARGET_ENEMY = 2,
ABILITY_TARGET_HEAL = 3,
ABILITY_TARGET_BUFF = 4,
ABILITY_TARGET_SPECIAL = 5
};
@@ -163,66 +157,66 @@ struct PlayerAbilityStruct
static PlayerAbilityStruct PlayerAbility[12][3] =
{
// 1 warrior
{ {SPELL_WR_SPELL_REFLECT, ABILITY_TARGET_SELF, 10000ms},
{SPELL_WR_WHIRLWIND, ABILITY_TARGET_SELF, 10000ms},
{SPELL_WR_MORTAL_STRIKE, ABILITY_TARGET_VICTIM, 6000ms}
{ { SPELL_WR_SPELL_REFLECT, ABILITY_TARGET_SELF, 10000ms },
{ SPELL_WR_WHIRLWIND, ABILITY_TARGET_SELF, 10000ms },
{ SPELL_WR_MORTAL_STRIKE, ABILITY_TARGET_VICTIM, 6000ms }
},
// 2 paladin
{ {SPELL_PA_CONSECRATION, ABILITY_TARGET_SELF, 10000ms},
{SPELL_PA_HOLY_LIGHT, ABILITY_TARGET_HEAL, 10000ms},
{SPELL_PA_AVENGING_WRATH, ABILITY_TARGET_SELF, 10000ms}
{ { SPELL_PA_CONSECRATION, ABILITY_TARGET_SELF, 10000ms },
{ SPELL_PA_HOLY_LIGHT, ABILITY_TARGET_HEAL, 10000ms },
{ SPELL_PA_AVENGING_WRATH, ABILITY_TARGET_SELF, 10000ms }
},
// 3 hunter
{ {SPELL_HU_EXPLOSIVE_TRAP, ABILITY_TARGET_SELF, 10000ms},
{SPELL_HU_FREEZING_TRAP, ABILITY_TARGET_SELF, 10000ms},
{SPELL_HU_SNAKE_TRAP, ABILITY_TARGET_SELF, 10000ms}
{ { SPELL_HU_EXPLOSIVE_TRAP, ABILITY_TARGET_SELF, 10000ms },
{ SPELL_HU_FREEZING_TRAP, ABILITY_TARGET_SELF, 10000ms },
{ SPELL_HU_SNAKE_TRAP, ABILITY_TARGET_SELF, 10000ms }
},
// 4 rogue
{ {SPELL_RO_WOUND_POISON, ABILITY_TARGET_VICTIM, 3000ms},
{SPELL_RO_SLICE_DICE, ABILITY_TARGET_SELF, 10000ms},
{SPELL_RO_BLIND, ABILITY_TARGET_ENEMY, 10000ms}
{ { SPELL_RO_WOUND_POISON, ABILITY_TARGET_VICTIM, 3000ms },
{ SPELL_RO_SLICE_DICE, ABILITY_TARGET_SELF, 10000ms },
{ SPELL_RO_BLIND, ABILITY_TARGET_ENEMY, 10000ms }
},
// 5 priest
{ {SPELL_PR_PAIN_SUPP, ABILITY_TARGET_HEAL, 10000ms},
{SPELL_PR_HEAL, ABILITY_TARGET_HEAL, 10000ms},
{SPELL_PR_PSYCHIC_SCREAM, ABILITY_TARGET_SELF, 10000ms}
{ { SPELL_PR_PAIN_SUPP, ABILITY_TARGET_HEAL, 10000ms },
{ SPELL_PR_HEAL, ABILITY_TARGET_HEAL, 10000ms },
{ SPELL_PR_PSYCHIC_SCREAM, ABILITY_TARGET_SELF, 10000ms }
},
// 6 death knight
{
{SPELL_DK_PLAGUE_STRIKE, ABILITY_TARGET_ENEMY, 2000ms},
{SPELL_DK_DEATH_AND_DECAY, ABILITY_TARGET_SELF, 10000ms},
{SPELL_DK_BLOOD_WORMS, ABILITY_TARGET_ENEMY, 5000ms}
{ SPELL_DK_PLAGUE_STRIKE, ABILITY_TARGET_ENEMY, 2000ms },
{ SPELL_DK_DEATH_AND_DECAY, ABILITY_TARGET_SELF, 10000ms },
{ SPELL_DK_BLOOD_WORMS, ABILITY_TARGET_ENEMY, 5000ms }
},
// 7 shaman
{ {SPELL_SH_FIRE_NOVA, ABILITY_TARGET_SELF, 10000ms},
{SPELL_SH_HEALING_WAVE, ABILITY_TARGET_HEAL, 10000ms},
{SPELL_SH_CHAIN_LIGHT, ABILITY_TARGET_ENEMY, 8000ms}
{ { SPELL_SH_FIRE_NOVA, ABILITY_TARGET_SELF, 10000ms },
{ SPELL_SH_HEALING_WAVE, ABILITY_TARGET_HEAL, 10000ms },
{ SPELL_SH_CHAIN_LIGHT, ABILITY_TARGET_ENEMY, 8000ms }
},
// 8 mage
{ {SPELL_MG_FIREBALL, ABILITY_TARGET_ENEMY, 5000ms},
{SPELL_MG_FROSTBOLT, ABILITY_TARGET_ENEMY, 5000ms},
{SPELL_MG_ICE_LANCE, ABILITY_TARGET_SPECIAL, 2000ms}
{ { SPELL_MG_FIREBALL, ABILITY_TARGET_ENEMY, 5000ms },
{ SPELL_MG_FROSTBOLT, ABILITY_TARGET_ENEMY, 5000ms },
{ SPELL_MG_ICE_LANCE, ABILITY_TARGET_SPECIAL, 2000ms }
},
// 9 warlock
{ {SPELL_WL_CURSE_OF_DOOM, ABILITY_TARGET_ENEMY, 10000ms},
{SPELL_WL_RAIN_OF_FIRE, ABILITY_TARGET_ENEMY, 10000ms},
{SPELL_WL_UNSTABLE_AFFL, ABILITY_TARGET_ENEMY, 10000ms}
{ { SPELL_WL_CURSE_OF_DOOM, ABILITY_TARGET_ENEMY, 10000ms },
{ SPELL_WL_RAIN_OF_FIRE, ABILITY_TARGET_ENEMY, 10000ms },
{ SPELL_WL_UNSTABLE_AFFL, ABILITY_TARGET_ENEMY, 10000ms }
},
// 10 UNK class (should never be set)
{
{0, ABILITY_TARGET_SELF, 0ms},
{0, ABILITY_TARGET_SELF, 0ms},
{0, ABILITY_TARGET_SELF, 0ms}
{ 0, ABILITY_TARGET_SELF, 0ms},
{ 0, ABILITY_TARGET_SELF, 0ms},
{ 0, ABILITY_TARGET_SELF, 0ms}
},
// 11 druid
{ {SPELL_DR_LIFEBLOOM, ABILITY_TARGET_HEAL, 10000ms},
{SPELL_DR_THORNS, ABILITY_TARGET_SELF, 10000ms},
{SPELL_DR_MOONFIRE, ABILITY_TARGET_ENEMY, 8000ms}
{ { SPELL_DR_LIFEBLOOM, ABILITY_TARGET_HEAL, 10000ms },
{ SPELL_DR_THORNS, ABILITY_TARGET_SELF, 10000ms },
{ SPELL_DR_MOONFIRE, ABILITY_TARGET_ENEMY, 8000ms }
},
// MISC shadow priest
{ {SPELL_PR_MIND_CONTROL, ABILITY_TARGET_ENEMY, 15000ms},
{SPELL_PR_MIND_BLAST, ABILITY_TARGET_ENEMY, 5000ms},
{SPELL_PR_SW_DEATH, ABILITY_TARGET_ENEMY, 10000ms}
{ { SPELL_PR_MIND_CONTROL, ABILITY_TARGET_ENEMY, 15000ms },
{ SPELL_PR_MIND_BLAST, ABILITY_TARGET_ENEMY, 5000ms },
{ SPELL_PR_SW_DEATH, ABILITY_TARGET_ENEMY, 10000ms }
}
};
@@ -243,7 +237,7 @@ struct boss_hexlord_malacrass : public BossAI
for (uint8 i = 0; i < MAX_ADD_COUNT; ++i)
{
uint8 flip = urand(0, 1);
me->SummonCreature(addEntrySets[i][flip], addPosition[i], TEMPSUMMON_DEAD_DESPAWN, 0);
me->SummonCreature(AddEntrySets[i][flip], AddPosition[i], TEMPSUMMON_DEAD_DESPAWN, 0);
}
}
@@ -274,21 +268,14 @@ struct boss_hexlord_malacrass : public BossAI
siphonTrigger->AI()->DoCast(target, SPELL_SIPHON_SOUL, true);
siphonTrigger->GetMotionMaster()->MoveChase(me);
if (Player* player = target->ToPlayer())
{
if (player->HasAura(AURA_SHADOW_FORM))
_currentClass = ADDITIONAL_CLASS_SPRIEST;
else
_currentClass = player->getClass() - 1;
}
_currentClass = player->HasAura(AURA_SHADOW_FORM) ? ADDITIONAL_CLASS_SPRIEST : player->getClass() - 1;
}
}
});
}, 40s);
ScheduleTimedEvent(_classAbilityTimer, [&]{
if (_currentClass)
{
UseAbility();
}
}, _classAbilityTimer);
}
@@ -15,13 +15,6 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Boss_Janalai
SD%Complete: 100
SDComment:
SDCategory: Zul'Aman
EndScriptData */
#include "CellImpl.h"
#include "CreatureScript.h"
#include "GridNotifiers.h"
@@ -125,7 +118,13 @@ enum Misc
struct boss_janalai : public BossAI
{
boss_janalai(Creature* creature) : BossAI(creature, DATA_JANALAI) { }
boss_janalai(Creature* creature) : BossAI(creature, DATA_JANALAI)
{
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
@@ -134,18 +133,17 @@ struct boss_janalai : public BossAI
_isBombing = false;
_isFlameBreathing = false;
ScheduleHealthCheckEvent(25, [&]{
DoCastSelf(SPELL_ENRAGE, true);
});
ScheduleHealthCheckEvent(35, [&]{
Talk(SAY_ALL_EGGS);
me->AttackStop();
me->GetMotionMaster()->Clear();
me->SetPosition(janalainPos);
me->StopMovingOnCurrentPos();
DoCastSelf(SPELL_HATCH_ALL);
HatchAllEggs(HATCH_ALL);
DoCastAOE(SPELL_HATCH_ALL);
});
ScheduleHealthCheckEvent(25, [&] {
DoCastSelf(SPELL_ENRAGE, true);
});
}
@@ -209,19 +207,19 @@ struct boss_janalai : public BossAI
return false;
if (hatchAction == HATCH_RESET)
{
for (Creature* egg : eggList)
egg->Respawn();
else if (hatchAction == HATCH_ALL)
DoCastSelf(SPELL_HATCH_EGG_ALL);
if (hatchAction == HATCH_RESET)
{
std::list<Creature* > hatchlingList;
me->GetCreaturesWithEntryInRange(hatchlingList, 100.0f, NPC_HATCHLING);
for (Creature* hatchling : hatchlingList)
hatchling->DespawnOrUnsummon();
hatchlingList.clear();
}
else if (hatchAction == HATCH_ALL)
DoCastSelf(SPELL_HATCH_EGG_ALL);
eggList.clear();
return true;
}
@@ -239,9 +237,7 @@ struct boss_janalai : public BossAI
: me->SummonCreature(NPC_FIRE_BOMB, fireWallCoords[i].GetPositionX() - 2 + 4 * j, fireWallCoords[i].GetPositionY(), fireWallCoords[i].GetPositionZ(), fireWallCoords[i].GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 15000);
if (wall)
{
wall->AI()->DoCastSelf(SPELL_FIRE_WALL, true);
}
}
}
}
@@ -259,18 +255,16 @@ struct boss_janalai : public BossAI
void Boom()
{
std::list<Creature*> fireBombs;
me->GetCreaturesWithEntryInRange(fireBombs, 100.0f, NPC_FIRE_BOMB);
if (fireBombs.empty())
return;
for (Creature* bomb : fireBombs)
{
bomb->AI()->DoCastSelf(SPELL_FIRE_BOMB_DAMAGE, true);
bomb->RemoveAllAuras();
}
fireBombs.clear();
summons.DoForAllSummons([&](WorldObject* summon) {
if (summon->GetEntry() == NPC_FIRE_BOMB)
{
if (Creature* bomb = summon->ToCreature())
{
bomb->AI()->DoCastSelf(SPELL_FIRE_BOMB_DAMAGE, true);
bomb->RemoveAllAuras();
}
}
});
}
void StartBombing()
@@ -286,14 +280,11 @@ struct boss_janalai : public BossAI
SpawnBombs();
_isBombing = true;
if (Map* map = me->GetMap())
me->GetMap()->DoForAllPlayers([&](Player* player)
{
map->DoForAllPlayers([&](Player* player)
{
if (player->IsAlive())
DoTeleportPlayer(player, janalainPos.GetPositionX() - 5 + rand() % 10, janalainPos.GetPositionY() - 5 + rand() % 10, janalainPos.GetPositionZ(), 0.0f);
});
}
if (player->IsAlive())
DoTeleportPlayer(player, janalainPos.GetPositionX() - 5 + rand() % 10, janalainPos.GetPositionY() - 5 + rand() % 10, janalainPos.GetPositionZ(), 0.0f);
});
//DoCast(Temp, SPELL_SUMMON_PLAYERS, true) // core bug, spell does not work if too far
ThrowBombs();
@@ -308,20 +299,23 @@ struct boss_janalai : public BossAI
void ThrowBombs()
{
std::list<Creature*> fireBombs;
std::chrono::milliseconds bombTimer = 100ms;
me->GetCreaturesWithEntryInRange(fireBombs, 100.0f, NPC_FIRE_BOMB);
for (Creature* bomb : fireBombs)
{
scheduler.Schedule(bombTimer, [this, bomb](TaskContext)
summons.DoForAllSummons([this, &bombTimer](WorldObject* summon) {
if (summon->GetEntry() == NPC_FIRE_BOMB)
{
bomb->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
DoCast(bomb, SPELL_FIRE_BOMB_THROW, true);
bomb->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
});
bombTimer = bombTimer + 100ms;
}
fireBombs.clear();
if (Creature* bomb = summon->ToCreature())
{
bomb->m_Events.AddEventAtOffset([this, bomb] {
bomb->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
DoCast(bomb, SPELL_FIRE_BOMB_THROW, true);
bomb->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}, bombTimer);
}
bombTimer += 100ms;
}
});
}
bool CheckEvadeIfOutOfCombatArea() const override
@@ -333,23 +327,9 @@ private:
bool _isFlameBreathing;
};
struct npc_janalai_firebomb : public NullCreatureAI
{
npc_janalai_firebomb(Creature* creature) : NullCreatureAI(creature) { }
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_FIRE_BOMB_THROW)
DoCastSelf(SPELL_FIRE_BOMB_DUMMY, true);
}
};
struct npc_janalai_hatcher : public ScriptedAI
{
npc_janalai_hatcher(Creature* creature) : ScriptedAI(creature)
{
_instance = creature->GetInstanceScript();
}
npc_janalai_hatcher(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
@@ -358,7 +338,6 @@ struct npc_janalai_hatcher : public ScriptedAI
_side = (me->GetPositionY() < 1150);
_waypoint = 0;
_isHatching = false;
_hasChangedSide = false;
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(0, hatcherway[_side][0]);
}
@@ -368,29 +347,13 @@ struct npc_janalai_hatcher : public ScriptedAI
if (_waypoint == 5)
{
_isHatching = true;
std::list<Creature* > eggList;
me->GetCreaturesWithEntryInRange(eggList, 50.0f, NPC_EGG);
scheduler.Schedule(1500ms, SCHEDULER_GROUP_HATCHING, [this, eggList](TaskContext context)
scheduler.Schedule(1500ms, SCHEDULER_GROUP_HATCHING, [this](TaskContext context)
{
std::list<Creature* > unhatchedEggs;
for (Creature* egg : eggList)
{
if (egg->IsAlive())
unhatchedEggs.emplace_front(egg);
}
Acore::Containers::RandomResize(unhatchedEggs, 1);
if (Creature* egg = unhatchedEggs.front())
egg->AI()->DoCastSelf(SPELL_HATCH_EGG_SINGULAR);
else if (!_hasChangedSide)
{
_side = _side ? 0 : 1;
_isHatching = false;
_waypoint = 3;
MoveToNewWaypoint(_waypoint);
_hasChangedSide = true;
context.CancelGroup(SCHEDULER_GROUP_HATCHING);
}
context.Repeat(1500ms);
me->CastCustomSpell(SPELL_HATCH_EGG_ALL, SPELLVALUE_MAX_TARGETS, context.GetRepeatCounter() + 1);
if (me->FindNearestCreature(NPC_EGG, 100.0f))
context.Repeat(4s);
});
}
else
@@ -422,19 +385,14 @@ struct npc_janalai_hatcher : public ScriptedAI
void MoveInLineOfSight(Unit* /*who*/) override { }
private:
InstanceScript* _instance;
uint8 _side;
uint8 _waypoint;
bool _isHatching;
bool _hasChangedSide;
};
struct npc_janalai_hatchling : public ScriptedAI
{
npc_janalai_hatchling(Creature* creature) : ScriptedAI(creature)
{
_instance = creature->GetInstanceScript();
}
npc_janalai_hatchling(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
@@ -444,7 +402,6 @@ struct npc_janalai_hatchling : public ScriptedAI
else
me->GetMotionMaster()->MovePoint(0, hatcherway[1][3].GetPositionX() + rand() % 4 - 2, 1150.0f + rand() % 4 - 2, hatcherway[1][3].GetPositionY());
me->SetDisableGravity(true);
me->SetInCombatWithZone();
}
@@ -465,27 +422,11 @@ struct npc_janalai_hatchling : public ScriptedAI
DoMeleeAttackIfReady();
}
private:
InstanceScript* _instance;
};
struct npc_janalai_egg : public NullCreatureAI
{
npc_janalai_egg(Creature* creature) : NullCreatureAI(creature) { }
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_HATCH_EGG_ALL || spell->Id == SPELL_HATCH_EGG_SINGULAR)
DoCastSelf(SPELL_SUMMON_HATCHLING);
}
};
void AddSC_boss_janalai()
{
RegisterZulAmanCreatureAI(boss_janalai);
RegisterZulAmanCreatureAI(npc_janalai_firebomb);
RegisterZulAmanCreatureAI(npc_janalai_hatcher);
RegisterZulAmanCreatureAI(npc_janalai_hatchling);
RegisterZulAmanCreatureAI(npc_janalai_egg);
}

Some files were not shown because too many files have changed in this diff Show More