Skip to content

Commit 5ac6887

Browse files
committed
[2227] Implement Drak Tharon Keep - Novos the Summoner
Thanks to Nonid for old EventAI script for timers and base mechanics The visual Beam Channel spells need some core fix to proper ignore LoS barriers
1 parent 557dbb2 commit 5ac6887

6 files changed

Lines changed: 520 additions & 9 deletions

File tree

scripts/northrend/draktharon_keep/boss_novos.cpp

Lines changed: 295 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
/* ScriptData
1818
SDName: Boss_Novos
19-
SD%Complete: 20%
20-
SDComment:
19+
SD%Complete: 80%
20+
SDComment: Summon Timers are vague, many visual spells fail (LoS)
2121
SDCategory: Drak'Tharon Keep
2222
EndScriptData */
2323

@@ -35,30 +35,106 @@ enum
3535

3636
EMOTE_ASSISTANCE = -1600011,
3737

38+
SPELL_ARCANE_FIELD = 47346,
39+
SPELL_IMMUNITY = 34098,
40+
SPELL_SUMMON_MINIONS_H = 59910, // (at least) on Phase-Switch, TODO1 research on appearence; TODO2 implement spell. Triggers 59935, 59938, 59939, 59940
41+
SPELL_FROSTBOLT = 49037,
42+
SPELL_FROSTBOLT_H = 59855,
43+
SPELL_ARCANE_BLAST = 49198,
44+
SPELL_ARCANE_BLAST_H = 59909,
45+
SPELL_BLIZZARD = 49034,
46+
SPELL_BLIZZARD_H = 59854,
47+
SPELL_TOUCH_OF_MISERY = 50090, // TODO - purpose of this spell (triggers SPELL_WRATH_OF_MISERY) unknown
48+
SPELL_WRATH_OF_MISERY = 50089,
49+
SPELL_WRATH_OF_MISERY_H = 59856,
50+
51+
// SPELL_SUMMON_CRYSTAL_HANDLER = 49179, // Spell seems to be unused, perhaps only server-side, and especially no suitable positioned caster are found for this spell
52+
SPELL_SUMMON_FETID_TROLL_CORPSE = 49103,
53+
SPELL_SUMMON_HULKING_CORPSE = 49104,
54+
SPELL_SUMMON_RISON_SHADOWCASTER = 49105,
55+
56+
// Spells 'Crystal Handler Death' 47336, 55801, 55803, 55805 (defined in instance script)
57+
3858
NPC_CRYSTAL_HANDLER = 26627,
3959
NPC_HULKING_CORPSE = 27597,
4060
NPC_FETID_TROLL_CORPSE = 27598,
4161
NPC_RISON_SHADOWCASTER = 27600
4262
};
4363

64+
// The Crystal Handlers are summoned around the two entrances of the room
65+
static const float aHandlerSummonPos[2][3] =
66+
{
67+
{-342.894836f, -727.016846f, 28.581081f},
68+
{-410.644653f, -731.826904f, 28.580412f}
69+
};
70+
4471
/*######
4572
## boss_novos
4673
######*/
4774

48-
struct MANGOS_DLL_DECL boss_novosAI : public ScriptedAI
75+
enum Phases
4976
{
50-
boss_novosAI(Creature* pCreature) : ScriptedAI(pCreature)
77+
PHASE_SHIELDED = 0,
78+
PHASE_WAITING = 1,
79+
PHASE_NORMAL = 2,
80+
};
81+
82+
struct MANGOS_DLL_DECL boss_novosAI : public Scripted_NoMovementAI
83+
{
84+
boss_novosAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature)
5185
{
52-
m_pInstance = (ScriptedInstance*)pCreature->GetInstanceData();
86+
m_pInstance = (instance_draktharon_keep*)pCreature->GetInstanceData();
5387
m_bIsRegularMode = pCreature->GetMap()->IsRegularDifficulty();
5488
Reset();
5589
}
5690

57-
ScriptedInstance* m_pInstance;
91+
instance_draktharon_keep* m_pInstance;
5892
bool m_bIsRegularMode;
5993

