Skip to content

Commit

Permalink
Razorgore: fix exploit and event breaking case
Browse files Browse the repository at this point in the history
* Give NPC Orb Controller more control over things during the event. Sniffs show it is source and target for many spells in the encounter. Thanks @Tobschinski the Almighty Fish for his precious data. Many visuals and two cases of event reset/failure are now working properly
* Use proper healing spell for Razorgore in transition before the last phase
* Fix an exploit where players were able to kill Razorgore while bypassing most of the event and not even registering it, making it farmable at will
  • Loading branch information
cala committed Oct 11, 2017
1 parent 1f29a2c commit 17368d7
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 43 deletions.
3 changes: 2 additions & 1 deletion sql/scriptdev2/scriptdev2.sql
Expand Up @@ -347,6 +347,7 @@ UPDATE gameobject_template SET ScriptName='go_father_flame' WHERE entry=175245;
/* BLACKWING LAIR */
UPDATE instance_template SET ScriptName='instance_blackwing_lair' WHERE map=469;
UPDATE creature_template SET ScriptName='boss_razorgore' WHERE entry=12435;
UPDATE creature_template SET ScriptName='npc_blackwing_orb' WHERE entry=14449;
UPDATE gameobject_template SET ScriptName='go_black_dragon_egg' WHERE entry=177807;
UPDATE creature_template SET ScriptName='boss_vaelastrasz' WHERE entry=13020;
UPDATE creature_template SET ScriptName='boss_broodlord' WHERE entry=12017;
Expand Down Expand Up @@ -3478,7 +3479,7 @@ INSERT INTO script_texts (entry,content_default,sound,type,language,emote,commen
(-1469022,'You\'ll pay for forcing me to do this.',8275,1,0,0,'razorgore SAY_EGGS_BROKEN1'),
(-1469023,'Fools! These eggs are more precious than you know!',8276,1,0,0,'razorgore SAY_EGGS_BROKEN2'),
(-1469024,'No - not another one! I\'ll have your heads for this atrocity!',8277,1,0,0,'razorgore SAY_EGGS_BROKEN3'),
(-1469025,'If I fall into the abyss I\'ll take all of you mortals with me...',8278,1,0,0,'razorgore SAY_DEATH'),
(-1469025,'If I fall into the abyss I\'ll take all of you mortals with me...',8278,1,0,0,'razorgore SAY_RAZORGORE_DEATH'),

(-1469026,'Too late, friends! Nefarius\' corruption has taken hold...I cannot...control myself.',8281,1,0,1,'vaelastrasz SAY_LINE1'),
(-1469027,'I beg you, mortals - FLEE! Flee before I lose all sense of control! The black fire rages within my heart! I MUST- release it!',8282,1,0,1,'vaelastrasz SAY_LINE2'),
Expand Down
Expand Up @@ -53,12 +53,17 @@ enum

EMOTE_ORB_SHUT_OFF = -1469035,
EMOTE_TROOPS_FLEE = -1469033, // emote by Nefarian's Troops npc
SAY_RAZORGORE_DEATH = -1469025,

// Spells used by the monster generator in Razorgore encounter
// SPELL_SUMMON_LEGIONNAIRES = 19824, // Periodically triggers 19826
SPELL_SUMMON_LEGIONNAIRE = 19826,
SPELL_SUMMON_MAGE = 19827,
SPELL_SUMMON_DRAGONSPAWN = 19828,
SPELL_WARMING_FLAMES = 23040, // Used by Razorgore to fully heal in in phase 1 to 2 transition
SPELL_CONTROL_ORB = 23018, // Visual used by Grethok the Controller
SPELL_FIREBALL = 23024, // Used by Razorgore to reset the event (and kill everyone like a badass)
SPELL_EXPLODE_ORB = 20037, // used if attacked without destroying the eggs - triggers 20038

MAX_EGGS_DEFENDERS = 4,
MAX_DRAGONSPAWN = 12,
Expand Down
Expand Up @@ -17,7 +17,7 @@
/* ScriptData
SDName: Boss_Razorgore
SD%Complete: 95
SDComment: When casting spell Explode Orb all triggers are killed by the triggered spell 20038 casted by trigger NPCs 16604 (including some of those): this breaks the event. Triggers should survive/not be targeted. Possibly a core issue.
SDComment: Threat management after Mind Control is released needs core support (boss should have aggro on its previous controller and its previous victim should have threat transfered from boss to controlling player)
SDCategory: Blackwing Lair
EndScriptData */

Expand All @@ -29,11 +29,9 @@ enum
SAY_EGGS_BROKEN_1 = -1469022,
SAY_EGGS_BROKEN_2 = -1469023,
SAY_EGGS_BROKEN_3 = -1469024,
SAY_DEATH = -1469025,

SPELL_POSSESS = 23014, // visual effect and increase the damage taken
SPELL_POSSESS_VISUAL = 23014, // visual effect and increase the damage taken
SPELL_DESTROY_EGG = 19873,
SPELL_EXPLODE_ORB = 20037, // used if attacked without destroying the eggs - triggers 20038

SPELL_CLEAVE = 19632,
SPELL_WARSTOMP = 24375,
Expand All @@ -51,7 +49,6 @@ struct boss_razorgoreAI : public ScriptedAI

instance_blackwing_lair* m_pInstance;

uint32 m_uiIntroVisualTimer;
uint32 m_uiCleaveTimer;
uint32 m_uiWarStompTimer;
uint32 m_uiFireballVolleyTimer;
Expand All @@ -61,7 +58,6 @@ struct boss_razorgoreAI : public ScriptedAI

void Reset() override
{
m_uiIntroVisualTimer = 5000;
m_bEggsExploded = false;

m_uiCleaveTimer = urand(4000, 8000);
Expand All @@ -70,18 +66,6 @@ struct boss_razorgoreAI : public ScriptedAI
m_uiFireballVolleyTimer = urand(15000, 20000);
}

void JustDied(Unit* /*pKiller*/) override
{
if (m_pInstance)
{
// Don't set instance data unless all eggs are destroyed
if (m_pInstance->GetData(TYPE_RAZORGORE) != SPECIAL)
return;

m_pInstance->SetData(TYPE_RAZORGORE, DONE);
}
}

void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage, DamageEffectType /*damagetype*/) override
{
if (uiDamage < m_creature->GetHealth())
Expand All @@ -103,8 +87,8 @@ struct boss_razorgoreAI : public ScriptedAI
uiDamage = 0;
m_bEggsExploded = true;
m_pInstance->SetData(TYPE_RAZORGORE, FAIL);
DoScriptText(SAY_DEATH, m_creature);
DoCastSpellIfCan(m_creature, SPELL_EXPLODE_ORB, CAST_TRIGGERED);
DoScriptText(SAY_RAZORGORE_DEATH, m_creature);
m_creature->CastSpell(m_creature, SPELL_FIREBALL, TRIGGERED_OLD_TRIGGERED);
m_creature->ForcedDespawn(5000);
}
}
Expand All @@ -118,28 +102,7 @@ struct boss_razorgoreAI : public ScriptedAI
void UpdateAI(const uint32 uiDiff) override
{
if (!m_creature->SelectHostileTarget() || !m_creature->getVictim())
{
// Set visual only on OOC timer
if (m_uiIntroVisualTimer)
{
if (m_uiIntroVisualTimer <= uiDiff)
{
if (!m_pInstance)
{
script_error_log("Instance Blackwing Lair: ERROR Failed to load instance data for this instace.");
return;
}

if (Creature* pOrbTrigger = m_pInstance->GetSingleCreatureFromStorage(NPC_BLACKWING_ORB_TRIGGER))
pOrbTrigger->CastSpell(m_creature, SPELL_POSSESS, TRIGGERED_NONE);
m_uiIntroVisualTimer = 0;
}
else
m_uiIntroVisualTimer -= uiDiff;
}

return;
}

// Cleave
if (m_uiCleaveTimer < uiDiff)
Expand Down Expand Up @@ -203,6 +166,77 @@ CreatureAI* GetAI_boss_razorgore(Creature* pCreature)
return new boss_razorgoreAI(pCreature);
}

