diff --git a/data/sql/updates/pending_db_world/rev_1557266833616361000.sql b/data/sql/updates/pending_db_world/rev_1557266833616361000.sql new file mode 100644 index 000000000..cb03db708 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1557266833616361000.sql @@ -0,0 +1,7 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1557266833616361000'); + + +UPDATE `creature_template` SET `ScriptName`='npc_burning_spirit' WHERE `entry`=9178; + +DELETE FROM `creature_text` WHERE `CreatureID` = 9156; +INSERT INTO `creature_text` (`CreatureID`, `Text`, `Type`, `Probability`, `comment`) VALUES ('9156', 'Your reign of terror ends now! Face your doom mortals!', '14', '100', 'Ambassador_flamelash_aggro'); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h index 9081bb2a1..543d23d97 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/blackrock_depths.h @@ -14,6 +14,11 @@ enum FactionIds FACTION_FRIEND = 35 }; +enum BRDBosses +{ + BOSS_AMBASSADOR_FLAMELASH = 0, +}; + enum DataTypes { TYPE_RING_OF_LAW = 1, @@ -48,7 +53,7 @@ enum DataTypes DATA_SF_BRAZIER_S = 26, DATA_MOIRA = 27, - DATA_OPEN_COFFER_DOORS = 30, + DATA_OPEN_COFFER_DOORS = 30 }; #endif diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp index 501d13968..d2290f9ef 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_ambassador_flamelash.cpp @@ -6,12 +6,40 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "blackrock_depths.h" enum Spells { - SPELL_FIREBLAST = 15573 + // Old fireblast value 15573 + SPELL_FIREBLAST = 13342, + SPELL_BURNING_SPIRIT = 14744, }; +enum AmbassadorEvents +{ + AGGRO_TEXT = 0, + EVENT_SPELL_FIREBLAST = 1, + EVENT_SUMMON_SPIRITS = 2, + EVENT_CHASE_AMBASSADOR = 3, + EVENT_KILL_SPIRIT = 4, +}; + +const uint32 NPC_FIRE_SPIRIT = 9178; +const uint32 NPC_AMBASSADOR_FLAMELASHER = 9156; + +const Position SummonPositions[7] = +{ + {1028.786987f, -224.787186f, -61.840500f, 3.617599f}, + {1045.144775f, -241.108292f, -61.967422f, 3.617599f}, + {1028.852905f, -257.484222f, -61.981380f, 3.617599f}, + {1012.461060f, -273.803406f, -61.994171f, 3.617599f}, + { 995.503052f, -257.563751f, -62.013153f, 3.617599f}, + { 979.358704f, -240.535309f, -61.983044f, 3.617599f}, + {1012.252747f, -206.696487f, -61.980618f, 3.617599f}, +}; + +vector gobjectDwarfRunesEntry { 170578, 170579, 170580, 170581, 170582, 170583, 170584 }; + class boss_ambassador_flamelash : public CreatureScript { public: @@ -19,53 +47,222 @@ public: CreatureAI* GetAI(Creature* creature) const { - return new boss_ambassador_flamelashAI(creature); + return GetInstanceAI(creature); } - struct boss_ambassador_flamelashAI : public ScriptedAI + struct boss_ambassador_flamelashAI : public BossAI { - boss_ambassador_flamelashAI(Creature* creature) : ScriptedAI(creature) { } + boss_ambassador_flamelashAI(Creature* creature) : BossAI(creature, BOSS_AMBASSADOR_FLAMELASH), summons(me) { } - uint32 FireBlast_Timer; - uint32 Spirit_Timer; + EventMap _events; - void Reset() + // This will help reseting the boss + SummonList summons; + + // This will allow to find a valid position to spawn them + vector validPosition; + bool foundValidPosition = false; + + void JustSummoned(Creature* cr) override { summons.Summon(cr); } + + void DoAction(int32 param) override { - FireBlast_Timer = 2000; - Spirit_Timer = 24000; + switch (param) + { + case EVENT_SUMMON_SPIRITS: + _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, urand(12, 14)*IN_MILLISECONDS); + break; + } } - void EnterCombat(Unit* /*who*/) { } - - void SummonSpirits(Unit* victim) + void Reset() override { - if (Creature* Spirit = DoSpawnCreature(9178, float(irand(-9, 9)), float(irand(-9, 9)), 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000)) - Spirit->AI()->AttackStart(victim); + _events.Reset(); + summons.DespawnAll(); + TurnRunes(false); + foundValidPosition = false; + validPosition.clear(); } - void UpdateAI(uint32 diff) + void TurnRunes(bool mode) + { + // Active makes the runes burn, ready turns them off + GOState state = mode ? GO_STATE_ACTIVE : GO_STATE_READY; + + for (int RuneEntry : gobjectDwarfRunesEntry) + if (GameObject* dwarfRune = me->FindNearestGameObject(RuneEntry, 200.0f)) + dwarfRune->SetGoState(state); + } + + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_SPELL_FIREBLAST, 2 * IN_MILLISECONDS); + + // Spawn 7 Embers initially + for (int i = 0; i < 4; ++i) + _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, 4 * IN_MILLISECONDS); + + // Activate the runes (Start burning) + TurnRunes(true); + + Talk(AGGRO_TEXT); + } + + void JustDied(Unit* /*killer*/) override + { + TurnRunes(false); + _events.Reset(); + summons.DespawnAll(); + } + + int getValidRandomPosition() + { + /* Generate a random position which + * have not been used in 4 summonings. + * Since we are calling the event whenever the Spirit + * dies and not all at the time, we need to save at + * least 4 positions until reseting the vector + */ + + // Searching a new position so reset this bool check + foundValidPosition = false; + int randomPosition; + + while (!foundValidPosition) + { + /* When we have summoned 4 creatures, reset the vector + * so we can summon new spirits in other positions.*/ + if (validPosition.size() == 4) + validPosition.clear(); + + // The random ranges from the position 0 to the position 6 + randomPosition = urand(0, 6); + + // When we have an empty vector we can use any random position generated. + if (validPosition.empty()) + foundValidPosition = true; + + /* This check is done to avoid running the vector + * when it is empty. Because if it is empty, then any + * position can be used to summon Spirits. + */ + if (!foundValidPosition) + { + // Check every position inside the vector + for (int pos : validPosition) + { + // If the random is different, we found a temporary true, + // until we find one that is equal, which means it has been used. + if (pos != randomPosition) + foundValidPosition = true; + else + { + foundValidPosition = false; + break; + } + } + } + } + + // We found a valid position. Save it and return it to summon. + validPosition.emplace_back(randomPosition); + return randomPosition; + } + + void SummonSpirits() + { + // Make the Spirits chase Ambassador Flamelash + if (Creature* Spirit = me->SummonCreature(NPC_FIRE_SPIRIT, SummonPositions[getValidRandomPosition()], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000)) + Spirit->AI()->DoAction(EVENT_CHASE_AMBASSADOR); + _events.ScheduleEvent(EVENT_SUMMON_SPIRITS, urand(12, 14) * IN_MILLISECONDS); + } + + void UpdateAI(uint32 diff) override { //Return since we have no target if (!UpdateVictim()) return; + + _events.Update(diff); - //FireBlast_Timer - if (FireBlast_Timer <= diff) + switch(_events.ExecuteEvent()) { - DoCastVictim(SPELL_FIREBLAST); - FireBlast_Timer = 7000; - } else FireBlast_Timer -= diff; + case EVENT_SPELL_FIREBLAST: + DoCastVictim(SPELL_FIREBLAST); + _events.ScheduleEvent(EVENT_SPELL_FIREBLAST, 7 * IN_MILLISECONDS); + break; + case EVENT_SUMMON_SPIRITS: + SummonSpirits(); + break; + } - //Spirit_Timer - if (Spirit_Timer <= diff) + DoMeleeAttackIfReady(); + } + }; +}; + +class npc_burning_spirit : public CreatureScript +{ +public: + npc_burning_spirit() : CreatureScript("npc_burning_spirit") { } + + CreatureAI* GetAI(Creature* creature) const + { + return GetInstanceAI(creature); + } + + struct npc_burning_spiritAI : public CreatureAI + { + npc_burning_spiritAI(Creature* creature) : CreatureAI(creature) { } + + EventMap _events; + + void Reset() override + { + // TODO: Swap this with an execute event + _events.ScheduleEvent(EVENT_CHASE_AMBASSADOR, 1); + } + + void DoAction(int32 param) override + { + switch (param) { - SummonSpirits(me->GetVictim()); - SummonSpirits(me->GetVictim()); - SummonSpirits(me->GetVictim()); - SummonSpirits(me->GetVictim()); + case EVENT_CHASE_AMBASSADOR: + // TODO: Swap this with an execute event + _events.ScheduleEvent(EVENT_CHASE_AMBASSADOR, 0.1f * IN_MILLISECONDS); + break; + } + } - Spirit_Timer = 30000; - } else Spirit_Timer -= diff; + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + switch(_events.ExecuteEvent()) + { + // Don't need to repeat this events because, once new spirits are summoned + // those new spirits will summon new spirits. If we repeated we would have + // an inmense number of spirits summoned if they were not all killed + case EVENT_CHASE_AMBASSADOR: + if (!UpdateVictim()) + { + if (Creature* boss = me->FindNearestCreature(NPC_AMBASSADOR_FLAMELASHER, 5000.0f, true)) + { + if (me->GetDistance(boss->GetPosition()) <= 5.0f) + { + boss->CastSpell(boss, SPELL_BURNING_SPIRIT); + boss->Kill(boss, me); + } + + if (me->IsAlive()) + me->GetMotionMaster()->MoveChase(boss); + _events.ScheduleEvent(EVENT_CHASE_AMBASSADOR, 0.5f * IN_MILLISECONDS); + } + } + else + _events.ScheduleEvent(EVENT_CHASE_AMBASSADOR, 0.5f * IN_MILLISECONDS); + break; + } DoMeleeAttackIfReady(); } @@ -75,4 +272,5 @@ public: void AddSC_boss_ambassador_flamelash() { new boss_ambassador_flamelash(); + new npc_burning_spirit(); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp index 43af86d7e..f6b9d7fd5 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp @@ -51,7 +51,7 @@ enum GameObjects GO_GOLEM_ROOM_S = 170574, // Magmus door Soutsh GO_THRONE_ROOM = 170575, // Throne door GO_SPECTRAL_CHALICE = 164869, - GO_CHEST_SEVEN = 169243 + GO_CHEST_SEVEN = 169243, }; class instance_blackrock_depths : public InstanceMapScript @@ -212,13 +212,13 @@ public: switch (type) { - case DATA_EVENSTARTER: - TombEventStarterGUID = data; - if (!TombEventStarterGUID) - TombOfSevenReset();//reset - else - TombOfSevenStart();//start - break; + case DATA_EVENSTARTER: + TombEventStarterGUID = data; + if (!TombEventStarterGUID) + TombOfSevenReset();//reset + else + TombOfSevenStart();//start + break; } } @@ -230,39 +230,39 @@ public: switch (type) { - case TYPE_RING_OF_LAW: - encounter[0] = data; - break; - case TYPE_VAULT: - encounter[1] = data; - break; - case TYPE_BAR: - if (data == SPECIAL) - ++BarAleCount; - else - encounter[2] = data; - break; - case TYPE_TOMB_OF_SEVEN: - encounter[3] = data; - break; - case TYPE_LYCEUM: - encounter[4] = data; - break; - case TYPE_IRON_HALL: - encounter[5] = data; - break; - case DATA_GHOSTKILL: - GhostKillCount += data; - break; - case DATA_OPEN_COFFER_DOORS: - OpenedCoofers += 1; - if (OpenedCoofers == 12) - { - Position pos = {812.15f, -348.91f, -50.579f, 0.7f}; - if (TempSummon* summon = instance->SummonCreature(NPC_WATCHMAN_DOOMGRIP, pos)) - summon->SetTempSummonType(TEMPSUMMON_MANUAL_DESPAWN); - } - break; + case TYPE_RING_OF_LAW: + encounter[0] = data; + break; + case TYPE_VAULT: + encounter[1] = data; + break; + case TYPE_BAR: + if (data == SPECIAL) + ++BarAleCount; + else + encounter[2] = data; + break; + case TYPE_TOMB_OF_SEVEN: + encounter[3] = data; + break; + case TYPE_LYCEUM: + encounter[4] = data; + break; + case TYPE_IRON_HALL: + encounter[5] = data; + break; + case DATA_GHOSTKILL: + GhostKillCount += data; + break; + case DATA_OPEN_COFFER_DOORS: + OpenedCoofers += 1; + if (OpenedCoofers == 12) + { + Position pos = {812.15f, -348.91f, -50.579f, 0.7f}; + if (TempSummon* summon = instance->SummonCreature(NPC_WATCHMAN_DOOMGRIP, pos)) + summon->SetTempSummonType(TEMPSUMMON_MANUAL_DESPAWN); + } + break; } if (data == DONE || GhostKillCount >= 7)