94+
uint32 m_uiSummonHandlerTimer; // TODO the summoning timers are weak
95+
uint32 m_uiSummonShadowcasterTimer;
96+
uint32 m_uiSummonFetidTrollTimer;
97+
uint32 m_uiSummonHulkingCorpseTimer;
98+
uint32 m_uiPhaseTimer;
99+
uint32 m_uiArcaneBlastTimer;
100+
uint32 m_uiBlizzardTimer;
101+
uint32 m_uiWrathTimer;
102+
103+
uint8 m_uiSummonedHandlers;
104+
uint8 m_uiLostCrystals;
105+
Phases m_uiPhase;
106+
60107
void Reset()
61108
{
109+
m_uiSummonHandlerTimer = 25000;
110+
m_uiSummonShadowcasterTimer = 3000;
111+
m_uiSummonFetidTrollTimer = 10000;
112+
m_uiSummonHulkingCorpseTimer = 30000;
113+
m_uiPhaseTimer = 3000;
114+
m_uiArcaneBlastTimer = urand(6000, 8000);
115+
m_uiBlizzardTimer = urand(8000, 12000);
116+
m_uiWrathTimer = urand(12000, 15000);
117+
118+
m_uiSummonedHandlers = 0;
119+
m_uiLostCrystals = 0;
120+
// This ensures that in the shield phase m_pInstance is valid
121+
m_uiPhase = m_pInstance ? PHASE_SHIELDED : PHASE_NORMAL;
122+
}
123+
124+
void LostOneCrystal()
125+
{
126+
++m_uiLostCrystals;
127+
128+
DoScriptText(urand(0, 1) ? SAY_BUBBLE_1 : SAY_BUBBLE_2, m_creature);
129+
130+
if (m_uiLostCrystals == MAX_CRYSTALS)
131+
{
132+
// Enter Phase 2
133+
if (m_bIsRegularMode)
134+
DoCastSpellIfCan(m_creature, SPELL_SUMMON_MINIONS_H);
135+
136+
m_uiPhase = PHASE_WAITING;
137+
}
62138
}
63139

64140
void MoveInLineOfSight(Unit* pWho)
@@ -79,6 +155,9 @@ struct MANGOS_DLL_DECL boss_novosAI : public ScriptedAI
79155
{
80156
DoScriptText(SAY_AGGRO, m_creature);
81157

158+
DoCastSpellIfCan(m_creature, SPELL_ARCANE_FIELD);
159+
DoCastSpellIfCan(m_creature, SPELL_IMMUNITY, CAST_TRIGGERED);
160+
82161
if (m_pInstance)
83162
m_pInstance->SetData(TYPE_NOVOS, IN_PROGRESS);
84163
}
@@ -102,12 +181,129 @@ struct MANGOS_DLL_DECL boss_novosAI : public ScriptedAI
102181
m_pInstance->SetData(TYPE_NOVOS, FAIL);
103182
}
104183