struct npc_blackwing_orbAI : public ScriptedAI
{
npc_blackwing_orbAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = (instance_blackwing_lair*)pCreature->GetInstanceData();
Reset();
}

instance_blackwing_lair* m_pInstance;

uint32 m_uiIntroVisualTimer;

void Reset() override
{
m_uiIntroVisualTimer = 4000;
}

void SpellHit(Unit* /* pCaster */, const SpellEntry* pSpell) override
{
// If hit by Razorgore's fireball: explodes everything in the room
if (pSpell->Id == SPELL_FIREBALL)
m_creature->CastSpell(m_creature, SPELL_EXPLODE_ORB, TRIGGERED_IGNORE_UNATTACKABLE_FLAG);
}

void UpdateAI(const uint32 uiDiff) override
{
// Set visual only on OOC timer
if (m_uiIntroVisualTimer)
{
if (m_uiIntroVisualTimer <= uiDiff)
{
if (!m_pInstance)
{
script_error_log("Instance Blackwing Lair: ERROR Failed to load instance data for this instace.");
return;
}

// If Razorgore is not respawned yet: wait
if (Creature* pRazorgore = m_pInstance->GetSingleCreatureFromStorage(NPC_RAZORGORE))
{
if (!(pRazorgore->isAlive()))
{
m_uiIntroVisualTimer = 2000;
return;
}
}

// If Grethok the Controller is here and spawned, start the visual, else wait for him
if (Creature* pGrethok = GetClosestCreatureWithEntry(m_creature, NPC_GRETHOK_CONTROLLER, 2.0f))
{
if (pGrethok->isAlive())
{
m_creature->CastSpell(m_creature, SPELL_POSSESS_VISUAL, TRIGGERED_OLD_TRIGGERED);
pGrethok->CastSpell(pGrethok, SPELL_CONTROL_ORB, TRIGGERED_OLD_TRIGGERED);
m_uiIntroVisualTimer = 0;
}
}
else
m_uiIntroVisualTimer = 2000;
}
else
m_uiIntroVisualTimer -= uiDiff;
}
}
};

