diff options
author | Vincent_Michael <Vincent_Michael@gmx.de> | 2013-12-11 20:34:44 +0100 |
---|---|---|
committer | Vincent_Michael <Vincent_Michael@gmx.de> | 2013-12-11 20:34:44 +0100 |
commit | 07024d3ea84db832e5bdd221c293e5daf297181a (patch) | |
tree | 663c0412bd112c0bef4f9b106c597e2bc0d05eb5 /src | |
parent | 70445f34a6b66b603662716371c6f6a873a95855 (diff) | |
parent | fb26e1852394a1aa6e7695030eabcf8e85b1c5c8 (diff) |
Merge branch 'master' of github.com:TrinityCore/TrinityCore into 4.3.4
Conflicts:
src/server/game/DataStores/DBCEnums.h
src/server/scripts/Commands/cs_wp.cpp
src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp
Diffstat (limited to 'src')
27 files changed, 1882 insertions, 1906 deletions
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index 1f312de73f6..18daf1ef8de 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -409,7 +409,7 @@ Unit* PetAI::SelectNextTarget(bool allowAutoSelect) const if (me->HasReactState(REACT_AGGRESSIVE) && allowAutoSelect) { if (!me->GetCharmInfo()->IsReturning() || me->GetCharmInfo()->IsFollowing() || me->GetCharmInfo()->IsAtStay()) - if (Unit* nearTarget = me->ToCreature()->SelectNearestHostileUnitInAggroRange(true)) + if (Unit* nearTarget = me->SelectNearestHostileUnitInAggroRange(true)) return nearTarget; } diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index e98c196d6e3..a1042a25c01 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -525,9 +525,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.cast.spell); int32 mana = me->GetPower(POWER_MANA); - if (me->GetDistance((*itr)->ToUnit()) > spellInfo->GetMaxRange(true) || - me->GetDistance((*itr)->ToUnit()) < spellInfo->GetMinRange(true) || - !me->ToUnit()->IsWithinLOSInMap((*itr)->ToUnit()) || + if (me->GetDistance(*itr) > spellInfo->GetMaxRange(true) || + me->GetDistance(*itr) < spellInfo->GetMinRange(true) || + !me->IsWithinLOSInMap(*itr) || mana < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask())) _allowMove = true; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 74d2d5c49a1..3835220b453 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -107,6 +107,8 @@ SmartWaypointMgr::~SmartWaypointMgr() void SmartAIMgr::LoadSmartAIFromDB() { + LoadHelperStores(); + uint32 oldMSTime = getMSTime(); for (uint8 i = 0; i < SMART_SCRIPT_TYPE_MAX; i++) @@ -118,7 +120,6 @@ void SmartAIMgr::LoadSmartAIFromDB() if (!result) { TC_LOG_INFO("server.loading", ">> Loaded 0 SmartAI scripts. DB table `smartai_scripts` is empty."); - return; } @@ -236,6 +237,7 @@ void SmartAIMgr::LoadSmartAIFromDB() TC_LOG_INFO("server.loading", ">> Loaded %u SmartAI scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + UnLoadHelperStores(); } bool SmartAIMgr::IsTargetValid(SmartScriptHolder const& e) @@ -769,27 +771,14 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) break; } case SMART_ACTION_SUMMON_CREATURE: + { if (!IsCreatureValid(e, e.action.summonCreature.creature)) return false; - for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i); - if (!spellInfo) - continue; - - for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) - { - if (spellInfo->Effects[j].Effect == SPELL_EFFECT_SUMMON) - { - uint32 creatureSummonEntry = spellInfo->Effects[j].MiscValue; - - if (e.action.summonCreature.creature == creatureSummonEntry) - TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u creature summon: There is a summon spell for creature entry %u (SpellId: %u, effect: %u)", - e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonCreature.creature, spellInfo->Id, j); - } - } - } + CacheSpellContainerBounds sBounds = GetSummonCreatureSpellContainerBounds(e.action.summonCreature.creature); + for (CacheSpellContainer::const_iterator itr = sBounds.first; itr != sBounds.second; ++itr) + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u creature summon: There is a summon spell for creature entry %u (SpellId: %u, effect: %u)", + e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonCreature.creature, itr->second.first, itr->second.second); if (e.action.summonCreature.type < TEMPSUMMON_TIMED_OR_DEAD_DESPAWN || e.action.summonCreature.type > TEMPSUMMON_MANUAL_DESPAWN) { @@ -797,27 +786,16 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) return false; } break; + } case SMART_ACTION_CALL_KILLEDMONSTER: + { if (!IsCreatureValid(e, e.action.killedMonster.creature)) return false; - for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i); - if (!spellInfo) - continue; - - for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) - { - if (spellInfo->Effects[j].Effect == SPELL_EFFECT_KILL_CREDIT || spellInfo->Effects[j].Effect == SPELL_EFFECT_KILL_CREDIT2) - { - uint32 killCredit = spellInfo->Effects[j].MiscValue; - - if (e.action.killedMonster.creature == killCredit) - TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u Kill Credit: %u has already spell kill credit (SpellId: %u effect: %u)", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.killedMonster.creature, spellInfo->Id, j); - } - } - } + CacheSpellContainerBounds sBounds = GetKillCreditSpellContainerBounds(e.action.killedMonster.creature); + for (CacheSpellContainer::const_iterator itr = sBounds.first; itr != sBounds.second; ++itr) + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u Kill Credit: There is a killcredit spell for creatureEntry %u (SpellId: %u effect: %u)", + e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.killedMonster.creature, itr->second.first, itr->second.second); if (e.GetTargetType() == SMART_TARGET_POSITION) { @@ -825,6 +803,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) return false; } break; + } case SMART_ACTION_UPDATE_TEMPLATE: if (e.action.updateTemplate.creature && !IsCreatureValid(e, e.action.updateTemplate.creature)) return false; @@ -846,28 +825,16 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) break; } case SMART_ACTION_SUMMON_GO: + { if (!IsGameObjectValid(e, e.action.summonGO.entry)) return false; - for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i); - if (!spellInfo) - continue; - - for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) - { - if (spellInfo->Effects[j].Effect == SPELL_EFFECT_SUMMON_OBJECT_WILD) - { - uint32 goSummonEntry = spellInfo->Effects[j].MiscValue; - - if (e.action.summonGO.entry == goSummonEntry) - TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u gameobject summon: There is a summon spell for gameobject entry %u (SpellId: %u, effect: %u)", - e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonGO.entry, spellInfo->Id, j); - } - } - } + CacheSpellContainerBounds sBounds = GetSummonGameObjectSpellContainerBounds(e.action.summonGO.entry); + for (CacheSpellContainer::const_iterator itr = sBounds.first; itr != sBounds.second; ++itr) + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u gameobject summon: There is a summon spell for gameobject entry %u (SpellId: %u, effect: %u)", + e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonGO.entry, itr->second.first, itr->second.second); break; + } case SMART_ACTION_ADD_ITEM: case SMART_ACTION_REMOVE_ITEM: if (!IsItemValid(e, e.action.item.entry)) @@ -1073,3 +1040,52 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) } return true; }*/ + +void SmartAIMgr::LoadHelperStores() +{ + uint32 oldMSTime = getMSTime(); + + SpellInfo const* spellInfo = NULL; + for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) + { + spellInfo = sSpellMgr->GetSpellInfo(i); + if (!spellInfo) + continue; + + for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) + { + if (spellInfo->Effects[j].IsEffect(SPELL_EFFECT_SUMMON)) + SummonCreatureSpellStore.insert(std::make_pair(uint32(spellInfo->Effects[j].MiscValue), std::make_pair(i, SpellEffIndex(j)))); + + else if (spellInfo->Effects[j].IsEffect(SPELL_EFFECT_SUMMON_OBJECT_WILD)) + SummonGameObjectSpellStore.insert(std::make_pair(uint32(spellInfo->Effects[j].MiscValue), std::make_pair(i, SpellEffIndex(j)))); + + else if (spellInfo->Effects[j].IsEffect(SPELL_EFFECT_KILL_CREDIT) || spellInfo->Effects[j].IsEffect(SPELL_EFFECT_KILL_CREDIT2)) + KillCreditSpellStore.insert(std::make_pair(uint32(spellInfo->Effects[j].MiscValue), std::make_pair(i, SpellEffIndex(j)))); + } + } + + TC_LOG_INFO("server.loading", ">> Loaded SmartAIMgr Helpers in %u ms", GetMSTimeDiffToNow(oldMSTime)); +} + +void SmartAIMgr::UnLoadHelperStores() +{ + SummonCreatureSpellStore.clear(); + SummonGameObjectSpellStore.clear(); + KillCreditSpellStore.clear(); +} + +CacheSpellContainerBounds SmartAIMgr::GetSummonCreatureSpellContainerBounds(uint32 creatureEntry) const +{ + return SummonCreatureSpellStore.equal_range(creatureEntry); +} + +CacheSpellContainerBounds SmartAIMgr::GetSummonGameObjectSpellContainerBounds(uint32 gameObjectEntry) const +{ + return SummonGameObjectSpellStore.equal_range(gameObjectEntry); +} + +CacheSpellContainerBounds SmartAIMgr::GetKillCreditSpellContainerBounds(uint32 killCredit) const +{ + return KillCreditSpellStore.equal_range(killCredit); +} diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 093f71ad1bd..2ad68474f8a 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -1346,12 +1346,16 @@ typedef std::vector<SmartScriptHolder> SmartAIEventList; // all events for all entries / guids typedef UNORDERED_MAP<int32, SmartAIEventList> SmartAIEventMap; +// Helper Stores +typedef std::map<uint32 /*entry*/, std::pair<uint32 /*spellId*/, SpellEffIndex /*effIndex*/> > CacheSpellContainer; +typedef std::pair<CacheSpellContainer::const_iterator, CacheSpellContainer::const_iterator> CacheSpellContainerBounds; + class SmartAIMgr { friend class ACE_Singleton<SmartAIMgr, ACE_Null_Mutex>; - SmartAIMgr(){ } + SmartAIMgr() { } public: - ~SmartAIMgr(){ } + ~SmartAIMgr() { } void LoadSmartAIFromDB(); @@ -1506,6 +1510,18 @@ class SmartAIMgr } //bool IsTextValid(SmartScriptHolder const& e, uint32 id); + + // Helpers + void LoadHelperStores(); + void UnLoadHelperStores(); + + CacheSpellContainerBounds GetSummonCreatureSpellContainerBounds(uint32 creatureEntry) const; + CacheSpellContainerBounds GetSummonGameObjectSpellContainerBounds(uint32 gameObjectEntry) const; + CacheSpellContainerBounds GetKillCreditSpellContainerBounds(uint32 killCredit) const; + + CacheSpellContainer SummonCreatureSpellStore; + CacheSpellContainer SummonGameObjectSpellStore; + CacheSpellContainer KillCreditSpellStore; }; #define sSmartScriptMgr ACE_Singleton<SmartAIMgr, ACE_Null_Mutex>::instance() diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index f5a5d02e7ac..1e21b640ddb 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -825,7 +825,7 @@ Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, fl return 0; } - Creature* creature = new Creature; + Creature* creature = new Creature(); if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, 0, team, x, y, z, o)) { TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u", entry); diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index e3aff877319..473303dc5ee 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -1648,7 +1648,7 @@ Creature* Battleground::AddCreature(uint32 entry, uint32 type, uint32 teamval, f if (!map) return NULL; - Creature* creature = new Creature; + Creature* creature = new Creature(); if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, 0, teamval, x, y, z, o)) { TC_LOG_ERROR("bg.battleground", "Battleground::AddCreature: cannot create creature (entry: %u) for BG (map: %u, instance id: %u)!", diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index b8a52152202..0d7d520b4fa 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -290,36 +290,36 @@ enum AchievementCriteriaTypes enum AreaFlags { - AREA_FLAG_SNOW = 0x00000001, // snow (only Dun Morogh, Naxxramas, Razorfen Downs and Winterspring) - AREA_FLAG_UNK1 = 0x00000002, // Razorfen Downs, Naxxramas and Acherus: The Ebon Hold (3.3.5a) - AREA_FLAG_UNK2 = 0x00000004, // Only used for areas on map 571 (development before) - AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // city and city subsones - AREA_FLAG_UNK3 = 0x00000010, // can't find common meaning - AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag? - AREA_FLAG_ALLOW_DUELS = 0x00000040, // allow to duel here - AREA_FLAG_ARENA = 0x00000080, // arena, both instanced and world arenas - AREA_FLAG_CAPITAL = 0x00000100, // main capital city flag - AREA_FLAG_CITY = 0x00000200, // only for one zone named "City" (where it located?) - AREA_FLAG_OUTLAND = 0x00000400, // expansion zones? (only Eye of the Storm not have this flag, but have 0x00004000 flag) - AREA_FLAG_SANCTUARY = 0x00000800, // sanctuary area (PvP disabled) - AREA_FLAG_NEED_FLY = 0x00001000, // Respawn alive at the graveyard without corpse - AREA_FLAG_UNUSED1 = 0x00002000, // Unused in 3.3.5a - AREA_FLAG_OUTLAND2 = 0x00004000, // expansion zones? (only Circle of Blood Arena not have this flag, but have 0x00000400 flag) - AREA_FLAG_OUTDOOR_PVP = 0x00008000, // pvp objective area? (Death's Door also has this flag although it's no pvp object area) - AREA_FLAG_ARENA_INSTANCE = 0x00010000, // used by instanced arenas only - AREA_FLAG_UNUSED2 = 0x00020000, // Unused in 3.3.5a - AREA_FLAG_CONTESTED_AREA = 0x00040000, // On PvP servers these areas are considered contested, even though the zone it is contained in is a Horde/Alliance territory. - AREA_FLAG_UNK6 = 0x00080000, // Valgarde and Acherus: The Ebon Hold - AREA_FLAG_LOWLEVEL = 0x00100000, // used for some starting areas with area_level <= 15 - AREA_FLAG_TOWN = 0x00200000, // small towns with Inn - AREA_FLAG_UNK7 = 0x00400000, // Warsong Hold, Acherus: The Ebon Hold, New Agamand Inn, Vengeance Landing Inn, Sunreaver Pavilion (Something to do with team?) - AREA_FLAG_UNK8 = 0x00800000, // Valgarde, Acherus: The Ebon Hold, Westguard Inn, Silver Covenant Pavilion (Something to do with team?) - AREA_FLAG_WINTERGRASP = 0x01000000, // Wintergrasp and it's subzones - AREA_FLAG_INSIDE = 0x02000000, // used for determinating spell related inside/outside questions in Map::IsOutdoors - AREA_FLAG_OUTSIDE = 0x04000000, // used for determinating spell related inside/outside questions in Map::IsOutdoors - AREA_FLAG_WINTERGRASP_2 = 0x08000000, // Can Hearth And Resurrect From Area - AREA_FLAG_NO_FLY_ZONE = 0x20000000, // Marks zones where you cannot fly - AREA_FLAG_UNK9 = 0x40000000, + AREA_FLAG_SNOW = 0x00000001, // snow (only Dun Morogh, Naxxramas, Razorfen Downs and Winterspring) + AREA_FLAG_UNK1 = 0x00000002, // Razorfen Downs, Naxxramas and Acherus: The Ebon Hold (3.3.5a) + AREA_FLAG_UNK2 = 0x00000004, // Only used for areas on map 571 (development before) + AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // city and city subsones + AREA_FLAG_UNK3 = 0x00000010, // can't find common meaning + AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag? + AREA_FLAG_ALLOW_DUELS = 0x00000040, // allow to duel here + AREA_FLAG_ARENA = 0x00000080, // arena, both instanced and world arenas + AREA_FLAG_CAPITAL = 0x00000100, // main capital city flag + AREA_FLAG_CITY = 0x00000200, // only for one zone named "City" (where it located?) + AREA_FLAG_OUTLAND = 0x00000400, // expansion zones? (only Eye of the Storm not have this flag, but have 0x00004000 flag) + AREA_FLAG_SANCTUARY = 0x00000800, // sanctuary area (PvP disabled) + AREA_FLAG_NEED_FLY = 0x00001000, // Respawn alive at the graveyard without corpse + AREA_FLAG_UNUSED1 = 0x00002000, // Unused in 3.3.5a + AREA_FLAG_OUTLAND2 = 0x00004000, // expansion zones? (only Circle of Blood Arena not have this flag, but have 0x00000400 flag) + AREA_FLAG_OUTDOOR_PVP = 0x00008000, // pvp objective area? (Death's Door also has this flag although it's no pvp object area) + AREA_FLAG_ARENA_INSTANCE = 0x00010000, // used by instanced arenas only + AREA_FLAG_UNUSED2 = 0x00020000, // Unused in 3.3.5a + AREA_FLAG_CONTESTED_AREA = 0x00040000, // On PvP servers these areas are considered contested, even though the zone it is contained in is a Horde/Alliance territory. + AREA_FLAG_UNK6 = 0x00080000, // Valgarde and Acherus: The Ebon Hold + AREA_FLAG_LOWLEVEL = 0x00100000, // used for some starting areas with area_level <= 15 + AREA_FLAG_TOWN = 0x00200000, // small towns with Inn + AREA_FLAG_REST_ZONE_HORDE = 0x00400000, // Warsong Hold, Acherus: The Ebon Hold, New Agamand Inn, Vengeance Landing Inn, Sunreaver Pavilion (Something to do with team?) + AREA_FLAG_REST_ZONE_ALLIANCE = 0x00800000, // Valgarde, Acherus: The Ebon Hold, Westguard Inn, Silver Covenant Pavilion (Something to do with team?) + AREA_FLAG_WINTERGRASP = 0x01000000, // Wintergrasp and it's subzones + AREA_FLAG_INSIDE = 0x02000000, // used for determinating spell related inside/outside questions in Map::IsOutdoors + AREA_FLAG_OUTSIDE = 0x04000000, // used for determinating spell related inside/outside questions in Map::IsOutdoors + AREA_FLAG_WINTERGRASP_2 = 0x08000000, // Can Hearth And Resurrect From Area + AREA_FLAG_NO_FLY_ZONE = 0x20000000, // Marks zones where you cannot fly + AREA_FLAG_UNK9 = 0x40000000 }; enum Difficulty diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 7d89b90c5dc..70c932da272 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1344,7 +1344,7 @@ bool Creature::IsInvisibleDueToDespawn() const if (Unit::IsInvisibleDueToDespawn()) return true; - if (IsAlive() || m_corpseRemoveTime > time(NULL)) + if (IsAlive() || isDying() || m_corpseRemoveTime > time(NULL)) return false; return true; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index cd4fa2139f9..18732984559 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -10640,6 +10640,9 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced) void Unit::setDeathState(DeathState s) { + // Death state needs to be updated before RemoveAllAurasOnDeath() is called, to prevent entering combat + m_deathState = s; + if (s != ALIVE && s != JUST_RESPAWNED) { CombatStop(); @@ -10688,8 +10691,6 @@ void Unit::setDeathState(DeathState s) } else if (s == JUST_RESPAWNED) RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); // clear skinnable for creature and player (at battleground) - - m_deathState = s; } /*######################################## @@ -10697,14 +10698,14 @@ void Unit::setDeathState(DeathState s) ######## AGGRO SYSTEM ######## ######## ######## ########################################*/ -bool Unit::CanHaveThreatList() const +bool Unit::CanHaveThreatList(bool skipAliveCheck) const { // only creatures can have threat list if (GetTypeId() != TYPEID_UNIT) return false; // only alive units can have threat list - if (!IsAlive() || isDying()) + if (!skipAliveCheck && !IsAlive()) return false; // totems can not have threat list @@ -10747,7 +10748,7 @@ void Unit::AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask, Sp void Unit::DeleteThreatList() { - if (CanHaveThreatList() && !m_ThreatManager.isThreatListEmpty()) + if (CanHaveThreatList(true) && !m_ThreatManager.isThreatListEmpty()) SendClearThreatListOpcode(); m_ThreatManager.clearReferences(); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index cb228bd135d..416a2257b92 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1906,7 +1906,7 @@ class Unit : public WorldObject uint32 m_lastSanctuaryTime; // Threat related methods - bool CanHaveThreatList() const; + bool CanHaveThreatList(bool skipAliveCheck = false) const; void AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = NULL); float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL); void DeleteThreatList(); diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index e3d15728297..a09500cb67e 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -1181,7 +1181,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id) // We use spawn coords to spawn if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY)) { - Creature* creature = new Creature; + Creature* creature = new Creature(); //TC_LOG_DEBUG("misc", "Spawning creature %u", *itr); if (!creature->LoadCreatureFromDB(*itr, map)) delete creature; diff --git a/src/server/game/Pools/PoolMgr.cpp b/src/server/game/Pools/PoolMgr.cpp index d4211f83a11..1eed9feb289 100644 --- a/src/server/game/Pools/PoolMgr.cpp +++ b/src/server/game/Pools/PoolMgr.cpp @@ -364,7 +364,7 @@ void PoolGroup<Creature>::Spawn1Object(PoolObject* obj) // We use spawn coords to spawn if (!map->Instanceable() && map->IsGridLoaded(data->posX, data->posY)) { - Creature* creature = new Creature; + Creature* creature = new Creature(); //TC_LOG_DEBUG("pool", "Spawning creature %u", guid); if (!creature->LoadCreatureFromDB(obj->guid, map)) { diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 976a3e6fb7a..82c6cbdf0b5 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -452,6 +452,7 @@ void AddSC_oculus(); void AddSC_boss_malygos(); // The Nexus: Eye of Eternity void AddSC_instance_eye_of_eternity(); void AddSC_boss_sartharion(); //Obsidian Sanctum +void AddSC_obsidian_sanctum(); void AddSC_instance_obsidian_sanctum(); void AddSC_boss_bjarngrim(); //Ulduar Halls of Lightning void AddSC_boss_loken(); @@ -1299,6 +1300,7 @@ void AddNorthrendScripts() AddSC_boss_malygos(); // The Nexus: Eye of Eternity AddSC_instance_eye_of_eternity(); AddSC_boss_sartharion(); //Obsidian Sanctum + AddSC_obsidian_sanctum(); AddSC_instance_obsidian_sanctum(); AddSC_boss_bjarngrim(); //Ulduar Halls of Lightning AddSC_boss_loken(); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 05b56e7b25d..63fe3e91433 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -924,7 +924,7 @@ public: if (!ve) return false; - Creature* v = new Creature; + Creature* v = new Creature(); Map* map = handler->GetSession()->GetPlayer()->GetMap(); diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index 86d079f53e4..711e40b0659 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -693,8 +693,13 @@ public: wpCreature->AddObjectToRemoveList(); } // re-create +<<<<<<< HEAD Creature* wpCreature2 = new Creature; if (!wpCreature2->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMgr().GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, 0, 0, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation())) +======= + Creature* wpCreature2 = new Creature(); + if (!wpCreature2->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), VISUAL_WAYPOINT, 0, 0, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation())) +>>>>>>> fb26e1852394a1aa6e7695030eabcf8e85b1c5c8 { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT); delete wpCreature2; @@ -917,8 +922,13 @@ public: Map* map = chr->GetMap(); float o = chr->GetOrientation(); +<<<<<<< HEAD Creature* wpCreature = new Creature; if (!wpCreature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMgr().GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) +======= + Creature* wpCreature = new Creature(); + if (!wpCreature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) +>>>>>>> fb26e1852394a1aa6e7695030eabcf8e85b1c5c8 { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete wpCreature; @@ -981,8 +991,13 @@ public: float o = chr->GetOrientation(); Map* map = chr->GetMap(); +<<<<<<< HEAD Creature* creature = new Creature; if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMgr().GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) +======= + Creature* creature = new Creature(); + if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) +>>>>>>> fb26e1852394a1aa6e7695030eabcf8e85b1c5c8 { handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); delete creature; @@ -1030,7 +1045,7 @@ public: Player* chr = handler->GetSession()->GetPlayer(); Map* map = chr->GetMap(); - Creature* creature = new Creature; + Creature* creature = new Creature(); if (!creature->Create(sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT), map, chr->GetPhaseMgr().GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o)) { handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id); diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_ras_frostwhisper.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_ras_frostwhisper.cpp index 451fd46938d..f93f0a94414 100644 --- a/src/server/scripts/EasternKingdoms/Scholomance/boss_ras_frostwhisper.cpp +++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_ras_frostwhisper.cpp @@ -69,7 +69,7 @@ public: DoCast(me, SPELL_ICEARMOR, true); } - void EnterCombat(Unit* /*who*/)OVERRIDE { } + void EnterCombat(Unit* /*who*/) OVERRIDE { } void UpdateAI(uint32 diff) OVERRIDE { diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp index ca5dcf5360e..80b4b98b0a4 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp @@ -351,8 +351,7 @@ public: break; } case 6: - me->SetOrientation(me->GetAngle(breathX, breathY)); - me->StopMoving(); + me->SetFacingTo(me->GetAngle(breathX, breathY)); //DoTextEmote("takes a deep breath.", NULL); events.ScheduleEvent(EVENT_FLIGHT_SEQUENCE, 10000); break; diff --git a/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp b/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp index 76c2e635a82..1606b59e786 100644 --- a/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_ghostlands.cpp @@ -125,7 +125,7 @@ public: break; case 11: Talk(SAY_PROGRESS2, player->GetGUID()); - me->SetOrientation(4.762841f); + me->SetFacingTo(4.762841f); break; case 18: { @@ -150,11 +150,11 @@ public: player->GroupEventHappens(QUEST_ESCAPE_FROM_THE_CATACOMBS, me); break; case 32: - me->SetOrientation(2.978281f); + me->SetFacingTo(2.978281f); Talk(SAY_END1, player->GetGUID()); break; case 33: - me->SetOrientation(5.858011f); + me->SetFacingTo(5.858011f); Talk(SAY_END2, player->GetGUID()); Creature* CaptainHelios = me->FindNearestCreature(NPC_CAPTAIN_HELIOS, 50); if (CaptainHelios) diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp index 53e09c918ea..8dd009cdb32 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp @@ -308,18 +308,15 @@ public: { if (instance) { - Unit* pKri = Unit::GetUnit(*me, instance->GetData64(DATA_KRI)); - Unit* pVem = Unit::GetUnit(*me, instance->GetData64(DATA_VEM)); - switch (urand(0, 2)) { case 0: - if (pKri) - DoCast(pKri, SPELL_HEAL); + if (Creature* kri = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_KRI))) + DoCast(kri, SPELL_HEAL); break; case 1: - if (pVem) - DoCast(pVem, SPELL_HEAL); + if (Creature* vem = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_VEM))) + DoCast(vem, SPELL_HEAL); break; case 2: DoCast(me, SPELL_HEAL); diff --git a/src/server/scripts/Northrend/CMakeLists.txt b/src/server/scripts/Northrend/CMakeLists.txt index 3c56361d096..285f8f866cb 100644 --- a/src/server/scripts/Northrend/CMakeLists.txt +++ b/src/server/scripts/Northrend/CMakeLists.txt @@ -45,6 +45,7 @@ set(scripts_STAT_SRCS Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp + Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.cpp diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp index 2088ef2fa2e..a212a74df86 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp @@ -37,9 +37,6 @@ enum Enums SAY_SARTHARION_SLAY = 8, WHISPER_LAVA_CHURN = 9, - WHISPER_HATCH_EGGS = 6, - WHISPER_OPEN_PORTAL = 6, // whisper, shared by two dragons - WHISPER_SHADRON_DICIPLE = 7, WHISPER_VESPERON_DICIPLE = 7, @@ -58,70 +55,25 @@ enum Enums SPELL_PYROBUFFET = 56916, // currently used for hard enrage after 15 minutes SPELL_PYROBUFFET_RANGE = 58907, // possibly used when player get too far away from dummy creatures (2x Creature entry 30494) - SPELL_TWILIGHT_SHIFT_ENTER = 57620, // enter phase. Player get this when click GO - SPELL_TWILIGHT_SHIFT = 57874, // Twilight Shift Aura - SPELL_TWILIGHT_SHIFT_REMOVAL = 61187, // leave phase - SPELL_TWILIGHT_SHIFT_REMOVAL_ALL = 61190, // leave phase (probably version to make all leave) - - //Mini bosses common spells - SPELL_TWILIGHT_RESIDUE = 61885, // makes immune to shadow damage, applied when leave phase - - //Miniboses (Vesperon, Shadron, Tenebron) - SPELL_SHADOW_BREATH_H = 59126, // Inflicts 8788 to 10212 Fire damage to enemies in a cone in front of the caster. - SPELL_SHADOW_BREATH = 57570, // Inflicts 6938 to 8062 Fire damage to enemies in a cone in front of the caster. - - SPELL_SHADOW_FISSURE_H = 59127, // Deals 9488 to 13512 Shadow damage to any enemy within the Shadow fissure after 5 sec. - SPELL_SHADOW_FISSURE = 57579, // Deals 6188 to 8812 Shadow damage to any enemy within the Shadow fissure after 5 sec. - //Vesperon //In portal is a disciple, when disciple killed remove Power_of_vesperon, portal open multiple times NPC_ACOLYTE_OF_VESPERON = 31219, // Acolyte of Vesperon SPELL_POWER_OF_VESPERON = 61251, // Vesperon's presence decreases the maximum health of all enemies by 25%. - SPELL_TWILIGHT_TORMENT_VESP = 57948, // (Shadow only) trigger 57935 then 57988 - SPELL_TWILIGHT_TORMENT_VESP_ACO = 58853, // (Fire and Shadow) trigger 58835 then 57988 //Shadron //In portal is a disciple, when disciple killed remove Power_of_vesperon, portal open multiple times NPC_ACOLYTE_OF_SHADRON = 31218, // Acolyte of Shadron SPELL_POWER_OF_SHADRON = 58105, // Shadron's presence increases Fire damage taken by all enemies by 100%. - SPELL_GIFT_OF_TWILIGTH_SHA = 57835, // TARGET_SCRIPT shadron - SPELL_GIFT_OF_TWILIGTH_SAR = 58766, // TARGET_SCRIPT sartharion - SPELL_VOID_BLAST = 57581, // Twilight Fissure - SPELL_VOID_BLAST_H = 59128, //Tenebron //in the portal spawns 6 eggs, if not killed in time (approx. 20s) they will hatch, whelps can cast 60708 SPELL_POWER_OF_TENEBRON = 61248, // Tenebron's presence increases Shadow damage taken by all enemies by 100%. - //Tenebron, dummy spell - SPELL_SUMMON_TWILIGHT_WHELP = 58035, // doesn't work, will spawn NPC_TWILIGHT_WHELP - SPELL_SUMMON_SARTHARION_TWILIGHT_WHELP = 58826, // doesn't work, will spawn NPC_SHARTHARION_TWILIGHT_WHELP - - SPELL_HATCH_EGGS_H = 59189, - SPELL_HATCH_EGGS = 58542, - SPELL_HATCH_EGGS_EFFECT_H = 59190, - SPELL_HATCH_EGGS_EFFECT = 58685, - NPC_TWILIHT_WHELP = 31214, - NPC_TWILIGHT_EGG = 30882, - NPC_SARTHARION_TWILIGHT_EGG = 31204, - - //Whelps - NPC_TWILIGHT_WHELP = 30890, - NPC_SHARTHARION_TWILIGHT_WHELP = 31214, - SPELL_FADE_ARMOR = 60708, // Reduces the armor of an enemy by 1500 for 15s - - //flame tsunami - SPELL_FLAME_TSUNAMI = 57494, // the visual dummy - SPELL_FLAME_TSUNAMI_LEAP = 60241, // SPELL_EFFECT_138 some leap effect, causing caster to move in direction - - SPELL_FLAME_TSUNAMI_DMG_AURA = 57491, // periodic damage, npc has this aura - SPELL_FLAME_TSUNAMI_BUFF = 60430, NPC_FLAME_TSUNAMI = 30616, // for the flame waves - NPC_LAVA_BLAZE = 30643, // adds spawning from flame strike //using these custom points for dragons start and end POINT_ID_INIT = 100, - POINT_ID_LAND = 200, + POINT_ID_LAND = 200 }; enum Misc @@ -129,25 +81,32 @@ enum Misc DATA_CAN_LOOT = 0 }; -struct Waypoint -{ - float m_fX, m_fY, m_fZ; -}; - struct Location { float x, y, z; }; -struct Locations + +static Location FlameRight1Spawn = { 3200.00f, 573.211f, 57.1551f }; +static Location FlameRight1Direction = { 3289.28f, 573.211f, 57.1551f }; +static Location FlameRight2Spawn = { 3200.00f, 532.211f, 57.1551f }; +static Location FlameRight2Direction = { 3289.28f, 532.211f, 57.1551f }; +static Location FlameRight3Spawn = { 3200.00f, 491.211f, 57.1551f }; +static Location FlameRight3Direction = { 3289.28f, 491.211f, 57.1551f }; +static Location FlameLeft1Spawn = { 3289.28f, 511.711f, 57.1551f }; +static Location FlameLeft1Direction = { 3200.00f, 511.711f, 57.1551f }; +static Location FlameLeft2Spawn = { 3289.28f, 552.711f, 57.1551f }; +static Location FlameLeft2Direction = { 3200.00f, 552.711f, 57.1551f }; + +struct Waypoint { - float x, y, z; + float m_fX, m_fY, m_fZ; }; //each dragons special points. First where fly to before connect to connon, second where land point is. Waypoint m_aTene[]= { - {3212.854f, 575.597f, 109.856f}, //init - {3246.425f, 565.367f, 61.249f} //end + {3212.854f, 575.597f, 109.856f}, // init + {3246.425f, 565.367f, 61.249f} // end }; Waypoint m_aShad[]= @@ -162,54 +121,19 @@ Waypoint m_aVesp[]= {3227.268f, 533.238f, 59.995f} }; -#define MAX_WAYPOINT 6 -//points around raid "isle", counter clockwise. should probably be adjusted to be more alike -Waypoint m_aDragonCommon[MAX_WAYPOINT]= +enum SartharionEvents { - {3214.012f, 468.932f, 98.652f}, - {3244.950f, 468.427f, 98.652f}, - {3283.520f, 496.869f, 98.652f}, - {3287.316f, 555.875f, 98.652f}, - {3250.479f, 585.827f, 98.652f}, - {3209.969f, 566.523f, 98.652f} + EVENT_HARD_ENRAGE = 1, + EVENT_FLAME_TSUNAMI = 2, + EVENT_FLAME_BREATH = 3, + EVENT_TAIL_SWEEP = 4, + EVENT_CLEAVE_ATTACK = 5, + EVENT_LAVA_STRIKE = 6, + EVENT_CALL_TENEBRON = 7, + EVENT_CALL_SHADRON = 8, + EVENT_CALL_VESPERON = 9 }; -static Location FlameRight1Spawn = { 3200.00f, 573.211f, 57.1551f }; -static Location FlameRight1Direction = { 3289.28f, 573.211f, 57.1551f }; -static Location FlameRight2Spawn = { 3200.00f, 532.211f, 57.1551f }; -static Location FlameRight2Direction = { 3289.28f, 532.211f, 57.1551f }; -static Location FlameRight3Spawn = { 3200.00f, 491.211f, 57.1551f }; -static Location FlameRight3Direction = { 3289.28f, 491.211f, 57.1551f }; -static Location FlameLeft1Spawn = { 3289.28f, 511.711f, 57.1551f }; -static Location FlameLeft1Direction = { 3200.00f, 511.711f, 57.1551f }; -static Location FlameLeft2Spawn = { 3289.28f, 552.711f, 57.1551f }; -static Location FlameLeft2Direction = { 3200.00f, 552.711f, 57.1551f }; - -static Location AcolyteofShadron = { 3363.92f, 534.703f, 97.2683f }; -static Location AcolyteofShadron2 = { 3246.57f, 551.263f, 58.6164f }; -static Location AcolyteofVesperon = { 3145.68f, 520.71f, 89.7f }; -static Location AcolyteofVesperon2 = { 3246.57f, 551.263f, 58.6164f }; -Locations TwilightEggs[] = -{ - {3219.28f, 669.121f, 88.5549f}, - {3221.55f, 682.852f, 90.5361f}, - {3239.77f, 685.94f, 90.3168f}, - {3250.33f, 669.749f, 88.7637f}, - {3246.6f, 642.365f, 84.8752f}, - {3233.68f, 653.117f, 85.7051f} -}; -Locations TwilightEggsSarth[] = -{ - {3252.73f, 515.762f, 58.5501f}, - {3256.56f, 521.119f, 58.6061f}, - {3255.63f, 527.513f, 58.7568f}, - {3264.90f, 525.865f, 58.6436f}, - {3264.26f, 516.364f, 58.8011f}, - {3257.54f, 502.285f, 58.2077f} -}; - -#define TWILIGHT_ACHIEVEMENTS 1 - /*###### ## Boss Sartharion ######*/ @@ -219,169 +143,71 @@ class boss_sartharion : public CreatureScript public: boss_sartharion() : CreatureScript("boss_sartharion") { } - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new boss_sartharionAI(creature); - } - - struct boss_sartharionAI : public ScriptedAI + struct boss_sartharionAI : public BossAI { - boss_sartharionAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } - - InstanceScript* instance; - - bool m_bIsBerserk; - bool m_bIsSoftEnraged; - - uint32 m_uiEnrageTimer; - bool m_bIsHardEnraged; - - uint32 m_uiTenebronTimer; - uint32 m_uiShadronTimer; - uint32 m_uiVesperonTimer; - - uint32 m_uiFlameTsunamiTimer; - uint32 m_uiFlameBreathTimer; - uint32 m_uiTailSweepTimer; - uint32 m_uiCleaveTimer; - uint32 m_uiLavaStrikeTimer; - - bool m_bHasCalledTenebron; - bool m_bHasCalledShadron; - bool m_bHasCalledVesperon; - - uint8 drakeCount; + boss_sartharionAI(Creature* creature) : BossAI(creature, DATA_SARTHARION) { } void Reset() OVERRIDE { - m_bIsBerserk = false; - m_bIsSoftEnraged = false; - - m_uiEnrageTimer = 15*MINUTE*IN_MILLISECONDS; - m_bIsHardEnraged = false; - - m_uiTenebronTimer = 30000; - m_uiShadronTimer = 75000; - m_uiVesperonTimer = 120000; - - m_uiFlameTsunamiTimer = 30000; - m_uiFlameBreathTimer = 20000; - m_uiTailSweepTimer = 20000; - m_uiCleaveTimer = 7000; - m_uiLavaStrikeTimer = 5000; - - m_bHasCalledTenebron = false; - m_bHasCalledShadron = false; - m_bHasCalledVesperon = false; + _isBerserk = false; + _isSoftEnraged = false; + _isHardEnraged = false; + drakeCount = 0; if (me->HasAura(SPELL_TWILIGHT_REVENGE)) me->RemoveAurasDueToSpell(SPELL_TWILIGHT_REVENGE); me->SetHomePosition(3246.57f, 551.263f, 58.6164f, 4.66003f); - drakeCount = 0; - - // Drakes respawning system if (instance) { - Creature* pTenebron = Unit::GetCreature(*me, instance->GetData64(DATA_TENEBRON)); - Creature* pShadron = Unit::GetCreature(*me, instance->GetData64(DATA_SHADRON)); - Creature* pVesperon = Unit::GetCreature(*me, instance->GetData64(DATA_VESPERON)); - if (pTenebron) - { - pTenebron->SetHomePosition(3239.07f, 657.235f, 86.8775f, 4.74729f); - if (pTenebron->IsAlive()) - { - if (pTenebron->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - pTenebron->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - pTenebron->GetMotionMaster()->MoveTargetedHome(); - }else - { - if (instance->GetData(TYPE_TENEBRON_PREKILLED) == false) - { - pTenebron->Respawn(); - pTenebron->GetMotionMaster()->MoveTargetedHome(); - pTenebron->AI()->SetData(DATA_CAN_LOOT, 0); - } - } - } - if (pShadron) - { - pShadron->SetHomePosition(3363.06f, 525.28f, 98.362f, 4.76475f); - if (pShadron->IsAlive()) - { - if (pShadron->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - pShadron->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - pShadron->GetMotionMaster()->MoveTargetedHome(); - }else - { - if (instance->GetData(TYPE_SHADRON_PREKILLED) == false) - { - pShadron->Respawn(); - pShadron->GetMotionMaster()->MoveTargetedHome(); - pShadron->AI()->SetData(DATA_CAN_LOOT, 0); - } - } - } - if (pVesperon) - { - pVesperon->SetHomePosition(3145.68f, 520.71f, 89.7f, 4.64258f); - if (pVesperon->IsAlive()) - { - if (pVesperon->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - pVesperon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - pVesperon->GetMotionMaster()->MoveTargetedHome(); - }else - { - if (instance->GetData(TYPE_VESPERON_PREKILLED) == false) - { - pVesperon->Respawn(); - pVesperon->GetMotionMaster()->MoveTargetedHome(); - pVesperon->AI()->SetData(DATA_CAN_LOOT, 0); - } - } - } + DrakeRespawn(); + instance->SetBossState(DATA_PORTAL_OPEN, NOT_STARTED); } } void JustReachedHome() OVERRIDE { - if (instance) - instance->SetData(TYPE_SARTHARION_EVENT, NOT_STARTED); + _Reset(); } void EnterCombat(Unit* /*who*/) OVERRIDE { Talk(SAY_SARTHARION_AGGRO); + _EnterCombat(); DoZoneInCombat(); if (instance) - { - instance->SetData(TYPE_SARTHARION_EVENT, IN_PROGRESS); FetchDragons(); - } + + events.ScheduleEvent(EVENT_LAVA_STRIKE, 5000); + events.ScheduleEvent(EVENT_CLEAVE_ATTACK, 7000); + events.ScheduleEvent(EVENT_FLAME_BREATH, 20000); + events.ScheduleEvent(EVENT_TAIL_SWEEP, 20000); + events.ScheduleEvent(EVENT_FLAME_TSUNAMI, 30000); + events.ScheduleEvent(EVENT_CALL_TENEBRON, 30000); + events.ScheduleEvent(EVENT_CALL_SHADRON, 75000); + events.ScheduleEvent(EVENT_CALL_VESPERON, 120000); } void JustDied(Unit* /*killer*/) OVERRIDE { Talk(SAY_SARTHARION_DEATH); + _JustDied(); if (instance) { - Creature* pTenebron = Unit::GetCreature(*me, instance->GetData64(DATA_TENEBRON)); - Creature* pShadron = Unit::GetCreature(*me, instance->GetData64(DATA_SHADRON)); - Creature* pVesperon = Unit::GetCreature(*me, instance->GetData64(DATA_VESPERON)); - if (pTenebron && pTenebron->IsAlive()) - pTenebron->DisappearAndDie(); - if (pShadron && pShadron->IsAlive()) - pShadron->DisappearAndDie(); - if (pVesperon && pVesperon->IsAlive()) - pVesperon->DisappearAndDie(); - - instance->SetData(TYPE_SARTHARION_EVENT, DONE); + if (Creature* tenebron = Unit::GetCreature(*me, instance->GetData64(DATA_TENEBRON))) + if (tenebron->IsAlive()) + tenebron->DisappearAndDie(); + + if (Creature* shadron = Unit::GetCreature(*me, instance->GetData64(DATA_SHADRON))) + if (shadron->IsAlive()) + shadron->DisappearAndDie(); + + if (Creature* vesperon = Unit::GetCreature(*me, instance->GetData64(DATA_VESPERON))) + if (vesperon->IsAlive()) + vesperon->DisappearAndDie(); } } @@ -402,83 +228,140 @@ public: me->AddLootMode(LOOT_MODE_HARD_MODE_1); // Add 1st Drake loot mode } - uint32 GetData(uint32 type) const OVERRIDE + void DrakeRespawn() // Drakes respawning system { - if (type == TWILIGHT_ACHIEVEMENTS) - return drakeCount; + if (Creature* tenebron = Unit::GetCreature(*me, instance->GetData64(DATA_TENEBRON))) + { + tenebron->SetHomePosition(3239.07f, 657.235f, 86.8775f, 4.74729f); + if (tenebron->IsAlive()) + { + if (tenebron->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + tenebron->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + tenebron->GetMotionMaster()->MoveTargetedHome(); + } + else + { + if (instance->GetBossState(DATA_TENEBRON) != DONE) + { + tenebron->Respawn(); + tenebron->GetMotionMaster()->MoveTargetedHome(); + tenebron->AI()->SetData(DATA_CAN_LOOT, 0); + } + } + } - return 0; + if (Creature* shadron = Unit::GetCreature(*me, instance->GetData64(DATA_SHADRON))) + { + shadron->SetHomePosition(3363.06f, 525.28f, 98.362f, 4.76475f); + if (shadron->IsAlive()) + { + if (shadron->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + shadron->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + shadron->GetMotionMaster()->MoveTargetedHome(); + } + else + { + if (instance->GetBossState(DATA_SHADRON) != DONE) + { + shadron->Respawn(); + shadron->GetMotionMaster()->MoveTargetedHome(); + shadron->AI()->SetData(DATA_CAN_LOOT, 0); + } + } + } + + if (Creature* vesperon = Unit::GetCreature(*me, instance->GetData64(DATA_VESPERON))) + { + vesperon->SetHomePosition(3145.68f, 520.71f, 89.7f, 4.64258f); + if (vesperon->IsAlive()) + { + if (vesperon->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + vesperon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + vesperon->GetMotionMaster()->MoveTargetedHome(); + } + else + { + if (instance->GetBossState(DATA_VESPERON) != DONE) + { + vesperon->Respawn(); + vesperon->GetMotionMaster()->MoveTargetedHome(); + vesperon->AI()->SetData(DATA_CAN_LOOT, 0); + } + } + } } void FetchDragons() { - if (!instance) - return; - me->ResetLootMode(); drakeCount = 0; - Creature* pFetchTene = Unit::GetCreature(*me, instance->GetData64(DATA_TENEBRON)); - Creature* pFetchShad = Unit::GetCreature(*me, instance->GetData64(DATA_SHADRON)); - Creature* pFetchVesp = Unit::GetCreature(*me, instance->GetData64(DATA_VESPERON)); - //if at least one of the dragons are alive and are being called - bool bCanUseWill = false; + bool _canUseWill = false; - if (pFetchTene && pFetchTene->IsAlive() && !pFetchTene->GetVictim()) + if (Creature* fetchTene = Unit::GetCreature(*me, instance->GetData64(DATA_TENEBRON))) { - bCanUseWill = true; - if (!pFetchTene->IsInCombat()) + if (fetchTene->IsAlive() && !fetchTene->GetVictim()) { - DoCast(me, SPELL_POWER_OF_TENEBRON); - AddDrakeLootMode(); - ++drakeCount; - } - pFetchTene->GetMotionMaster()->MovePoint(POINT_ID_INIT, m_aTene[0].m_fX, m_aTene[0].m_fY, m_aTene[0].m_fZ); + _canUseWill = true; + if (!fetchTene->IsInCombat()) + { + DoCast(me, SPELL_POWER_OF_TENEBRON); + AddDrakeLootMode(); + ++drakeCount; + } + fetchTene->GetMotionMaster()->MovePoint(POINT_ID_INIT, m_aTene[0].m_fX, m_aTene[0].m_fY, m_aTene[0].m_fZ); - if (!pFetchTene->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - pFetchTene->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + if (!fetchTene->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + fetchTene->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } } - if (pFetchShad && pFetchShad->IsAlive() && !pFetchShad->GetVictim()) + if (Creature* fetchShad = Unit::GetCreature(*me, instance->GetData64(DATA_SHADRON))) { - bCanUseWill = true; - if (!pFetchShad->IsInCombat()) + if (fetchShad->IsAlive() && !fetchShad->GetVictim()) { - DoCast(me, SPELL_POWER_OF_SHADRON); - AddDrakeLootMode(); - ++drakeCount; - } - pFetchShad->GetMotionMaster()->MovePoint(POINT_ID_INIT, m_aShad[0].m_fX, m_aShad[0].m_fY, m_aShad[0].m_fZ); + _canUseWill = true; + if (!fetchShad->IsInCombat()) + { + DoCast(me, SPELL_POWER_OF_SHADRON); + AddDrakeLootMode(); + ++drakeCount; + } + fetchShad->GetMotionMaster()->MovePoint(POINT_ID_INIT, m_aShad[0].m_fX, m_aShad[0].m_fY, m_aShad[0].m_fZ); - if (!pFetchShad->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - pFetchShad->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + if (!fetchShad->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + fetchShad->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } } - if (pFetchVesp && pFetchVesp->IsAlive() && !pFetchVesp->GetVictim()) + if (Creature* fetchVesp = Unit::GetCreature(*me, instance->GetData64(DATA_VESPERON))) { - bCanUseWill = true; - if (!pFetchVesp->IsInCombat()) + if (fetchVesp && fetchVesp->IsAlive() && !fetchVesp->GetVictim()) { - DoCast(me, SPELL_POWER_OF_VESPERON); - AddDrakeLootMode(); - ++drakeCount; - } - pFetchVesp->GetMotionMaster()->MovePoint(POINT_ID_INIT, m_aVesp[0].m_fX, m_aVesp[0].m_fY, m_aVesp[0].m_fZ); + _canUseWill = true; + if (!fetchVesp->IsInCombat()) + { + DoCast(me, SPELL_POWER_OF_VESPERON); + AddDrakeLootMode(); + ++drakeCount; + } + fetchVesp->GetMotionMaster()->MovePoint(POINT_ID_INIT, m_aVesp[0].m_fX, m_aVesp[0].m_fY, m_aVesp[0].m_fZ); - if (!pFetchVesp->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - pFetchVesp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + if (!fetchVesp->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + fetchVesp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } } - if (bCanUseWill) + if (_canUseWill) DoCast(me, SPELL_WILL_OF_SARTHARION); } - void CallDragon(uint32 uiDataId) + void CallDragon(uint32 dataId) { if (instance) { - if (Creature* temp = Unit::GetCreature(*me, instance->GetData64(uiDataId))) + if (Creature* temp = Unit::GetCreature(*me, instance->GetData64(dataId))) { if (temp->IsAlive() && !temp->GetVictim()) { @@ -487,33 +370,41 @@ public: if (temp->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) temp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - int32 iTextId = 0; + int32 textId = 0; switch (temp->GetEntry()) { case NPC_TENEBRON: - iTextId = SAY_SARTHARION_CALL_TENEBRON; + textId = SAY_SARTHARION_CALL_TENEBRON; temp->AddAura(SPELL_POWER_OF_TENEBRON, temp); temp->GetMotionMaster()->MovePoint(POINT_ID_LAND, m_aTene[1].m_fX, m_aTene[1].m_fY, m_aTene[1].m_fZ); break; case NPC_SHADRON: - iTextId = SAY_SARTHARION_CALL_SHADRON; + textId = SAY_SARTHARION_CALL_SHADRON; temp->AddAura(SPELL_POWER_OF_SHADRON, temp); temp->GetMotionMaster()->MovePoint(POINT_ID_LAND, m_aShad[1].m_fX, m_aShad[1].m_fY, m_aShad[1].m_fZ); break; case NPC_VESPERON: - iTextId = SAY_SARTHARION_CALL_VESPERON; + textId = SAY_SARTHARION_CALL_VESPERON; temp->AddAura(SPELL_POWER_OF_VESPERON, temp); temp->GetMotionMaster()->MovePoint(POINT_ID_LAND, m_aVesp[1].m_fX, m_aVesp[1].m_fY, m_aVesp[1].m_fZ); break; } - Talk(iTextId); + Talk(textId); } } } } + uint32 GetData(uint32 type) const OVERRIDE + { + if (type == TWILIGHT_ACHIEVEMENTS) + return drakeCount; + + return 0; + } + void SendFlameTsunami() { if (Map* map = me->GetMap()) @@ -532,16 +423,16 @@ public: // FIXME: Frequency of the casts reduced to compensate 100% chance of spawning a Lava Blaze add void CastLavaStrikeOnTarget(Unit* target) { - std::list<Creature*> pFireCyclonesList; + std::list<Creature*> fireCyclonesList; Trinity::AllCreaturesOfEntryInRange checker(me, NPC_FIRE_CYCLONE, 200.0f); - Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(me, pFireCyclonesList, checker); + Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(me, fireCyclonesList, checker); me->VisitNearbyObject(200.0f, searcher); - if (pFireCyclonesList.empty()) + if (fireCyclonesList.empty()) return; - std::list<Creature*>::iterator itr = pFireCyclonesList.begin(); - uint32 rnd = rand()%pFireCyclonesList.size(); + std::list<Creature*>::iterator itr = fireCyclonesList.begin(); + uint32 rnd = rand()%fireCyclonesList.size(); for (uint32 i = 0; i < rnd; ++i) ++itr; @@ -549,1224 +440,121 @@ public: (*itr)->CastSpell(target, SPELL_LAVA_STRIKE, true); } - void UpdateAI(uint32 uiDiff) OVERRIDE + void UpdateAI(uint32 diff) OVERRIDE { - //Return since we have no target if (!UpdateVictim()) return; - Unit* pTene = Unit::GetUnit(*me, instance ? instance->GetData64(DATA_TENEBRON) : 0); - Unit* pShad = Unit::GetUnit(*me, instance ? instance->GetData64(DATA_SHADRON) : 0); - Unit* pVesp = Unit::GetUnit(*me, instance ? instance->GetData64(DATA_VESPERON) : 0); - - //spell will target dragons, if they are still alive at 35% - if (!m_bIsBerserk && !HealthAbovePct(35) - && ((pTene && pTene->IsAlive()) || (pShad && pShad->IsAlive()) || (pVesp && pVesp->IsAlive()))) - { - Talk(SAY_SARTHARION_BERSERK); - DoCast(me, SPELL_BERSERK); - m_bIsBerserk = true; - } + events.Update(diff); - //soft enrage - if (!m_bIsSoftEnraged && HealthBelowPct(10)) + while (uint32 eventId = events.ExecuteEvent()) { - // m_bIsSoftEnraged is used while determining Lava Strike cooldown. - m_bIsSoftEnraged = true; - } - - // hard enrage - if (!m_bIsHardEnraged) - { - if (m_uiEnrageTimer <= uiDiff) + switch (eventId) { - DoCast(me, SPELL_PYROBUFFET, true); - m_bIsHardEnraged = true; - } - else - m_uiEnrageTimer -= uiDiff; - } - - // flame tsunami - if (m_uiFlameTsunamiTimer <= uiDiff) - { - SendFlameTsunami(); - switch (urand(0, 1)) - { - case 0: - { - Creature* Right1 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameRight1Spawn.x, FlameRight1Spawn.y, FlameRight1Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000); - Creature* Right2 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameRight2Spawn.x, FlameRight2Spawn.y, FlameRight2Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000); - Creature* Right3 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameRight3Spawn.x, FlameRight3Spawn.y, FlameRight3Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000); - Right1->GetMotionMaster()->MovePoint(0, FlameRight1Direction.x, FlameRight1Direction.y, FlameRight1Direction.z); - Right2->GetMotionMaster()->MovePoint(0, FlameRight2Direction.x, FlameRight2Direction.y, FlameRight2Direction.z); - Right3->GetMotionMaster()->MovePoint(0, FlameRight3Direction.x, FlameRight3Direction.y, FlameRight3Direction.z); + case EVENT_HARD_ENRAGE: + if (!_isHardEnraged) + { + DoCast(me, SPELL_PYROBUFFET, true); + _isHardEnraged = true; + } break; - } - case 1: - { - Creature* Left1 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameLeft1Spawn.x, FlameLeft1Spawn.y, FlameLeft1Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000); - Creature* Left2 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameLeft2Spawn.x, FlameLeft2Spawn.y, FlameLeft2Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000); - Left1->GetMotionMaster()->MovePoint(0, FlameLeft1Direction.x, FlameLeft1Direction.y, FlameLeft1Direction.z); - Left2->GetMotionMaster()->MovePoint(0, FlameLeft2Direction.x, FlameLeft2Direction.y, FlameLeft2Direction.z); + case EVENT_FLAME_TSUNAMI: + SendFlameTsunami(); + switch (urand(0, 1)) + { + case 0: + { + if (Creature* right1 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameRight1Spawn.x, FlameRight1Spawn.y, FlameRight1Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000)) + right1->GetMotionMaster()->MovePoint(0, FlameRight1Direction.x, FlameRight1Direction.y, FlameRight1Direction.z); + if (Creature* right2 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameRight2Spawn.x, FlameRight2Spawn.y, FlameRight2Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000)) + right2->GetMotionMaster()->MovePoint(0, FlameRight2Direction.x, FlameRight2Direction.y, FlameRight2Direction.z); + if (Creature* right3 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameRight3Spawn.x, FlameRight3Spawn.y, FlameRight3Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000)) + right3->GetMotionMaster()->MovePoint(0, FlameRight3Direction.x, FlameRight3Direction.y, FlameRight3Direction.z); + break; + } + case 1: + { + if (Creature* left1 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameLeft1Spawn.x, FlameLeft1Spawn.y, FlameLeft1Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000)) + left1->GetMotionMaster()->MovePoint(0, FlameLeft1Direction.x, FlameLeft1Direction.y, FlameLeft1Direction.z); + if (Creature* left2 = me->SummonCreature(NPC_FLAME_TSUNAMI, FlameLeft2Spawn.x, FlameLeft2Spawn.y, FlameLeft2Spawn.z, 0, TEMPSUMMON_TIMED_DESPAWN, 12000)) + left2->GetMotionMaster()->MovePoint(0, FlameLeft2Direction.x, FlameLeft2Direction.y, FlameLeft2Direction.z); + break; + } + } + events.ScheduleEvent(EVENT_FLAME_TSUNAMI, 30000); break; - } - } - - m_uiFlameTsunamiTimer = 30000; - } - else - m_uiFlameTsunamiTimer -= uiDiff; - - // flame breath - if (m_uiFlameBreathTimer <= uiDiff) - { - Talk(SAY_SARTHARION_BREATH); - DoCastVictim(RAID_MODE(SPELL_FLAME_BREATH, SPELL_FLAME_BREATH_H)); - m_uiFlameBreathTimer = urand(25000, 35000); - } - else - m_uiFlameBreathTimer -= uiDiff; - - // Tail Sweep - if (m_uiTailSweepTimer <= uiDiff) - { - DoCastVictim(RAID_MODE(SPELL_TAIL_LASH, SPELL_TAIL_LASH_H)); - m_uiTailSweepTimer = urand(15000, 20000); - } - else - m_uiTailSweepTimer -= uiDiff; - - // Cleave - if (m_uiCleaveTimer <= uiDiff) - { - DoCastVictim(SPELL_CLEAVE); - m_uiCleaveTimer = urand(7000, 10000); - } - else - m_uiCleaveTimer -= uiDiff; - - // Lavas Strike - if (m_uiLavaStrikeTimer <= uiDiff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - CastLavaStrikeOnTarget(target); - - if (urand(0, 5) == 0) - Talk(SAY_SARTHARION_SPECIAL); - } - m_uiLavaStrikeTimer = (m_bIsSoftEnraged ? urand(1400, 2000) : urand(5000, 20000)); - } - else - m_uiLavaStrikeTimer -= uiDiff; - - // call tenebron - if (!m_bHasCalledTenebron && m_uiTenebronTimer <= uiDiff) - { - CallDragon(DATA_TENEBRON); - m_bHasCalledTenebron = true; - } - else - m_uiTenebronTimer -= uiDiff; - - // call shadron - if (!m_bHasCalledShadron && m_uiShadronTimer <= uiDiff) - { - CallDragon(DATA_SHADRON); - m_bHasCalledShadron = true; - } - else - m_uiShadronTimer -= uiDiff; - - // call vesperon - if (!m_bHasCalledVesperon && m_uiVesperonTimer <= uiDiff) - { - CallDragon(DATA_VESPERON); - m_bHasCalledVesperon = true; - } - else - m_uiVesperonTimer -= uiDiff; - - DoMeleeAttackIfReady(); - - EnterEvadeIfOutOfCombatArea(uiDiff); - } - }; - -}; - -enum TeneText -{ - SAY_TENEBRON_AGGRO = 0, - SAY_TENEBRON_SLAY = 1, - SAY_TENEBRON_DEATH = 2, - SAY_TENEBRON_BREATH = 3, - SAY_TENEBRON_RESPOND = 4, - SAY_TENEBRON_SPECIAL = 5 -}; - -enum ShadText -{ - SAY_SHADRON_AGGRO = 0, - SAY_SHADRON_SLAY = 1, - SAY_SHADRON_DEATH = 2, - SAY_SHADRON_BREATH = 3, - SAY_SHADRON_RESPOND = 4, - SAY_SHADRON_SPECIAL = 5 -}; - -enum VespText -{ - SAY_VESPERON_AGGRO = 0, - SAY_VESPERON_SLAY = 1, - SAY_VESPERON_DEATH = 2, - SAY_VESPERON_BREATH = 3, - SAY_VESPERON_RESPOND = 4, - SAY_VESPERON_SPECIAL = 5, -}; - -//to control each dragons common abilities -struct dummy_dragonAI : public ScriptedAI -{ - dummy_dragonAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } - - InstanceScript* instance; - - uint32 m_uiWaypointId; - uint32 m_uiMoveNextTimer; - int32 m_iPortalRespawnTime; - bool m_bCanMoveFree; - bool m_bCanLoot; - - void Reset() OVERRIDE - { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - - m_uiWaypointId = 0; - m_uiMoveNextTimer = 500; - m_iPortalRespawnTime = 30000; - m_bCanMoveFree = false; - m_bCanLoot = true; - } - - void SetData(uint32 type, uint32 value) OVERRIDE - { - if (type == DATA_CAN_LOOT) - m_bCanLoot = value; - } - - void MovementInform(uint32 uiType, uint32 uiPointId) OVERRIDE - { - if (!instance || uiType != POINT_MOTION_TYPE) - return; - -// debug_log("dummy_dragonAI: %s reached point %u", me->GetName(), uiPointId); - - //if healers messed up the raid and we was already initialized - if (instance->GetData(TYPE_SARTHARION_EVENT) != IN_PROGRESS) - { - EnterEvadeMode(); - return; - } - - //this is end, if we reach this, don't do much - if (uiPointId == POINT_ID_LAND) - { - me->GetMotionMaster()->Clear(); - me->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0, true)) - { - me->AddThreat(target, 1.0f); - me->Attack(target, true); - me->GetMotionMaster()->MoveChase(target); - } - - m_bCanMoveFree = false; - return; - } - - //get amount of common points - uint32 uiCommonWPCount = sizeof(m_aDragonCommon)/sizeof(Waypoint); - - //increase - m_uiWaypointId = uiPointId+1; - - //if we have reached a point bigger or equal to count, it mean we must reset to point 0 - if (m_uiWaypointId >= uiCommonWPCount) - { - if (!m_bCanMoveFree) - m_bCanMoveFree = true; - - m_uiWaypointId = 0; - } - - m_uiMoveNextTimer = 500; - } - - //used when open portal and spawn mobs in phase - void DoRaidWhisper(int32 iTextId) - { - Map* map = me->GetMap(); - - if (map && map->IsDungeon()) - { - Map::PlayerList const &PlayerList = map->GetPlayers(); - - if (!PlayerList.isEmpty()) - { - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - Talk(iTextId, i->GetSource()->GetGUID()); - } - } - } - - //"opens" the portal and does the "opening" whisper - void OpenPortal() - { - int32 iTextId = 0; - - //there are 4 portal spawn locations, each are expected to be spawned with negative spawntimesecs in database - - //using a grid search here seem to be more efficient than caching all four guids - //in instance script and calculate range to each. - GameObject* pPortal = me->FindNearestGameObject(GO_TWILIGHT_PORTAL, 50.0f); - - switch (me->GetEntry()) - { - case NPC_TENEBRON: - { - iTextId = WHISPER_HATCH_EGGS; - if (instance && !instance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) - { - for (uint32 i = 0; i < 6; ++i) - me->SummonCreature(NPC_TWILIGHT_EGG, TwilightEggs[i].x, TwilightEggs[i].y, TwilightEggs[i].z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); - } - else - { - for (uint32 i = 0; i < 6; ++i) - me->SummonCreature(NPC_SARTHARION_TWILIGHT_EGG, TwilightEggsSarth[i].x, TwilightEggsSarth[i].y, TwilightEggsSarth[i].z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); - } - break; - } - case NPC_SHADRON: - { - iTextId = WHISPER_OPEN_PORTAL; - if (instance && !instance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) - me->SummonCreature(NPC_ACOLYTE_OF_SHADRON, AcolyteofShadron.x, AcolyteofShadron.y, AcolyteofShadron.z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 28000); - else - me->SummonCreature(NPC_ACOLYTE_OF_SHADRON, AcolyteofShadron2.x, AcolyteofShadron2.y, AcolyteofShadron2.z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 28000); - - break; - } - case NPC_VESPERON: - { - iTextId = WHISPER_OPEN_PORTAL; - if (instance && !instance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) - { - if (Creature* Acolyte = me->SummonCreature(NPC_ACOLYTE_OF_VESPERON, AcolyteofVesperon.x, AcolyteofVesperon.y, AcolyteofVesperon.z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000)) - { - me->InterruptNonMeleeSpells(true); - Acolyte->InterruptNonMeleeSpells(true); - me->CastSpell(me, 32747, false); - } - } - else - { - if (Creature* Acolyte = me->SummonCreature(NPC_ACOLYTE_OF_VESPERON, AcolyteofVesperon2.x, AcolyteofVesperon2.y, AcolyteofVesperon2.z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000)) - { - me->InterruptNonMeleeSpells(true); - Acolyte->InterruptNonMeleeSpells(true); - me->CastSpell(me, 32747, false); - } - } - - break; - } - } - - DoRaidWhisper(iTextId); - - //By using SetRespawnTime() we will actually "spawn" the object with our defined time. - //Once time is up, portal will disappear again. - if (pPortal && !pPortal->isSpawned()) - pPortal->SetRespawnTime(m_iPortalRespawnTime); - - //Unclear what are expected to happen if one drake has a portal open already - //Refresh respawnTime so time again are set to 30secs? - } - - void JustDied(Unit* /*killer*/) OVERRIDE - { - if (!m_bCanLoot) - me->SetLootRecipient(NULL); - - int32 iTextId = 0; - uint32 uiSpellId = 0; - - switch (me->GetEntry()) - { - case NPC_TENEBRON: - iTextId = SAY_TENEBRON_DEATH; - uiSpellId = SPELL_POWER_OF_TENEBRON; - if (instance && instance->GetData(TYPE_SARTHARION_EVENT) != IN_PROGRESS) - instance->SetData(TYPE_TENEBRON_PREKILLED, 1); - break; - case NPC_SHADRON: - iTextId = SAY_SHADRON_DEATH; - uiSpellId = SPELL_POWER_OF_SHADRON; - if (instance && instance->GetData(TYPE_SARTHARION_EVENT) != IN_PROGRESS) - instance->SetData(TYPE_SHADRON_PREKILLED, 1); - if (Creature* pAcolyte = me->FindNearestCreature(NPC_ACOLYTE_OF_SHADRON, 100.0f)) - pAcolyte->Kill(pAcolyte); - break; - case NPC_VESPERON: - iTextId = SAY_VESPERON_DEATH; - uiSpellId = SPELL_POWER_OF_VESPERON; - if (instance && instance->GetData(TYPE_SARTHARION_EVENT) != IN_PROGRESS) - instance->SetData(TYPE_VESPERON_PREKILLED, 1); - if (Creature* pAcolyte = me->FindNearestCreature(NPC_ACOLYTE_OF_VESPERON, 100.0f)) - pAcolyte->Kill(pAcolyte); - break; - } - - Talk(iTextId); - - me->RemoveAurasDueToSpell(uiSpellId); - - if (instance) - { - instance->DoRemoveAurasDueToSpellOnPlayers(uiSpellId); - - // not if solo mini-boss fight - if (instance->GetData(TYPE_SARTHARION_EVENT) != IN_PROGRESS) - return; - - // Twilight Revenge to main boss - if (Unit* pSartharion = Unit::GetUnit(*me, instance->GetData64(DATA_SARTHARION))) - if (pSartharion->IsAlive()) - { - pSartharion->RemoveAurasDueToSpell(uiSpellId); - DoCast(pSartharion, SPELL_TWILIGHT_REVENGE, true); - } - } - } - - void UpdateAI(uint32 uiDiff) OVERRIDE - { - if (m_bCanMoveFree && m_uiMoveNextTimer) - { - if (m_uiMoveNextTimer <= uiDiff) - { - if (m_uiWaypointId < MAX_WAYPOINT) - me->GetMotionMaster()->MovePoint(m_uiWaypointId, - m_aDragonCommon[m_uiWaypointId].m_fX, m_aDragonCommon[m_uiWaypointId].m_fY, m_aDragonCommon[m_uiWaypointId].m_fZ); - -// debug_log("dummy_dragonAI: %s moving to point %u", me->GetName(), m_uiWaypointId); - m_uiMoveNextTimer = 0; - } - else - m_uiMoveNextTimer -= uiDiff; - } - } -}; - -/*###### -## Mob Tenebron -######*/ - -class npc_tenebron : public CreatureScript -{ -public: - npc_tenebron() : CreatureScript("npc_tenebron") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_tenebronAI(creature); - } - - struct npc_tenebronAI : public dummy_dragonAI - { - npc_tenebronAI(Creature* creature) : dummy_dragonAI(creature) { } - - uint32 m_uiShadowBreathTimer; - uint32 m_uiShadowFissureTimer; - uint32 m_uiHatchEggTimer; - - bool m_bHasPortalOpen; - - void Reset() OVERRIDE - { - dummy_dragonAI::Reset(); - - m_uiShadowBreathTimer = 20000; - m_uiShadowFissureTimer = 5000; - m_uiHatchEggTimer = 30000; - - m_bHasPortalOpen = false; - } - - void EnterCombat(Unit* /*who*/) OVERRIDE - { - Talk(SAY_TENEBRON_AGGRO); - DoZoneInCombat(); - } - - void KilledUnit(Unit* /*victim*/) OVERRIDE - { - Talk(SAY_TENEBRON_SLAY); - } - - void UpdateAI(uint32 uiDiff) OVERRIDE - { - //if no target, update dummy and return - if (!UpdateVictim()) - { - dummy_dragonAI::UpdateAI(uiDiff); - return; - } - - // shadow fissure - if (m_uiShadowFissureTimer <= uiDiff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, RAID_MODE(SPELL_SHADOW_FISSURE, SPELL_SHADOW_FISSURE)); - - m_uiShadowFissureTimer = urand(15000, 20000); - } - else - m_uiShadowFissureTimer -= uiDiff; - - // Hatch Egg - if (m_uiHatchEggTimer <= uiDiff) - { - OpenPortal(); - m_uiHatchEggTimer = 30000; - } - else - m_uiHatchEggTimer -= uiDiff; - - // shadow breath - if (m_uiShadowBreathTimer <= uiDiff) - { - Talk(SAY_TENEBRON_BREATH); - DoCastVictim(RAID_MODE(SPELL_SHADOW_BREATH, SPELL_SHADOW_BREATH_H)); - m_uiShadowBreathTimer = urand(20000, 25000); - } - else - m_uiShadowBreathTimer -= uiDiff; - - DoMeleeAttackIfReady(); - } - }; - -}; - -/*###### -## Mob Shadron -######*/ - -class npc_shadron : public CreatureScript -{ -public: - npc_shadron() : CreatureScript("npc_shadron") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_shadronAI(creature); - } - - struct npc_shadronAI : public dummy_dragonAI - { - npc_shadronAI(Creature* creature) : dummy_dragonAI(creature) { } - - uint32 m_uiShadowBreathTimer; - uint32 m_uiShadowFissureTimer; - uint32 m_uiAcolyteShadronTimer; - - bool m_bHasPortalOpen; - - void Reset() OVERRIDE - { - dummy_dragonAI::Reset(); - - m_uiShadowBreathTimer = 20000; - m_uiShadowFissureTimer = 5000; - m_uiAcolyteShadronTimer = 60000; - - if (me->HasAura(SPELL_TWILIGHT_TORMENT_VESP)) - me->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP); - - if (me->HasAura(SPELL_GIFT_OF_TWILIGTH_SHA)) - me->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SHA); - - m_bHasPortalOpen = false; - } - - void EnterCombat(Unit* /*who*/) OVERRIDE - { - Talk(SAY_SHADRON_AGGRO); - DoZoneInCombat(); - } - - void KilledUnit(Unit* /*victim*/) OVERRIDE - { - Talk(SAY_SHADRON_SLAY); - } - - void UpdateAI(uint32 uiDiff) OVERRIDE - { - //if no target, update dummy and return - if (!UpdateVictim()) - { - dummy_dragonAI::UpdateAI(uiDiff); - return; - } - - // shadow fissure - if (m_uiShadowFissureTimer <= uiDiff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, RAID_MODE(SPELL_SHADOW_FISSURE, SPELL_SHADOW_FISSURE_H)); - - m_uiShadowFissureTimer = urand(15000, 20000); - } - else - m_uiShadowFissureTimer -= uiDiff; - - // Portal Event - if (m_uiAcolyteShadronTimer <= uiDiff) - { - if (m_bHasPortalOpen) - m_uiAcolyteShadronTimer = 10000; - else - { - if (me->HasAura(SPELL_GIFT_OF_TWILIGTH_SHA)) - return; - - OpenPortal(); - m_bHasPortalOpen = true; - m_uiAcolyteShadronTimer = urand(60000, 65000); - } - } - else - m_uiAcolyteShadronTimer -= uiDiff; - - // shadow breath - if (m_uiShadowBreathTimer <= uiDiff) - { - Talk(SAY_SHADRON_BREATH); - DoCastVictim(RAID_MODE(SPELL_SHADOW_BREATH, SPELL_SHADOW_BREATH_H)); - m_uiShadowBreathTimer = urand(20000, 25000); - } - else - m_uiShadowBreathTimer -= uiDiff; - - DoMeleeAttackIfReady(); - } - }; - -}; - -/*###### -## Mob Vesperon -######*/ - -class npc_vesperon : public CreatureScript -{ -public: - npc_vesperon() : CreatureScript("npc_vesperon") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_vesperonAI(creature); - } - - struct npc_vesperonAI : public dummy_dragonAI - { - npc_vesperonAI(Creature* creature) : dummy_dragonAI(creature) { } - - uint32 m_uiShadowBreathTimer; - uint32 m_uiShadowFissureTimer; - uint32 m_uiAcolyteVesperonTimer; - - bool m_bHasPortalOpen; - - void Reset() OVERRIDE - { - dummy_dragonAI::Reset(); - - m_uiShadowBreathTimer = 20000; - m_uiShadowFissureTimer = 5000; - m_uiAcolyteVesperonTimer = 60000; - - m_bHasPortalOpen = false; - } - - void EnterCombat(Unit* /*who*/) OVERRIDE - { - Talk(SAY_VESPERON_AGGRO); - DoZoneInCombat(); - } - - void KilledUnit(Unit* /*victim*/) OVERRIDE - { - Talk(SAY_VESPERON_SLAY); - } - - void UpdateAI(uint32 uiDiff) OVERRIDE - { - //if no target, update dummy and return - if (!UpdateVictim()) - { - dummy_dragonAI::UpdateAI(uiDiff); - return; - } - - // shadow fissure - if (m_uiShadowFissureTimer <= uiDiff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, RAID_MODE(SPELL_SHADOW_FISSURE, SPELL_SHADOW_FISSURE_H)); - - m_uiShadowFissureTimer = urand(15000, 20000); - } - else - m_uiShadowFissureTimer -= uiDiff; - - // Portal Event - if (m_uiAcolyteVesperonTimer <= uiDiff) - { - if (m_bHasPortalOpen) - m_uiAcolyteVesperonTimer = 10000; - else - { - OpenPortal(); - DoCastVictim(SPELL_TWILIGHT_TORMENT_VESP); - m_uiAcolyteVesperonTimer = urand(60000, 70000); - } - } - else - m_uiAcolyteVesperonTimer -= uiDiff; - - // shadow breath - if (m_uiShadowBreathTimer <= uiDiff) - { - Talk(SAY_VESPERON_BREATH); - DoCastVictim(RAID_MODE(SPELL_SHADOW_BREATH, SPELL_SHADOW_BREATH_H)); - m_uiShadowBreathTimer = urand(20000, 25000); - } - else - m_uiShadowBreathTimer -= uiDiff; - - DoMeleeAttackIfReady(); - } - }; - -}; - -/*###### -## Mob Acolyte of Shadron -######*/ - -class npc_acolyte_of_shadron : public CreatureScript -{ -public: - npc_acolyte_of_shadron() : CreatureScript("npc_acolyte_of_shadron") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_acolyte_of_shadronAI(creature); - } - - struct npc_acolyte_of_shadronAI : public ScriptedAI - { - npc_acolyte_of_shadronAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } - - InstanceScript* instance; - uint32 uiDespawnTimer; - - void Reset() OVERRIDE - { - uiDespawnTimer = 28000; - if (instance) - { - Creature* target = NULL; - //if not solo figth, buff main boss, else place debuff on mini-boss. both spells TARGET_SCRIPT - if (instance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) - { - target = Unit::GetCreature((*me), instance->GetData64(DATA_SARTHARION)); - if (target) - target->AddAura(SPELL_GIFT_OF_TWILIGTH_SAR, target); - } - else - { - target = Unit::GetCreature((*me), instance->GetData64(DATA_SHADRON)); - if (target) - target->AddAura(SPELL_GIFT_OF_TWILIGTH_SHA, target); - } - } - - me->AddAura(SPELL_TWILIGHT_SHIFT_ENTER, me); - } - - void JustDied(Unit* /*killer*/) OVERRIDE - { - if (instance) - { - Creature* Shadron = instance->instance->GetCreature(instance->GetData64(DATA_SHADRON)); - if (Shadron) - { - (CAST_AI(npc_shadron::npc_shadronAI, Shadron->AI()))->m_bHasPortalOpen = false; - } - - Creature* pDebuffTarget = NULL; - Map* map = me->GetMap(); - if (map->IsDungeon()) - { - Map::PlayerList const &PlayerList = map->GetPlayers(); - - if (PlayerList.isEmpty()) - return; - - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - { - if (i->GetSource()->IsAlive() && i->GetSource()->HasAura(SPELL_TWILIGHT_SHIFT, 0) && !i->GetSource()->GetVictim()) + case EVENT_FLAME_BREATH: + Talk(SAY_SARTHARION_BREATH); + DoCastVictim(RAID_MODE(SPELL_FLAME_BREATH, SPELL_FLAME_BREATH_H)); + events.ScheduleEvent(EVENT_FLAME_BREATH, urand(25000, 35000)); + break; + case EVENT_TAIL_SWEEP: + DoCastVictim(RAID_MODE(SPELL_TAIL_LASH, SPELL_TAIL_LASH_H)); + events.ScheduleEvent(EVENT_TAIL_SWEEP, urand(15000, 20000)); + break; + case EVENT_CLEAVE_ATTACK: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE_ATTACK, urand(7000, 10000)); + break; + case EVENT_LAVA_STRIKE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) { - i->GetSource()->CastSpell(i->GetSource(), SPELL_TWILIGHT_SHIFT_REMOVAL_ALL, true); - i->GetSource()->CastSpell(i->GetSource(), SPELL_TWILIGHT_RESIDUE, true); - i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_SHIFT); - i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_SHIFT_ENTER); + CastLavaStrikeOnTarget(target); + if (urand(0, 5) == 0) + Talk(SAY_SARTHARION_SPECIAL); } - } + events.ScheduleEvent(EVENT_LAVA_STRIKE, (_isSoftEnraged ? urand(1400, 2000) : urand(5000, 20000))); + break; + case EVENT_CALL_TENEBRON: + CallDragon(DATA_TENEBRON); + break; + case EVENT_CALL_SHADRON: + CallDragon(DATA_SHADRON); + break; + case EVENT_CALL_VESPERON: + CallDragon(DATA_VESPERON); + break; + default: + break; } - - //not solo fight, so main boss has deduff - pDebuffTarget = instance->instance->GetCreature(instance->GetData64(DATA_SARTHARION)); - if (pDebuffTarget && pDebuffTarget->IsAlive() && pDebuffTarget->HasAura(SPELL_GIFT_OF_TWILIGTH_SAR)) - pDebuffTarget->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SAR); - - //event not in progress, then solo fight and must remove debuff mini-boss - pDebuffTarget = instance->instance->GetCreature(instance->GetData64(DATA_SHADRON)); - if (pDebuffTarget && pDebuffTarget->IsAlive() && pDebuffTarget->HasAura(SPELL_GIFT_OF_TWILIGTH_SHA)) - pDebuffTarget->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SHA); } - } - void UpdateAI(uint32 uiDiff) OVERRIDE - { - if (uiDespawnTimer < uiDiff) + // At 35% spell will target dragons, if they are still alive. + if (!_isBerserk && !HealthAbovePct(35)) { - me->SetVisible(false); - me->Kill(me); - uiDespawnTimer = 28000; - return; - }else uiDespawnTimer -= uiDiff; - - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); - } - }; - -}; - -/*###### -## Mob Acolyte of Vesperon -######*/ - -class npc_acolyte_of_vesperon : public CreatureScript -{ -public: - npc_acolyte_of_vesperon() : CreatureScript("npc_acolyte_of_vesperon") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_acolyte_of_vesperonAI(creature); - } - - struct npc_acolyte_of_vesperonAI : public ScriptedAI - { - npc_acolyte_of_vesperonAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } - - InstanceScript* instance; - uint32 uiDespawnTimer; - - void Reset() OVERRIDE - { - uiDespawnTimer = 28000; - if (instance) - me->AddAura(SPELL_TWILIGHT_SHIFT_ENTER, me); - DoCast(me, SPELL_TWILIGHT_TORMENT_VESP_ACO); - } - - void JustDied(Unit* /*killer*/) OVERRIDE - { - me->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP_ACO); - - // remove twilight torment on Vesperon - if (instance) - { - Creature* pVesperon = instance->instance->GetCreature(instance->GetData64(DATA_VESPERON)); - if (pVesperon) - (CAST_AI(npc_vesperon::npc_vesperonAI, pVesperon->AI()))->m_bHasPortalOpen = false; - - if (pVesperon && pVesperon->IsAlive() && pVesperon->HasAura(SPELL_TWILIGHT_TORMENT_VESP)) - pVesperon->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP); - - Map* map = me->GetMap(); - if (map->IsDungeon()) + if (instance->GetBossState(DATA_TENEBRON) != DONE || instance->GetBossState(DATA_SHADRON) != DONE || instance->GetBossState(DATA_VESPERON) != DONE) { - Map::PlayerList const &PlayerList = map->GetPlayers(); - - if (PlayerList.isEmpty()) - return; - - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - { - if (i->GetSource()->IsAlive() && i->GetSource()->HasAura(SPELL_TWILIGHT_SHIFT, 0) && !i->GetSource()->GetVictim()) - { - i->GetSource()->CastSpell(i->GetSource(), SPELL_TWILIGHT_SHIFT_REMOVAL_ALL, true); - i->GetSource()->CastSpell(i->GetSource(), SPELL_TWILIGHT_RESIDUE, true); - i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_SHIFT); - i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_SHIFT_ENTER); - } - if (i->GetSource()->IsAlive() && i->GetSource()->HasAura(SPELL_TWILIGHT_TORMENT_VESP, 0) && !i->GetSource()->GetVictim()) - i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP); - } + Talk(SAY_SARTHARION_BERSERK); + DoCast(me, SPELL_BERSERK); + _isBerserk = true; } - - instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_VESP_ACO); - instance->DoRemoveAurasDueToSpellOnPlayers(57935); - instance->DoRemoveAurasDueToSpellOnPlayers(58835); // Components of spell Twilight Torment } - } - void UpdateAI(uint32 uiDiff) OVERRIDE - { - if (uiDespawnTimer < uiDiff) + // Soft Enrage used while determining Lava Strike cooldown. + if (!_isSoftEnraged && HealthBelowPct(10)) { - me->SetVisible(false); - me->Kill(me); - uiDespawnTimer = 28000; - return; - }else uiDespawnTimer -= uiDiff; - - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); - } - }; - -}; - -/*###### -## Mob Twilight Eggs -######*/ - -class npc_twilight_eggs : public CreatureScript -{ -public: - npc_twilight_eggs() : CreatureScript("npc_twilight_eggs") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_twilight_eggsAI(creature); - } - - struct npc_twilight_eggsAI : public ScriptedAI - { - npc_twilight_eggsAI(Creature* creature) : ScriptedAI(creature) - { - SetCombatMovement(false); - instance = creature->GetInstanceScript(); - } - - uint32 m_uiFadeArmorTimer; - uint32 m_uiHatchEggTimer; - - InstanceScript* instance; - - void Reset() OVERRIDE - { - if (instance) - me->AddAura(SPELL_TWILIGHT_SHIFT_ENTER, me); - m_uiFadeArmorTimer = 1000; - m_uiHatchEggTimer = 20000; - } - - void SpawnWhelps() - { - me->RemoveAllAuras(); - - if (!instance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS) - me->SummonCreature(NPC_TWILIGHT_WHELP, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); - else - me->SummonCreature(NPC_SHARTHARION_TWILIGHT_WHELP, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); - me->DealDamage(me, me->GetHealth()); - } - - void JustSummoned(Creature* who) OVERRIDE - { - who->SetInCombatWithZone(); - } - - void UpdateAI(uint32 uiDiff) OVERRIDE - { - if (m_uiHatchEggTimer <= uiDiff) - { - Creature* Tenebron = instance->instance->GetCreature(instance->GetData64(DATA_TENEBRON)); - if (Tenebron) - (CAST_AI(npc_tenebron::npc_tenebronAI, Tenebron->AI()))->m_bHasPortalOpen = false; - SpawnWhelps(); + _isSoftEnraged = true; } - else - m_uiHatchEggTimer -= uiDiff; - } - - void AttackStart(Unit* /*who*/) OVERRIDE { } - void MoveInLineOfSight(Unit* /*who*/) OVERRIDE { } - - }; - -}; - -/*###### -## Mob Flame Tsunami -######*/ -class npc_flame_tsunami : public CreatureScript -{ -public: - npc_flame_tsunami() : CreatureScript("npc_flame_tsunami") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_flame_tsunamiAI(creature); - } - - struct npc_flame_tsunamiAI : public ScriptedAI - { - npc_flame_tsunamiAI(Creature* creature) : ScriptedAI(creature) - { - me->SetDisplayId(11686); - me->AddAura(SPELL_FLAME_TSUNAMI, me); - } - - uint32 Tsunami_Timer; - uint32 TsunamiBuff_timer; - uint32 entry; - - void Reset() OVERRIDE - { - me->SetReactState(REACT_PASSIVE); - Tsunami_Timer = 100; - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - TsunamiBuff_timer = 1000; - entry = 0; - } - - void UpdateAI(uint32 diff) OVERRIDE - { - if (Tsunami_Timer <= diff) - { - DoCast(me, SPELL_FLAME_TSUNAMI_DMG_AURA); - Tsunami_Timer = 500; - }else Tsunami_Timer -= diff; - - if (TsunamiBuff_timer <= diff) - { - if (Unit* LavaBlaze = GetClosestCreatureWithEntry(me, NPC_LAVA_BLAZE, 10.0f, true)) - LavaBlaze->CastSpell(LavaBlaze, SPELL_FLAME_TSUNAMI_BUFF, true); - TsunamiBuff_timer = 1000; - }else TsunamiBuff_timer -= diff; - } - }; - -}; - -// Twilight Fissure -class npc_twilight_fissure : public CreatureScript -{ -public: - npc_twilight_fissure() : CreatureScript("npc_twilight_fissure") { } - - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return new npc_twilight_fissureAI(creature); - } - - struct npc_twilight_fissureAI : public ScriptedAI - { - npc_twilight_fissureAI(Creature* creature) : ScriptedAI(creature) - { - SetCombatMovement(false); - } - uint32 VoidBlast_Timer; + DoMeleeAttackIfReady(); - void Reset() OVERRIDE - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->AddAura( 46265, me ); // Wrong, can't find proper visual - me->AddAura( 69422, me ); - VoidBlast_Timer = 5000; + EnterEvadeIfOutOfCombatArea(diff); } - void UpdateAI(uint32 diff) OVERRIDE - { - if (VoidBlast_Timer <= diff) - { - DoCastAOE(RAID_MODE(SPELL_VOID_BLAST, SPELL_VOID_BLAST_H)); - ////twilight realm - //DoCastVictim(57620, true); - //DoCastVictim(57874, true); - VoidBlast_Timer = 9000; - me->RemoveAllAuras(); - me->Kill(me); - } else VoidBlast_Timer -= diff; - } + private: + bool _isBerserk; + bool _isSoftEnraged; + bool _isHardEnraged; + uint8 drakeCount; }; -}; - -/*###### -## Mob Twilight Whelps -######*/ - -class npc_twilight_whelp : public CreatureScript -{ -public: - npc_twilight_whelp() : CreatureScript("npc_twilight_whelp") { } - CreatureAI* GetAI(Creature* creature) const OVERRIDE { - return new npc_twilight_whelpAI(creature); + return new boss_sartharionAI(creature); } - - struct npc_twilight_whelpAI : public ScriptedAI - { - npc_twilight_whelpAI(Creature* creature) : ScriptedAI(creature) - { - Reset(); - } - - uint32 m_uiFadeArmorTimer; - - void Reset() OVERRIDE - { - me->RemoveAllAuras(); - me->SetInCombatWithZone(); - m_uiFadeArmorTimer = 1000; - } - - void UpdateAI(uint32 uiDiff) OVERRIDE - { - //Return since we have no target - if (!UpdateVictim()) - return; - - // twilight torment - if (m_uiFadeArmorTimer <= uiDiff) - { - DoCastVictim(SPELL_FADE_ARMOR); - m_uiFadeArmorTimer = urand(5000, 10000); - } - else - m_uiFadeArmorTimer -= uiDiff; - - DoMeleeAttackIfReady(); - } - }; - -}; - -class achievement_twilight_assist : public AchievementCriteriaScript -{ - public: - achievement_twilight_assist() : AchievementCriteriaScript("achievement_twilight_assist") - { - } - - bool OnCheck(Player* /*player*/, Unit* target) OVERRIDE - { - if (!target) - return false; - - if (Creature* Sartharion = target->ToCreature()) - if (Sartharion->AI()->GetData(TWILIGHT_ACHIEVEMENTS) >= 1) - return true; - - return false; - } -}; - -class achievement_twilight_duo : public AchievementCriteriaScript -{ - public: - achievement_twilight_duo() : AchievementCriteriaScript("achievement_twilight_duo") - { - } - - bool OnCheck(Player* /*player*/, Unit* target) OVERRIDE - { - if (!target) - return false; - - if (Creature* Sartharion = target->ToCreature()) - if (Sartharion->AI()->GetData(TWILIGHT_ACHIEVEMENTS) >= 2) - return true; - - return false; - } -}; - -class achievement_twilight_zone : public AchievementCriteriaScript -{ - public: - achievement_twilight_zone() : AchievementCriteriaScript("achievement_twilight_zone") - { - } - - bool OnCheck(Player* /*player*/, Unit* target) OVERRIDE - { - if (!target) - return false; - - if (Creature* Sartharion = target->ToCreature()) - if (Sartharion->AI()->GetData(TWILIGHT_ACHIEVEMENTS) == 3) - return true; - - return false; - } }; void AddSC_boss_sartharion() { new boss_sartharion(); - new npc_vesperon(); - new npc_shadron(); - new npc_tenebron(); - new npc_acolyte_of_shadron(); - new npc_acolyte_of_vesperon(); - new npc_twilight_eggs(); - new npc_flame_tsunami(); - new npc_twilight_fissure(); - new npc_twilight_whelp(); - new achievement_twilight_assist(); - new achievement_twilight_duo(); - new achievement_twilight_zone(); } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp index ace2258ac9f..ad1346e7f37 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/instance_obsidian_sanctum.cpp @@ -19,8 +19,6 @@ #include "InstanceScript.h" #include "obsidian_sanctum.h" -#define MAX_ENCOUNTER 1 - /* Obsidian Sanctum encounters: 0 - Sartharion */ @@ -28,48 +26,18 @@ class instance_obsidian_sanctum : public InstanceMapScript { public: - instance_obsidian_sanctum() : InstanceMapScript("instance_obsidian_sanctum", 615) { } - - InstanceScript* GetInstanceScript(InstanceMap* map) const OVERRIDE - { - return new instance_obsidian_sanctum_InstanceMapScript(map); - } + instance_obsidian_sanctum() : InstanceMapScript(OSScriptName, 615) { } struct instance_obsidian_sanctum_InstanceMapScript : public InstanceScript { instance_obsidian_sanctum_InstanceMapScript(Map* map) : InstanceScript(map) { } - uint32 m_auiEncounter[MAX_ENCOUNTER]; - uint64 m_uiSartharionGUID; - uint64 m_uiTenebronGUID; - uint64 m_uiShadronGUID; - uint64 m_uiVesperonGUID; - - bool m_bTenebronKilled; - bool m_bShadronKilled; - bool m_bVesperonKilled; - void Initialize() OVERRIDE { - memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); - - m_uiSartharionGUID = 0; - m_uiTenebronGUID = 0; - m_uiShadronGUID = 0; - m_uiVesperonGUID = 0; - - m_bTenebronKilled = false; - m_bShadronKilled = false; - m_bVesperonKilled = false; - } - - bool IsEncounterInProgress() const OVERRIDE - { - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - if (m_auiEncounter[i] == IN_PROGRESS) - return true; - - return false; + sartharionGUID = 0; + tenebronGUID = 0; + shadronGUID = 0; + vesperonGUID = 0; } void OnCreatureCreate(Creature* creature) OVERRIDE @@ -77,68 +45,113 @@ public: switch (creature->GetEntry()) { case NPC_SARTHARION: - m_uiSartharionGUID = creature->GetGUID(); + sartharionGUID = creature->GetGUID(); break; - //three dragons below set to active state once created. - //we must expect bigger raid to encounter main boss, and then three dragons must be active due to grid differences + // Three dragons below set to active state once created. + // We must expect bigger raid to encounter main boss, and then three dragons must be active due to grid differences case NPC_TENEBRON: - m_uiTenebronGUID = creature->GetGUID(); + tenebronGUID = creature->GetGUID(); creature->setActive(true); break; case NPC_SHADRON: - m_uiShadronGUID = creature->GetGUID(); + shadronGUID = creature->GetGUID(); creature->setActive(true); break; case NPC_VESPERON: - m_uiVesperonGUID = creature->GetGUID(); + vesperonGUID = creature->GetGUID(); creature->setActive(true); break; } } - void SetData(uint32 uiType, uint32 uiData) OVERRIDE - { - if (uiType == TYPE_SARTHARION_EVENT) - m_auiEncounter[0] = uiData; - else if (uiType == TYPE_TENEBRON_PREKILLED) - m_bTenebronKilled = true; - else if (uiType == TYPE_SHADRON_PREKILLED) - m_bShadronKilled = true; - else if (uiType == TYPE_VESPERON_PREKILLED) - m_bVesperonKilled = true; - } - - uint32 GetData(uint32 uiType) const OVERRIDE + bool SetBossState(uint32 type, EncounterState state) OVERRIDE { - if (uiType == TYPE_SARTHARION_EVENT) - return m_auiEncounter[0]; - else if (uiType == TYPE_TENEBRON_PREKILLED) - return m_bTenebronKilled; - else if (uiType == TYPE_SHADRON_PREKILLED) - return m_bShadronKilled; - else if (uiType == TYPE_VESPERON_PREKILLED) - return m_bVesperonKilled; + if (!InstanceScript::SetBossState(type, state)) + return false; - return 0; + switch (type) + { + case DATA_SARTHARION: + case DATA_TENEBRON: + case DATA_SHADRON: + case DATA_VESPERON: + break; + default: + break; + } + return true; } - uint64 GetData64(uint32 uiData) const OVERRIDE + uint64 GetData64(uint32 Data) const OVERRIDE { - switch (uiData) + switch (Data) { case DATA_SARTHARION: - return m_uiSartharionGUID; + return sartharionGUID; case DATA_TENEBRON: - return m_uiTenebronGUID; + return tenebronGUID; case DATA_SHADRON: - return m_uiShadronGUID; + return shadronGUID; case DATA_VESPERON: - return m_uiVesperonGUID; + return vesperonGUID; } return 0; } + + std::string GetSaveData() OVERRIDE + { + OUT_SAVE_INST_DATA; + + std::ostringstream saveStream; + saveStream << "O S " << GetBossSaveData(); + + OUT_SAVE_INST_DATA_COMPLETE; + return saveStream.str(); + } + + void Load(const char* str) OVERRIDE + { + if (!str) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(str); + + char dataHead1, dataHead2; + + std::istringstream loadStream(str); + loadStream >> dataHead1 >> dataHead2; + + if (dataHead1 == 'O' && dataHead2 == 'S') + { + for (uint32 i = 0; i < EncounterCount; ++i) + { + uint32 tmpState; + loadStream >> tmpState; + if (tmpState == IN_PROGRESS || tmpState > SPECIAL) + tmpState = NOT_STARTED; + SetBossState(i, EncounterState(tmpState)); + } + } + else + OUT_LOAD_INST_DATA_FAIL; + + OUT_LOAD_INST_DATA_COMPLETE; + } + + protected: + uint64 sartharionGUID; + uint64 tenebronGUID; + uint64 shadronGUID; + uint64 vesperonGUID; }; + InstanceScript* GetInstanceScript(InstanceMap* map) const OVERRIDE + { + return new instance_obsidian_sanctum_InstanceMapScript(map); + } }; void AddSC_instance_obsidian_sanctum() diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp new file mode 100644 index 00000000000..5fc4869c4af --- /dev/null +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp @@ -0,0 +1,1159 @@ +/* + * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "Cell.h" +#include "CellImpl.h" +#include "obsidian_sanctum.h" + +enum Enums +{ + WHISPER_HATCH_EGGS = 6, + WHISPER_OPEN_PORTAL = 6, // whisper, shared by two dragons + + //Mini bosses common spells + SPELL_TWILIGHT_RESIDUE = 61885, // makes immune to shadow damage, applied when leave phase + + //Miniboses (Vesperon, Shadron, Tenebron) + SPELL_SHADOW_BREATH_H = 59126, // Inflicts 8788 to 10212 Fire damage to enemies in a cone in front of the caster. + SPELL_SHADOW_BREATH = 57570, // Inflicts 6938 to 8062 Fire damage to enemies in a cone in front of the caster. + + SPELL_SHADOW_FISSURE_H = 59127, // Deals 9488 to 13512 Shadow damage to any enemy within the Shadow fissure after 5 sec. + SPELL_SHADOW_FISSURE = 57579, // Deals 6188 to 8812 Shadow damage to any enemy within the Shadow fissure after 5 sec. + + //Vesperon + //In portal is a disciple, when disciple killed remove Power_of_vesperon, portal open multiple times + NPC_ACOLYTE_OF_VESPERON = 31219, // Acolyte of Vesperon + SPELL_POWER_OF_VESPERON = 61251, // Vesperon's presence decreases the maximum health of all enemies by 25%. + SPELL_TWILIGHT_TORMENT_VESP = 57948, // (Shadow only) trigger 57935 then 57988 + SPELL_TWILIGHT_TORMENT_VESP_ACO = 58853, // (Fire and Shadow) trigger 58835 then 57988 + + //Shadron + //In portal is a disciple, when disciple killed remove Power_of_vesperon, portal open multiple times + NPC_ACOLYTE_OF_SHADRON = 31218, // Acolyte of Shadron + SPELL_POWER_OF_SHADRON = 58105, // Shadron's presence increases Fire damage taken by all enemies by 100%. + SPELL_GIFT_OF_TWILIGTH_SHA = 57835, // TARGET_SCRIPT shadron + SPELL_GIFT_OF_TWILIGTH_SAR = 58766, // TARGET_SCRIPT sartharion + SPELL_VOID_BLAST = 57581, // Twilight Fissure + SPELL_VOID_BLAST_H = 59128, + + //Tenebron + //in the portal spawns 6 eggs, if not killed in time (approx. 20s) they will hatch, whelps can cast 60708 + SPELL_POWER_OF_TENEBRON = 61248, // Tenebron's presence increases Shadow damage taken by all enemies by 100%. + //Tenebron, dummy spell + SPELL_SUMMON_TWILIGHT_WHELP = 58035, // doesn't work, will spawn NPC_TWILIGHT_WHELP + SPELL_SUMMON_SARTHARION_TWILIGHT_WHELP = 58826, // doesn't work, will spawn NPC_SHARTHARION_TWILIGHT_WHELP + SPELL_TWILIGHT_REVENGE = 60639, + SPELL_HATCH_EGGS_H = 59189, + SPELL_HATCH_EGGS = 58542, + SPELL_HATCH_EGGS_EFFECT_H = 59190, + SPELL_HATCH_EGGS_EFFECT = 58685, + NPC_TWILIHT_WHELP = 31214, + NPC_TWILIGHT_EGG = 30882, + NPC_SARTHARION_TWILIGHT_EGG = 31204, + + SPELL_TWILIGHT_SHIFT_ENTER = 57620, // enter phase. Player get this when click GO + SPELL_TWILIGHT_SHIFT = 57874, // Twilight Shift Aura + SPELL_TWILIGHT_SHIFT_REMOVAL = 61187, // leave phase + SPELL_TWILIGHT_SHIFT_REMOVAL_ALL = 61190, // leave phase (probably version to make all leave) + + //Whelps + NPC_TWILIGHT_WHELP = 30890, + NPC_SHARTHARION_TWILIGHT_WHELP = 31214, + SPELL_FADE_ARMOR = 60708, // Reduces the armor of an enemy by 1500 for 15s + + //flame tsunami + SPELL_FLAME_TSUNAMI = 57494, // the visual dummy + SPELL_FLAME_TSUNAMI_LEAP = 60241, // SPELL_EFFECT_138 some leap effect, causing caster to move in direction + + SPELL_FLAME_TSUNAMI_DMG_AURA = 57491, // periodic damage, npc has this aura + SPELL_FLAME_TSUNAMI_BUFF = 60430, + NPC_LAVA_BLAZE = 30643, // adds spawning from flame strike + + //using these custom points for dragons start and end + POINT_ID_INIT = 100, + POINT_ID_LAND = 200 +}; + +enum Misc +{ + DATA_CAN_LOOT = 0 +}; + +struct Location +{ + float x, y, z; +}; + +struct Locations +{ + float x, y, z; +}; + +struct Waypoint +{ + float m_fX, m_fY, m_fZ; +}; + +#define MAX_WAYPOINT 6 +//points around raid "isle", counter clockwise. should probably be adjusted to be more alike +Waypoint dragonCommon[MAX_WAYPOINT]= +{ + {3214.012f, 468.932f, 98.652f}, + {3244.950f, 468.427f, 98.652f}, + {3283.520f, 496.869f, 98.652f}, + {3287.316f, 555.875f, 98.652f}, + {3250.479f, 585.827f, 98.652f}, + {3209.969f, 566.523f, 98.652f} +}; + +static Location AcolyteofShadron = { 3363.92f, 534.703f, 97.2683f }; +static Location AcolyteofShadron2 = { 3246.57f, 551.263f, 58.6164f }; +static Location AcolyteofVesperon = { 3145.68f, 520.71f, 89.7f }; +static Location AcolyteofVesperon2 = { 3246.57f, 551.263f, 58.6164f }; + +Locations TwilightEggs[] = +{ + {3219.28f, 669.121f, 88.5549f}, + {3221.55f, 682.852f, 90.5361f}, + {3239.77f, 685.94f, 90.3168f}, + {3250.33f, 669.749f, 88.7637f}, + {3246.6f, 642.365f, 84.8752f}, + {3233.68f, 653.117f, 85.7051f} +}; +Locations TwilightEggsSarth[] = +{ + {3252.73f, 515.762f, 58.5501f}, + {3256.56f, 521.119f, 58.6061f}, + {3255.63f, 527.513f, 58.7568f}, + {3264.90f, 525.865f, 58.6436f}, + {3264.26f, 516.364f, 58.8011f}, + {3257.54f, 502.285f, 58.2077f} +}; + +enum SharedTextIDs +{ + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_DEATH = 2, + SAY_BREATH = 3, + SAY_RESPOND = 4, + SAY_SPECIAL = 5 +}; + +enum DummyDragonEvents +{ + EVENT_FREE_MOVEMENT = 1 +}; + +//to control each dragons common abilities +struct dummy_dragonAI : public ScriptedAI +{ + dummy_dragonAI(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + } + + void Reset() OVERRIDE + { + if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + waypointId = 0; + portalRespawnTime = 30000; + _canMoveFree = false; + _canLoot = true; + } + + void SetData(uint32 type, uint32 value) OVERRIDE + { + if (type == DATA_CAN_LOOT) + _canLoot = value; + } + + void MovementInform(uint32 type, uint32 pointId) OVERRIDE + { + if (!instance || type != POINT_MOTION_TYPE) + return; + + // debug_log("dummy_dragonAI: %s reached point %u", me->GetName(), uiPointId); + + // if healers messed up the raid and we was already initialized + if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) + { + EnterEvadeMode(); + return; + } + + // this is end, if we reach this, don't do much + if (pointId == POINT_ID_LAND) + { + me->GetMotionMaster()->Clear(); + me->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0, true)) + { + me->AddThreat(target, 1.0f); + me->Attack(target, true); + me->GetMotionMaster()->MoveChase(target); + } + + _canMoveFree = false; + return; + } + + // get amount of common points + uint32 commonWPCount = sizeof(dragonCommon)/sizeof(Waypoint); + + // increase + waypointId = pointId+1; + + // if we have reached a point bigger or equal to count, it mean we must reset to point 0 + if (waypointId >= commonWPCount) + { + if (!_canMoveFree) + _canMoveFree = true; + + waypointId = 0; + } + + events.ScheduleEvent(EVENT_FREE_MOVEMENT, 500); + } + + // used when open portal and spawn mobs in phase + void DoRaidWhisper(int32 iTextId) + { + Map* map = me->GetMap(); + + if (map && map->IsDungeon()) + { + Map::PlayerList const &PlayerList = map->GetPlayers(); + + if (!PlayerList.isEmpty()) + { + for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + Talk(iTextId, i->GetSource()->GetGUID()); + } + } + } + + // "opens" the portal and does the "opening" whisper + void OpenPortal() + { + int32 textId = 0; + + // there are 4 portal spawn locations, each are expected to be spawned with negative spawntimesecs in database + + // using a grid search here seem to be more efficient than caching all four guids + // in instance script and calculate range to each. + GameObject* portal = me->FindNearestGameObject(GO_TWILIGHT_PORTAL, 50.0f); + + switch (me->GetEntry()) + { + case NPC_TENEBRON: + { + textId = WHISPER_HATCH_EGGS; + if (instance && !instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + { + for (uint32 i = 0; i < 6; ++i) + me->SummonCreature(NPC_TWILIGHT_EGG, TwilightEggs[i].x, TwilightEggs[i].y, TwilightEggs[i].z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + } + else + { + for (uint32 i = 0; i < 6; ++i) + me->SummonCreature(NPC_SARTHARION_TWILIGHT_EGG, TwilightEggsSarth[i].x, TwilightEggsSarth[i].y, TwilightEggsSarth[i].z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); + } + break; + } + case NPC_SHADRON: + { + textId = WHISPER_OPEN_PORTAL; + if (instance && !instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + me->SummonCreature(NPC_ACOLYTE_OF_SHADRON, AcolyteofShadron.x, AcolyteofShadron.y, AcolyteofShadron.z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 28000); + else + me->SummonCreature(NPC_ACOLYTE_OF_SHADRON, AcolyteofShadron2.x, AcolyteofShadron2.y, AcolyteofShadron2.z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 28000); + + break; + } + case NPC_VESPERON: + { + textId = WHISPER_OPEN_PORTAL; + if (instance && !instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + { + if (Creature* acolyte = me->SummonCreature(NPC_ACOLYTE_OF_VESPERON, AcolyteofVesperon.x, AcolyteofVesperon.y, AcolyteofVesperon.z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000)) + { + me->InterruptNonMeleeSpells(true); + acolyte->InterruptNonMeleeSpells(true); + me->CastSpell(me, 32747, false); + } + } + else + { + if (Creature* acolyte = me->SummonCreature(NPC_ACOLYTE_OF_VESPERON, AcolyteofVesperon2.x, AcolyteofVesperon2.y, AcolyteofVesperon2.z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000)) + { + me->InterruptNonMeleeSpells(true); + acolyte->InterruptNonMeleeSpells(true); + me->CastSpell(me, 32747, false); + } + } + + break; + } + } + + DoRaidWhisper(textId); + + // By using SetRespawnTime() we will actually "spawn" the object with our defined time. + // Once time is up, portal will disappear again. + if (portal && !portal->isSpawned()) + portal->SetRespawnTime(portalRespawnTime); + + // Unclear what are expected to happen if one drake has a portal open already + // Refresh respawnTime so time again are set to 30secs? + } + + void JustDied(Unit* /*killer*/) OVERRIDE + { + if (!_canLoot) + me->SetLootRecipient(NULL); + + uint32 spellId = 0; + + switch (me->GetEntry()) + { + case NPC_TENEBRON: + spellId = SPELL_POWER_OF_TENEBRON; + if (instance && instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) + instance->SetBossState(DATA_TENEBRON, DONE); + break; + case NPC_SHADRON: + spellId = SPELL_POWER_OF_SHADRON; + if (instance && instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) + instance->SetBossState(DATA_SHADRON, DONE); + if (Creature* acolyte = me->FindNearestCreature(NPC_ACOLYTE_OF_SHADRON, 100.0f)) + acolyte->Kill(acolyte); + break; + case NPC_VESPERON: + spellId = SPELL_POWER_OF_VESPERON; + if (instance && instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) + instance->SetBossState(DATA_VESPERON, DONE); + if (Creature* acolyte = me->FindNearestCreature(NPC_ACOLYTE_OF_VESPERON, 100.0f)) + acolyte->Kill(acolyte); + break; + } + + Talk(SAY_DEATH); + me->RemoveAurasDueToSpell(spellId); + + if (instance) + { + instance->DoRemoveAurasDueToSpellOnPlayers(spellId); + + // not if solo mini-boss fight + if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS) + return; + + // Twilight Revenge to main boss + if (Unit* sartharion = Unit::GetUnit(*me, instance->GetData64(DATA_SARTHARION))) + if (sartharion->IsAlive()) + { + sartharion->RemoveAurasDueToSpell(spellId); + DoCast(sartharion, SPELL_TWILIGHT_REVENGE, true); + } + } + } + + void UpdateAI(uint32 diff) OVERRIDE + { + events.Update(diff); + + if (events.ExecuteEvent() == EVENT_FREE_MOVEMENT) + { + if (_canMoveFree && waypointId < MAX_WAYPOINT) + me->GetMotionMaster()->MovePoint(waypointId, dragonCommon[waypointId].m_fX, dragonCommon[waypointId].m_fY, dragonCommon[waypointId].m_fZ); + } + } + + private: + InstanceScript* instance; + EventMap events; + uint32 waypointId; + int32 portalRespawnTime; + bool _canMoveFree; + bool _canLoot; +}; + +/*###### +## Tenebron +######*/ + +enum TenebronEvents +{ + EVENT_SHADOW_FISSURE_TENEBRON = 2, + EVENT_HATCH_EGGS = 3, + EVENT_SHADOW_BREATH_TENEBRON = 4 +}; + +class npc_tenebron : public CreatureScript +{ +public: + npc_tenebron() : CreatureScript("npc_tenebron") { } + + struct npc_tenebronAI : public dummy_dragonAI + { + npc_tenebronAI(Creature* creature) : dummy_dragonAI(creature) { } + + void Reset() OVERRIDE + { + dummy_dragonAI::Reset(); + } + + void EnterCombat(Unit* /*who*/) OVERRIDE + { + Talk(SAY_AGGRO); + DoZoneInCombat(); + events.ScheduleEvent(EVENT_SHADOW_FISSURE_TENEBRON, 5000); + events.ScheduleEvent(EVENT_HATCH_EGGS, 30000); + events.ScheduleEvent(EVENT_SHADOW_BREATH_TENEBRON, 20000); + } + + void KilledUnit(Unit* /*victim*/) OVERRIDE + { + Talk(SAY_SLAY); + } + + void UpdateAI(uint32 diff) OVERRIDE + { + //if no target, update dummy and return + if (!UpdateVictim()) + { + dummy_dragonAI::UpdateAI(diff); + return; + } + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SHADOW_FISSURE_TENEBRON: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, RAID_MODE(SPELL_SHADOW_FISSURE, SPELL_SHADOW_FISSURE)); + events.ScheduleEvent(EVENT_SHADOW_FISSURE_TENEBRON, urand(15000, 20000)); + break; + case EVENT_HATCH_EGGS: + OpenPortal(); + events.ScheduleEvent(EVENT_HATCH_EGGS, 30000); + break; + case EVENT_SHADOW_BREATH_TENEBRON: + Talk(SAY_BREATH); + DoCastVictim(RAID_MODE(SPELL_SHADOW_BREATH, SPELL_SHADOW_BREATH_H)); + events.ScheduleEvent(EVENT_SHADOW_BREATH_TENEBRON, urand(20000, 25000)); + break; + } + } + DoMeleeAttackIfReady(); + } + + private: + EventMap events; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_tenebronAI(creature); + } +}; + +/*###### +## Shadron +######*/ + +enum ShadronEvents +{ + EVENT_SHADOW_FISSURE_SHADRON = 5, + EVENT_ACOLYTE_SHADRON = 6, + EVENT_SHADOW_BREATH_SHADRON = 7 +}; + +class npc_shadron : public CreatureScript +{ +public: + npc_shadron() : CreatureScript("npc_shadron") { } + + struct npc_shadronAI : public dummy_dragonAI + { + npc_shadronAI(Creature* creature) : dummy_dragonAI(creature) + { + instance = creature->GetInstanceScript(); + } + + void Reset() OVERRIDE + { + dummy_dragonAI::Reset(); + + if (me->HasAura(SPELL_TWILIGHT_TORMENT_VESP)) + me->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP); + + if (me->HasAura(SPELL_GIFT_OF_TWILIGTH_SHA)) + me->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SHA); + + if (instance) + instance->SetBossState(DATA_PORTAL_OPEN, NOT_STARTED); + } + + void EnterCombat(Unit* /*who*/) OVERRIDE + { + Talk(SAY_AGGRO); + DoZoneInCombat(); + events.ScheduleEvent(EVENT_SHADOW_FISSURE_SHADRON, 5000); + events.ScheduleEvent(EVENT_ACOLYTE_SHADRON, 60000); + events.ScheduleEvent(EVENT_SHADOW_BREATH_SHADRON, 20000); + } + + void KilledUnit(Unit* /*victim*/) OVERRIDE + { + Talk(SAY_SLAY); + } + + void UpdateAI(uint32 diff) OVERRIDE + { + //if no target, update dummy and return + if (!UpdateVictim()) + { + dummy_dragonAI::UpdateAI(diff); + return; + } + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SHADOW_FISSURE_SHADRON: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, RAID_MODE(SPELL_SHADOW_FISSURE, SPELL_SHADOW_FISSURE_H)); + events.ScheduleEvent(EVENT_SHADOW_FISSURE_SHADRON, urand(15000, 20000)); + break; + case EVENT_ACOLYTE_SHADRON: + if (instance->GetBossState(DATA_PORTAL_OPEN) == NOT_STARTED) + events.ScheduleEvent(EVENT_ACOLYTE_SHADRON, 10000); + else + { + if (me->HasAura(SPELL_GIFT_OF_TWILIGTH_SHA)) + return; + + OpenPortal(); + + if (instance) + instance->SetBossState(DATA_PORTAL_OPEN, IN_PROGRESS); + + events.ScheduleEvent(EVENT_ACOLYTE_SHADRON, urand(60000, 65000)); + } + break; + case EVENT_SHADOW_BREATH_SHADRON: + Talk(SAY_BREATH); + DoCastVictim(RAID_MODE(SPELL_SHADOW_BREATH, SPELL_SHADOW_BREATH_H)); + events.ScheduleEvent(EVENT_SHADOW_BREATH_SHADRON, urand(20000, 25000)); + break; + } + } + DoMeleeAttackIfReady(); + } + + private: + InstanceScript* instance; + EventMap events; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_shadronAI(creature); + } +}; + +/*###### +## Vesperon +######*/ + +enum VesperonEvents +{ + EVENT_SHADOW_FISSURE_VESPERON = 8, + EVENT_ACOLYTE_VESPERON = 9, + EVENT_SHADOW_BREATH_VESPERON = 10 +}; + +class npc_vesperon : public CreatureScript +{ +public: + npc_vesperon() : CreatureScript("npc_vesperon") { } + + struct npc_vesperonAI : public dummy_dragonAI + { + npc_vesperonAI(Creature* creature) : dummy_dragonAI(creature) + { + instance = creature->GetInstanceScript(); + } + + void Reset() OVERRIDE + { + dummy_dragonAI::Reset(); + } + + void EnterCombat(Unit* /*who*/) OVERRIDE + { + Talk(SAY_AGGRO); + DoZoneInCombat(); + events.ScheduleEvent(EVENT_SHADOW_FISSURE_VESPERON, 5000); + events.ScheduleEvent(EVENT_ACOLYTE_VESPERON, 60000); + events.ScheduleEvent(EVENT_SHADOW_BREATH_VESPERON, 20000); + } + + void KilledUnit(Unit* /*victim*/) OVERRIDE + { + Talk(SAY_SLAY); + } + + void UpdateAI(uint32 diff) OVERRIDE + { + //if no target, update dummy and return + if (!UpdateVictim()) + { + dummy_dragonAI::UpdateAI(diff); + return; + } + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SHADOW_FISSURE_VESPERON: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, RAID_MODE(SPELL_SHADOW_FISSURE, SPELL_SHADOW_FISSURE_H)); + events.ScheduleEvent(EVENT_SHADOW_FISSURE_VESPERON, urand(15000, 20000)); + break; + case EVENT_ACOLYTE_VESPERON: + if (instance->GetBossState(DATA_PORTAL_OPEN) == IN_PROGRESS) + events.ScheduleEvent(EVENT_ACOLYTE_VESPERON, 10000); + else + { + OpenPortal(); + DoCastVictim(SPELL_TWILIGHT_TORMENT_VESP); + events.ScheduleEvent(EVENT_ACOLYTE_VESPERON, urand(60000, 70000)); + } + break; + case EVENT_SHADOW_BREATH_VESPERON: + Talk(SAY_BREATH); + DoCastVictim(RAID_MODE(SPELL_SHADOW_BREATH, SPELL_SHADOW_BREATH_H)); + events.ScheduleEvent(EVENT_SHADOW_BREATH_VESPERON, urand(20000, 25000)); + break; + } + } + DoMeleeAttackIfReady(); + } + + private: + InstanceScript* instance; + EventMap events; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_vesperonAI(creature); + } +}; + +/*###### +## Acolyte of Shadron +######*/ + +class npc_acolyte_of_shadron : public CreatureScript +{ +public: + npc_acolyte_of_shadron() : CreatureScript("npc_acolyte_of_shadron") { } + + struct npc_acolyte_of_shadronAI : public ScriptedAI + { + npc_acolyte_of_shadronAI(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + } + + void Reset() OVERRIDE + { + // Despawn the NPC automatically after 28 seconds + me->DespawnOrUnsummon(28000); + + if (instance) + { + //if not solo fight, buff main boss, else place debuff on mini-boss. both spells TARGET_SCRIPT + if (instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + { + if (Creature* sartharion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SARTHARION))) + sartharion->AddAura(SPELL_GIFT_OF_TWILIGTH_SAR, sartharion); + } + else + { + if (Creature* shadron = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SHADRON))) + shadron->AddAura(SPELL_GIFT_OF_TWILIGTH_SHA, shadron); + } + } + + me->AddAura(SPELL_TWILIGHT_SHIFT_ENTER, me); + } + + void JustDied(Unit* /*killer*/) OVERRIDE + { + if (instance) + { + if (ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SHADRON))) + instance->SetBossState(DATA_PORTAL_OPEN, NOT_STARTED); + + Map* map = me->GetMap(); + if (map->IsDungeon()) + { + Map::PlayerList const &PlayerList = map->GetPlayers(); + + if (PlayerList.isEmpty()) + return; + + for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + { + if (i->GetSource()->IsAlive() && i->GetSource()->HasAura(SPELL_TWILIGHT_SHIFT, 0) && !i->GetSource()->GetVictim()) + { + i->GetSource()->CastSpell(i->GetSource(), SPELL_TWILIGHT_SHIFT_REMOVAL_ALL, true); + i->GetSource()->CastSpell(i->GetSource(), SPELL_TWILIGHT_RESIDUE, true); + i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_SHIFT); + i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_SHIFT_ENTER); + } + } + } + + // not solo fight, so main boss has debuff + if (Creature* debuffTarget = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SARTHARION))) + if (debuffTarget->IsAlive() && debuffTarget->HasAura(SPELL_GIFT_OF_TWILIGTH_SAR)) + debuffTarget->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SAR); + + // event not in progress, then solo fight and must remove debuff mini-boss + if (Creature* debuffTarget = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SHADRON))) + if (debuffTarget->IsAlive() && debuffTarget->HasAura(SPELL_GIFT_OF_TWILIGTH_SHA)) + debuffTarget->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SHA); + } + } + + void UpdateAI(uint32 /*diff*/) OVERRIDE + { + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + + private: + InstanceScript* instance; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_acolyte_of_shadronAI(creature); + } +}; + +/*###### +## Acolyte of Vesperon +######*/ + +class npc_acolyte_of_vesperon : public CreatureScript +{ +public: + npc_acolyte_of_vesperon() : CreatureScript("npc_acolyte_of_vesperon") { } + + struct npc_acolyte_of_vesperonAI : public ScriptedAI + { + npc_acolyte_of_vesperonAI(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + } + + void Reset() OVERRIDE + { + // Despawn the NPC automatically after 28 seconds + me->DespawnOrUnsummon(28000); + + if (instance) + me->AddAura(SPELL_TWILIGHT_SHIFT_ENTER, me); + + DoCast(me, SPELL_TWILIGHT_TORMENT_VESP_ACO); + } + + void JustDied(Unit* /*killer*/) OVERRIDE + { + me->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP_ACO); + + // remove twilight torment on Vesperon + if (instance) + { + if (Creature* vesperon = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_VESPERON))) + { + instance->SetBossState(DATA_PORTAL_OPEN, NOT_STARTED); + + if (vesperon->IsAlive() && vesperon->HasAura(SPELL_TWILIGHT_TORMENT_VESP)) + vesperon->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP); + } + + Map* map = me->GetMap(); + if (map->IsDungeon()) + { + Map::PlayerList const &PlayerList = map->GetPlayers(); + + if (PlayerList.isEmpty()) + return; + + for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + { + if (i->GetSource()->IsAlive() && i->GetSource()->HasAura(SPELL_TWILIGHT_SHIFT, 0) && !i->GetSource()->GetVictim()) + { + i->GetSource()->CastSpell(i->GetSource(), SPELL_TWILIGHT_SHIFT_REMOVAL_ALL, true); + i->GetSource()->CastSpell(i->GetSource(), SPELL_TWILIGHT_RESIDUE, true); + i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_SHIFT); + i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_SHIFT_ENTER); + } + if (i->GetSource()->IsAlive() && i->GetSource()->HasAura(SPELL_TWILIGHT_TORMENT_VESP, 0) && !i->GetSource()->GetVictim()) + i->GetSource()->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP); + } + } + + instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_TWILIGHT_TORMENT_VESP_ACO); + instance->DoRemoveAurasDueToSpellOnPlayers(57935); + instance->DoRemoveAurasDueToSpellOnPlayers(58835); // Components of spell Twilight Torment + } + } + + void UpdateAI(uint32 /*diff*/) OVERRIDE + { + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + + private: + InstanceScript* instance; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_acolyte_of_vesperonAI(creature); + } +}; + +/*###### +## Twilight Eggs +######*/ + +enum TwilightEggs +{ + EVENT_TWILIGHT_EGGS = 11 +}; + +class npc_twilight_eggs : public CreatureScript +{ +public: + npc_twilight_eggs() : CreatureScript("npc_twilight_eggs") { } + + struct npc_twilight_eggsAI : public ScriptedAI + { + npc_twilight_eggsAI(Creature* creature) : ScriptedAI(creature) + { + SetCombatMovement(false); + instance = creature->GetInstanceScript(); + } + + void Reset() OVERRIDE + { + if (instance) + me->AddAura(SPELL_TWILIGHT_SHIFT_ENTER, me); + + events.ScheduleEvent(EVENT_TWILIGHT_EGGS, 20000); + } + + void SpawnWhelps() + { + me->RemoveAllAuras(); + + if (!instance->GetBossState(DATA_SARTHARION) == IN_PROGRESS) + me->SummonCreature(NPC_TWILIGHT_WHELP, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); + else + me->SummonCreature(NPC_SHARTHARION_TWILIGHT_WHELP, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); + me->DealDamage(me, me->GetHealth()); + } + + void JustSummoned(Creature* who) OVERRIDE + { + who->SetInCombatWithZone(); + } + + void UpdateAI(uint32 diff) OVERRIDE + { + events.Update(diff); + + if (events.ExecuteEvent() == EVENT_TWILIGHT_EGGS) + { + if (ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_TENEBRON))) + instance->SetBossState(DATA_PORTAL_OPEN, NOT_STARTED); + + SpawnWhelps(); + } + } + + private: + InstanceScript* instance; + EventMap events; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_twilight_eggsAI(creature); + } +}; + +/*###### +## Flame Tsunami +######*/ + +enum FlameTsunami +{ + EVENT_TSUNAMI_TIMER = 12, + EVENT_TSUNAMI_BUFF = 13 +}; + +class npc_flame_tsunami : public CreatureScript +{ +public: + npc_flame_tsunami() : CreatureScript("npc_flame_tsunami") { } + + struct npc_flame_tsunamiAI : public ScriptedAI + { + npc_flame_tsunamiAI(Creature* creature) : ScriptedAI(creature) + { + me->SetDisplayId(11686); + me->AddAura(SPELL_FLAME_TSUNAMI, me); + } + + void Reset() OVERRIDE + { + me->SetReactState(REACT_PASSIVE); + events.ScheduleEvent(EVENT_TSUNAMI_TIMER, 100); + events.ScheduleEvent(EVENT_TSUNAMI_BUFF, 1000); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + } + + void UpdateAI(uint32 diff) OVERRIDE + { + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_TSUNAMI_TIMER: + DoCast(me, SPELL_FLAME_TSUNAMI_DMG_AURA); + events.ScheduleEvent(EVENT_TSUNAMI_TIMER, 500); + break; + case EVENT_TSUNAMI_BUFF: + if (Unit* lavaBlaze = GetClosestCreatureWithEntry(me, NPC_LAVA_BLAZE, 10.0f, true)) + lavaBlaze->CastSpell(lavaBlaze, SPELL_FLAME_TSUNAMI_BUFF, true); + events.ScheduleEvent(EVENT_TSUNAMI_BUFF, 1000); + break; + } + } + } + + private: + EventMap events; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_flame_tsunamiAI(creature); + } +}; + +/*###### +## Twilight Fissure +######*/ + +enum TwilightFissure +{ + EVENT_VOID_BLAST = 14 +}; + +class npc_twilight_fissure : public CreatureScript +{ +public: + npc_twilight_fissure() : CreatureScript("npc_twilight_fissure") { } + + struct npc_twilight_fissureAI : public ScriptedAI + { + npc_twilight_fissureAI(Creature* creature) : ScriptedAI(creature) + { + SetCombatMovement(false); + } + + void Reset() OVERRIDE + { + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->AddAura(46265, me); // Wrong, can't find proper visual + me->AddAura(69422, me); + events.ScheduleEvent(EVENT_VOID_BLAST, 5000); + } + + void UpdateAI(uint32 diff) OVERRIDE + { + events.Update(diff); + + if (events.ExecuteEvent() == EVENT_VOID_BLAST) + { + DoCastAOE(RAID_MODE(SPELL_VOID_BLAST, SPELL_VOID_BLAST_H)); + ////twilight realm + //DoCastVictim(57620, true); + //DoCastVictim(57874, true); + me->RemoveAllAuras(); + me->Kill(me); + } + } + + private: + EventMap events; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_twilight_fissureAI(creature); + } +}; + +/*###### +## Twilight Whelps +######*/ + +enum TwilightWhelps +{ + EVENT_FADE_ARMOR = 15 +}; + +class npc_twilight_whelp : public CreatureScript +{ +public: + npc_twilight_whelp() : CreatureScript("npc_twilight_whelp") { } + + struct npc_twilight_whelpAI : public ScriptedAI + { + npc_twilight_whelpAI(Creature* creature) : ScriptedAI(creature) + { + Reset(); + } + + void Reset() OVERRIDE + { + me->RemoveAllAuras(); + me->SetInCombatWithZone(); + events.ScheduleEvent(EVENT_FADE_ARMOR, 1000); + } + + void UpdateAI(uint32 diff) OVERRIDE + { + if (!UpdateVictim()) + return; + + // twilight torment + events.Update(diff); + + if (events.ExecuteEvent() == EVENT_FADE_ARMOR) + { + DoCastVictim(SPELL_FADE_ARMOR); + events.ScheduleEvent(EVENT_FADE_ARMOR, urand(5000, 10000)); + } + + DoMeleeAttackIfReady(); + } + + private: + EventMap events; + }; + + CreatureAI* GetAI(Creature* creature) const OVERRIDE + { + return new npc_twilight_whelpAI(creature); + } +}; + +class achievement_twilight_assist : public AchievementCriteriaScript +{ + public: + achievement_twilight_assist() : AchievementCriteriaScript("achievement_twilight_assist") { } + + bool OnCheck(Player* /*player*/, Unit* target) OVERRIDE + { + return target && target->GetAI()->GetData(TWILIGHT_ACHIEVEMENTS) >= 1; + } +}; + +class achievement_twilight_duo : public AchievementCriteriaScript +{ + public: + achievement_twilight_duo() : AchievementCriteriaScript("achievement_twilight_duo") { } + + bool OnCheck(Player* /*player*/, Unit* target) OVERRIDE + { + return target && target->GetAI()->GetData(TWILIGHT_ACHIEVEMENTS) >= 2; + } +}; + +class achievement_twilight_zone : public AchievementCriteriaScript +{ + public: + achievement_twilight_zone() : AchievementCriteriaScript("achievement_twilight_zone") { } + + bool OnCheck(Player* /*player*/, Unit* target) OVERRIDE + { + return target && target->GetAI()->GetData(TWILIGHT_ACHIEVEMENTS) == 3; + } +}; + +void AddSC_obsidian_sanctum() +{ + new npc_vesperon(); + new npc_shadron(); + new npc_tenebron(); + new npc_acolyte_of_shadron(); + new npc_acolyte_of_vesperon(); + new npc_twilight_eggs(); + new npc_flame_tsunami(); + new npc_twilight_fissure(); + new npc_twilight_whelp(); + new achievement_twilight_assist(); + new achievement_twilight_duo(); + new achievement_twilight_zone(); +} + diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h index 7d2403be469..8cfb3931372 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h @@ -18,17 +18,18 @@ #ifndef DEF_OBSIDIAN_SANCTUM_H #define DEF_OBSIDIAN_SANCTUM_H +#define OSScriptName "instance_obsidian_sanctum" + +uint32 const EncounterCount = 5; + enum DataTypes { - TYPE_SARTHARION_EVENT = 1, - TYPE_TENEBRON_PREKILLED = 2, - TYPE_SHADRON_PREKILLED = 3, - TYPE_VESPERON_PREKILLED = 4, - - DATA_SARTHARION = 10, - DATA_TENEBRON = 11, - DATA_SHADRON = 12, - DATA_VESPERON = 13 + DATA_SARTHARION = 0, + DATA_TENEBRON = 1, + DATA_SHADRON = 2, + DATA_VESPERON = 3, + DATA_PORTAL_OPEN = 4, + TWILIGHT_ACHIEVEMENTS = 5 }; enum CreaturesIds diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp index d518747a462..f7e8d9ea7d6 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp @@ -81,15 +81,40 @@ enum Creatures NPC_SCOURGE_HULK = 26555 }; -enum SvalaPhase +enum Phases { - IDLE, + IDLE = 1, INTRO, NORMAL, SACRIFICING, SVALADEAD }; +enum Events +{ + //INTRO + EVENT_INTRO_SVALA_TALK_0 = 1, + EVENT_INTRO_ARTHAS_TALK_0, + EVENT_INTRO_TRANSFORM_0, + EVENT_INTRO_TRANSFORM_1, + EVENT_INTRO_TRANSFORM_2, + EVENT_INTRO_SVALA_TALK_1, + EVENT_INTRO_ARTHAS_TALK_1, + EVENT_INTRO_SVALA_TALK_2, + EVENT_INTRO_RELOCATE_SVALA, + EVENT_INTRO_DESPAWN_ARTHAS, + + //NORMAL + EVENT_SINISTER_STRIKE, + EVENT_CALL_FLAMES, + EVENT_RITUAL_PREPARATION, + + //SACRIFICING + EVENT_SPAWN_RITUAL_CHANNELERS, + EVENT_RITUAL_STRIKE, + EVENT_RITUAL_DISARM +}; + enum Misc { DATA_INCREDIBLE_HULK = 2043 @@ -112,38 +137,23 @@ class boss_svala : public CreatureScript { boss_svalaAI(Creature* creature) : BossAI(creature, DATA_SVALA_SORROWGRAVE) { - Phase = IDLE; + _introCompleted = false; } - SvalaPhase Phase; - - uint32 introTimer; - uint8 introPhase; - uint8 sacrePhase; - - uint64 arthasGUID; - - uint32 sinsterStrikeTimer; - uint32 callFlamesTimer; - uint32 sacrificeTimer; - - bool sacrificed; - void Reset() OVERRIDE { _Reset(); - sacrificed = false; + _sacrificed = false; SetCombatMovement(true); - if (Phase > NORMAL) - Phase = NORMAL; + if (_introCompleted) + events.SetPhase(NORMAL); + else + events.SetPhase(IDLE); - me->SetDisableGravity(Phase == NORMAL); + me->SetDisableGravity(events.IsInPhase(NORMAL)); - introTimer = 1 * IN_MILLISECONDS; - introPhase = 0; - sacrePhase = 0; - arthasGUID = 0; + _arthasGUID = 0; instance->SetData64(DATA_SACRIFICED_PLAYER, 0); } @@ -152,16 +162,12 @@ class boss_svala : public CreatureScript { _EnterCombat(); Talk(SAY_AGGRO); - - sinsterStrikeTimer = 7 * IN_MILLISECONDS; - callFlamesTimer = urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS); } void JustSummoned(Creature* summon) OVERRIDE { if (summon->GetEntry() == NPC_RITUAL_CHANNELER) summon->CastSpell(summon, SPELL_SUMMONED_VIS, true); - summons.Summon(summon); } @@ -170,9 +176,9 @@ class boss_svala : public CreatureScript if (!who) return; - if (Phase == IDLE && me->IsValidAttackTarget(who) && me->IsWithinDistInMap(who, 40)) + if (events.IsInPhase(IDLE) && me->IsValidAttackTarget(who) && me->IsWithinDistInMap(who, 40)) { - Phase = INTRO; + events.SetPhase(INTRO); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); if (GameObject* mirror = ObjectAccessor::GetGameObject(*me, DATA_UTGARDE_MIRROR)) @@ -181,8 +187,9 @@ class boss_svala : public CreatureScript if (Creature* arthas = me->SummonCreature(NPC_ARTHAS, ArthasPos, TEMPSUMMON_MANUAL_DESPAWN)) { arthas->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - arthasGUID = arthas->GetGUID(); + _arthasGUID = arthas->GetGUID(); } + events.ScheduleEvent(EVENT_INTRO_SVALA_TALK_0, 1 * IN_MILLISECONDS, 0, INTRO); } } @@ -194,20 +201,20 @@ class boss_svala : public CreatureScript void JustDied(Unit* /*killer*/) OVERRIDE { - if (Phase == SACRIFICING) + if (events.IsInPhase(SACRIFICING)) SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE); - me->HandleEmoteCommand(EMOTE_ONESHOT_FLYDEATH); - _JustDied(); Talk(SAY_DEATH); } void SpellHitTarget(Unit* /*target*/, SpellInfo const* spellInfo) OVERRIDE { - if (spellInfo->Id == SPELL_RITUAL_STRIKE_EFF_1 && Phase != NORMAL && Phase != SVALADEAD) + if (spellInfo->Id == SPELL_RITUAL_STRIKE_EFF_1 && !events.IsInPhase(NORMAL) && !events.IsInPhase(SVALADEAD)) { - Phase = NORMAL; + events.SetPhase(NORMAL); + events.ScheduleEvent(EVENT_SINISTER_STRIKE, 7 * IN_MILLISECONDS, 0, NORMAL); + events.ScheduleEvent(EVENT_CALL_FLAMES, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, NORMAL); SetCombatMovement(true); if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 300.0f, true)) @@ -217,199 +224,163 @@ class boss_svala : public CreatureScript void UpdateAI(uint32 diff) OVERRIDE { - if (Phase == IDLE) + if (events.IsInPhase(IDLE)) return; - if (Phase == INTRO) + if (events.IsInPhase(NORMAL) && !UpdateVictim()) + return; + + events.Update(diff); + + if (!_sacrificed && HealthBelowPct(50)) { - if (introTimer <= diff) - { - Creature* arthas = ObjectAccessor::GetCreature(*me, arthasGUID); - if (!arthas) - return; + _sacrificed = true; + events.SetPhase(SACRIFICING); + events.ScheduleEvent(EVENT_RITUAL_PREPARATION, 0, 0, SACRIFICING); + } - switch (introPhase) - { - case 0: - Talk(SAY_SVALA_INTRO_0); - ++introPhase; - introTimer = 8100; - break; - case 1: + if (events.IsInPhase(NORMAL)) + DoMeleeAttackIfReady(); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_INTRO_SVALA_TALK_0: + Talk(SAY_SVALA_INTRO_0); + events.ScheduleEvent(EVENT_INTRO_ARTHAS_TALK_0, 8.1 * IN_MILLISECONDS, 0, INTRO); + break; + case EVENT_INTRO_ARTHAS_TALK_0: + if (Creature* arthas = ObjectAccessor::GetCreature(*me, _arthasGUID)) arthas->AI()->Talk(SAY_DIALOG_OF_ARTHAS_1); - ++introPhase; - introTimer = 10000; - break; - case 2: - { + events.ScheduleEvent(EVENT_INTRO_TRANSFORM_0, 10 * IN_MILLISECONDS, 0, INTRO); + break; + case EVENT_INTRO_TRANSFORM_0: + { + if (Creature* arthas = ObjectAccessor::GetCreature(*me, _arthasGUID)) arthas->CastSpell(me, SPELL_TRANSFORMING_CHANNEL, false); - Position pos; - pos.Relocate(me); - pos.m_positionZ += 8.0f; - me->GetMotionMaster()->MoveTakeoff(0, pos); - // spectators flee event - std::list<Creature*> lspectatorList; - GetCreatureListWithEntryInGrid(lspectatorList, me, NPC_SPECTATOR, 100.0f); - for (std::list<Creature*>::iterator itr = lspectatorList.begin(); itr != lspectatorList.end(); ++itr) + Position pos; + pos.Relocate(me); + pos.m_positionZ += 8.0f; + me->GetMotionMaster()->MoveTakeoff(0, pos); + // spectators flee event + std::list<Creature*> lspectatorList; + GetCreatureListWithEntryInGrid(lspectatorList, me, NPC_SPECTATOR, 100.0f); + for (std::list<Creature*>::iterator itr = lspectatorList.begin(); itr != lspectatorList.end(); ++itr) + { + if ((*itr)->IsAlive()) { - if ((*itr)->IsAlive()) - { - (*itr)->SetStandState(UNIT_STAND_STATE_STAND); - (*itr)->SetWalk(false); - (*itr)->GetMotionMaster()->MovePoint(1, spectatorWP[0]); - } + (*itr)->SetStandState(UNIT_STAND_STATE_STAND); + (*itr)->SetWalk(false); + (*itr)->GetMotionMaster()->MovePoint(1, spectatorWP[0]); } - - ++introPhase; - introTimer = 4200; - break; } - case 3: - me->CastSpell(me, SPELL_SVALA_TRANSFORMING1, false); - ++introPhase; - introTimer = 6200; - break; - case 4: - me->CastSpell(me, SPELL_SVALA_TRANSFORMING2, false); + events.ScheduleEvent(EVENT_INTRO_TRANSFORM_1, 4.2 * IN_MILLISECONDS, 0, INTRO); + break; + } + case EVENT_INTRO_TRANSFORM_1: + me->CastSpell(me, SPELL_SVALA_TRANSFORMING1, false); + events.ScheduleEvent(EVENT_INTRO_TRANSFORM_2, 6.2 * IN_MILLISECONDS, 0, INTRO); + break; + case EVENT_INTRO_TRANSFORM_2: + me->CastSpell(me, SPELL_SVALA_TRANSFORMING2, false); + if (Creature* arthas = ObjectAccessor::GetCreature(*me, _arthasGUID)) + { arthas->InterruptNonMeleeSpells(true); - me->RemoveAllAuras(); - me->UpdateEntry(NPC_SVALA_SORROWGRAVE); me->SetFacingToObject(arthas); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - ++introPhase; - introTimer = 3200; - break; - case 5: - Talk(SAY_SVALA_INTRO_1); - ++introPhase; - introTimer = 10000; - break; - case 6: + } + me->RemoveAllAuras(); + me->UpdateEntry(NPC_SVALA_SORROWGRAVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + events.ScheduleEvent(EVENT_INTRO_SVALA_TALK_1, 10 * IN_MILLISECONDS, 0, INTRO); + break; + case EVENT_INTRO_SVALA_TALK_1: + Talk(SAY_SVALA_INTRO_1); + events.ScheduleEvent(EVENT_INTRO_ARTHAS_TALK_1, 3.2 * IN_MILLISECONDS, 0, INTRO); + break; + case EVENT_INTRO_ARTHAS_TALK_1: + if (Creature* arthas = ObjectAccessor::GetCreature(*me, _arthasGUID)) arthas->AI()->Talk(SAY_DIALOG_OF_ARTHAS_2); - ++introPhase; - introTimer = 7200; - break; - case 7: - Talk(SAY_SVALA_INTRO_2); - me->SetFacingTo(1.58f); + events.ScheduleEvent(EVENT_INTRO_SVALA_TALK_2, 7.2 * IN_MILLISECONDS, 0, INTRO); + break; + case EVENT_INTRO_SVALA_TALK_2: + Talk(SAY_SVALA_INTRO_2); + me->SetFacingTo(1.58f); + if (Creature* arthas = ObjectAccessor::GetCreature(*me, _arthasGUID)) arthas->SetVisible(false); - ++introPhase; - introTimer = 13800; - break; - case 8: - { - Position pos; - pos.Relocate(me); - pos.m_positionX = me->GetHomePosition().GetPositionX(); - pos.m_positionY = me->GetHomePosition().GetPositionY(); - pos.m_positionZ = 90.6065f; - me->GetMotionMaster()->MoveLand(0, pos); - me->SetDisableGravity(false, true); - me->SetHover(true); - ++introPhase; - introTimer = 3000; - break; - } - case 9: - if (GameObject* mirror = ObjectAccessor::GetGameObject(*me, DATA_UTGARDE_MIRROR)) - mirror->SetGoState(GO_STATE_ACTIVE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - arthas->DespawnOrUnsummon(); - arthasGUID = 0; - Phase = NORMAL; - break; - } - } - else - introTimer -= diff; - - return; - } - - if (Phase == NORMAL) - { - if (!UpdateVictim()) - return; - - if (sinsterStrikeTimer <= diff) - { - DoCastVictim(SPELL_SINSTER_STRIKE); - sinsterStrikeTimer = urand(5 * IN_MILLISECONDS, 9 * IN_MILLISECONDS); - } - else - sinsterStrikeTimer -= diff; - - if (callFlamesTimer <= diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + events.ScheduleEvent(EVENT_INTRO_RELOCATE_SVALA, 13.8 * IN_MILLISECONDS, 0, INTRO); + break; + case EVENT_INTRO_RELOCATE_SVALA: { - DoCast(target, SPELL_CALL_FLAMES); - callFlamesTimer = urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS); + Position pos; + pos.Relocate(me); + pos.m_positionX = me->GetHomePosition().GetPositionX(); + pos.m_positionY = me->GetHomePosition().GetPositionY(); + pos.m_positionZ = 90.6065f; + me->GetMotionMaster()->MoveLand(0, pos); + me->SetDisableGravity(false, true); + me->SetHover(true); + events.ScheduleEvent(EVENT_INTRO_DESPAWN_ARTHAS, 3 * IN_MILLISECONDS, 0, INTRO); + break; } - } - else - callFlamesTimer -= diff; - - if (!sacrificed) - { - if (HealthBelowPct(50)) - { + case EVENT_INTRO_DESPAWN_ARTHAS: + if (GameObject* mirror = ObjectAccessor::GetGameObject(*me, DATA_UTGARDE_MIRROR)) + mirror->SetGoState(GO_STATE_ACTIVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + if (Creature* arthas = ObjectAccessor::GetCreature(*me, _arthasGUID)) + arthas->DespawnOrUnsummon(); + _arthasGUID = 0; + events.SetPhase(NORMAL); + _introCompleted = true; + events.ScheduleEvent(EVENT_SINISTER_STRIKE, 7 * IN_MILLISECONDS, 0, NORMAL); + events.ScheduleEvent(EVENT_CALL_FLAMES, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, NORMAL); + break; + case EVENT_SINISTER_STRIKE: + DoCastVictim(SPELL_SINSTER_STRIKE); + events.ScheduleEvent(EVENT_SINISTER_STRIKE, urand(5 * IN_MILLISECONDS, 9 * IN_MILLISECONDS), 0, NORMAL); + break; + case EVENT_CALL_FLAMES: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_CALL_FLAMES); + events.ScheduleEvent(EVENT_CALL_FLAMES, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, NORMAL); + break; + case EVENT_RITUAL_PREPARATION: if (Unit* sacrificeTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 80.0f, true)) { instance->SetData64(DATA_SACRIFICED_PLAYER, sacrificeTarget->GetGUID()); - Talk(SAY_SACRIFICE_PLAYER); - DoCast(sacrificeTarget, SPELL_RITUAL_PREPARATION); - SetCombatMovement(false); - - Phase = SACRIFICING; - sacrePhase = 0; - sacrificeTimer = 1 * IN_MILLISECONDS; - DoCast(me, SPELL_RITUAL_OF_THE_SWORD); - sacrificed = true; } - } - } - - DoMeleeAttackIfReady(); - } - else // SACRIFICING - { - if (sacrificeTimer <= diff) - { - switch (sacrePhase) - { - case 0: - // spawn ritual channelers - DoCast(me, SPELL_RITUAL_CHANNELER_1, true); - DoCast(me, SPELL_RITUAL_CHANNELER_2, true); - DoCast(me, SPELL_RITUAL_CHANNELER_3, true); - ++sacrePhase; - sacrificeTimer = 2 * IN_MILLISECONDS; - break; - case 1: - me->StopMoving(); - me->GetMotionMaster()->MoveIdle(); - me->InterruptNonMeleeSpells(true); - DoCast(me, SPELL_RITUAL_STRIKE_TRIGGER, true); - ++sacrePhase; - sacrificeTimer = 200; - break; - case 2: - DoCast(me, SPELL_RITUAL_DISARM); - ++sacrePhase; - break; - case 3: - break; - } + events.ScheduleEvent(EVENT_SPAWN_RITUAL_CHANNELERS, 1 * IN_MILLISECONDS, 0, SACRIFICING); + break; + case EVENT_SPAWN_RITUAL_CHANNELERS: + DoCast(me, SPELL_RITUAL_CHANNELER_1, true); + DoCast(me, SPELL_RITUAL_CHANNELER_2, true); + DoCast(me, SPELL_RITUAL_CHANNELER_3, true); + events.ScheduleEvent(EVENT_RITUAL_STRIKE, 2 * IN_MILLISECONDS, 0, SACRIFICING); + break; + case EVENT_RITUAL_STRIKE: + me->StopMoving(); + me->GetMotionMaster()->MoveIdle(); + me->InterruptNonMeleeSpells(true); + DoCast(me, SPELL_RITUAL_STRIKE_TRIGGER, true); + events.ScheduleEvent(EVENT_RITUAL_DISARM, 200, 0, SACRIFICING); + break; + case EVENT_RITUAL_DISARM: + DoCast(me, SPELL_RITUAL_DISARM); + break; + default: + break; } - else - sacrificeTimer -= diff; } } + + private: + uint64 _arthasGUID; + bool _sacrificed; + bool _introCompleted; }; CreatureAI* GetAI(Creature* creature) const OVERRIDE diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index 9209969ce45..7a2afe5505f 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -452,14 +452,14 @@ enum BrannBronzebeard EVENT_SCRIPT_13 = 13 }; -class npc_brann_bronzebeard : public CreatureScript +class npc_brann_bronzebeard_keystone : public CreatureScript { public: - npc_brann_bronzebeard() : CreatureScript("npc_brann_bronzebeard") { } + npc_brann_bronzebeard_keystone() : CreatureScript("npc_brann_bronzebeard_keystone") { } - struct npc_brann_bronzebeardAI : public ScriptedAI + struct npc_brann_bronzebeard_keystoneAI : public ScriptedAI { - npc_brann_bronzebeardAI(Creature* creature) : ScriptedAI(creature) { } + npc_brann_bronzebeard_keystoneAI(Creature* creature) : ScriptedAI(creature) { } void Reset() OVERRIDE { @@ -583,7 +583,7 @@ public: CreatureAI* GetAI(Creature* creature) const OVERRIDE { - return new npc_brann_bronzebeardAI(creature); + return new npc_brann_bronzebeard_keystoneAI(creature); } }; @@ -642,6 +642,10 @@ enum JokkumScriptcast { SPELL_JOKKUM_KILL_CREDIT = 56545, SPELL_JOKKUM_SUMMON = 56541, + SPELL_RIDE_JOKKUM = 56606, + NPC_KINGJOKKUM = 30331, + SAY_HOLD_ON = 0, + PATH_JOKKUM = 2072200 }; class spell_jokkum_scriptcast : public SpellScriptLoader @@ -665,6 +669,17 @@ class spell_jokkum_scriptcast : public SpellScriptLoader { player->CastSpell(player, SPELL_JOKKUM_KILL_CREDIT, true); player->CastSpell(player, SPELL_JOKKUM_SUMMON, true); + + if (Creature* kingjokkum = GetClosestCreatureWithEntry(player, NPC_KINGJOKKUM, 10.0f)) + { + kingjokkum->setFaction(player->getFaction()); + player->CastSpell(kingjokkum, SPELL_RIDE_JOKKUM, true); + player->SetUInt64Value(PLAYER_FARSIGHT, player->GetGUID()); + kingjokkum->AI()->Talk(0, player->GetGUID()); + kingjokkum->ToPlayer()->SetClientControl(kingjokkum, 1); + kingjokkum->GetMotionMaster()->MovePath(PATH_JOKKUM, false); + kingjokkum->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + } } } @@ -688,7 +703,7 @@ void AddSC_storm_peaks() new npc_freed_protodrake(); new npc_icefang(); new npc_hyldsmeet_protodrake(); - new npc_brann_bronzebeard(); + new npc_brann_bronzebeard_keystone(); new spell_close_rift(); new spell_jokkum_scriptcast(); } diff --git a/src/server/scripts/Outland/zone_netherstorm.cpp b/src/server/scripts/Outland/zone_netherstorm.cpp index 5cf152bd6e2..e1b95aea972 100644 --- a/src/server/scripts/Outland/zone_netherstorm.cpp +++ b/src/server/scripts/Outland/zone_netherstorm.cpp @@ -400,7 +400,7 @@ public: struct npc_commander_dawnforgeAI : public ScriptedAI { - npc_commander_dawnforgeAI(Creature* creature) : ScriptedAI(creature) { Reset(); } + npc_commander_dawnforgeAI(Creature* creature) : ScriptedAI(creature) { } uint64 PlayerGUID; uint64 ardonisGUID; @@ -411,9 +411,6 @@ public: uint32 Phase_Timer; bool isEvent; - float angle_dawnforge; - float angle_ardonis; - void Reset() OVERRIDE { PlayerGUID = 0; @@ -438,21 +435,15 @@ public: { Creature* ardonis = ObjectAccessor::GetCreature(*me, ardonisGUID); Creature* pathaleon = ObjectAccessor::GetCreature(*me, pathaleonGUID); - Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID); - if (!ardonis || !pathaleon || !player) + if (!ardonis || !pathaleon) return; - //Calculate the angle to Pathaleon - angle_dawnforge = me->GetAngle(pathaleon->GetPositionX(), pathaleon->GetPositionY()); - angle_ardonis = ardonis->GetAngle(pathaleon->GetPositionX(), pathaleon->GetPositionY()); + // Turn Dawnforge + me->SetFacingToObject(pathaleon); - //Turn Dawnforge and update - me->SetOrientation(angle_dawnforge); - me->SendUpdateToPlayer(player); - //Turn Ardonis and update - ardonis->SetOrientation(angle_ardonis); - ardonis->SendUpdateToPlayer(player); + // Turn Ardonis + ardonis->SetFacingToObject(pathaleon); //Set them to kneel me->SetStandState(UNIT_STAND_STATE_KNEEL); @@ -464,20 +455,11 @@ public: { if (Unit* ardonis = ObjectAccessor::GetUnit(*me, ardonisGUID)) { - Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID); - - if (!player) - return; - - angle_dawnforge = me->GetAngle(ardonis->GetPositionX(), ardonis->GetPositionY()); - angle_ardonis = ardonis->GetAngle(me->GetPositionX(), me->GetPositionY()); + // Turn Dawnforge + me->SetFacingToObject(ardonis); - //Turn Dawnforge and update - me->SetOrientation(angle_dawnforge); - me->SendUpdateToPlayer(player); - //Turn Ardonis and update - ardonis->SetOrientation(angle_ardonis); - ardonis->SendUpdateToPlayer(player); + // Turn Ardonis + ardonis->SetFacingToObject(me); //Set state me->SetStandState(UNIT_STAND_STATE_STAND); |