184+
void JustSummoned(Creature* pSummoned)
185+
{
186+
switch (pSummoned->GetEntry())
187+
{
188+
case NPC_CRYSTAL_HANDLER:
189+
if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0))
190+
pSummoned->AI()->AttackStart(pTarget);
191+
break;
192+
}
193+
}
194+
195+
void SummonedCreatureJustDied(Creature* pSummoned)
196+
{
197+
if (pSummoned->GetEntry() == NPC_CRYSTAL_HANDLER)
198+
{
199+
uint8 uiIndex = 0;
200+
if (m_pInstance)
201+
{
202+
if (Creature* pTarget = m_pInstance->GetNextCrystalTarget(pSummoned, uiIndex))
203+
pSummoned->CastSpell(pTarget, aCrystalHandlerDeathSpells[uiIndex], true, NULL, NULL, m_creature->GetObjectGuid());
204+
}
205+
}
206+
}
207+
105208
void UpdateAI(const uint32 uiDiff)
106209
{
107210
if (!m_creature->SelectHostileTarget() || !m_creature->getVictim())
108211
return;
109212

110-
DoMeleeAttackIfReady();
213+
switch (m_uiPhase)
214+
{
215+
case PHASE_SHIELDED: // Event Phase, only summoning of mobs
216+
if (m_uiSummonHandlerTimer < uiDiff)
217+
{
218+
float fX, fY, fZ;
219+
++m_uiSummonedHandlers;
220+
m_creature->GetRandomPoint(aHandlerSummonPos[m_uiSummonedHandlers % 2][0], aHandlerSummonPos[m_uiSummonedHandlers % 2][1], aHandlerSummonPos[m_uiSummonedHandlers % 2][2], 10.0f, fX, fY, fZ);
221+
m_creature->SummonCreature(NPC_CRYSTAL_HANDLER, fX, fY, fZ, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0);
222+
223+
DoScriptText(SAY_ADDS, m_creature);
224+
DoScriptText(EMOTE_ASSISTANCE, m_creature);
225+
226+
m_uiSummonHandlerTimer = 40000;
227+
}
228+
else
229+
m_uiSummonHandlerTimer -= uiDiff;
230+
231+
if (m_uiSummonShadowcasterTimer < uiDiff)
232+
{
233+
if (Creature* pSummoner = m_pInstance->GetSummonDummy())
234+
pSummoner->CastSpell(pSummoner, SPELL_SUMMON_RISON_SHADOWCASTER, false, NULL, NULL, m_creature->GetObjectGuid());
235+
m_uiSummonShadowcasterTimer = 25000;
236+
}
237+
else
238+
m_uiSummonShadowcasterTimer -= uiDiff;
239+
240+
if (m_uiSummonFetidTrollTimer < uiDiff)
241+
{
242+
if (Creature* pSummoner = m_pInstance->GetSummonDummy())
243+
pSummoner->CastSpell(pSummoner, SPELL_SUMMON_FETID_TROLL_CORPSE, false, NULL, NULL, m_creature->GetObjectGuid());
244+
m_uiSummonFetidTrollTimer = 5000;
245+
}
246+
else
247+
m_uiSummonFetidTrollTimer -= uiDiff;
248+
249+
if (m_uiSummonHulkingCorpseTimer < uiDiff)
250+
{
251+
if (Creature* pSummoner = m_pInstance->GetSummonDummy())
252+
pSummoner->CastSpell(pSummoner, SPELL_SUMMON_HULKING_CORPSE, false, NULL, NULL, m_creature->GetObjectGuid());
253+
m_uiSummonHulkingCorpseTimer = 30000;
254+
}
255+
else
256+
m_uiSummonHulkingCorpseTimer -= uiDiff;
257+
258+
break;
259+
260+
case PHASE_WAITING: // Short delay between last destroyed crystal and entering combat
261+
if (m_uiPhaseTimer < uiDiff)
262+
{
263+
m_uiPhase = PHASE_NORMAL;
264+
// Remove Immunity and Shield Aura
265+
m_creature->InterruptNonMeleeSpells(true);
266+
m_creature->RemoveAllAuras();
267+
}
268+
else
269+
m_uiPhaseTimer -= uiDiff;
270+
271+
break;
272+
273+
case PHASE_NORMAL: // Normal Phase, attack enemies
274+
if (m_uiArcaneBlastTimer < uiDiff)
275+
{
276+
// TODO - might be possible that this spell is only casted, when there is an enemy in range
277+
if (DoCastSpellIfCan(m_creature, m_bIsRegularMode ? SPELL_ARCANE_BLAST : SPELL_ARCANE_BLAST_H) == CAST_OK)
278+
m_uiArcaneBlastTimer = urand(7000, 9000);
279+
}
280+
else
281+
m_uiArcaneBlastTimer -= uiDiff;
282+
283+
if (m_uiBlizzardTimer < uiDiff)
284+
{
285+
if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0))
286+
if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_BLIZZARD : SPELL_BLIZZARD_H) == CAST_OK)
287+
m_uiBlizzardTimer = urand(9000, 13500);
288+
}
289+
else
290+
m_uiBlizzardTimer -= uiDiff;
291+
292+
if (m_uiWrathTimer < uiDiff)
293+
{
294+
if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0))
295+
if (DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_WRATH_OF_MISERY : SPELL_WRATH_OF_MISERY_H) == CAST_OK)
296+
m_uiWrathTimer = urand(12500, 17200);
297+
}
298+
else
299+
m_uiWrathTimer -= uiDiff;
300+
301+
if (!m_creature->IsNonMeleeSpellCasted(true)) // TODO Use this additional check, because might want to change the random target to be a target that is in LoS (which then is expensive)
302+
if (Unit* pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0))
303+
DoCastSpellIfCan(pTarget, m_bIsRegularMode ? SPELL_FROSTBOLT : SPELL_FROSTBOLT_H);
304+
305+
break;
306+
}
111307
}
112308
};
113309

@@ -116,6 +312,92 @@ CreatureAI* GetAI_boss_novos(Creature* pCreature)
116312
return new boss_novosAI(pCreature);
117313
}
118314