CreatureAI* GetAI_npc_blackwing_orb(Creature* pCreature)
{
return new npc_blackwing_orbAI(pCreature);
}

bool EffectDummyGameObj_go_black_dragon_egg(Unit* pCaster, uint32 uiSpellId, SpellEffectIndex uiEffIndex, GameObject* pGOTarget, ObjectGuid /*originalCasterGuid*/)
{
if (uiSpellId == SPELL_DESTROY_EGG && uiEffIndex == EFFECT_INDEX_1)
Expand Down Expand Up @@ -241,6 +275,11 @@ void AddSC_boss_razorgore()
pNewScript->GetAI = &GetAI_boss_razorgore;
pNewScript->RegisterSelf();

pNewScript = new Script;
pNewScript->Name = "npc_blackwing_orb";
pNewScript->GetAI = &GetAI_npc_blackwing_orb;
pNewScript->RegisterSelf();

pNewScript = new Script;
pNewScript->Name = "go_black_dragon_egg";
pNewScript->pEffectDummyGO = &EffectDummyGameObj_go_black_dragon_egg;
Expand Down
Expand Up @@ -130,6 +130,11 @@ void instance_blackwing_lair::SetData(uint32 uiType, uint32 uiData)

// Reset the Orb of Domination and the eggs
DoToggleGameObjectFlags(GO_ORB_OF_DOMINATION, GO_FLAG_NO_INTERACT, true);
if (Creature* pOrb = GetSingleCreatureFromStorage(NPC_BLACKWING_ORB_TRIGGER))
{
if (pOrb->isAlive())
pOrb->AI()->EnterEvadeMode();
}

// Reset defenders
for (GuidList::const_iterator itr = m_lDefendersGuids.begin(); itr != m_lDefendersGuids.end(); ++itr)
Expand Down Expand Up @@ -274,7 +279,7 @@ void instance_blackwing_lair::SetData64(uint32 uiData, uint64 uiGuid)
if (Creature* pRazorgore = GetSingleCreatureFromStorage(NPC_RAZORGORE))
{
pRazorgore->RemoveAllAuras();
pRazorgore->SetHealth(pRazorgore->GetMaxHealth());
pRazorgore->CastSpell(pRazorgore, SPELL_WARMING_FLAMES, TRIGGERED_OLD_TRIGGERED);
}

// All defenders evade and despawn
Expand Down Expand Up @@ -312,6 +317,30 @@ void instance_blackwing_lair::OnCreatureDeath(Creature* pCreature)
if (Creature* pOrbTrigger = GetSingleCreatureFromStorage(NPC_BLACKWING_ORB_TRIGGER))
pOrbTrigger->InterruptNonMeleeSpells(false);
break;
case NPC_RAZORGORE:
// Only set the event as done if Razorgore dies in last phase
if (GetData(TYPE_RAZORGORE) == SPECIAL)
{
SetData(TYPE_RAZORGORE, DONE);
break;
}

// If the event is not already failed in Razorgore script, then force group wipe by making the boss trigger an AoE
// this is basically a duplicate of what is in Razorgore script because when the boss is Mind Controlled the AI is overriden
// So we have to handle it in the instance script instead to prevent the event to be stucked or exploited
if (GetData(TYPE_RAZORGORE) != FAIL)
{
if (Creature* pRazorgore = GetSingleCreatureFromStorage(NPC_RAZORGORE))
{
pRazorgore->CastSpell(pRazorgore, SPELL_FIREBALL, TRIGGERED_OLD_TRIGGERED);
SetData(TYPE_RAZORGORE, FAIL);
DoScriptText(SAY_RAZORGORE_DEATH, pRazorgore);
pRazorgore->ForcedDespawn();
}
if (Creature* pOrbTrigger = GetSingleCreatureFromStorage(NPC_BLACKWING_ORB_TRIGGER))
pOrbTrigger->CastSpell(pOrbTrigger, SPELL_EXPLODE_ORB, TRIGGERED_IGNORE_UNATTACKABLE_FLAG);
}
break;
case NPC_BLACKWING_LEGIONNAIRE:
case NPC_BLACKWING_MAGE:
m_uiBlackwingDefCount--;
Expand Down

1 comment on commit 17368d7

@cala
Copy link
Contributor Author

@cala cala commented on 17368d7 Oct 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requires DB update: cmangos/wotlk-db@ba21891

Please sign in to comment.