fix(Core/Dungeon) Blackrock Depths - Ambassador Flamelash (#1803)

* Boss re-order

* Revert "Boss re-order"

This reverts commit 03289cb064ae51c38e3efebba11f3e3882ae350a.

* Reworking Ambassador flamelash

- Removed timers
- Scripting the boss properly, missing event and talk emote

* Update boss_ambassador_flamelash.cpp

* Update boss_ambassador_flamelash.cpp

* Ambassador Flamelash reworked

- Burning spirits spawn at the correct places
- When they get close to the boss they die, thus buffing the boss with a stackable buff
- When encounter begins the runes start burning
- Burning Spirits chase the boss around the room

* Missing aggro emote and cleanup

* travis

* Cleaner code

Changed the enum usage to a Vector and iterated over every gameobject entry to make them active or ready.

* Cleanup

Removed not used enum

* types

* Update boss_ambassador_flamelash.cpp

* Added dynamic spawning algorithm

Before this commit, Spirits could be summoned on the same rune.
After this commit Spirits will not be summoned on the same position.

* bad null conversion

* declaration and initialization inside function

changed randomPosition to an Int inside the getValidRandomPosition().

* wrong type declaration

variable needs to be declared outside of while cycle.

* useless variable

* Bugfixing

- New fireblast
- When burning spirits die killed by the player, new event to summon new ones
- Make them aggro the player and attack them

* miss typed ;

* Trying to fix mentioned issues

- If spirits are in combat, keep summoning new ones
- If spirits are in combat, then the boss can't kill them if they get close to them

* IsInCombat typo

* Exploit fixing

* Summoning massive waves fix

* Fixing last issue hopefully
This commit is contained in:
Pondaveia
2019-05-14 11:28:49 +01:00
committed by Poszer
parent 963b2b8cb6
commit 85e46e06b2
4 changed files with 280 additions and 70 deletions

View File

@@ -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

View File

@@ -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<int> 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<boss_ambassador_flamelashAI>(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<int> 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<npc_burning_spiritAI>(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();
}

View File

@@ -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)