315+
// Small helper script to handle summoned adds for Novos
316+
struct MANGOS_DLL_DECL npc_crystal_channel_targetAI : public ScriptedAI
317+
{
318+
npc_crystal_channel_targetAI(Creature* pCreature) : ScriptedAI(pCreature)
319+
{
320+
m_pInstance = (instance_draktharon_keep*)pCreature->GetInstanceData();
321+
}
322+
323+
instance_draktharon_keep* m_pInstance;
324+
325+
void Reset() {}
326+
void MoveInLineOfSight(Unit* pWho) {}
327+
void AttackStart(Unit* pWho) {}
328+
void UpdateAI(const uint32 uiDiff) {}
329+
330+
void JustSummoned(Creature* pSummoned)
331+
{
332+
if (pSummoned->GetEntry() == NPC_HULKING_CORPSE || pSummoned->GetEntry() == NPC_FETID_TROLL_CORPSE || pSummoned->GetEntry() == NPC_RISON_SHADOWCASTER)
333+
{
334+
// Let them move down the stairs
335+
float fX, fY, fZ;
336+
337+
// The end of the stairs is approximately at 1/3 of the way between summoning-position and novos, height of Novos
338+
if (Creature* pNovos = m_pInstance->GetSingleCreatureFromStorage(NPC_NOVOS))
339+
{
340+
m_creature->GetRandomPoint(0.70*pNovos->GetPositionX() + 0.30*pSummoned->GetPositionX(), 0.70*pNovos->GetPositionY() + 0.30*pSummoned->GetPositionY(), pNovos->GetPositionZ() + 1.5f, 4.0f, fX, fY, fZ);
341+
pSummoned->GetMotionMaster()->MovePoint(1, fX, fY, fZ);
342+
}
343+
}
344+
}
345+
346+
void SummonedMovementInform(Creature* pSummoned, uint32 uiMotionType, uint32 uiPointId)
347+
{
348+
if (uiPointId != 1 || uiMotionType != POINT_MOTION_TYPE || (pSummoned->GetEntry() != NPC_HULKING_CORPSE && pSummoned->GetEntry() != NPC_FETID_TROLL_CORPSE && pSummoned->GetEntry() != NPC_RISON_SHADOWCASTER))
349+
return;
350+
351+
if (!pSummoned->isInCombat() && m_pInstance)
352+
{
353+
if (Creature* pNovos = m_pInstance->GetSingleCreatureFromStorage(NPC_NOVOS))
354+
{
355+
if (Unit* pTarget = pNovos->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0))
356+
pSummoned->AI()->AttackStart(pTarget);
357+
}
358+
}
359+
}
360+
};
361+
362+
CreatureAI* GetAI_npc_crystal_channel_target(Creature* pCreature)
363+
{
364+
return new npc_crystal_channel_targetAI(pCreature);
365+
}
366+
367+
368+
// Handling of the dummy auras of Crystal Handler Death spells, on remove the Crystal needs to be opened
369+
bool EffectAuraDummy_npc_crystal_channel_target(const Aura* pAura, bool bApply)
370+
{
371+
for (uint8 i = 0; i < MAX_CRYSTALS; ++i)
372+
{
373+
if (pAura->GetId() == aCrystalHandlerDeathSpells[i])
374+
{
375+
if (pAura->GetEffIndex() == EFFECT_INDEX_0 && !bApply)
376+
{
377+
if (Creature* pCreature = (Creature*)pAura->GetTarget())
378+
{
379+
if (instance_draktharon_keep* pInstance = (instance_draktharon_keep*)pCreature->GetInstanceData())
380+
{
381+
if (pInstance->GetData(TYPE_NOVOS) == NOT_STARTED || pInstance->GetData(TYPE_NOVOS) == FAIL)
382+
return true;
383+
384+
pInstance->DoHandleCrystal(i);
385+
386+
// Inform Novos about removed
387+
if (Creature* pNovos = pInstance->GetSingleCreatureFromStorage(NPC_NOVOS))
388+
if (boss_novosAI* pNovosAI = dynamic_cast<boss_novosAI*>(pNovos->AI()))
389+
pNovosAI->LostOneCrystal();
390+
}
391+
}
392+
}
393+
394+
return true;
395+
}
396+
}
397+
398+
return false;
399+
}
400+
119401
void AddSC_boss_novos()
120402
{
121403
Script* pNewScript;
@@ -124,4 +406,10 @@ void AddSC_boss_novos()
124406
pNewScript->Name = "boss_novos";
125407
pNewScript->GetAI = &GetAI_boss_novos;
126408
pNewScript->RegisterSelf();
409+
410+
pNewScript = new Script;
411+
pNewScript->Name = "npc_crystal_channel_target";
412+
pNewScript->GetAI = &GetAI_npc_crystal_channel_target;
413+
pNewScript->pEffectAuraDummy = &EffectAuraDummy_npc_crystal_channel_target;
414+
pNewScript->RegisterSelf();
127415
}

0 commit comments

Comments
 (0)