diff options
Diffstat (limited to 'src')
17 files changed, 2208 insertions, 746 deletions
diff --git a/src/server/collision/BoundingIntervalHierarchy.cpp b/src/server/collision/BoundingIntervalHierarchy.cpp index bca738d1ff6..4c1f449da25 100644 --- a/src/server/collision/BoundingIntervalHierarchy.cpp +++ b/src/server/collision/BoundingIntervalHierarchy.cpp @@ -20,6 +20,8 @@ #if defined __APPLE__ #define isnan std::isnan +#elif defined __CYGWIN__ + #define isnan std::isnan #elif defined _MSC_VER #define isnan _isnan #endif diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.h b/src/server/game/Battlefield/Zones/BattlefieldWG.h index 09ad5b284fd..047455ce8fb 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.h +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.h @@ -102,21 +102,21 @@ enum WintergraspData enum WintergraspAchievements { ACHIEVEMENTS_WIN_WG = 1717, - ACHIEVEMENTS_WIN_WG_100 = 1718, // todo - ACHIEVEMENTS_WG_GNOMESLAUGHTER = 1723, // todo + ACHIEVEMENTS_WIN_WG_100 = 1718, /// @todo: Has to be implemented + ACHIEVEMENTS_WG_GNOMESLAUGHTER = 1723, /// @todo: Has to be implemented ACHIEVEMENTS_WG_TOWER_DESTROY = 1727, - ACHIEVEMENTS_DESTRUCTION_DERBY_A = 1737, // todo - ACHIEVEMENTS_WG_TOWER_CANNON_KILL = 1751, // todo - ACHIEVEMENTS_WG_MASTER_A = 1752, // todo + ACHIEVEMENTS_DESTRUCTION_DERBY_A = 1737, /// @todo: Has to be implemented + ACHIEVEMENTS_WG_TOWER_CANNON_KILL = 1751, /// @todo: Has to be implemented + ACHIEVEMENTS_WG_MASTER_A = 1752, /// @todo: Has to be implemented ACHIEVEMENTS_WIN_WG_TIMER_10 = 1755, - ACHIEVEMENTS_STONE_KEEPER_50 = 2085, // todo - ACHIEVEMENTS_STONE_KEEPER_100 = 2086, // todo - ACHIEVEMENTS_STONE_KEEPER_250 = 2087, // todo - ACHIEVEMENTS_STONE_KEEPER_500 = 2088, // todo - ACHIEVEMENTS_STONE_KEEPER_1000 = 2089, // todo - ACHIEVEMENTS_WG_RANGER = 2199, // todo - ACHIEVEMENTS_DESTRUCTION_DERBY_H = 2476, // todo - ACHIEVEMENTS_WG_MASTER_H = 2776 // todo + ACHIEVEMENTS_STONE_KEEPER_50 = 2085, /// @todo: Has to be implemented + ACHIEVEMENTS_STONE_KEEPER_100 = 2086, /// @todo: Has to be implemented + ACHIEVEMENTS_STONE_KEEPER_250 = 2087, /// @todo: Has to be implemented + ACHIEVEMENTS_STONE_KEEPER_500 = 2088, /// @todo: Has to be implemented + ACHIEVEMENTS_STONE_KEEPER_1000 = 2089, /// @todo: Has to be implemented + ACHIEVEMENTS_WG_RANGER = 2199, /// @todo: Has to be implemented + ACHIEVEMENTS_DESTRUCTION_DERBY_H = 2476, /// @todo: Has to be implemented + ACHIEVEMENTS_WG_MASTER_H = 2776 /// @todo: Has to be implemented }; enum WintergraspWorldStates @@ -499,7 +499,7 @@ enum WintergraspTeamControl BATTLEFIELD_WG_TEAM_NEUTRAL }; -// TODO: Handle this with creature_text ? +/// @todo: Can this be handled with creature_text or SmartAI ? enum WintergraspText { BATTLEFIELD_WG_TEXT_WORKSHOP_NAME_NE = 12055, @@ -562,7 +562,7 @@ struct WintergraspObjectPositionData }; // ***************************************************** -// ************ Destructible (Wall, Tower..) ************ +// ************ Destructible (Wall, Tower..) *********** // ***************************************************** struct WintergraspBuildingSpawnData @@ -755,7 +755,7 @@ const WintergraspTeleporterData WGPortalDefenderData[WG_MAX_TELEPORTER] = }; // ********************************************************* -// **********Tower Element(GameObject, Creature)************* +// **********Tower Element(GameObject, Creature)************ // ********************************************************* struct WintergraspTowerData @@ -764,7 +764,7 @@ struct WintergraspTowerData uint8 nbObject; // Number of gameobjects spawned on this point WintergraspObjectPositionData GameObject[6]; // Gameobject position and entry (Horde/Alliance) - // Creature : Turrets and Guard, TODO: check if killed on tower destruction? tower damage? + // Creature: Turrets and Guard /// @todo: Killed on Tower destruction ? Tower damage ? Requires confirming uint8 nbCreatureBottom; WintergraspObjectPositionData CreatureBottom[9]; uint8 nbCreatureTop; @@ -1055,7 +1055,7 @@ const WGWorkshopData WorkshopsData[WG_MAX_WORKSHOP] = }; // ******************************************************************** -// * Structs using for Building, Graveyard, Workshop * +// * Structs using for Building, Graveyard, Workshop * // ******************************************************************** // Structure for different buildings that can be destroyed during battle struct BfWGGameObjectBuilding diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h index 7cd4d571cad..7a0af0b06d7 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h @@ -53,7 +53,7 @@ #define AV_EVENT_START_BATTLE 9166 // Achievement: The Alterac Blitz enum BG_AV_Sounds -{ //TODO: get out if there comes a sound when neutral team captures mine +{ /// @todo: get out if there comes a sound when neutral team captures mine /* 8212: @@ -86,7 +86,7 @@ horde: horde wins */ - AV_SOUND_NEAR_VICTORY = 8456, //not confirmed yet + AV_SOUND_NEAR_VICTORY = 8456, /// @todo: Not confirmed yet AV_SOUND_ALLIANCE_ASSAULTS = 8212, //tower, grave + enemy boss if someone tries to attack him AV_SOUND_HORDE_ASSAULTS = 8174, @@ -104,7 +104,7 @@ enum BG_AV_OTHER_VALUES AV_NORTH_MINE = 0, AV_SOUTH_MINE = 1, AV_MINE_TICK_TIMER = 45000, - AV_MINE_RECLAIM_TIMER = 1200000, //TODO: get the right value.. this is currently 20 minutes + AV_MINE_RECLAIM_TIMER = 1200000, /// @todo: get the right value.. this is currently 20 minutes AV_NEUTRAL_TEAM = 0 //this is the neutral owner of snowfall }; enum BG_AV_ObjectIds @@ -547,7 +547,7 @@ enum BG_AV_CreaturePlace AV_CPLACE_SPIRIT_FROST_HUT = 6, AV_CPLACE_SPIRIT_MAIN_ALLIANCE = 7, AV_CPLACE_SPIRIT_MAIN_HORDE = 8, -//i don't will add for all 4 positions a variable.. i think one is enough to compute the rest +//I don't add a variable for all 4 positions... I think one is enough to compute the rest AV_CPLACE_DEFENSE_STORM_AID = 9, AV_CPLACE_DEFEMSE_STORM_GRAVE = 13, AV_CPLACE_DEFENSE_STONE_GRAVE = 17, @@ -680,7 +680,7 @@ const float BG_AV_CreaturePos[AV_CPLACE_MAX][4] = {575.411f, -83.597f, 52.3626f, 6.26573f}, {571.352f, -75.6582f, 52.479f, 0.523599f}, //dun north - OK - {668.60f, -122.53f, 64.12f, 2.34f}, //not 100% ok + {668.60f, -122.53f, 64.12f, 2.34f}, /// @todo: To be confirm - Not completely okay {662.253f, -129.105f, 64.1794f, 2.77507f}, {661.209f, -138.877f, 64.2251f, 3.38594f}, {665.481f, -146.857f, 64.1271f, 3.75246f}, @@ -720,7 +720,7 @@ const float BG_AV_CreaturePos[AV_CPLACE_MAX][4] = {723.058f, -14.1548f, 50.7046f, 3.40339f}, // north {715.691f, -4.72233f, 50.2187f, 3.47321f}, // icewing {720.046f, -19.9413f, 50.2187f, 3.36849f}, // stone -//horde (coords not 100% ok) +/// horde @todo: Confirm positions {-1363.99f, -221.99f, 98.4053f, 4.93012f}, {-1370.96f, -223.532f, 98.4266f, 4.93012f}, {-1378.37f, -228.614f, 99.3546f, 5.38565f}, @@ -1039,14 +1039,14 @@ enum BG_AV_CreatureIds }; //entry, team, minlevel, maxlevel -//TODO this array should be removed, the only needed things are the entrys (for spawning(?) and handlekillunit) +/// @todo: this array should be removed, the only needed things are the entrys (for spawning(?) and handlekillunit) const uint32 BG_AV_CreatureInfo[AV_NPC_INFO_MAX][4] = { { 12050, 1216, 58, 58 }, //Stormpike Defender { 13326, 1216, 59, 59 }, //Seasoned Defender { 13331, 1216, 60, 60 }, //Veteran Defender { 13422, 1216, 61, 61 }, //Champion Defender - { 13358, 1216, 59, 60 }, //Stormpike Bowman //i think its 60, 61 and 69, 70.. but this is until now not possible TODO look if this is ok + { 13358, 1216, 59, 60 }, //Stormpike Bowman /// @todo: Confirm if this is correct. Author assumpted 60,61 & 69,70, but wouldn't work here { 11949, 469, 0, 0}, //not spawned with this data, but used for handlekillunit { 11948, 469, 0, 0}, //not spawned with this data, but used for handlekillunit { 12053, 1214, 58, 58 }, //Frostwolf Guardian @@ -1071,7 +1071,7 @@ const uint32 BG_AV_CreatureInfo[AV_NPC_INFO_MAX][4] = { 11602, 59, 54, 55 }, //Irondeep Skullthumper { 11657, 59, 58, 58 }, //Morloch - {13396, 469, 52, 53}, //irondeep alliance TODO: get the right ids + {13396, 469, 52, 53}, // irondeep alliance /// @todo: Correct and give correct ids {13080, 469, 53, 54}, {13098, 469, 54, 55}, {13078, 469, 58, 58}, @@ -1246,7 +1246,7 @@ const uint32 BG_AV_StaticCreatureInfo[51][4] = { 11675, 514, 53, 53 }, //Snowblind Windcaller { 11678, 14, 52, 53 }, //Snowblind Ambusher { 11839, 39, 56, 56 }, //Wildpaw Brute - { 11947, 1214, 61, 61 }, //Captain Galvangar --TODO: doubled + { 11947, 1214, 61, 61 }, // Captain Galvangar /// @todo: Duplicate ? Check and confirm { 11948, 1216, 63, 63 }, //Vanndar Stormpike { 11949, 1216, 61, 61 }, //Captain Balinda Stonehearth { 11997, 1334, 60, 60 }, //Stormpike Herald @@ -1277,7 +1277,7 @@ const uint32 BG_AV_StaticCreatureInfo[51][4] = { 14282, 1214, 53, 54 }, //Frostwolf Bloodhound { 14283, 1216, 53, 54 }, //Stormpike Owl { 14284, 1216, 61, 61 }, //Stormpike Battleguard - { 11946, 1214, 63, 63 }, //Drek'Thar //TODO: make the levels right (boss=0 maybe) + { 11946, 1214, 63, 63 }, //Drek'Thar /// @todo: Correct the level (Level 80 for boss ?) { 11948, 1216, 63, 63 }, //Vanndar Stormpike { 11947, 1214, 61, 61 }, //Captain Galvangar { 11949, 1216, 61, 61 } //Captain Balinda Stonehearth @@ -1310,7 +1310,7 @@ const uint32 BG_AV_GraveyardIds[9]= }; enum BG_AV_BUFF -{ //TODO add all other buffs here +{ /// @todo: Add all other buffs here AV_BUFF_ARMOR = 21163, AV_BUFF_A_CAPTAIN = 23693, //the buff which the alliance captain does AV_BUFF_H_CAPTAIN = 22751 //the buff which the horde captain does @@ -1607,7 +1607,7 @@ class BattlegroundAV : public Battleground /*general */ Creature* AddAVCreature(uint16 cinfoid, uint16 type); - uint16 GetBonusHonor(uint8 kills); //TODO remove this when the core handles this right + uint16 GetBonusHonor(uint8 kills); /// @todo: Remove this when the core handles this properly /*variables */ int32 m_Team_Scores[2]; @@ -1622,7 +1622,7 @@ class BattlegroundAV : public Battleground uint32 m_CaptainBuffTimer[2]; bool m_CaptainAlive[2]; - uint8 m_MaxLevel; //TODO remove this when battleground-getmaxlevel() returns something usefull + uint8 m_MaxLevel; /// @todo: Remove this once battleground->getmaxlevel() returns something usefull/is reworked (?) bool m_IsInformedNearVictory[2]; }; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 82b0573d875..816c6e73b7a 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1708,7 +1708,7 @@ struct SpellEffectEntry // SpellAuraOptions.dbc struct SpellAuraOptionsEntry { - uint32 Id; // 0 m_ID + uint32 Id; // 0 m_ID uint32 StackAmount; // 1 m_cumulativeAura uint32 procChance; // 2 m_procChance uint32 procCharges; // 3 m_procCharges diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 4127ab281dd..23a443e134f 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -483,15 +483,13 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature SetReactState(REACT_DEFENSIVE);*/; } - ///// TODO RENAME THIS!!!!! + /// @todo Rename these properly bool isCanTrainingOf(Player* player, bool msg) const; bool isCanInteractWithBattleMaster(Player* player, bool msg) const; bool isCanTrainingAndResetTalentsOf(Player* player) const; bool canCreatureAttack(Unit const* victim, bool force = true) const; - bool IsImmunedToSpell(SpellInfo const* spellInfo); - // redefine Unit::IsImmunedToSpell - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const; - // redefine Unit::IsImmunedToSpellEffect + bool IsImmunedToSpell(SpellInfo const* spellInfo); //override Unit::IsImmunedToSpell + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const; //override Unit::IsImmunedToSpellEffect bool isElite() const { if (isPet()) @@ -766,10 +764,10 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature bool DisableReputationGain; - CreatureTemplate const* m_creatureInfo; // in difficulty mode > 0 can different from sObjectMgr->GetCreatureTemplate(GetEntry()) + CreatureTemplate const* m_creatureInfo; // Can differ from sObjectMgr->GetCreatureTemplate(GetEntry()) in difficulty mode > 0 CreatureData const* m_creatureData; - uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable + uint16 m_LootMode; // Bitmask (default: LOOT_MODE_DEFAULT) that determines what loot will be lootable uint32 guid_transport; bool IsInvisibleDueToDespawn() const; diff --git a/src/server/game/Entities/Item/ItemPrototype.h b/src/server/game/Entities/Item/ItemPrototype.h index 6f0919520a3..9393b32d48a 100644 --- a/src/server/game/Entities/Item/ItemPrototype.h +++ b/src/server/game/Entities/Item/ItemPrototype.h @@ -107,10 +107,8 @@ enum ItemBondingType #define MAX_BIND_TYPE 6 -/* TODO - // need to know cases when using item is not allowed in shapeshift - ITEM_PROTO_FLAG_USABLE_WHEN_SHAPESHIFTED = 0x00800000, // Item can be used in shapeshift forms -*/ +/* /// @todo: Requiring actual cases in which using (an) item isn't allowed while shapeshifted. Else, this flag would need an implementation. + ITEM_PROTO_FLAG_USABLE_WHEN_SHAPESHIFTED = 0x00800000, // Item can be used in shapeshift forms */ enum ItemProtoFlags { @@ -148,9 +146,6 @@ enum ItemProtoFlags ITEM_PROTO_FLAG_BOP_TRADEABLE = 0x80000000 // bound item that can be traded }; -/* TODO -*/ - enum ItemFieldFlags { ITEM_FLAG_SOULBOUND = 0x00000001, // Item is soulbound and cannot be traded <<-- diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 948bb240026..b07c6183257 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -21886,7 +21886,7 @@ void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 ite if (rec > 0) ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell); - if (catrec > 0) + if (catrec > 0 && !(spellInfo->AttributesEx6 & SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell); // replace negative cooldowns by 0 diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 6295db693d6..89a3a47b25d 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1336,11 +1336,16 @@ class Player : public Unit, public GridObject<Player> PhaseMgr& GetPhaseMgr() { return phaseMgr; } + /// Handles said message in regular chat based on declared language and in config pre-defined Range. void Say(std::string const& text, const uint32 language); + /// Handles yelled message in regular chat based on declared language and in config pre-defined Range. void Yell(std::string const& text, const uint32 language); + /// Outputs an universal text which is supposed to be an action. void TextEmote(std::string const& text); + /// Handles whispers from Addons and players based on sender, receiver's guid and language. void Whisper(std::string const& text, const uint32 language, uint64 receiver); void WhisperAddon(std::string const& text, std::string const& prefix, Player* receiver); + /// Constructs the player Chat data for the specific functions to use void BuildPlayerChat(WorldPacket* data, uint8 msgtype, std::string const& text, uint32 language, const char* addonPrefix = NULL) const; /*********************************************************/ diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 97758b3d0b7..6421f6d07d3 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -10652,7 +10652,7 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) return false; - if (!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_UNK3)) + if (!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) { if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) { @@ -10699,7 +10699,7 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co // PvC case - player can assist creature only if has specific type flags // !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && else if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) - && (!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_UNK3)) + && (!bySpell || !(bySpell->AttributesEx6 & SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) && !((target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_PVP))) { if (Creature const* creatureTarget = target->ToCreature()) diff --git a/src/server/game/Globals/ObjectAccessor.h b/src/server/game/Globals/ObjectAccessor.h index 113ef1dec83..6a53d58c57f 100644 --- a/src/server/game/Globals/ObjectAccessor.h +++ b/src/server/game/Globals/ObjectAccessor.h @@ -90,7 +90,7 @@ class ObjectAccessor ObjectAccessor& operator=(const ObjectAccessor&); public: - // TODO: override these template functions for each holder type and add assertions + /// @todo: Override these template functions for each holder type and add assertions template<class T> static T* GetObjectInOrOutOfWorld(uint64 guid, T* /*typeSpecifier*/) { diff --git a/src/server/game/Instances/InstanceSaveMgr.h b/src/server/game/Instances/InstanceSaveMgr.h index a99b4c66b27..c5dcaf32463 100644 --- a/src/server/game/Instances/InstanceSaveMgr.h +++ b/src/server/game/Instances/InstanceSaveMgr.h @@ -101,8 +101,8 @@ class InstanceSave private: bool UnloadIfEmpty(); /* the only reason the instSave-object links are kept is because - the object-instSave links need to be broken at reset time - TODO: maybe it's enough to just store the number of players/groups */ + the object-instSave links need to be broken at reset time */ + /// @todo: Check if maybe it's enough to just store the number of players/groups PlayerListType m_playerList; GroupListType m_groupList; time_t m_resetTime; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 0a2761abf6c..a54e671d7d0 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -484,8 +484,8 @@ enum SpellAttr5 SPELL_ATTR5_UNK24 = 0x01000000, // 24 SPELL_ATTR5_UNK25 = 0x02000000, // 25 SPELL_ATTR5_UNK26 = 0x04000000, // 26 aoe related - Boulder, Cannon, Corpse Explosion, Fire Nova, Flames, Frost Bomb, Living Bomb, Seed of Corruption, Starfall, Thunder Clap, Volley - SPELL_ATTR5_UNK27 = 0x08000000, // 27 - SPELL_ATTR5_UNK28 = 0x10000000, // 28 + SPELL_ATTR5_DONT_SHOW_AURA_IF_SELF_CAST = 0x08000000, // 27 Auras with this attribute are not visible on units that are the caster + SPELL_ATTR5_DONT_SHOW_AURA_IF_NOT_SELF_CAST = 0x10000000, // 28 Auras with this attribute are not visible on units that are not the caster SPELL_ATTR5_UNK29 = 0x20000000, // 29 SPELL_ATTR5_UNK30 = 0x40000000, // 30 SPELL_ATTR5_UNK31 = 0x80000000 // 31 Forces all nearby enemies to focus attacks caster @@ -496,10 +496,10 @@ enum SpellAttr6 SPELL_ATTR6_DONT_DISPLAY_COOLDOWN = 0x00000001, // 0 client doesn't display cooldown in tooltip for these spells SPELL_ATTR6_ONLY_IN_ARENA = 0x00000002, // 1 only usable in arena SPELL_ATTR6_IGNORE_CASTER_AURAS = 0x00000004, // 2 - SPELL_ATTR6_UNK3 = 0x00000008, // 3 + SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG = 0x00000008, // 3 skips checking UNIT_FLAG_IMMUNE_TO_PC and UNIT_FLAG_IMMUNE_TO_NPC flags on assist SPELL_ATTR6_UNK4 = 0x00000010, // 4 SPELL_ATTR6_UNK5 = 0x00000020, // 5 - SPELL_ATTR6_PRINT_SPELLNAME = 0x00000040, // 6 when activated, spell name is shown at the center of the screen: <Spell name> (client-side attribute) + SPELL_ATTR6_USE_SPELL_CAST_EVENT = 0x00000040, // 6 Auras with this attribute trigger SPELL_CAST combat log event instead of SPELL_AURA_START (clientside attribute) SPELL_ATTR6_UNK7 = 0x00000080, // 7 SPELL_ATTR6_CANT_TARGET_CROWD_CONTROLLED = 0x00000100, // 8 SPELL_ATTR6_UNK9 = 0x00000200, // 9 @@ -513,7 +513,7 @@ enum SpellAttr6 SPELL_ATTR6_UNK17 = 0x00020000, // 17 Mount spell SPELL_ATTR6_CAST_BY_CHARMER = 0x00040000, // 18 client won't allow to cast these spells when unit is not possessed && charmer of caster will be original caster SPELL_ATTR6_UNK19 = 0x00080000, // 19 only 47488, 50782 - SPELL_ATTR6_UNK20 = 0x00100000, // 20 only 58371, 62218 + SPELL_ATTR6_ONLY_VISIBLE_TO_CASTER = 0x00100000, // 20 Auras with this attribute are only visible to their caster (or pet's owner) SPELL_ATTR6_CLIENT_UI_TARGET_EFFECTS = 0x00200000, // 21 it's only client-side attribute SPELL_ATTR6_UNK22 = 0x00400000, // 22 only 72054 SPELL_ATTR6_UNK23 = 0x00800000, // 23 @@ -524,7 +524,7 @@ enum SpellAttr6 SPELL_ATTR6_UNK28 = 0x10000000, // 28 Death Grip SPELL_ATTR6_NO_DONE_PCT_DAMAGE_MODS = 0x20000000, // 29 ignores done percent damage mods? SPELL_ATTR6_UNK30 = 0x40000000, // 30 - SPELL_ATTR6_UNK31 = 0x80000000 // 31 some special cooldown calc? only 2894 + SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS = 0x80000000 // 31 Spells with this attribute skip applying modifiers to category cooldowns }; enum SpellAttr7 @@ -557,7 +557,7 @@ enum SpellAttr7 SPELL_ATTR7_UNK25 = 0x02000000, // 25 SPELL_ATTR7_UNK26 = 0x04000000, // 26 SPELL_ATTR7_UNK27 = 0x08000000, // 27 Not set - SPELL_ATTR7_BENEFIT_FROM_SPELLMOD = 0x10000000, // 28 Non-permanent, non-passive buffs that may benefit from spellmods + SPELL_ATTR7_CONSOLIDATED_RAID_BUFF = 0x10000000, // 28 May be collapsed in raid buff frame (clientside attribute) SPELL_ATTR7_UNK29 = 0x20000000, // 29 only 69028, 71237 SPELL_ATTR7_UNK30 = 0x40000000, // 30 Burning Determination, Divine Sacrifice, Earth Shield, Prayer of Mending SPELL_ATTR7_CLIENT_INDICATOR = 0x80000000 diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index febfac27ee1..3c134b3a88e 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3592,6 +3592,30 @@ void SpellMgr::LoadSpellInfoCorrections() break; // ENDOF RUBY SANCTUM SPELLS // + // EYE OF ETERNITY SPELLS + // All spells below work even without these changes. The LOS attribute is due to problem + // from collision between maps & gos with active destroyed state. + case 57473: // Arcane Storm bonus explicit visual spell + case 57430: // Summon Static Field + case 56091: // Flame Spike (Wyrmrest Skytalon) + case 56092: // Engulf in Flames (Wyrmrest Skytalon) + case 57090: // Revivify (Wyrmrest Skytalon) + case 57143: // Life Burst (Wyrmrest Skytalon) + spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + break; + // ENDOF EYE OF ETERNITY SPELLS + // + // OCULUS SPELLS + // The spells below are here because their effect 1 is giving warning due to + // triggered spell not found in any dbc and is missing from encounter source* of data. + // Even judged as clientside these spells can't be guessed for* now. + case 49462: // Call Ruby Drake + case 49461: // Call Amber Drake + case 49345: // Call Emerald Drake + spellInfo->Effects[EFFECT_1].Effect = 0; + break; + // ENDOF OCULUS SPELLS + // case 40055: // Introspection case 40165: // Introspection case 40166: // Introspection @@ -3602,13 +3626,6 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->ManaCost = 0; spellInfo->ManaPerSecond = 0; break; - // OCULUS SPELLS - // The spells below are here, because their effect 1 is giving warning, because the triggered spell is not found in dbc and is missing from encounter sniff. - case 49462: // Call Ruby Drake - case 49461: // Call Amber Drake - case 49345: // Call Emerald Drake - spellInfo->Effects[EFFECT_1].Effect = 0; - break; // Halls Of Origination spells // Temple Guardian Anhuur case 76606: // Disable Beacon Beams L diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index b0a34047c0e..b258be20feb 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -16,143 +16,208 @@ */ /* Script Data Start -SDName: Boss malygos +SDName: Boss Malygos Script Data End */ -// TO-DOs: -// Implement a better pathing for Malygos. -// Find sniffed spawn position for chest -// Implement a better way to disappear the gameobjects -// Implement achievements -// Remove hack that re-adds targets to the aggro list after they enter to a vehicle when it works as expected -// Improve whatever can be improved :) +/* Check TO DOs in the script, but here is one essential hack to be removed only if core changes are made: + At Wyrmrest Skytalon script I make it kill player if the drake dies, because at 100.0f z yards the map is + supposed to handle the air as ground or something to which you will fall and die. However, currently maps/movements + stop flying below it, however they don't stop movementflag FALLING. Which leads to % player falling at - 1200000.0 z, + Which leads due to some desynch to not being able to press "release spirit". + Also the teleport using while on vehicle is lacking support, but soon will have. + You can still click afer leaving vehicle. */ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "SpellScript.h" #include "SpellAuraEffects.h" -#include "PassiveAI.h" #include "eye_of_eternity.h" -#include "ScriptedEscortAI.h" #include "Player.h" #include "Vehicle.h" #include "CombatAI.h" +#include "GameObjectAI.h" #include "CreatureTextMgr.h" - -enum Achievements -{ - ACHIEV_TIMED_START_EVENT = 20387, -}; +#include "MoveSplineInit.h" enum Events { - // =========== PHASE ONE =============== - EVENT_ARCANE_BREATH = 1, - EVENT_ARCANE_STORM = 2, - EVENT_VORTEX = 3, - EVENT_POWER_SPARKS = 4, + // =========== INTRO BEFORE WE START ENCOUNTER =============== + EVENT_STOP_PORTAL_BEAM = 1, + EVENT_RANDOM_PORTAL = 2, + EVENT_SAY_INTRO = 3, + EVENT_LAND_START_ENCOUNTER = 4, - // =========== PHASE TWO =============== - EVENT_SURGE_POWER = 5, // wowhead is wrong, Surge of Power is casted instead of Arcane Pulse (source sniffs!) - EVENT_SUMMON_ARCANE = 6, + // =========== PHASE ONE =============== + EVENT_ARCANE_BREATH = 5, + EVENT_ARCANE_STORM = 6, + EVENT_VORTEX = 7, + EVENT_POWER_SPARKS = 8, // =========== PHASE TWO =============== - EVENT_SURGE_POWER_PHASE_3 = 7, - EVENT_STATIC_FIELD = 8, - - // =============== YELLS =============== - EVENT_YELL_0 = 9, - EVENT_YELL_1 = 10, - EVENT_YELL_2 = 11, - EVENT_YELL_3 = 12, - EVENT_YELL_4 = 13, + EVENT_FLY_OUT_OF_PLATFORM = 9, + EVENT_DELAYED_REINFORCEMENTS = 10, + EVENT_PATHING_AROUND_PLATFORM = 11, + EVENT_SURGE_OF_POWER_P_TWO = 12, + EVENT_SUMMON_ARCANE_BOMB = 13, + EVENT_MOVE_TO_POINT_SURGE_P_TWO = 14, + EVENT_LIGHT_DIMENSION_CHANGE = 15, + EVENT_MOVE_TO_P_THREE_POINT = 16, + EVENT_START_P_THREE = 17, + + // =========== PHASE THREE ============= + EVENT_ARCANE_PULSE = 18, + EVENT_SURGE_OF_POWER_P_THREE = 19, + EVENT_STATIC_FIELD = 20, + + // ========== MISC MECHANICS =========== + EVENT_PREVENT_SAY_SPAM_ON_KILL = 21, + EVENT_MOVE_TO_VORTEX_POINT = 22, // This should be fixed someday in core, we can't call new movement from MovementInform + EVENT_START_FIRST_RANDOM_PORTAL = 23, // There is something that is still loading when we first enter instance and it breaks + // first visual cast of intro portal beam mechanic, so we need short delay from the event. + // ============ NEXUS LORDS ============ + EVENT_ARCANE_SHOCK = 1, + EVENT_HASTE_BUFF = 2, + EVENT_NUKE_DUMMY = 3, + + // ======== SCIONS OF ETERNITY ========= + EVENT_ARCANE_BARRAGE = 1 }; enum Phases { - PHASE_ONE = 1, - PHASE_TWO = 2, - PHASE_THREE = 3 + PHASE_NOT_STARTED = 1, + PHASE_ONE = 2, + PHASE_TWO = 3, + PHASE_THREE = 4 }; enum Spells { - SPELL_ARCANE_BREATH = 56272, - SPELL_ARCANE_STORM = 57459, - SPELL_BERSEKER = 60670, - - SPELL_VORTEX_1 = 56237, // seems that frezze object animation - SPELL_VORTEX_2 = 55873, // visual effect - SPELL_VORTEX_3 = 56105, // this spell must handle all the script - casted by the boss and to himself - //SPELL_VORTEX_4 = 55853, // damage | used to enter to the vehicle - defined in eye_of_eternity.h - //SPELL_VORTEX_5 = 56263, // damage | used to enter to the vehicle - defined in eye_of_eternity.h - SPELL_VORTEX_6 = 73040, // teleport - (casted to all raid) | caster 30090 | target player - - SPELL_PORTAL_VISUAL_CLOSED = 55949, - SPELL_SUMMON_POWER_PARK = 56142, - SPELL_POWER_SPARK_DEATH = 55852, - SPELL_POWER_SPARK_MALYGOS = 56152, - - SPELL_SURGE_POWER = 56505, // used in phase 2 - SPELL_SUMMON_ARCANE_BOMB = 56429, - SPELL_ARCANE_OVERLOAD = 56432, - SPELL_SUMMOM_RED_DRAGON = 56070, - SPELL_SURGE_POWER_PHASE_3 = 57407, - SPELL_STATIC_FIELD = 57430 + // Intro + SPELL_RANDOM_PORTAL = 56047, + SPELL_PORTAL_BEAM = 56046, // Malygos cast on portal to activate it during PHASE_NOT_STARTED + + //Phase I + SPELL_BERSEKER = 60670, + SPELL_MALYGOS_BERSERK = 47008, // it's the berserk spell that will hit only Malygos after 10 min of 60670 + SPELL_PORTAL_VISUAL_CLOSED = 55949, + SPELL_SUMMON_POWER_PARK = 56142, + SPELL_POWER_SPARK_DEATH = 55852, + SPELL_POWER_SPARK_MALYGOS = 56152, + SPELL_ARCANE_BREATH = 56272, + SPELL_ARCANE_STORM_P_I = 61693, + SPELL_VORTEX_1 = 56237, // seems that frezze object animation + SPELL_VORTEX_2 = 55873, // visual effect + SPELL_VORTEX_3 = 56105, // this spell must handle all the script - casted by the boss and to himself + SPELL_VORTEX_6 = 73040, // teleport - (casted to all raid), caster vortex bunnies, targets players. + + // Phase II + SPELL_TELEPORT_VISUAL_ONLY = 41232, // Light blue animation cast by arcane NPCs when spawned on Hover Disks + SPELL_RIDE_HOVER_DISK = 61421, + SPELL_ALIGN_DISK_AGGRO = 61210, + SPELL_DUMMY_NUKE = 61215, + SPELL_SUMMON_ARCANE_BOMB = 56429, + SPELL_ARCANE_BOMB_TRIGGER = 56430, + SPELL_ARCANE_BOMB_KNOCKBACK_DAMAGE = 56431, + SPELL_ARCANE_OVERLOAD_1 = 56432, // casted by npc Arcane Overload ID: 30282 + // SPELL_ARCANE_OVERLOAD_2 = 56435, // Triggered by 56432 + // SPELL_ARCANE_OVERLOAD_3 = 56438, // Triggered by 56432 + SPELL_SURGE_OF_POWER_P_II = 56505, + // SPELL_SURGE_OF_POWER_TRIGGERED = 56548, + SPELL_ARCANE_SHOCK = 57058, // used by Nexus Lords + SPELL_HASTE = 57060, // used by Nexus Lords + SPELL_ARCANE_BARRAGE = 56397, // used by Scions of Eternity + + // Transition /II-III/ + SPELL_SUMMOM_RED_DRAGON_BUDYY = 56070, + SPELL_RIDE_RED_DRAGON_BUDDY = 56071, + SPELL_SUMMON_RED_DRAGON_BUDDY_F_CAST = 58846, // After implicitly hit player targets they will force cast 56070 on self + SPELL_DESTROY_PLATFORM_CHANNEL = 58842, + SPELL_DESTROY_PLATFORM_BOOM_VISUAL = 59084, + SPELL_DESTROY_PLATFORM_EVENT = 59099, + + // Phase III + SPELL_CLEAR_ALL_DEBUFFS = 34098, + SPELL_IMMUNE_CURSES = 64515, + SPELL_STATIC_FIELD_MISSLE = 57430, + SPELL_ARCANE_PULSE = 57432, + SPELL_SURGE_OF_POWER_PHASE_3_10 = 57407, + SPELL_SURGE_OF_POWER_PHASE_3_25 = 60936, + SPELL_SURGE_OF_POWER_WARNING_SELECTOR_25 = 60939, // used in 25 player mode for selecting targets for warnings and then sends to actual spell + SPELL_ARCANE_STORM_P_III = 57459, + + // Phase I and III + SPELL_ARCANE_STORM_EXTRA_VISUAL = 57473, + + // Outro + SPELL_ALEXSTRASZAS_GIFT_BEAM_VISUAL = 61023 }; enum Movements { - MOVE_VORTEX = 1, - MOVE_PHASE_TWO, - MOVE_DEEP_BREATH_ROTATION, - MOVE_INIT_PHASE_ONE, - MOVE_CENTER_PLATFORM + POINT_NEAR_RANDOM_PORTAL_P_NONE = 1, + POINT_LAND_P_ONE, + POINT_VORTEX_P_ONE, + POINT_LAND_AFTER_VORTEX_P_ONE, + POINT_LIFT_IN_AIR_P_ONE, + POINT_PHASE_ONE_TO_TWO_TRANSITION, + POINT_FLY_OUT_OF_PLATFORM_P_TWO, + POINT_SURGE_OF_POWER_P_TWO, + POINT_DESTROY_PLATFORM_P_TWO, + POINT_IDLE_P_THREE, }; enum Seats { - SEAT_0 = 0, + SEAT_0 = 0 }; enum Factions { - FACTION_FRIENDLY = 35, - FACTION_HOSTILE = 14 + // Needed for melee hover disks /when Nexus Lords die/ + FACTION_FRIENDLY = 35 }; enum Actions { - ACTION_HOVER_DISK_START_WP_1, - ACTION_HOVER_DISK_START_WP_2 -}; - -enum MalygosEvents -{ - DATA_SUMMON_DEATHS, // phase 2 - DATA_PHASE + // Malygos + ACTION_LAND_ENCOUNTER_START = 0, + ACTION_EXECUTE_VORTEX = 1, + ACTION_HANDLE_P_THREE_INTRO = 2, + ACTION_LIFT_IN_AIR = 3, + ACTION_HANDLE_RESPAWN = 4, + ACTION_CYCLIC_MOVEMENT = 5, + + // Caster hover disk despawn action + ACTION_DELAYED_DESPAWN = 8, + + // Nexus Lord's action used to shedule casting spell that determine disk's target to chase + ACTION_SET_DISK_VICTIM_CHASE = 0 }; -#define TEN_MINUTES 600000 - enum Texts { // Malygos - SAY_AGGRO_P_ONE = 0, - SAY_KILLED_PLAYER_P_ONE = 1, - SAY_END_P_ONE = 2, - SAY_AGGRO_P_TWO = 3, - SAY_ANTI_MAGIC_SHELL = 4, // not sure when execute it - SAY_MAGIC_BLAST = 5, // not sure when execute it - SAY_KILLED_PLAYER_P_TWO = 6, - SAY_END_P_TWO = 7, - SAY_INTRO_P_THREE = 8, - SAY_AGGRO_P_THREE = 9, - SAY_SURGE_POWER = 10, // not sure when execute it - SAY_BUFF_SPARK = 11, - SAY_KILLED_PLAYER_P_THREE = 12, - SAY_SPELL_CASTING_P_THREE = 13, - SAY_DEATH, + SAY_INTRO_EVENT = 0, + SAY_START_P_ONE = 1, + SAY_DEEP_BREATH = 2, + SAY_KILLED_PLAYER_P_ONE = 3, + SAY_END_P_ONE = 4, + // SAY_START_P_TWO = 5, // Unused by Blizzard for some reason on any version + SAY_ANTI_MAGIC_SHELL = 6, + SAY_MAGIC_BLAST = 7, + SAY_KILLED_PLAYER_P_TWO = 8, + SAY_END_P_TWO = 9, + SAY_START_P_THREE = 10, + // SAY_START_P_THREE = 11, // Unused by Blizzard for some reason on any version + EMOTE_SURGE_OF_POWER_WARNING_P2 = 12, + SAY_SURGE_OF_POWER = 13, + SAY_BUFF_SPARK = 14, + SAY_KILLED_PLAYER_P_THREE = 15, + SAY_SPELL_CASTING_P_THREE = 16, + SAY_DEATH = 17, + EMOTE_SURGE_OF_POWER_WARNING_P3 = 18, + EMOTE_HIT_BERSERKER_TIMER = 19, // Alexstrasza SAY_ONE = 0, @@ -161,252 +226,438 @@ enum Texts SAY_FOUR = 3, // Power Sparks - EMOTE_POWER_SPARK_SUMMONED = 0 + EMOTE_POWER_SPARK_SUMMONED = 0 }; +#define MAX_SUMMONS_PHASE_TWO_10MAN 6 +#define MAX_SUMMONS_PHASE_TWO_25MAN 12 -#define MAX_HOVER_DISK_WAYPOINTS 18 +#define MAX_RANGE_HOVER_DISK_SPAWNPOINTS 8 +Position const RangeHoverDisksSpawnPositions[MAX_RANGE_HOVER_DISK_SPAWNPOINTS] = +{ + { 782.9821f, 1296.652f, 282.1114f, 0.0f }, + { 764.3126f, 1328.871f, 282.3091f, 0.0f }, + { 725.8506f, 1306.749f, 282.2698f, 0.0f }, + { 744.5175f, 1274.396f, 282.3402f, 0.0f }, + { 764.3936f, 1274.371f, 282.6011f, 0.0f }, + { 779.3761f, 1316.166f, 282.1653f, 0.0f }, + { 744.4915f, 1328.901f, 282.2112f, 0.0f }, + { 729.2364f, 1287.328f, 282.4173f, 0.0f } +}; -// Sniffed data (x, y, z) -const Position HoverDiskWaypoints[MAX_HOVER_DISK_WAYPOINTS] = +#define MAX_MELEE_HOVER_DISK_SPAWNPOINTS 4 +Position const MeleeHoverDisksSpawnPositions[MAX_RANGE_HOVER_DISK_SPAWNPOINTS] = { - {782.9821f, 1296.652f, 282.1114f, 0.0f}, - {779.5459f, 1287.228f, 282.1393f, 0.0f}, - {773.0028f, 1279.52f, 282.4164f, 0.0f}, - {764.3626f, 1274.476f, 282.4731f, 0.0f}, - {754.3961f, 1272.639f, 282.4171f, 0.0f}, - {744.4422f, 1274.412f, 282.222f, 0.0f}, - {735.575f, 1279.742f, 281.9674f, 0.0f}, - {729.2788f, 1287.187f, 281.9943f, 0.0f}, - {726.1191f, 1296.688f, 282.2997f, 0.0f}, - {725.9396f, 1306.531f, 282.2448f, 0.0f}, - {729.3045f, 1316.122f, 281.9108f, 0.0f}, - {735.8322f, 1323.633f, 282.1887f, 0.0f}, - {744.4616f, 1328.999f, 281.9948f, 0.0f}, - {754.4739f, 1330.666f, 282.049f, 0.0f}, - {764.074f, 1329.053f, 281.9949f, 0.0f}, - {772.8409f, 1323.951f, 282.077f, 0.0f}, - {779.5085f, 1316.412f, 281.9145f, 0.0f}, - {782.8365f, 1306.778f, 282.3035f, 0.0f}, + { 754.4617f, 1283.859f, 285.0522f, 0.0f }, + { 771.7864f, 1301.853f, 285.0522f, 0.0f }, + { 753.9635f, 1319.003f, 285.0522f, 0.0f }, + { 736.4914f, 1301.683f, 285.0522f, 0.0f } }; -#define GROUND_Z 268 +#define MAX_MELEE_HOVER_DISK_WAYPOINTS 16 +Position const MeleeHoverDisksWaypoints[MAX_MELEE_HOVER_DISK_WAYPOINTS] = +{ + // First melee hover disk wps + { 766.2931f, 1312.904f, 277.0551f, 0.0f }, + { 754.3397f, 1319.759f, 274.0536f, 0.0f }, + { 742.1018f, 1312.714f, 270.1367f, 0.0f }, + { 735.6851f, 1301.422f, 266.7208f, 0.0f }, + // Second melee hover disk wps + { 742.6257f, 1313.471f, 275.9713f, 0.0f }, + { 736.8845f, 1301.921f, 274.0264f, 0.0f }, + { 742.6632f, 1289.951f, 269.8603f, 0.0f }, + { 754.3682f, 1283.942f, 266.6098f, 0.0f }, + // Third melee hover disk wps + { 742.2078f, 1290.518f, 276.2484f, 0.0f }, + { 754.5398f, 1284.311f, 273.5815f, 0.0f }, + { 766.5588f, 1290.345f, 269.6655f, 0.0f }, + { 773.4768f, 1301.474f, 266.5821f, 0.0f }, + // Forth melee hover disk wps + { 766.1189f, 1290.197f, 276.9436f, 0.0f }, + { 771.9507f, 1301.602f, 273.9712f, 0.0f }, + { 766.1253f, 1313.451f, 270.4991f, 0.0f }, + { 754.5378f, 1319.399f, 266.6653f, 0.0f } +}; -// Source: Sniffs (x, y, z) -#define MALYGOS_MAX_WAYPOINTS 16 -const Position MalygosPhaseTwoWaypoints[MALYGOS_MAX_WAYPOINTS] = +#define MAX_MALYGOS_POS 10 +Position const MalygosPositions[MAX_MALYGOS_POS] = { - {812.7299f, 1391.672f, 283.2763f, 0.0f}, - {848.2912f, 1358.61f, 283.2763f, 0.0f}, - {853.9227f, 1307.911f, 283.2763f, 0.0f}, - {847.1437f, 1265.538f, 283.2763f, 0.0f}, - {839.9229f, 1245.245f, 283.2763f, 0.0f}, - {827.3463f, 1221.818f, 283.2763f, 0.0f}, - {803.2727f, 1203.851f, 283.2763f, 0.0f}, - {772.9372f, 1197.981f, 283.2763f, 0.0f}, - {732.1138f, 1200.647f, 283.2763f, 0.0f}, - {693.8761f, 1217.995f, 283.2763f, 0.0f}, - {664.5038f, 1256.539f, 283.2763f, 0.0f}, - {650.1497f, 1303.485f, 283.2763f, 0.0f}, - {662.9109f, 1350.291f, 283.2763f, 0.0f}, - {677.6391f, 1377.607f, 283.2763f, 0.0f}, - {704.8198f, 1401.162f, 283.2763f, 0.0f}, - {755.2642f, 1417.1f, 283.2763f, 0.0f}, + { 754.544f, 1301.71f, 320.01f, 0.0f }, // Point destroy platform + { 754.393f, 1301.27f, 292.91f, 0.0f }, // Point vortex + { 754.362f, 1301.61f, 266.17f, 0.0f }, // Land after vortex + { 754.695f, 1301.66f, 316.65f, 0.0f }, // Point surge of Power phase II + { 755.681f, 1298.41f, 220.06f, 0.0f } // Point idle phase III }; -#define MAX_SUMMONS_PHASE_TWO 4 +Position const AlexstraszaSpawnPos = { 854.551f, 1225.31f, 300.901f, 0.0f }; // Alexstrasza's spawn position +Position const HeartOfMagicSpawnPos = { 755.351f, 1298.31f, 223.909f, 0.0f }; // Heart of Magic spawn position -#define MAX_MALYGOS_POS 2 -const Position MalygosPositions[MAX_MALYGOS_POS] = +#define TEN_MINUTES (10*MINUTE*IN_MILLISECONDS) + +enum Achievements { - {754.544f, 1301.71f, 320.0f, 0.0f}, - {754.39f, 1301.27f, 292.91f, 0.0f}, + ACHIEV_TIMED_START_EVENT = 20387 +}; + +enum AreaIds +{ + AREA_EYE_OF_ETERNITY = 4500 +}; + +enum MiscData +{ + // Lights + LIGHT_GET_DEFAULT_FOR_MAP = 0, + LIGHT_OBSCURE_SPACE = 1822, + LIGHT_CHANGE_DIMENSIONS = 1823, + LIGHT_ARCANE_RUNES = 1824, + LIGHT_OBSCURE_ARCANE_RUNES = 1825, + + // Data (setters/getters) + DATA_SUMMON_DEATHS = 0, // phase 2 + DATA_PHASE = 1, + + // Target guids + DATA_LAST_OVERLOAD_GUID = 13, // used to store last Arcane Overload guid + DATA_FIRST_SURGE_TARGET_GUID = 14, + DATA_SECOND_SURGE_TARGET_GUID = 15, + DATA_THIRD_SURGE_TARGET_GUID = 16 +}; + +// Used to check if summons guids come from vehicles +class VehicleCheckPredicate +{ + public: + bool operator()(uint64 guid) { return IS_VEHICLE_GUID(guid); } }; class boss_malygos : public CreatureScript { public: - boss_malygos() : CreatureScript("boss_malygos") {} - - CreatureAI* GetAI(Creature* creature) const - { - return new boss_malygosAI(creature); - } + boss_malygos() : CreatureScript("boss_malygos") { } struct boss_malygosAI : public BossAI { boss_malygosAI(Creature* creature) : BossAI(creature, DATA_MALYGOS_EVENT) { - // If we enter in combat when MovePoint generator is active, it overrwrites our homeposition - _homePosition = creature->GetHomePosition(); + _despawned = false; // We determine if Malygos will be realocated to spawning position on reset triggered by boss despawn on evade + _flySpeed = me->GetSpeed(MOVE_FLIGHT); // Get initial fly speed, otherwise on each wipe fly speed would add up if we get it } void Reset() { - _Reset(); - - _bersekerTimer = 0; - _currentPos = 0; - - SetPhase(PHASE_ONE, true); - - _delayedMovementTimer = 8000; - _delayedMovement = false; + // EnterEvadeMode and Reset() links are cut for the sake of properly functioning despawner. + if (!_despawned) + _Reset(); _summonDeaths = 0; + _preparingPulsesChecker = 0; + _arcaneOverloadGUID = NULL; + _firstSelectedSurgeTargetGUID = NULL; + _secondSelectedSurgeTargetGUID = NULL; + _thirdSelectedSurgeTargetGUID = NULL; + + _killSpamFilter = false; + _canAttack = false; + _executingVortex = false; + _arcaneReinforcements = true; + _flyingOutOfPlatform = false; + _firstCyclicMovementStarted = false; + _performingSurgeOfPower = false; + _performingDestroyPlatform = false; - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - - _cannotMove = true; - - me->SetCanFly(true); + me->SetDisableGravity(true); + me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + // TO DO: find what in core is making boss slower than in retail (when correct speed data) or find missing movement flag update or forced spline change + me->SetSpeed(MOVE_FLIGHT, _flySpeed * 0.25f); + if (_despawned) + DoAction(ACTION_HANDLE_RESPAWN); + SetPhase(PHASE_NOT_STARTED, true); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_PASSIVE); if (instance) instance->DoStopTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); } uint32 GetData(uint32 data) const { - if (data == DATA_SUMMON_DEATHS) - return _summonDeaths; - else if (data == DATA_PHASE) - return _phase; + switch (data) + { + case DATA_SUMMON_DEATHS: + return _summonDeaths; + case DATA_PHASE: + return _phase; + } return 0; } void SetData(uint32 data, uint32 value) { - if (data == DATA_SUMMON_DEATHS && _phase == PHASE_TWO) + if (data == DATA_SUMMON_DEATHS && _phase == PHASE_TWO && !_despawned) { _summonDeaths = value; - if (_summonDeaths >= MAX_SUMMONS_PHASE_TWO) - StartPhaseThree(); + if (GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) + { + if (_summonDeaths == MAX_SUMMONS_PHASE_TWO_10MAN) + { + _performingDestroyPlatform = true; + DoAction(ACTION_HANDLE_P_THREE_INTRO); + } + } + else if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + { + if (_summonDeaths == MAX_SUMMONS_PHASE_TWO_25MAN) + { + _performingDestroyPlatform = true; + DoAction(ACTION_HANDLE_P_THREE_INTRO); + } + } } } - void EnterEvadeMode() + uint64 GetGUID(int32 data) const { - me->SetHomePosition(_homePosition); + switch (data) + { + case DATA_FIRST_SURGE_TARGET_GUID: + return _firstSelectedSurgeTargetGUID; + case DATA_SECOND_SURGE_TARGET_GUID: + return _secondSelectedSurgeTargetGUID; + case DATA_THIRD_SURGE_TARGET_GUID: + return _thirdSelectedSurgeTargetGUID; + } - me->SetDisableGravity(true); + return 0; + } - BossAI::EnterEvadeMode(); + void SetGUID(uint64 guid, int32 type) + { + switch (type) + { + case DATA_LAST_OVERLOAD_GUID: + _arcaneOverloadGUID = guid; + case DATA_FIRST_SURGE_TARGET_GUID: + _firstSelectedSurgeTargetGUID = guid; + case DATA_SECOND_SURGE_TARGET_GUID: + _secondSelectedSurgeTargetGUID = guid; + case DATA_THIRD_SURGE_TARGET_GUID: + _thirdSelectedSurgeTargetGUID = guid; + } + } - if (instance) - instance->SetBossState(DATA_MALYGOS_EVENT, FAIL); + void DoAction(int32 action) + { + switch (action) + { + case ACTION_LAND_ENCOUNTER_START: + events.CancelEventGroup(1); + if (Creature* alexstraszaBunny = me->GetMap()->GetCreature(instance->GetData64(DATA_ALEXSTRASZA_BUNNY_GUID))) + { + Position pos; + pos.m_positionZ = alexstraszaBunny->GetPositionZ(); + alexstraszaBunny->GetNearPoint2D(pos.m_positionX, pos.m_positionY, 30.0f, alexstraszaBunny->GetAngle(me)); + me->GetMotionMaster()->MoveLand(POINT_LAND_P_ONE, pos); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + events.ScheduleEvent(EVENT_LAND_START_ENCOUNTER, 7*IN_MILLISECONDS, 1, PHASE_NOT_STARTED); + } + break; + case ACTION_EXECUTE_VORTEX: + DoCast(me, SPELL_VORTEX_1, true); + DoCast(me, SPELL_VORTEX_2, true); + // the vortex execution continues in the dummy effect of this spell (see it's script) + DoCast(me, SPELL_VORTEX_3, true); + break; + case ACTION_LIFT_IN_AIR: + Position _zToLift; + me->GetPosition(&_zToLift); + if (_phase == PHASE_ONE) + { + _zToLift.m_positionZ += 20.0f; + me->GetMotionMaster()->MoveTakeoff(POINT_LIFT_IN_AIR_P_ONE, _zToLift); + } + else if (_phase == PHASE_TWO) + { + _zToLift.m_positionZ = 300.1f; + me->GetMotionMaster()->MoveTakeoff(POINT_PHASE_ONE_TO_TWO_TRANSITION, _zToLift); + } + break; + case ACTION_HANDLE_P_THREE_INTRO: + events.CancelEventGroup(0); + events.CancelEventGroup(1); + events.CancelEventGroup(2); + me->GetMotionMaster()->Initialize(); + me->StopMoving(); + // Vehicles shouldn't be despawned with 0 delay if the call comes from virtual function that overrides PassengerBoarded. + // Aside from that he doesn't despawn both vehicles and arcane overloads right away, but with some delay. + DummyEntryCheckPredicate pred; + summons.DoAction(ACTION_DELAYED_DESPAWN, pred); + Talk(SAY_END_P_TWO); + me->GetMotionMaster()->MovePoint(POINT_DESTROY_PLATFORM_P_TWO, MalygosPositions[0]); + events.ScheduleEvent(EVENT_LIGHT_DIMENSION_CHANGE, 1*IN_MILLISECONDS, 0, PHASE_TWO); + break; + case ACTION_HANDLE_RESPAWN: + // Teleport to spawn position, we can't use normal relocate + float x, y, z, o; + me->GetRespawnPosition(x, y, z, &o); + me->NearTeleportTo(x, y, z, o); + // Respawn Iris + instance->SetData(DATA_RESPAWN_IRIS, 0); + _despawned = false; + break; + case ACTION_CYCLIC_MOVEMENT: + Movement::MoveSplineInit init(me); + FillCirclePath(MalygosPositions[3], 130.0f, 283.2763f, init.Path(), true); + init.SetFly(); + init.SetCyclic(); + init.Launch(); + break; + } } void SetPhase(uint8 phase, bool setEvents = false) { events.Reset(); - events.SetPhase(phase); _phase = phase; - if (setEvents) SetPhaseEvents(); } - void StartPhaseThree() - { - if (!instance) - return; - - SetPhase(PHASE_THREE, true); - - // this despawns Hover Disks - summons.DespawnAll(); - // players that used Hover Disk are no in the aggro list - me->SetInCombatWithZone(); - ThreatContainer::StorageType const& m_threatlist = me->getThreatManager().getThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) - { - if (Unit* target = (*itr)->getTarget()) - { - if (target->GetTypeId() != TYPEID_PLAYER) - continue; - - // The rest is handled in the AI of the vehicle. - target->CastSpell(target, SPELL_SUMMOM_RED_DRAGON, true); - me->Attack(target, false); - } - } - - if (GameObject* go = GameObject::GetGameObject(*me, instance->GetData64(DATA_PLATFORM))) - go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); // In sniffs it has this flag, but i don't know how is applied. - - // pos sniffed - me->GetMotionMaster()->MoveIdle(); - me->GetMotionMaster()->MovePoint(MOVE_CENTER_PLATFORM, MalygosPositions[0].GetPositionX(), MalygosPositions[0].GetPositionY(), MalygosPositions[0].GetPositionZ()); - } - void SetPhaseEvents() { switch (_phase) { + case PHASE_NOT_STARTED: + events.ScheduleEvent(EVENT_SAY_INTRO, 1*IN_MILLISECONDS, 1, _phase); + events.ScheduleEvent(EVENT_START_FIRST_RANDOM_PORTAL, 2*IN_MILLISECONDS, 1, _phase); + break; case PHASE_ONE: events.ScheduleEvent(EVENT_ARCANE_BREATH, urand(15, 20)*IN_MILLISECONDS, 0, _phase); - events.ScheduleEvent(EVENT_ARCANE_STORM, urand(5, 10)*IN_MILLISECONDS, 0, _phase); + events.ScheduleEvent(EVENT_ARCANE_STORM, 10*IN_MILLISECONDS, 0, _phase); events.ScheduleEvent(EVENT_VORTEX, urand(30, 40)*IN_MILLISECONDS, 0, _phase); events.ScheduleEvent(EVENT_POWER_SPARKS, urand(30, 35)*IN_MILLISECONDS, 0, _phase); break; case PHASE_TWO: - events.ScheduleEvent(EVENT_YELL_0, 0, 0, _phase); - events.ScheduleEvent(EVENT_YELL_1, 24*IN_MILLISECONDS, 0, _phase); - events.ScheduleEvent(EVENT_SURGE_POWER, urand(60, 70)*IN_MILLISECONDS, 0, _phase); - events.ScheduleEvent(EVENT_SUMMON_ARCANE, urand(2, 5)*IN_MILLISECONDS, 0, _phase); + events.ScheduleEvent(EVENT_MOVE_TO_POINT_SURGE_P_TWO, 60*IN_MILLISECONDS, 0, _phase); + me->AI()->DoAction(ACTION_LIFT_IN_AIR); break; case PHASE_THREE: - events.ScheduleEvent(EVENT_YELL_2, 0, 0, _phase); - events.ScheduleEvent(EVENT_YELL_3, 8*IN_MILLISECONDS, 0, _phase); - events.ScheduleEvent(EVENT_YELL_4, 16*IN_MILLISECONDS, 0, _phase); - events.ScheduleEvent(EVENT_SURGE_POWER_PHASE_3, urand(7, 16)*IN_MILLISECONDS, 0, _phase); + events.ScheduleEvent(EVENT_ARCANE_PULSE, 13*IN_MILLISECONDS, 0, _phase); + events.ScheduleEvent(EVENT_ARCANE_STORM, 20*IN_MILLISECONDS, 0, _phase); + events.ScheduleEvent(EVENT_SURGE_OF_POWER_P_THREE, urand(7, 16)*IN_MILLISECONDS, 0, _phase); events.ScheduleEvent(EVENT_STATIC_FIELD, urand(20, 30)*IN_MILLISECONDS, 0, _phase); break; - default: - break; } } + // There are moments where boss will do nothing while being attacked + void AttackStart(Unit* target) + { + if (_canAttack) + BossAI::AttackStart(target); + } + void EnterCombat(Unit* /*who*/) { - _EnterCombat(); + // We can't call full function here since it includes DoZoneInCombat(), + // if someone does it will be returned with a warning. + me->setActive(true); + if (instance) + { + if (!instance->CheckRequiredBosses(DATA_MALYGOS_EVENT)) + { + EnterEvadeMode(); + return; + } + + instance->SetBossState(DATA_MALYGOS_EVENT, IN_PROGRESS); + } + + Talk(SAY_START_P_ONE); + DoCast(SPELL_BERSEKER); // periodic aura, first tick in 10 minutes + if (instance) + instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); + } + + void EnterEvadeMode() + { + if (instance) + instance->SetBossState(DATA_MALYGOS_EVENT, FAIL); - me->SetDisableGravity(false); - me->SetCanFly(false); + SendLightOverride(LIGHT_GET_DEFAULT_FOR_MAP, 1*IN_MILLISECONDS); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + if (_phase == PHASE_THREE) + me->SetControlled(false, UNIT_STATE_ROOT); + + uint32 corpseDelay = me->GetCorpseDelay(); + uint32 respawnDelay = me->GetRespawnDelay(); + me->SetCorpseDelay(1); + me->SetRespawnDelay(29); + me->DespawnOrUnsummon(); + me->SetCorpseDelay(corpseDelay); + me->SetRespawnDelay(respawnDelay); - Talk(SAY_AGGRO_P_ONE); + // Set speed to normal value + me->SetSpeed(MOVE_FLIGHT, _flySpeed); + me->RemoveAllAuras(); + me->CombatStop(); // Sometimes threat can remain, so it's a safety measure - DoCast(SPELL_BERSEKER); // periodic aura, first tick in 10 minutes + if (!_despawned) + _despawned = true; + + me->ResetLootMode(); + events.Reset(); + if (!summons.empty()) + { + if (_phase == PHASE_TWO) + { + VehicleCheckPredicate pred; + summons.DoAction(ACTION_DELAYED_DESPAWN, pred); + summons.remove_if(pred); + summons.DespawnAll(); + } + else if (_phase == PHASE_THREE) + summons.DespawnAll(); + } if (instance) - instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); + instance->SetBossState(DATA_MALYGOS_EVENT, NOT_STARTED); } - void KilledUnit(Unit* who) + void KilledUnit(Unit* victim) { - if (who->GetTypeId() != TYPEID_PLAYER) + if (victim->GetTypeId() != TYPEID_PLAYER) return; - switch (_phase) + if (!_killSpamFilter) { - case PHASE_ONE: - Talk(SAY_KILLED_PLAYER_P_ONE); - break; - case PHASE_TWO: - Talk(SAY_KILLED_PLAYER_P_TWO); - break; - case PHASE_THREE: - Talk(SAY_KILLED_PLAYER_P_THREE); - break; + switch (_phase) + { + case PHASE_ONE: + Talk(SAY_KILLED_PLAYER_P_ONE); + events.ScheduleEvent(EVENT_PREVENT_SAY_SPAM_ON_KILL, 5*IN_MILLISECONDS, 0, _phase); + _killSpamFilter = true; + break; + case PHASE_TWO: + Talk(SAY_KILLED_PLAYER_P_TWO); + events.ScheduleEvent(EVENT_PREVENT_SAY_SPAM_ON_KILL, 5*IN_MILLISECONDS, 0, _phase); + _killSpamFilter = true; + break; + case PHASE_THREE: + Talk(SAY_KILLED_PLAYER_P_THREE); + events.ScheduleEvent(EVENT_PREVENT_SAY_SPAM_ON_KILL, 5*IN_MILLISECONDS, 0, _phase); + _killSpamFilter = true; + break; + } } } - void SpellHit(Unit* caster, const SpellInfo* spell) + void SpellHit(Unit* caster, SpellInfo const* spell) { if (spell->Id == SPELL_POWER_SPARK_MALYGOS) { @@ -415,346 +666,415 @@ public: Talk(SAY_BUFF_SPARK); } + else if (spell->Id == SPELL_MALYGOS_BERSERK) + sCreatureTextMgr->SendChat(me, EMOTE_HIT_BERSERKER_TIMER, 0, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_MAP); } void MoveInLineOfSight(Unit* who) { - if (!me->isInCombat()) + if (!me->isInCombat() || _phase != PHASE_ONE) return; if (who->GetEntry() == NPC_POWER_SPARK) - { - // not sure about the distance | I think it is better check this here than in the UpdateAI function... if (who->GetDistance(me) <= 2.5f) who->CastSpell(me, SPELL_POWER_SPARK_MALYGOS, true); - } - } - - void PrepareForVortex() - { - me->SetDisableGravity(true); - me->SetCanFly(true); - - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->MovePoint(MOVE_VORTEX, MalygosPositions[1].GetPositionX(), MalygosPositions[1].GetPositionY(), MalygosPositions[1].GetPositionZ()); - // continues in MovementInform function. - } - - void ExecuteVortex() - { - DoCast(me, SPELL_VORTEX_1, true); - DoCast(me, SPELL_VORTEX_2, true); - - // the vortex execution continues in the dummy effect of this spell (see its script) - DoCast(me, SPELL_VORTEX_3, true); } void MovementInform(uint32 type, uint32 id) { - if (type != POINT_MOTION_TYPE) + if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE) return; switch (id) { - case MOVE_VORTEX: + case POINT_NEAR_RANDOM_PORTAL_P_NONE: + if (Creature* portal = me->FindNearestCreature(NPC_PORTAL_TRIGGER, 31.0f, true)) + { + events.ScheduleEvent(EVENT_STOP_PORTAL_BEAM, 10*IN_MILLISECONDS, 1, PHASE_NOT_STARTED); + events.ScheduleEvent(EVENT_RANDOM_PORTAL, 14*IN_MILLISECONDS, 1, PHASE_NOT_STARTED); + DoCast(portal, SPELL_PORTAL_BEAM); + } + break; + case POINT_LAND_P_ONE: + me->SetDisableGravity(false); + break; + case POINT_VORTEX_P_ONE: me->GetMotionMaster()->MoveIdle(); - ExecuteVortex(); + DoAction(ACTION_EXECUTE_VORTEX); break; - case MOVE_DEEP_BREATH_ROTATION: - _currentPos = _currentPos == MALYGOS_MAX_WAYPOINTS - 1 ? 0 : _currentPos+1; - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->MovePoint(MOVE_DEEP_BREATH_ROTATION, MalygosPhaseTwoWaypoints[_currentPos]); + case POINT_LAND_AFTER_VORTEX_P_ONE: + me->SetDisableGravity(false); + _executingVortex = false; + _canAttack = true; break; - case MOVE_INIT_PHASE_ONE: - me->SetInCombatWithZone(); + case POINT_LIFT_IN_AIR_P_ONE: + me->SetDisableGravity(true); + events.ScheduleEvent(EVENT_MOVE_TO_VORTEX_POINT, 1, 0, PHASE_ONE); break; - case MOVE_CENTER_PLATFORM: - // Malygos is already flying here, there is no need to set it again. - _cannotMove = false; - // malygos will move into center of platform and then he does not chase dragons, he just turns to his current target. - me->GetMotionMaster()->MoveIdle(); + case POINT_FLY_OUT_OF_PLATFORM_P_TWO: + if (!_firstCyclicMovementStarted) + { + _firstCyclicMovementStarted = true; + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetFacingToObject(me->GetMap()->GetCreature(instance->GetData64(DATA_ALEXSTRASZA_BUNNY_GUID))); + events.ScheduleEvent(EVENT_SUMMON_ARCANE_BOMB, 1*IN_MILLISECONDS, 0, PHASE_TWO); + } + _flyingOutOfPlatform = false; + _performingSurgeOfPower = false; + events.ScheduleEvent(EVENT_PATHING_AROUND_PLATFORM, 1*IN_MILLISECONDS, 0, PHASE_TWO); + break; + case POINT_PHASE_ONE_TO_TWO_TRANSITION: + me->SetDisableGravity(true); + me->SetFacingToObject(me->GetMap()->GetCreature(instance->GetData64(DATA_ALEXSTRASZA_BUNNY_GUID))); + SendLightOverride(LIGHT_ARCANE_RUNES, 5*IN_MILLISECONDS); + events.ScheduleEvent(EVENT_FLY_OUT_OF_PLATFORM, 18*IN_MILLISECONDS, 0, PHASE_TWO); + break; + case POINT_SURGE_OF_POWER_P_TWO: + if (!_performingDestroyPlatform) + { + Talk(EMOTE_SURGE_OF_POWER_WARNING_P2); + DoCastAOE(SPELL_SURGE_OF_POWER_P_II, true); + events.ScheduleEvent(EVENT_FLY_OUT_OF_PLATFORM, 7*IN_MILLISECONDS, 0, PHASE_TWO); + } + break; + case POINT_DESTROY_PLATFORM_P_TWO: + SendLightOverride(LIGHT_OBSCURE_SPACE, 1*IN_MILLISECONDS); + DoCast(me, SPELL_DESTROY_PLATFORM_CHANNEL); + events.ScheduleEvent(EVENT_MOVE_TO_P_THREE_POINT, 11*IN_MILLISECONDS, 0, PHASE_TWO); + break; + case POINT_IDLE_P_THREE: + me->SetControlled(true, UNIT_STATE_ROOT); + events.ScheduleEvent(EVENT_START_P_THREE, 5*IN_MILLISECONDS, 0, PHASE_TWO); break; - } - } - - void StartPhaseTwo() - { - SetPhase(PHASE_TWO, true); - - me->SetDisableGravity(true); - me->SetCanFly(true); - - me->GetMotionMaster()->MoveIdle(); - me->GetMotionMaster()->MovePoint(MOVE_DEEP_BREATH_ROTATION, MalygosPhaseTwoWaypoints[0]); - - for (uint8 i = 0; i < 2; i++) - { - // Starting position. One starts from the first waypoint and another from the last. - uint8 pos = !i ? MAX_HOVER_DISK_WAYPOINTS-1 : 0; - if (Creature* summon = me->SummonCreature(NPC_HOVER_DISK_CASTER, HoverDiskWaypoints[pos])) - if (summon->IsAIEnabled) - summon->AI()->DoAction(ACTION_HOVER_DISK_START_WP_1+i); - - // not sure about its position. - if (Creature* summon = me->SummonCreature(NPC_HOVER_DISK_MELEE, HoverDiskWaypoints[0])) - summon->SetInCombatWithZone(); } } void UpdateAI(uint32 diff) { - if (!UpdateVictim()) + if (!UpdateVictim() && _phase != PHASE_NOT_STARTED && _phase != PHASE_TWO) return; events.Update(diff); - if (_phase == PHASE_THREE) - { - if (!_cannotMove) - { - // it can change if the player falls from the vehicle. - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != IDLE_MOTION_TYPE) - { - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->MoveIdle(); - } - } else - { - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE) - { - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->MovePoint(MOVE_CENTER_PLATFORM, MalygosPositions[0].GetPositionX(), MalygosPositions[0].GetPositionY(), MalygosPositions[0].GetPositionZ()); - } - } - } + // we can't cast if we are casting already unless in PHASE_NOT_STARTED channeling PORTAL_BEAM + if (me->HasUnitState(UNIT_STATE_CASTING) && _phase != PHASE_NOT_STARTED) + return; - // we need a better way for pathing - if (_delayedMovement) + // at 50% hp Malygos switchs to phase 2 and removes hovering until reset or end of encounter + if (_phase == PHASE_ONE && me->GetHealthPct() <= 50.0f) { - if (_delayedMovementTimer <= diff) - { - me->GetMotionMaster()->MovePoint(MOVE_DEEP_BREATH_ROTATION, MalygosPhaseTwoWaypoints[_currentPos]); - _delayedMovementTimer = 8000; - _delayedMovement = false; - } _delayedMovementTimer -= diff; + SetPhase(PHASE_TWO, true); + _canAttack = false; + me->AttackStop(); + Talk(SAY_END_P_ONE); } - // at 50 % health malygos switch to phase 2 - if (me->GetHealthPct() <= 50.0f && _phase == PHASE_ONE) - StartPhaseTwo(); - - // the boss is handling vortex - if (me->HasAura(SPELL_VORTEX_2)) - return; - - // We can't cast if we are casting already. - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { - case EVENT_YELL_2: - Talk(SAY_END_P_TWO); - break; - case EVENT_YELL_3: - Talk(SAY_INTRO_P_THREE); + case EVENT_START_FIRST_RANDOM_PORTAL: + me->CastCustomSpell(SPELL_RANDOM_PORTAL, SPELLVALUE_MAX_TARGETS, 1); break; - case EVENT_YELL_4: - Talk(SAY_AGGRO_P_THREE); + case EVENT_STOP_PORTAL_BEAM: + me->InterruptNonMeleeSpells(true); break; - case EVENT_YELL_0: - Talk(SAY_END_P_ONE); + case EVENT_RANDOM_PORTAL: + me->CastCustomSpell(SPELL_RANDOM_PORTAL, SPELLVALUE_MAX_TARGETS, 1); break; - case EVENT_YELL_1: - Talk(SAY_AGGRO_P_TWO); - break; - case EVENT_ARCANE_BREATH: - DoCast(me->getVictim(), SPELL_ARCANE_BREATH); - events.ScheduleEvent(EVENT_ARCANE_BREATH, urand(35, 60)*IN_MILLISECONDS, 0, PHASE_ONE); + case EVENT_LAND_START_ENCOUNTER: + if (GameObject* iris = me->GetMap()->GetGameObject(instance->GetData64(DATA_FOCUSING_IRIS_GUID))) + { + me->SetFacingToObject(iris); + iris->Delete(); // this is not the best way. + } + _canAttack = true; + SetPhase(PHASE_ONE, true); break; - case EVENT_ARCANE_STORM: - DoCast(me->getVictim(), SPELL_ARCANE_STORM); - events.ScheduleEvent(EVENT_ARCANE_STORM, urand(5, 10)*IN_MILLISECONDS, 0, PHASE_ONE); + case EVENT_SAY_INTRO: + Talk(SAY_INTRO_EVENT); + events.ScheduleEvent(EVENT_SAY_INTRO, urand(85, 95)*IN_MILLISECONDS, 1, PHASE_NOT_STARTED); break; case EVENT_VORTEX: - PrepareForVortex(); + _executingVortex = true; + DoAction(ACTION_LIFT_IN_AIR); events.ScheduleEvent(EVENT_VORTEX, urand(60, 80)*IN_MILLISECONDS, 0, PHASE_ONE); break; + case EVENT_MOVE_TO_VORTEX_POINT: + _canAttack = false; + me->AttackStop(); + me->GetMotionMaster()->MovePoint(POINT_VORTEX_P_ONE, MalygosPositions[1]); + break; case EVENT_POWER_SPARKS: instance->SetData(DATA_POWER_SPARKS_HANDLING, 0); events.ScheduleEvent(EVENT_POWER_SPARKS, urand(30, 35)*IN_MILLISECONDS, 0, PHASE_ONE); break; - case EVENT_SURGE_POWER: - me->GetMotionMaster()->MoveIdle(); - _delayedMovement = true; - DoCast(SPELL_SURGE_POWER); - events.ScheduleEvent(EVENT_SURGE_POWER, urand(60, 70)*IN_MILLISECONDS, 0, PHASE_TWO); + case EVENT_ARCANE_BREATH: + if (_executingVortex) + { + events.ScheduleEvent(EVENT_ARCANE_BREATH, 20*IN_MILLISECONDS, 0, PHASE_ONE); + break; + } + + me->CastSpell(me->getVictim(), SPELL_ARCANE_BREATH); + events.ScheduleEvent(EVENT_ARCANE_BREATH, 20*IN_MILLISECONDS, 0, PHASE_ONE); break; - case EVENT_SUMMON_ARCANE: - DoCast(SPELL_SUMMON_ARCANE_BOMB); - events.ScheduleEvent(EVENT_SUMMON_ARCANE, urand(12, 15)*IN_MILLISECONDS, 0, PHASE_TWO); + case EVENT_ARCANE_STORM: + if (_phase == PHASE_ONE) + { + if (_executingVortex) + { + events.ScheduleEvent(EVENT_ARCANE_STORM, 10*IN_MILLISECONDS, 0, PHASE_ONE); + break; + } + + DoCast(me, SPELL_ARCANE_STORM_P_I, true); + events.ScheduleEvent(EVENT_ARCANE_STORM, 10*IN_MILLISECONDS, 0, PHASE_ONE); + } + else if (_phase == PHASE_THREE) + { + DoCast(me, SPELL_ARCANE_STORM_P_III, true); + events.ScheduleEvent(EVENT_ARCANE_STORM, urand(6, 12)*IN_MILLISECONDS, 0, PHASE_THREE); + } break; - case EVENT_SURGE_POWER_PHASE_3: - DoCast(GetTargetPhaseThree(), SPELL_SURGE_POWER_PHASE_3); - events.ScheduleEvent(EVENT_SURGE_POWER_PHASE_3, urand(7, 16)*IN_MILLISECONDS, 0, PHASE_THREE); + case EVENT_FLY_OUT_OF_PLATFORM: + if (Creature* alexstraszaBunny = me->GetMap()->GetCreature(instance->GetData64(DATA_ALEXSTRASZA_BUNNY_GUID))) + { + Position randomPosOnRadius; + // Hardcodded retail value, reason is Z getters can fail... (TO DO: Change to getter when height calculation works on 100%!) + randomPosOnRadius.m_positionZ = 283.0521f; + alexstraszaBunny->GetNearPoint2D(randomPosOnRadius.m_positionX, randomPosOnRadius.m_positionY, 130.0f, alexstraszaBunny->GetAngle(me)); + me->GetMotionMaster()->MovePoint(POINT_FLY_OUT_OF_PLATFORM_P_TWO, randomPosOnRadius); + _flyingOutOfPlatform = true; + } + + if (_arcaneReinforcements && instance) + { + for (uint8 rangeDisks = 0; rangeDisks < (GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL ? 4 : 5); rangeDisks++) + { + Creature* casterDiskSummon = me->SummonCreature(NPC_HOVER_DISK_CASTER, RangeHoverDisksSpawnPositions[rangeDisks]); + + if (casterDiskSummon->IsAIEnabled) + casterDiskSummon->AI()->DoAction(rangeDisks); + } + + for (uint8 meleeDisks = 0; meleeDisks < 2; meleeDisks++) + { + Creature* meleeDiskSummon = me->SummonCreature(NPC_HOVER_DISK_MELEE, MeleeHoverDisksSpawnPositions[meleeDisks]); + meleeDiskSummon->GetMotionMaster()->MovePoint(meleeDisks * MAX_MELEE_HOVER_DISK_SPAWNPOINTS, MeleeHoverDisksWaypoints[meleeDisks * MAX_MELEE_HOVER_DISK_SPAWNPOINTS]); + } + + _arcaneReinforcements = false; + + if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + events.ScheduleEvent(EVENT_DELAYED_REINFORCEMENTS, 1*IN_MILLISECONDS, 0, PHASE_TWO); + } + break; + case EVENT_DELAYED_REINFORCEMENTS: + for (uint8 rangeDisks = 5; rangeDisks < 8; rangeDisks++) + { + Creature* casterDiskSummon = me->SummonCreature(NPC_HOVER_DISK_CASTER, RangeHoverDisksSpawnPositions[rangeDisks]); + + if (casterDiskSummon->IsAIEnabled) + casterDiskSummon->AI()->DoAction(rangeDisks); + } + + for (uint8 meleeDisks = 2; meleeDisks < 4; meleeDisks++) + { + Creature* meleeDiskSummon = me->SummonCreature(NPC_HOVER_DISK_MELEE, MeleeHoverDisksSpawnPositions[meleeDisks]); + meleeDiskSummon->GetMotionMaster()->MovePoint(meleeDisks * MAX_MELEE_HOVER_DISK_SPAWNPOINTS, MeleeHoverDisksWaypoints[meleeDisks * MAX_MELEE_HOVER_DISK_SPAWNPOINTS]); + } + break; + case EVENT_PATHING_AROUND_PLATFORM: + DoAction(ACTION_CYCLIC_MOVEMENT); + break; + case EVENT_MOVE_TO_POINT_SURGE_P_TWO: + if (!_performingDestroyPlatform) + { + _performingSurgeOfPower = true; + Talk(SAY_DEEP_BREATH); + me->GetMotionMaster()->MovePoint(POINT_SURGE_OF_POWER_P_TWO, MalygosPositions[3]); + events.ScheduleEvent(EVENT_MOVE_TO_POINT_SURGE_P_TWO, 60*IN_MILLISECONDS, 2, PHASE_TWO); + } + break; + case EVENT_SUMMON_ARCANE_BOMB: + if (!_performingSurgeOfPower && !_performingDestroyPlatform) + { + me->StopMoving(); + events.ScheduleEvent(EVENT_PATHING_AROUND_PLATFORM, 3*IN_MILLISECONDS, 0, PHASE_TWO); + } + + if (!_flyingOutOfPlatform) + { + DoCast(me, SPELL_SUMMON_ARCANE_BOMB, true); + if (Creature* lastArcaneOverloadBunny = me->GetMap()->GetCreature(_arcaneOverloadGUID)) + DoCast(lastArcaneOverloadBunny, SPELL_ARCANE_BOMB_TRIGGER, true); + } + events.ScheduleEvent(EVENT_SUMMON_ARCANE_BOMB, urand(15, 16)*IN_MILLISECONDS, 2, PHASE_TWO); + break; + case EVENT_ARCANE_PULSE: + if (_preparingPulsesChecker < 2) + { + DoCastAOE(SPELL_ARCANE_PULSE, true); + events.ScheduleEvent(EVENT_ARCANE_PULSE, 7*IN_MILLISECONDS, 0, PHASE_THREE); + _preparingPulsesChecker++; + } + else + { + DoCastAOE(SPELL_ARCANE_PULSE, true); + events.ScheduleEvent(EVENT_ARCANE_PULSE, 2*IN_MILLISECONDS, 0, PHASE_THREE); + } + break; + case EVENT_LIGHT_DIMENSION_CHANGE: + SendLightOverride(LIGHT_CHANGE_DIMENSIONS, 2*IN_MILLISECONDS); + break; + case EVENT_MOVE_TO_P_THREE_POINT: + Talk(SAY_START_P_THREE); + me->GetMotionMaster()->MovePoint(POINT_IDLE_P_THREE, MalygosPositions[4]); + break; + case EVENT_START_P_THREE: + SendLightOverride(LIGHT_OBSCURE_ARCANE_RUNES, 1*IN_MILLISECONDS); + DoCast(me, SPELL_CLEAR_ALL_DEBUFFS); + DoCast(me, SPELL_IMMUNE_CURSES); + _canAttack = true; + UpdateVictim(); + me->SetFacingToObject(me->getVictim()); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + SetPhase(PHASE_THREE, true); + break; + case EVENT_SURGE_OF_POWER_P_THREE: + if (GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) + { + _tempSurgeTarget = NULL; + _drakeVehicle = NULL; + _playerSurgeTarget = NULL; + if ((_tempSurgeTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, false, SPELL_RIDE_RED_DRAGON_BUDDY))) + if ((_drakeVehicle = _tempSurgeTarget->GetVehicleKit())) + if ((_playerSurgeTarget = _drakeVehicle->GetPassenger(0)->ToPlayer())) + { + Talk(EMOTE_SURGE_OF_POWER_WARNING_P3, _playerSurgeTarget->GetGUID()); + DoCast(_tempSurgeTarget, SPELL_SURGE_OF_POWER_PHASE_3_10); + } + } + else if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + { + _firstSelectedSurgeTargetGUID = NULL; + _secondSelectedSurgeTargetGUID = NULL; + _thirdSelectedSurgeTargetGUID = NULL; + DoCastAOE(SPELL_SURGE_OF_POWER_WARNING_SELECTOR_25); + } + + events.ScheduleEvent(EVENT_SURGE_OF_POWER_P_THREE, urand(9, 18)*IN_MILLISECONDS, 0, PHASE_THREE); break; case EVENT_STATIC_FIELD: - DoCast(GetTargetPhaseThree(), SPELL_STATIC_FIELD); - events.ScheduleEvent(EVENT_STATIC_FIELD, urand(20, 30)*IN_MILLISECONDS, 0, PHASE_THREE); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 60.0f, false, SPELL_RIDE_RED_DRAGON_BUDDY)) + DoCast(target, SPELL_STATIC_FIELD_MISSLE); + + events.ScheduleEvent(EVENT_STATIC_FIELD, urand(15, 30)*IN_MILLISECONDS, 0, PHASE_THREE); + break; + case EVENT_PREVENT_SAY_SPAM_ON_KILL: + _killSpamFilter = false; break; default: break; } } - DoMeleeAttackIfReady(); + if (_phase != PHASE_THREE) + DoMeleeAttackIfReady(); } - Unit* GetTargetPhaseThree() + void JustDied(Unit* /*killer*/) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0); - - // we are a drake - if (target->GetVehicleKit()) - return target; - - // we are a player using a drake (or at least you should) - if (target->GetTypeId() == TYPEID_PLAYER) + _JustDied(); + Talk(SAY_DEATH); + if (Creature* alexstraszaGiftBoxBunny = me->GetMap()->GetCreature(instance->GetData64(DATA_GIFT_BOX_BUNNY_GUID))) { - if (Unit* base = target->GetVehicleBase()) - return base; + if (GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) + alexstraszaGiftBoxBunny->SummonGameObject(GO_HEART_OF_MAGIC_10, HeartOfMagicSpawnPos.GetPositionX(), HeartOfMagicSpawnPos.GetPositionY(), + HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0); + else if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + alexstraszaGiftBoxBunny->SummonGameObject(GO_HEART_OF_MAGIC_25, HeartOfMagicSpawnPos.GetPositionX(), HeartOfMagicSpawnPos.GetPositionY(), + HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0); } - // is a player falling from a vehicle? - return NULL; - } - - void JustDied(Unit* /*killer*/) - { - Talk(SAY_DEATH); - _JustDied(); + me->SummonCreature(NPC_ALEXSTRASZA, AlexstraszaSpawnPos, TEMPSUMMON_MANUAL_DESPAWN); + me->DespawnOrUnsummon(5*IN_MILLISECONDS); } private: - uint8 _phase; - uint32 _bersekerTimer; - uint8 _currentPos; // used for phase 2 rotation... - bool _delayedMovement; // used in phase 2. - uint32 _delayedMovementTimer; // used in phase 2 - uint8 _summonDeaths; - Position _homePosition; // it can get bugged because core thinks we are pathing - bool _mustTalk; - bool _cannotMove; - }; -}; - -class spell_malygos_vortex_dummy : public SpellScriptLoader -{ -public: - spell_malygos_vortex_dummy() : SpellScriptLoader("spell_malygos_vortex_dummy") {} - - class spell_malygos_vortex_dummy_SpellScript : public SpellScript - { - PrepareSpellScript(spell_malygos_vortex_dummy_SpellScript) - - void HandleScript(SpellEffIndex /*effIndex*/) + // Used to generate perfect cyclic movements (Enter Circle). + void FillCirclePath(Position const& centerPos, float radius, float z, Movement::PointsArray& path, bool clockwise) { - Unit* caster = GetCaster(); + float step = clockwise ? -M_PI / 8.0f : M_PI / 8.0f; + float angle = centerPos.GetAngle(me->GetPositionX(), me->GetPositionY()); - if (!caster) - return; - - // each player will enter to the trigger vehicle (entry 30090) already spawned (each one can hold up to 5 players, it has 5 seats) - // the players enter to the vehicles casting SPELL_VORTEX_4 OR SPELL_VORTEX_5 - if (InstanceScript* instance = caster->GetInstanceScript()) - instance->SetData(DATA_VORTEX_HANDLING, 0); + for (uint8 i = 0; i < 16; angle += step, ++i) + { + G3D::Vector3 point; + point.x = centerPos.GetPositionX() + radius * cosf(angle); + point.y = centerPos.GetPositionY() + radius * sinf(angle); + point.z = z; // Don't use any height getters unless all bugs are fixed. + path.push_back(point); + } + } - // the rest of the vortex execution continues when SPELL_VORTEX_2 is removed. + // Function that will change lights of map for all players on map. + void SendLightOverride(uint32 overrideId, uint32 fadeInTime) const + { + WorldPacket data(SMSG_OVERRIDE_LIGHT, 12); + data << uint32(1773); // Light.dbc entry (map default) + data << uint32(overrideId); // Light.dbc entry (override) + data << uint32(fadeInTime); + SendPacketToPlayers(&data); } - void Register() + // Send packet to all players in Eye of Eternity + void SendPacketToPlayers(WorldPacket const* data) const { - OnEffectHitTarget += SpellEffectFn(spell_malygos_vortex_dummy_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + if (!players.isEmpty()) + for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) + if (Player* player = itr->getSource()) + if (player->GetAreaId() == AREA_EYE_OF_ETERNITY) + player->GetSession()->SendPacket(data); } + + uint8 _phase; // Counter for phases used with a getter. + uint8 _summonDeaths; // Keeps count of arcane trash. + uint8 _preparingPulsesChecker; // In retail they use 2 preparing pulses with 7 sec CD, after they pass 2 seconds. + uint64 _arcaneOverloadGUID; // Last Arcane Overload summoned to know to which should visual be cast to (the purple ball, not bubble). + uint64 _firstSelectedSurgeTargetGUID; // All these three are used to keep current tagets to which warning should be sent + uint64 _secondSelectedSurgeTargetGUID; // during Surge of Power 25 man, also they act as sent targets because of that mechanic. + uint64 _thirdSelectedSurgeTargetGUID; + + Unit* _tempSurgeTarget; // These three are used for 10 man Surge of Power targeting. + Vehicle* _drakeVehicle; + Player* _playerSurgeTarget; + + bool _killSpamFilter; // Prevent text spamming on killed player by helping implement a CD. + bool _canAttack; // Used to control attacking (Move Chase not being applied after Stop Attack, only few times should act like this). + bool _despawned; // Checks if boss pass through evade on reset. + bool _executingVortex; // Prevents some events being sheduled during Vortex takeoff/land. + bool _arcaneReinforcements; // Checks if 10 or 25 man arcane trash will be spawned. + bool _flyingOutOfPlatform; // Used to prevent Malygos casting Arcane Overload shields while leaving platform. + bool _firstCyclicMovementStarted; // At first movement start he throws one shield asap, so this check is needed for it only. + bool _performingSurgeOfPower; // Used to prevent starting Cyclic Movement called in Arcane Bomb event. + bool _performingDestroyPlatform; // Used to prevent starting some movements right when Destroy Platfrom event starts. + + float _flySpeed; // Used to store base fly speed to prevent stacking on each evade. }; - SpellScript* GetSpellScript() const + CreatureAI* GetAI(Creature* creature) const { - return new spell_malygos_vortex_dummy_SpellScript(); + return new boss_malygosAI(creature); } }; -class spell_malygos_vortex_visual : public SpellScriptLoader -{ - public: - spell_malygos_vortex_visual() : SpellScriptLoader("spell_malygos_vortex_visual") { } - - class spell_malygos_vortex_visual_AuraScript : public AuraScript - { - PrepareAuraScript(spell_malygos_vortex_visual_AuraScript); - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - { - ThreatContainer::StorageType const& m_threatlist = caster->getThreatManager().getThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) - { - if (Unit* target = (*itr)->getTarget()) - { - Player* targetPlayer = target->ToPlayer(); - - if (!targetPlayer || targetPlayer->isGameMaster()) - continue; - - if (InstanceScript* instance = caster->GetInstanceScript()) - { - // teleport spell - i am not sure but might be it must be casted by each vehicle when its passenger leaves it - if (Creature* trigger = caster->GetMap()->GetCreature(instance->GetData64(DATA_TRIGGER))) - trigger->CastSpell(targetPlayer, SPELL_VORTEX_6, true); - } - } - } - - if (Creature* malygos = caster->ToCreature()) - { - // This is a hack, we have to re add players to the threat list because when they enter to the vehicles they are removed. - // Anyway even with this issue, the boss does not enter in evade mode - this prevents iterate an empty list in the next vortex execution. - malygos->SetInCombatWithZone(); - - malygos->SetDisableGravity(false); - malygos->SetCanFly(false); - - malygos->GetMotionMaster()->MoveChase(caster->getVictim()); - malygos->RemoveAura(SPELL_VORTEX_1); - } - } - - } - - void Register() - { - AfterEffectRemove += AuraEffectRemoveFn(spell_malygos_vortex_visual_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const - { - return new spell_malygos_vortex_visual_AuraScript(); - } -}; - class npc_portal_eoe: public CreatureScript { public: - npc_portal_eoe() : CreatureScript("npc_portal_eoe") {} - - CreatureAI* GetAI(Creature* creature) const - { - return new npc_portal_eoeAI(creature); - } + npc_portal_eoe() : CreatureScript("npc_portal_eoe") { } struct npc_portal_eoeAI : public ScriptedAI { @@ -763,27 +1083,29 @@ public: _instance = creature->GetInstanceScript(); } - void SpellHit(Unit* /*caster*/, const SpellInfo* spell) + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) { if (spell->Id == SPELL_PORTAL_OPENED) { - DoCast(me, SPELL_SUMMON_POWER_PARK, true); + if (Creature* malygos = me->GetMap()->GetCreature(_instance->GetData64(DATA_MALYGOS))) + { + if (malygos->AI()->GetData(DATA_PHASE) == PHASE_ONE) + DoCast(me, SPELL_SUMMON_POWER_PARK, true); + } } } void UpdateAI(uint32 /*diff*/) { - // When duration of oppened riff visual ends, - // closed one should be cast - if (!me->HasAura(SPELL_PORTAL_VISUAL_CLOSED) && - !me->HasAura(SPELL_PORTAL_OPENED)) + // When duration of opened riff visual ends, closed one should be cast + if (!me->HasAura(SPELL_PORTAL_VISUAL_CLOSED) && !me->HasAura(SPELL_PORTAL_OPENED)) DoCast(me, SPELL_PORTAL_VISUAL_CLOSED, true); if (_instance) { - if (Creature* malygos = Unit::GetCreature(*me, _instance->GetData64(DATA_MALYGOS))) + if (Creature* malygos = me->GetMap()->GetCreature(_instance->GetData64(DATA_MALYGOS))) { - if (malygos->AI()->GetData(DATA_PHASE) != PHASE_ONE) + if (malygos->AI()->GetData(DATA_PHASE) != PHASE_ONE && me->HasAura(SPELL_PORTAL_OPENED)) { me->RemoveAura(SPELL_PORTAL_OPENED); DoCast(me, SPELL_PORTAL_VISUAL_CLOSED, true); @@ -792,42 +1114,29 @@ public: } } - void JustSummoned(Creature* summon) - { - summon->SetInCombatWithZone(); - } - private: - uint32 _summonTimer; InstanceScript* _instance; }; -}; + CreatureAI* GetAI(Creature* creature) const + { + return new npc_portal_eoeAI(creature); + } +}; class npc_power_spark: public CreatureScript { public: - npc_power_spark() : CreatureScript("npc_power_spark") {} - - CreatureAI* GetAI(Creature* creature) const - { - return new npc_power_sparkAI(creature); - } + npc_power_spark() : CreatureScript("npc_power_spark") { } struct npc_power_sparkAI : public ScriptedAI { npc_power_sparkAI(Creature* creature) : ScriptedAI(creature) { _instance = creature->GetInstanceScript(); - - MoveToMalygos(); // Talk range was not enough for this encounter sCreatureTextMgr->SendChat(me, EMOTE_POWER_SPARK_SUMMONED, 0, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_MAP); - } - - void EnterEvadeMode() - { - me->DespawnOrUnsummon(); + MoveToMalygos(); } void MoveToMalygos() @@ -835,10 +1144,8 @@ public: me->GetMotionMaster()->MoveIdle(); if (_instance) - { - if (Creature* malygos = Unit::GetCreature(*me, _instance->GetData64(DATA_MALYGOS))) + if (Creature* malygos = me->GetMap()->GetCreature(_instance->GetData64(DATA_MALYGOS))) me->GetMotionMaster()->MoveFollow(malygos, 0.0f, 0.0f); - } } void UpdateAI(uint32 /*diff*/) @@ -846,9 +1153,9 @@ public: if (!_instance) return; - if (Creature* malygos = Unit::GetCreature(*me, _instance->GetData64(DATA_MALYGOS))) + if (Creature* malygos = me->GetMap()->GetCreature(_instance->GetData64(DATA_MALYGOS))) { - if (malygos->AI()->GetData(DATA_PHASE) != PHASE_ONE) + if (malygos->AI()->GetData(DATA_PHASE) != PHASE_ONE || _instance->GetBossState(DATA_MALYGOS_EVENT) == FAIL) { me->DespawnOrUnsummon(); return; @@ -873,28 +1180,33 @@ public: private: InstanceScript* _instance; }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_power_sparkAI(creature); + } }; -class npc_hover_disk : public CreatureScript +class npc_melee_hover_disk : public CreatureScript { public: - npc_hover_disk() : CreatureScript("npc_hover_disk") { } + npc_melee_hover_disk() : CreatureScript("npc_melee_hover_disk") { } - CreatureAI* GetAI(Creature* creature) const + struct npc_melee_hover_diskAI : public VehicleAI { - return new npc_hover_diskAI(creature); - } + npc_melee_hover_diskAI(Creature* creature) : VehicleAI(creature) + { + _instance = creature->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); + // TO DO: These were a bit faster than what they should be. Not sure what is the reason. + me->SetSpeed(MOVE_FLIGHT, 1.25f); + } - struct npc_hover_diskAI : public npc_escortAI - { - npc_hover_diskAI(Creature* creature) : npc_escortAI(creature) + void Reset() { - if (me->GetEntry() == NPC_HOVER_DISK_CASTER) - me->SetReactState(REACT_PASSIVE); - else - me->SetInCombatWithZone(); + VehicleAI::Reset(); - _instance = creature->GetInstanceScript(); + _wpCount = 0; } void PassengerBoarded(Unit* unit, int8 /*seat*/, bool apply) @@ -903,208 +1215,1284 @@ public: { if (unit->GetTypeId() == TYPEID_UNIT) { - me->setFaction(FACTION_HOSTILE); + unit->CastSpell(unit, SPELL_TELEPORT_VISUAL_ONLY); unit->ToCreature()->SetInCombatWithZone(); } + else if (unit->GetTypeId() == TYPEID_PLAYER) + me->SetDisableGravity(true); } - else + else if (!apply) { - // Error found: This is not called if the passenger is a player - if (unit->GetTypeId() == TYPEID_UNIT || unit->GetTypeId() == TYPEID_PLAYER) + if (unit->GetTypeId() != TYPEID_PLAYER) { - // This will only be called if the passenger dies - if (_instance) - { - if (Creature* malygos = Unit::GetCreature(*me, _instance->GetData64(DATA_MALYGOS))) - malygos->AI()->SetData(DATA_SUMMON_DEATHS, malygos->AI()->GetData(DATA_SUMMON_DEATHS)+1); - } + me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation()); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetDisableGravity(false); + me->SetCanFly(false); } - - me->GetMotionMaster()->MoveIdle(); - - if (me->GetEntry() == NPC_HOVER_DISK_MELEE || me->GetEntry() == NPC_HOVER_DISK_CASTER) + else if (unit->GetTypeId() == TYPEID_PLAYER) { - // Hack: Fall ground function can fail (remember the platform is a gameobject), we will teleport the disk to the ground - if (me->GetPositionZ() > GROUND_Z) - me->NearTeleportTo(me->GetPositionX(), me->GetPositionY(), GROUND_Z, 0); - me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation()); - me->setFaction(FACTION_FRIENDLY); - me->AI()->EnterEvadeMode(); + me->SetDisableGravity(false); + me->SetCanFly(false); } + + me->setFaction(FACTION_FRIENDLY); + me->RemoveAllAuras(); } } - void EnterEvadeMode() + void UpdateAI(uint32 diff) { - // we dont evade + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + me->GetMotionMaster()->MovePoint(eventId, MeleeHoverDisksWaypoints[eventId]); } - void DoAction(int32 action) + void DoAction(int32 /*action*/) { - if (me->GetEntry() != NPC_HOVER_DISK_CASTER) + if (Vehicle* vehicleTemp = me->GetVehicleKit()) + if (vehicleTemp->GetPassenger(0) && vehicleTemp->GetPassenger(0)->GetTypeId() == TYPEID_PLAYER) + { + vehicleTemp->RemoveAllPassengers(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + me->DespawnOrUnsummon(3*IN_MILLISECONDS); + } + + void MovementInform(uint32 type, uint32 id) + { + if (type != POINT_MOTION_TYPE) return; - switch (action) + if (_wpCount < 3) { - case ACTION_HOVER_DISK_START_WP_1: - for (uint8 i = 0; i < MAX_HOVER_DISK_WAYPOINTS; i++) - AddWaypoint(i, HoverDiskWaypoints[i].GetPositionX(), HoverDiskWaypoints[i].GetPositionY(), HoverDiskWaypoints[i].GetPositionZ()); - break; - case ACTION_HOVER_DISK_START_WP_2: - { - uint8 count = 0; - for (uint8 i = MAX_HOVER_DISK_WAYPOINTS-1; i > 0; i--) - { - AddWaypoint(count, HoverDiskWaypoints[i].GetPositionX(), HoverDiskWaypoints[i].GetPositionY(), HoverDiskWaypoints[i].GetPositionZ()); - count++; - } - break; - } - default: - return; + _events.ScheduleEvent(id + 1, 1); + ++_wpCount; } + else if (Vehicle* hoverDisk = me->GetVehicleKit()) + if (Unit* lordPassenger = hoverDisk->GetPassenger(0)) + lordPassenger->ToCreature()->AI()->DoAction(ACTION_SET_DISK_VICTIM_CHASE); + } + + private: + uint8 _wpCount; // how many points are triggered + InstanceScript* _instance; + EventMap _events; + }; - Start(true, false, 0, 0, false, true); + CreatureAI* GetAI(Creature* creature) const + { + return new npc_melee_hover_diskAI(creature); + } +}; + +class npc_caster_hover_disk : public CreatureScript +{ +public: + npc_caster_hover_disk() : CreatureScript("npc_caster_hover_disk") { } + + struct npc_caster_hover_diskAI : public VehicleAI + { + npc_caster_hover_diskAI(Creature* creature) : VehicleAI(creature) + { + _instance = creature->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); + // TO DO: Something is wrong with calculations for flying creatures that are on WP/Cyclic path. + // They should get the same difference as to when at ground from run creature switch to walk. + me->SetSpeed(MOVE_FLIGHT, 0.45f); } - void UpdateEscortAI(const uint32 /*diff*/) + void Reset() { - // we dont do melee damage! + VehicleAI::Reset(); } - void WaypointReached(uint32 /*waypointId*/) + void EnterEvadeMode() { + } + + void PassengerBoarded(Unit* unit, int8 /*seat*/, bool apply) + { + if (apply) + { + if (unit->GetTypeId() == TYPEID_UNIT) + unit->CastSpell(unit, SPELL_TELEPORT_VISUAL_ONLY); + } + else if (!apply) + { + me->StopMoving(); + me->SetDisableGravity(false); + me->SetCanFly(false); + me->RemoveAllAuras(); + } + } + void DoAction(int32 action) + { + if (action < ACTION_DELAYED_DESPAWN) + { + Movement::MoveSplineInit init(me); + FillCirclePath(MalygosPositions[3], 35.0f, 282.3402f, init.Path(), true); + init.SetFly(); + init.SetCyclic(); + init.Launch(); + } + else + { + me->DespawnOrUnsummon(3*IN_MILLISECONDS); + } } private: + void FillCirclePath(Position const& centerPos, float radius, float z, Movement::PointsArray& path, bool clockwise) + { + float step = clockwise ? -M_PI / 9.0f : M_PI / 9.0f; + float angle = centerPos.GetAngle(me->GetPositionX(), me->GetPositionY()); + + for (uint8 i = 0; i < 18; angle += step, ++i) + { + G3D::Vector3 point; + point.x = centerPos.GetPositionX() + radius * cosf(angle); + point.y = centerPos.GetPositionY() + radius * sinf(angle); + point.z = z; // Don't use any height getters unless all bugs are fixed. + path.push_back(point); + } + } + InstanceScript* _instance; }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_caster_hover_diskAI(creature); + } }; +class npc_nexus_lord : public CreatureScript +{ + public: + npc_nexus_lord() : CreatureScript("npc_nexus_lord") { } + + struct npc_nexus_lordAI : public ScriptedAI + { + npc_nexus_lordAI(Creature* creature) : ScriptedAI(creature) + { + _instance = creature->GetInstanceScript(); + } + + void Reset() + { + _events.Reset(); + } + + void EnterEvadeMode() + { + } + + void IsSummonedBy(Unit* /*summoner*/) + { + _events.ScheduleEvent(EVENT_ARCANE_SHOCK, 2*IN_MILLISECONDS); + _events.ScheduleEvent(EVENT_HASTE_BUFF, 12*IN_MILLISECONDS); + } + + void DoAction(int32 /*action*/) + { + _events.ScheduleEvent(EVENT_NUKE_DUMMY, 1); + } + + void UpdateAI(uint32 diff) + { + if (!UpdateVictim()) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_ARCANE_SHOCK: + DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 5.0f, true), SPELL_ARCANE_SHOCK); + _events.ScheduleEvent(EVENT_ARCANE_SHOCK, 3*IN_MILLISECONDS); + break; + case EVENT_HASTE_BUFF: + DoCast(me, SPELL_HASTE); + _events.ScheduleEvent(EVENT_HASTE_BUFF, 15*IN_MILLISECONDS); + break; + case EVENT_NUKE_DUMMY: + DoCast(me->getVictim(), SPELL_DUMMY_NUKE, true); + DoCast(me, SPELL_ALIGN_DISK_AGGRO, true); + _events.ScheduleEvent(EVENT_NUKE_DUMMY, 1*IN_MILLISECONDS); + break; + } + } + + DoMeleeAttackIfReady(); + } + + void JustDied(Unit* /*killer*/) + { + if (Creature* malygos = me->GetMap()->GetCreature(_instance->GetData64(DATA_MALYGOS))) + malygos->AI()->SetData(DATA_SUMMON_DEATHS, malygos->AI()->GetData(DATA_SUMMON_DEATHS) + 1); + } + + private: + InstanceScript* _instance; + EventMap _events; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_nexus_lordAI(creature); + } +}; + +class npc_scion_of_eternity : public CreatureScript +{ + public: + npc_scion_of_eternity() : CreatureScript("npc_scion_of_eternity") { } + + struct npc_scion_of_eternityAI : public ScriptedAI + { + npc_scion_of_eternityAI(Creature* creature) : ScriptedAI(creature) + { + _instance = creature->GetInstanceScript(); + } + + void Reset() + { + _events.Reset(); + } + + void IsSummonedBy(Unit* /*summoner*/) + { + _events.SetPhase(PHASE_TWO); + _events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(14, 17)*IN_MILLISECONDS, 0, PHASE_TWO); + } + + void EnterCombat(Unit* /*who*/) + { + } + + void AttackStart(Unit* /*target*/) + { + } + + void EnterEvadeMode() + { + } + + void UpdateAI(uint32 diff) + { + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_ARCANE_BARRAGE: + DoCast(me, SPELL_ARCANE_BARRAGE); + _events.ScheduleEvent(EVENT_ARCANE_BARRAGE, urand(4, 12)*IN_MILLISECONDS, 0, PHASE_TWO); + break; + } + } + } + + void JustDied(Unit* /*killer*/) + { + if (Creature* malygos = me->GetMap()->GetCreature(_instance->GetData64(DATA_MALYGOS))) + malygos->AI()->SetData(DATA_SUMMON_DEATHS, malygos->AI()->GetData(DATA_SUMMON_DEATHS) + 1); + } + + private: + InstanceScript* _instance; + EventMap _events; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_scion_of_eternityAI(creature); + } +}; -// The reason of this AI is to make the creature able to enter in combat otherwise the spell casting of SPELL_ARCANE_OVERLOAD fails. class npc_arcane_overload : public CreatureScript { public: - npc_arcane_overload() : CreatureScript("npc_arcane_overload") {} - - CreatureAI* GetAI(Creature* creature) const - { - return new npc_arcane_overloadAI (creature); - } + npc_arcane_overload() : CreatureScript("npc_arcane_overload") { } struct npc_arcane_overloadAI : public ScriptedAI { - npc_arcane_overloadAI(Creature* creature) : ScriptedAI(creature) {} + npc_arcane_overloadAI(Creature* creature) : ScriptedAI(creature) + { + _instance = creature->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); + } - void AttackStart(Unit* who) + void IsSummonedBy(Unit* summoner) { - DoStartNoMovement(who); + if ((_malygos = summoner->ToCreature())) + _malygos->AI()->SetGUID(me->GetGUID(), DATA_LAST_OVERLOAD_GUID); } - void Reset() + void UpdateAI (uint32 /*diff*/) { - DoCast(me, SPELL_ARCANE_OVERLOAD, false); } - void UpdateAI(uint32 /*diff*/) + void DoAction(int32 /*action*/) { - // we dont do melee damage! + if (Creature* malygos = me->GetMap()->GetCreature(_instance->GetData64(DATA_MALYGOS))) + { + if (malygos->AI()->GetData(DATA_PHASE) == PHASE_TWO) + me->DespawnOrUnsummon(6*IN_MILLISECONDS); + // If evade is hit during phase II shields should disappear with no delay + else if (malygos->AI()->GetData(DATA_PHASE) == 0) + me->DespawnOrUnsummon(); + } + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) + { + if (spell->Id == SPELL_ARCANE_BOMB_TRIGGER) + { + DoCastAOE(SPELL_ARCANE_BOMB_KNOCKBACK_DAMAGE, true); + DoCast(me, SPELL_ARCANE_OVERLOAD_1, true); + } } + private: + InstanceScript* _instance; + Creature* _malygos; }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_arcane_overloadAI (creature); + } }; // SmartAI does not work correctly for vehicles class npc_wyrmrest_skytalon : public CreatureScript { public: - npc_wyrmrest_skytalon() : CreatureScript("npc_wyrmrest_skytalon") {} - - CreatureAI* GetAI(Creature* creature) const - { - return new npc_wyrmrest_skytalonAI (creature); - } + npc_wyrmrest_skytalon() : CreatureScript("npc_wyrmrest_skytalon") { } struct npc_wyrmrest_skytalonAI : public VehicleAI { - npc_wyrmrest_skytalonAI(Creature* creature) : VehicleAI(creature) { } + npc_wyrmrest_skytalonAI(Creature* creature) : VehicleAI(creature) + { + } void IsSummonedBy(Unit* summoner) { - summoner->CastSpell(me, SPELL_RIDE_RED_DRAGON, true); + me->CastSpell(summoner, SPELL_RIDE_RED_DRAGON_TRIGGERED, true); } - void PassengerBoarded(Unit* /*unit*/, int8 /*seat*/, bool apply) + void PassengerBoarded(Unit* unit, int8 /*seat*/, bool apply) { + if (apply) + { + _playerController = NULL; + _playerController = unit->ToPlayer(); + } if (!apply) - me->DespawnOrUnsummon(); + { + me->DespawnOrUnsummon(2050); + me->SetOrientation(2.5f); + me->SetSpeed(MOVE_FLIGHT, 1.0f, true); + Position pos; + me->GetPosition(&pos); + pos.m_positionX += 10.0f; + pos.m_positionY += 10.0f; + pos.m_positionZ += 12.0f; + me->GetMotionMaster()->MovePoint(1, pos); + } + } + + void JustDied(Unit* /*killer*/) + { + // TO DOs: check script beginning for more info. + _playerController->Kill(_playerController, true); } + + private: + Player* _playerController; }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_wyrmrest_skytalonAI (creature); + } +}; + +// We shouldn't use SAI for stuff that aren't within boss main mechanic +// and SAI type of despawn can cause problems here. +class npc_static_field : public CreatureScript +{ + public: + npc_static_field() : CreatureScript("npc_static_field") { } + + struct npc_static_fieldAI : public ScriptedAI + { + npc_static_fieldAI(Creature* creature) : ScriptedAI(creature) + { + } + + void IsSummonedBy(Unit* /*summoner*/) + { + // For some great reason the spell doesn't time it... + me->DespawnOrUnsummon(30*IN_MILLISECONDS); + } + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_static_fieldAI (creature); + } +}; + +class spell_malygos_portal_beam : public SpellScriptLoader +{ + public: + spell_malygos_portal_beam() : SpellScriptLoader("spell_malygos_portal_beam") { } + + class spell_malygos_portal_beam_AuraScript : public AuraScript + { + PrepareAuraScript(spell_malygos_portal_beam_AuraScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_PORTAL_OPENED)) + return false; + + return true; + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + target->CastSpell(target, SPELL_PORTAL_OPENED); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + target->RemoveAura(SPELL_PORTAL_OPENED); + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_malygos_portal_beam_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_malygos_portal_beam_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_malygos_portal_beam_AuraScript(); + } +}; + +class spell_malygos_random_portal : public SpellScriptLoader +{ + public: + spell_malygos_random_portal() : SpellScriptLoader("spell_malygos_random_portal") { } + + class spell_malygos_random_portal_SpellScript : public SpellScript + { + PrepareSpellScript(spell_malygos_random_portal_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Creature* malygos = GetCaster()->ToCreature(); + if (Creature* target = GetHitCreature()) + { + Position pos; + pos.m_positionZ = target->GetPositionZ(); + target->GetNearPoint2D(pos.m_positionX, pos.m_positionY, frand(29.1f, 30.0f), target->GetAngle(malygos)); + malygos->GetMotionMaster()->MovePoint(POINT_NEAR_RANDOM_PORTAL_P_NONE, pos); + } + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_malygos_random_portal_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_malygos_random_portal_SpellScript(); + } +}; + +class isPlayerOnVehicleChecker +{ + public: + isPlayerOnVehicleChecker(Unit* source) : _source(source) { } + + bool operator()(WorldObject* unit) + { + if (Unit* target = ObjectAccessor::GetUnit(*_source, unit->GetGUID())) + if (target->IsOnVehicle(target->GetVehicleBase())) + return true; + + return false; + } + + private: + Unit* _source; }; -class npc_alexstrasza_eoe : public CreatureScript +class spell_malygos_arcane_storm : public SpellScriptLoader +{ + public: + spell_malygos_arcane_storm() : SpellScriptLoader("spell_malygos_arcane_storm") { } + + class spell_malygos_arcane_storm_SpellScript : public SpellScript + { + PrepareSpellScript(spell_malygos_arcane_storm_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_ARCANE_STORM_EXTRA_VISUAL)) + return false; + + return true; + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + Creature* malygos = GetCaster()->ToCreature(); + if (GetSpellInfo()->Id == SPELL_ARCANE_STORM_P_III) + targets.remove_if(isPlayerOnVehicleChecker(malygos)); + + if (!targets.empty()) + Trinity::Containers::RandomResizeList(targets, (malygos->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL ? 4 : 10)); + + for (std::list<WorldObject*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) + malygos->CastSpell((*itr)->ToUnit(), SPELL_ARCANE_STORM_EXTRA_VISUAL, true); + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_malygos_arcane_storm_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_malygos_arcane_storm_SpellScript(); + } +}; + +class spell_malygos_vortex_dummy : public SpellScriptLoader { public: - npc_alexstrasza_eoe() : CreatureScript("npc_alexstrasza_eoe") {} + spell_malygos_vortex_dummy() : SpellScriptLoader("spell_malygos_vortex_dummy") { } - CreatureAI* GetAI(Creature* creature) const + class spell_malygos_vortex_dummy_SpellScript : public SpellScript { - return new npc_alexstrasza_eoeAI (creature); - } + PrepareSpellScript(spell_malygos_vortex_dummy_SpellScript) - struct npc_alexstrasza_eoeAI : public ScriptedAI - { - npc_alexstrasza_eoeAI(Creature* creature) : ScriptedAI(creature) {} + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } - void Reset() + void HandleScript(SpellEffIndex /*effIndex*/) { - _events.Reset(); - _events.ScheduleEvent(EVENT_YELL_1, 0); + Creature* caster = GetCaster()->ToCreature(); + // Each player will enter to the trigger vehicle (entry 30090) which is already spawned (each one can hold up to 5 players, it has 5 seats, + // the players enter the vehicles casting SPELL_VORTEX_4 or SPELL_VORTEX_5. + if (InstanceScript* instance = caster->GetInstanceScript()) + instance->SetData(DATA_VORTEX_HANDLING, 0); + + // the rest of the vortex execution continues when SPELL_VORTEX_2 is removed. } - void UpdateAI(uint32 /*diff*/) + void Register() { - while (uint32 eventId = _events.ExecuteEvent()) + OnEffectHitTarget += SpellEffectFn(spell_malygos_vortex_dummy_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_malygos_vortex_dummy_SpellScript(); + } +}; + +class spell_malygos_vortex_visual : public SpellScriptLoader +{ + public: + spell_malygos_vortex_visual() : SpellScriptLoader("spell_malygos_vortex_visual") { } + + class spell_malygos_vortex_visual_AuraScript : public AuraScript + { + PrepareAuraScript(spell_malygos_vortex_visual_AuraScript); + + bool Load() { - switch (eventId) + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_VORTEX_1) || !sSpellMgr->GetSpellInfo(SPELL_VORTEX_6)) + return false; + + return true; + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* caster = GetCaster()->ToCreature()) { - case EVENT_YELL_1: - Talk(SAY_ONE); - _events.ScheduleEvent(EVENT_YELL_2, 4*IN_MILLISECONDS); - break; - case EVENT_YELL_2: - Talk(SAY_TWO); - _events.ScheduleEvent(EVENT_YELL_3, 4*IN_MILLISECONDS); - break; - case EVENT_YELL_3: - Talk(SAY_THREE); - _events.ScheduleEvent(EVENT_YELL_4, 7*IN_MILLISECONDS); - break; - case EVENT_YELL_4: - Talk(SAY_FOUR); - break; + ThreatContainer::StorageType const& m_threatlist = caster->getThreatManager().getThreatList(); + for (ThreatContainer::StorageType::const_iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) + { + if (Unit* target = (*itr)->getTarget()) + { + Player* targetPlayer = target->ToPlayer(); + if (!targetPlayer || targetPlayer->isGameMaster()) + continue; + + if (InstanceScript* instance = caster->GetInstanceScript()) + { + // Teleport spell - I'm not sure but might be it must be casted by each vehicle when it's passenger leaves it. + if (Creature* trigger = caster->GetMap()->GetCreature(instance->GetData64(DATA_TRIGGER))) + trigger->CastSpell(targetPlayer, SPELL_VORTEX_6, true); + } + } + } + + if (Creature* malygos = caster->ToCreature()) + { + malygos->GetMotionMaster()->MoveLand(POINT_LAND_AFTER_VORTEX_P_ONE, MalygosPositions[2]); + malygos->RemoveAura(SPELL_VORTEX_1); + } } } + + void Register() + { + AfterEffectRemove += AuraEffectRemoveFn(spell_malygos_vortex_visual_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_malygos_vortex_visual_AuraScript(); } +}; + +class ExactDistanceCheck +{ + public: + ExactDistanceCheck(Unit* source, float dist) : _source(source), _dist(dist) { } + + bool operator()(WorldObject* unit) + { + return _source->GetExactDist2d(unit) > _dist; + } + private: - EventMap _events; - }; + Unit* _source; + float _dist; +}; + +class spell_arcane_overload : public SpellScriptLoader +{ + public: + spell_arcane_overload() : SpellScriptLoader("spell_arcane_overload") { } + + class spell_arcane_overload_SpellScript : public SpellScript + { + PrepareSpellScript(spell_arcane_overload_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void ResizeEffectRadiusTargetChecker(std::list<WorldObject*>& targets) + { + Creature* arcaneOverload = GetCaster()->ToCreature(); + targets.remove_if(ExactDistanceCheck(arcaneOverload, + GetSpellInfo()->Effects[EFFECT_0].CalcRadius(arcaneOverload) * arcaneOverload->GetFloatValue(OBJECT_FIELD_SCALE_X))); + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_arcane_overload_SpellScript::ResizeEffectRadiusTargetChecker, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_arcane_overload_SpellScript(); + } +}; + +class spell_nexus_lord_align_disk_aggro : public SpellScriptLoader +{ + public: + spell_nexus_lord_align_disk_aggro() : SpellScriptLoader("spell_nexus_lord_align_disk_aggro") { } + + class spell_nexus_lord_align_disk_aggro_SpellScript : public SpellScript + { + PrepareSpellScript(spell_nexus_lord_align_disk_aggro_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Creature* caster = GetCaster()->ToCreature(); + + if (Creature* target = GetHitCreature()) + target->GetMotionMaster()->MoveChase(caster->getVictim()); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_nexus_lord_align_disk_aggro_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_nexus_lord_align_disk_aggro_SpellScript(); + } +}; + +class IsPlayerOnHoverDiskCheck +{ + public: + IsPlayerOnHoverDiskCheck(Unit* source, bool isOnHoverDisk) : _source(source), _isOnHoverDisk(isOnHoverDisk) { } + + bool operator()(WorldObject* unit) + { + if (_isOnHoverDisk) + { + if (Unit* passenger = ObjectAccessor::GetUnit(*_source, unit->GetGUID())) + if (passenger->GetVehicleBase() && passenger->GetVehicleBase()->GetEntry() == NPC_HOVER_DISK_MELEE) + return true; + } + else if (!_isOnHoverDisk) + { + if (Unit* passenger = ObjectAccessor::GetUnit(*_source, unit->GetGUID())) + if (!passenger->GetVehicleBase()) + return true; + } + + return false; + } + + private: + Unit* _source; + + bool _isOnHoverDisk; +}; + +class spell_scion_of_eternity_arcane_barrage : public SpellScriptLoader +{ + public: + spell_scion_of_eternity_arcane_barrage() : SpellScriptLoader("spell_scion_of_eternity_arcane_barrage") { } + + class spell_scion_of_eternity_arcane_barrage_SpellScript : public SpellScript + { + PrepareSpellScript(spell_scion_of_eternity_arcane_barrage_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void FilterMeleeHoverDiskPassangers(std::list<WorldObject*>& targets) + { + if (targets.empty()) + return; + + Creature* caster = GetCaster()->ToCreature(); + for (std::list<WorldObject*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) + _playersWithoutDisk.push_back((*itr)); + + _playersWithoutDisk.remove_if(IsPlayerOnHoverDiskCheck(caster, false)); + if (_playersWithoutDisk.empty()) + targets.remove_if(IsPlayerOnHoverDiskCheck(caster, true)); + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_scion_of_eternity_arcane_barrage_SpellScript::FilterMeleeHoverDiskPassangers, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } + + std::list<WorldObject*> _playersWithoutDisk; + }; + + SpellScript* GetSpellScript() const + { + return new spell_scion_of_eternity_arcane_barrage_SpellScript(); + } +}; + +class spell_malygos_destroy_platform_channel : public SpellScriptLoader +{ + public: + spell_malygos_destroy_platform_channel() : SpellScriptLoader("spell_malygos_destroy_platform_channel") { } + + class spell_malygos_destroy_platform_channel_AuraScript : public AuraScript + { + PrepareAuraScript(spell_malygos_destroy_platform_channel_AuraScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_DESTROY_PLATFORM_BOOM_VISUAL)) + return false; + + return true; + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + if (InstanceScript* instance = target->GetInstanceScript()) + if (Creature* platformTrigger = target->GetMap()->GetCreature(instance->GetData64(DATA_ALEXSTRASZA_BUNNY_GUID))) + platformTrigger->CastSpell(platformTrigger, SPELL_DESTROY_PLATFORM_BOOM_VISUAL); + } + + void Register() + { + AfterEffectRemove += AuraEffectRemoveFn(spell_malygos_destroy_platform_channel_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_malygos_destroy_platform_channel_AuraScript(); + } +}; + +class spell_alexstrasza_bunny_destroy_platform_boom_visual : public SpellScriptLoader +{ + public: + spell_alexstrasza_bunny_destroy_platform_boom_visual() : SpellScriptLoader("spell_alexstrasza_bunny_destroy_platform_boom_visual") { } + + class spell_alexstrasza_bunny_destroy_platform_boom_visual_SpellScript : public SpellScript + { + PrepareSpellScript(spell_alexstrasza_bunny_destroy_platform_boom_visual_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_DESTROY_PLATFORM_EVENT)) + return false; + + return true; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Creature* target = GetHitCreature()) + { + target->CastSpell(target, SPELL_DESTROY_PLATFORM_EVENT); + } + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_alexstrasza_bunny_destroy_platform_boom_visual_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_alexstrasza_bunny_destroy_platform_boom_visual_SpellScript(); + } +}; + +class spell_alexstrasza_bunny_destroy_platform_event : public SpellScriptLoader +{ + public: + spell_alexstrasza_bunny_destroy_platform_event() : SpellScriptLoader("spell_alexstrasza_bunny_destroy_platform_event") { } + + class spell_alexstrasza_bunny_destroy_platform_event_SpellScript : public SpellScript + { + PrepareSpellScript(spell_alexstrasza_bunny_destroy_platform_event_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void HandleSendEvent(SpellEffIndex /*effIndex*/) + { + Creature* caster = GetCaster()->ToCreature(); + if (InstanceScript* instance = caster->GetInstanceScript()) + if (GameObject* platform = caster->GetMap()->GetGameObject(instance->GetData64(DATA_PLATFORM))) + platform->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + Creature* caster = GetCaster()->ToCreature(); + caster->CastSpell(caster, SPELL_SUMMON_RED_DRAGON_BUDDY_F_CAST, true); + } + + void Register() + { + OnEffectHit += SpellEffectFn(spell_alexstrasza_bunny_destroy_platform_event_SpellScript::HandleSendEvent, EFFECT_0, SPELL_EFFECT_SEND_EVENT); + OnEffectHitTarget += SpellEffectFn(spell_alexstrasza_bunny_destroy_platform_event_SpellScript::HandleScript, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_alexstrasza_bunny_destroy_platform_event_SpellScript(); + } +}; + +class spell_wyrmrest_skytalon_summon_red_dragon_buddy : public SpellScriptLoader +{ + public: + spell_wyrmrest_skytalon_summon_red_dragon_buddy() : SpellScriptLoader("spell_wyrmrest_skytalon_summon_red_dragon_buddy") { } + + class spell_wyrmrest_skytalon_summon_red_dragon_buddy_SpellScript : public SpellScript + { + PrepareSpellScript(spell_wyrmrest_skytalon_summon_red_dragon_buddy_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void ChangeSummonPos(SpellEffIndex /*effIndex*/) + { + // Adjust effect summon position to lower Z + WorldLocation summonPos = *GetExplTargetDest(); + Position offset = { 0.0f, 0.0f, -80.0f, 0.0f }; + summonPos.RelocateOffset(offset); + SetExplTargetDest(summonPos); + GetHitDest()->RelocateOffset(offset); + } + + void Register() + { + OnEffectHit += SpellEffectFn(spell_wyrmrest_skytalon_summon_red_dragon_buddy_SpellScript::ChangeSummonPos, EFFECT_0, SPELL_EFFECT_SUMMON); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_wyrmrest_skytalon_summon_red_dragon_buddy_SpellScript(); + } +}; + +class spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger : public SpellScriptLoader +{ + public: + spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger() : SpellScriptLoader("spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger") { } + + class spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger_SpellScript : public SpellScript + { + PrepareSpellScript(spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + target->CastSpell(GetCaster(), GetEffectValue(), true); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger_SpellScript(); + } +}; + +class spell_malygos_surge_of_power_warning_selector_25 : public SpellScriptLoader +{ + public: + spell_malygos_surge_of_power_warning_selector_25() : SpellScriptLoader("spell_malygos_surge_of_power_warning_selector_25") { } + + class spell_malygos_surge_of_power_warning_selector_25_SpellScript : public SpellScript + { + PrepareSpellScript(spell_malygos_surge_of_power_warning_selector_25_SpellScript) + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_SURGE_OF_POWER_PHASE_3_25)) + return false; + + return true; + } + + void SendThreeTargets(std::list<WorldObject*>& targets) + { + Creature* caster = GetCaster()->ToCreature(); + targets.remove_if(isPlayerOnVehicleChecker(caster)); + if (targets.empty()) + return; + + for (std::list<WorldObject*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) + _filteredSelectedTargets.push_back((*itr)); + + if (_filteredSelectedTargets.empty()) + return; + + uint8 guidDataSlot = 14; // SetGuid in Malygos AI is reserved for 14th, 15th and 16th Id for the three targets + Trinity::Containers::RandomResizeList(_filteredSelectedTargets, 3); + for (std::list<WorldObject*>::const_iterator itr = _filteredSelectedTargets.begin(); itr != _filteredSelectedTargets.end(); ++itr) + { + caster->AI()->SetGUID((*itr)->GetGUID(), guidDataSlot++); + if (IS_VEHICLE_GUID((*itr)->GetGUID())) + { + if (Vehicle* tempVehicle = (*itr)->ToCreature()->GetVehicleKit()) + if (tempVehicle->GetPassenger(0)) + if (Player* tempPlayer = tempVehicle->GetPassenger(0)->ToPlayer()) + caster->AI()->Talk(EMOTE_SURGE_OF_POWER_WARNING_P3, tempPlayer->GetGUID()); + } + else if (IS_PLAYER_GUID((*itr)->GetGUID())) + { + caster->AI()->Talk(EMOTE_SURGE_OF_POWER_WARNING_P3, (*itr)->GetGUID()); + } + } + } + + void ExecuteMainSpell() + { + // We shouldn't cast stuff from target selector hooks + Creature* caster = GetCaster()->ToCreature(); + caster->AI()->DoCastAOE(SPELL_SURGE_OF_POWER_PHASE_3_25); + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_malygos_surge_of_power_warning_selector_25_SpellScript::SendThreeTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + AfterHit += SpellHitFn(spell_malygos_surge_of_power_warning_selector_25_SpellScript::ExecuteMainSpell); + } + + std::list<WorldObject*> _filteredSelectedTargets; + }; + + SpellScript* GetSpellScript() const + { + return new spell_malygos_surge_of_power_warning_selector_25_SpellScript(); + } +}; + +class spell_malygos_surge_of_power_25 : public SpellScriptLoader +{ + public: + spell_malygos_surge_of_power_25() : SpellScriptLoader("spell_malygos_surge_of_power_25") { } + + class spell_malygos_surge_of_power_25_SpellScript : public SpellScript + { + PrepareSpellScript(spell_malygos_surge_of_power_25_SpellScript) + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void FilterTargets(std::list<WorldObject*>& targets) + { + Creature* caster = GetCaster()->ToCreature(); + if (!targets.empty()) + targets.clear(); + + for (int guidSlot = 14; guidSlot <= 16; guidSlot++) + { + uint64 guidTypeChecker = caster->AI()->GetGUID(guidSlot); + if (IS_EMPTY_GUID(guidTypeChecker)) + continue; + + if (IS_VEHICLE_GUID(guidTypeChecker)) + { + WorldObject* tempTarget = caster->GetMap()->GetCreature(guidTypeChecker); + targets.push_back(tempTarget); + } + else if (IS_PLAYER_GUID(guidTypeChecker)) + { + WorldObject* tempTarget = ObjectAccessor::GetPlayer(*caster, guidTypeChecker); + targets.push_back(tempTarget); + } + } + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_malygos_surge_of_power_25_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_malygos_surge_of_power_25_SpellScript(); + } +}; + +class spell_alexstrasza_gift_beam : public SpellScriptLoader +{ + public: + spell_alexstrasza_gift_beam() : SpellScriptLoader("spell_alexstrasza_gift_beam") { } + + class spell_alexstrasza_gift_beam_AuraScript : public AuraScript + { + PrepareAuraScript(spell_alexstrasza_gift_beam_AuraScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_ALEXSTRASZAS_GIFT_BEAM_VISUAL)) + return false; + + return true; + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + target->CastSpell(target, SPELL_ALEXSTRASZAS_GIFT_BEAM_VISUAL); + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + target->RemoveAura(SPELL_ALEXSTRASZAS_GIFT_BEAM_VISUAL); + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_alexstrasza_gift_beam_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_alexstrasza_gift_beam_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_alexstrasza_gift_beam_AuraScript(); + } +}; + +class spell_alexstrasza_gift_beam_visual : public SpellScriptLoader +{ + public: + spell_alexstrasza_gift_beam_visual() : SpellScriptLoader("spell_alexstrasza_gift_beam_visual") { } + + class spell_alexstrasza_gift_beam_visual_AuraScript : public AuraScript + { + PrepareAuraScript(spell_alexstrasza_gift_beam_visual_AuraScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_UNIT; + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + { + if (target->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) + _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_10, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0); + else if (target->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_25, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0); + } + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Creature* target = GetTarget()->ToCreature()) + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) + { + _alexstraszaGift->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + if ((_heartMagic = target->GetMap()->GetGameObject(instance->GetData64(DATA_HEART_OF_MAGIC_GUID)))) + { + _heartMagic->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + // TO DO: This is hack, core doesn't have support for these flags, + // remove line below if it ever gets supported otherwise object won't be accessible. + _heartMagic->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_INTERACT_COND); + } + } + } + + void Register() + { + OnEffectApply += AuraEffectApplyFn(spell_alexstrasza_gift_beam_visual_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_alexstrasza_gift_beam_visual_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + + GameObject* _alexstraszaGift; + GameObject* _heartMagic; + }; + + AuraScript* GetAuraScript() const + { + return new spell_alexstrasza_gift_beam_visual_AuraScript(); + } }; class achievement_denyin_the_scion : public AchievementCriteriaScript { public: - achievement_denyin_the_scion() : AchievementCriteriaScript("achievement_denyin_the_scion") {} + achievement_denyin_the_scion() : AchievementCriteriaScript("achievement_denyin_the_scion") { } bool OnCheck(Player* source, Unit* /*target*/) { + // Only melee disks can be used if (Unit* disk = source->GetVehicleBase()) - if (disk->GetEntry() == NPC_HOVER_DISK_CASTER || disk->GetEntry() == NPC_HOVER_DISK_MELEE) + if (disk->GetEntry() == NPC_HOVER_DISK_MELEE) return true; + return false; } }; @@ -1114,11 +2502,29 @@ void AddSC_boss_malygos() new boss_malygos(); new npc_portal_eoe(); new npc_power_spark(); - new npc_hover_disk(); + new npc_melee_hover_disk(); + new npc_caster_hover_disk(); + new npc_nexus_lord(); + new npc_scion_of_eternity(); new npc_arcane_overload(); new npc_wyrmrest_skytalon(); + new npc_static_field(); + new spell_malygos_portal_beam(); + new spell_malygos_random_portal(); + new spell_malygos_arcane_storm(); new spell_malygos_vortex_dummy(); new spell_malygos_vortex_visual(); - new npc_alexstrasza_eoe(); + new spell_arcane_overload(); + new spell_nexus_lord_align_disk_aggro(); + new spell_scion_of_eternity_arcane_barrage(); + new spell_malygos_destroy_platform_channel(); + new spell_alexstrasza_bunny_destroy_platform_boom_visual(); + new spell_alexstrasza_bunny_destroy_platform_event(); + new spell_wyrmrest_skytalon_summon_red_dragon_buddy(); + new spell_wyrmrest_skytalon_ride_red_dragon_buddy_trigger(); + new spell_malygos_surge_of_power_warning_selector_25(); + new spell_malygos_surge_of_power_25(); + new spell_alexstrasza_gift_beam(); + new spell_alexstrasza_gift_beam_visual(); new achievement_denyin_the_scion(); } diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h b/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h index c25feaafaf8..db879eab6fb 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/eye_of_eternity.h @@ -24,14 +24,19 @@ enum InstanceData MAX_ENCOUNTER, DATA_VORTEX_HANDLING, - DATA_POWER_SPARKS_HANDLING + DATA_POWER_SPARKS_HANDLING, + DATA_RESPAWN_IRIS }; enum InstanceData64 { DATA_TRIGGER, DATA_MALYGOS, - DATA_PLATFORM + DATA_PLATFORM, + DATA_ALEXSTRASZA_BUNNY_GUID, + DATA_HEART_OF_MAGIC_GUID, + DATA_FOCUSING_IRIS_GUID, + DATA_GIFT_BOX_BUNNY_GUID }; enum InstanceNpcs @@ -44,16 +49,22 @@ enum InstanceNpcs NPC_HOVER_DISK_CASTER = 30248, NPC_ARCANE_OVERLOAD = 30282, NPC_WYRMREST_SKYTALON = 30161, - NPC_ALEXSTRASZA = 32295 + NPC_ALEXSTRASZA = 32295, + NPC_ALEXSTRASZA_BUNNY = 31253, + NPC_ALEXSTRASZAS_GIFT = 32448, + NPC_SURGE_OF_POWER = 30334 }; enum InstanceGameObjects { GO_NEXUS_RAID_PLATFORM = 193070, GO_EXIT_PORTAL = 193908, - GO_FOCUSING_IRIS = 193958, - GO_ALEXSTRASZA_S_GIFT = 193905, - GO_ALEXSTRASZA_S_GIFT_2 = 193967 + GO_FOCUSING_IRIS_10 = 193958, + GO_FOCUSING_IRIS_25 = 193960, + GO_ALEXSTRASZA_S_GIFT_10 = 193905, + GO_ALEXSTRASZA_S_GIFT_25 = 193967, + GO_HEART_OF_MAGIC_10 = 194158, + GO_HEART_OF_MAGIC_25 = 194159 }; enum InstanceEvents @@ -63,10 +74,11 @@ enum InstanceEvents enum InstanceSpells { - SPELL_VORTEX_4 = 55853, // damage | used to enter to the vehicle - SPELL_VORTEX_5 = 56263, // damage | used to enter to the vehicle - SPELL_PORTAL_OPENED = 61236, - SPELL_RIDE_RED_DRAGON = 56071, + SPELL_VORTEX_4 = 55853, // damage | used to enter to the vehicle + SPELL_VORTEX_5 = 56263, // damage | used to enter to the vehicle + SPELL_PORTAL_OPENED = 61236, + SPELL_RIDE_RED_DRAGON_TRIGGERED = 56072, + SPELL_IRIS_OPENED = 61012 // visual when starting encounter }; #endif diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp index 6b1be15f110..52ce259117f 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp @@ -24,7 +24,7 @@ class instance_eye_of_eternity : public InstanceMapScript { public: - instance_eye_of_eternity() : InstanceMapScript("instance_eye_of_eternity", 616) {} + instance_eye_of_eternity() : InstanceMapScript("instance_eye_of_eternity", 616) { } InstanceScript* GetInstanceScript(InstanceMap* map) const { @@ -41,9 +41,11 @@ public: portalTriggers.clear(); malygosGUID = 0; + irisGUID = 0; lastPortalGUID = 0; platformGUID = 0; exitPortalGUID = 0; + alexstraszaBunnyGUID = 0; }; bool SetBossState(uint32 type, EncounterState state) @@ -65,31 +67,18 @@ public: } } - SpawnGameObject(GO_FOCUSING_IRIS, focusingIrisPosition); SpawnGameObject(GO_EXIT_PORTAL, exitPortalPosition); if (GameObject* platform = instance->GetGameObject(platformGUID)) platform->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); } else if (state == DONE) - { - if (Creature* malygos = instance->GetCreature(malygosGUID)) - malygos->SummonCreature(NPC_ALEXSTRASZA, 829.0679f, 1244.77f, 279.7453f, 2.32f); - SpawnGameObject(GO_EXIT_PORTAL, exitPortalPosition); - - // we make the platform appear again because at the moment we don't support looting using a vehicle - if (GameObject* platform = instance->GetGameObject(platformGUID)) - platform->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); - - if (GameObject* chest = instance->GetGameObject(chestGUID)) - chest->SetRespawnTime(7*DAY); - } } return true; } - //TODO: this should be handled in map, maybe add a summon function in map + // TO DO: this should be handled in map, maybe add a summon function in map // There is no other way afaik... void SpawnGameObject(uint32 entry, Position& pos) { @@ -112,16 +101,31 @@ public: case GO_NEXUS_RAID_PLATFORM: platformGUID = go->GetGUID(); break; - case GO_FOCUSING_IRIS: - go->GetPosition(&focusingIrisPosition); + case GO_FOCUSING_IRIS_10: + if (instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) + { + irisGUID = go->GetGUID(); + go->GetPosition(&focusingIrisPosition); + } + break; + case GO_FOCUSING_IRIS_25: + if (instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + { + irisGUID = go->GetGUID(); + go->GetPosition(&focusingIrisPosition); + } break; case GO_EXIT_PORTAL: exitPortalGUID = go->GetGUID(); go->GetPosition(&exitPortalPosition); break; - case GO_ALEXSTRASZA_S_GIFT: - case GO_ALEXSTRASZA_S_GIFT_2: - chestGUID = go->GetGUID(); + case GO_HEART_OF_MAGIC_10: + if (instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) + heartOfMagicGUID = go->GetGUID(); + break; + case GO_HEART_OF_MAGIC_25: + if (instance->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + heartOfMagicGUID = go->GetGUID(); break; } } @@ -139,18 +143,27 @@ public: case NPC_PORTAL_TRIGGER: portalTriggers.push_back(creature->GetGUID()); break; + case NPC_ALEXSTRASZA_BUNNY: + alexstraszaBunnyGUID = creature->GetGUID(); + break; + case NPC_ALEXSTRASZAS_GIFT: + giftBoxBunnyGUID = creature->GetGUID(); + break; } } - void ProcessEvent(WorldObject* obj, uint32 eventId) + void ProcessEvent(WorldObject* /*obj*/, uint32 eventId) { if (eventId == EVENT_FOCUSING_IRIS) { - if (GameObject* go = obj->ToGameObject()) - go->Delete(); // this is not the best way. + if (Creature* alexstraszaBunny = instance->GetCreature(alexstraszaBunnyGUID)) + { + alexstraszaBunny->CastSpell(alexstraszaBunny, SPELL_IRIS_OPENED); + instance->GetGameObject(irisGUID)->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + } if (Creature* malygos = instance->GetCreature(malygosGUID)) - malygos->GetMotionMaster()->MovePoint(4, 770.10f, 1275.33f, 267.23f); // MOVE_INIT_PHASE_ONE + malygos->AI()->DoAction(0); // ACTION_LAND_ENCOUNTER_START if (GameObject* exitPortal = instance->GetGameObject(exitPortalGUID)) exitPortal->Delete(); @@ -194,7 +207,7 @@ public: void PowerSparksHandling() { - bool next = (lastPortalGUID == portalTriggers.back() || !lastPortalGUID ? true : false); + bool next = (lastPortalGUID == portalTriggers.back() || !lastPortalGUID ? true : false); for (std::list<uint64>::const_iterator itr_trigger = portalTriggers.begin(); itr_trigger != portalTriggers.end(); ++itr_trigger) { @@ -223,6 +236,9 @@ public: case DATA_POWER_SPARKS_HANDLING: PowerSparksHandling(); break; + case DATA_RESPAWN_IRIS: + SpawnGameObject(instance->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL ? GO_FOCUSING_IRIS_10 : GO_FOCUSING_IRIS_25, focusingIrisPosition); + break; } } @@ -236,6 +252,14 @@ public: return malygosGUID; case DATA_PLATFORM: return platformGUID; + case DATA_ALEXSTRASZA_BUNNY_GUID: + return alexstraszaBunnyGUID; + case DATA_HEART_OF_MAGIC_GUID: + return heartOfMagicGUID; + case DATA_FOCUSING_IRIS_GUID: + return irisGUID; + case DATA_GIFT_BOX_BUNNY_GUID: + return giftBoxBunnyGUID; } return 0; @@ -287,10 +311,13 @@ public: std::list<uint64> vortexTriggers; std::list<uint64> portalTriggers; uint64 malygosGUID; + uint64 irisGUID; uint64 lastPortalGUID; uint64 platformGUID; uint64 exitPortalGUID; - uint64 chestGUID; + uint64 heartOfMagicGUID; + uint64 alexstraszaBunnyGUID; + uint64 giftBoxBunnyGUID; Position focusingIrisPosition; Position exitPortalPosition; }; diff --git a/src/server/shared/Common.h b/src/server/shared/Common.h index f4e4ce7fe06..44a7749334d 100644 --- a/src/server/shared/Common.h +++ b/src/server/shared/Common.h @@ -170,7 +170,7 @@ enum LocaleConstant }; const uint8 TOTAL_LOCALES = 9; -const LocaleConstant DEFAULT_LOCALE = LOCALE_enUS; +#define DEFAULT_LOCALE LOCALE_enUS #define MAX_LOCALES 8 #define MAX_ACCOUNT_TUTORIAL_VALUES 8 |