Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
boss_onyxia.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6/* ScriptData
7SDName: Boss_Onyxia
8SD%Complete: 95
9SDComment: <Known bugs>
10 Ground visual for Deep Breath effect;
11 Not summoning whelps on phase 3 (lacks info)
12 </Known bugs>
13SDCategory: Onyxia's Lair
14EndScriptData */
15
16#include "ScriptMgr.h"
17#include "ScriptedCreature.h"
18#include "Cell.h"
19#include "CellImpl.h"
20#include "GridNotifiers.h"
21#include "GridNotifiersImpl.h"
22#include "onyxias_lair.h"
23
25{
26 // Say
31 // Emote
33};
34
36{
37 // Phase 1 spells
40 SPELL_CLEAVE = 68868,
42
43 // Phase 2 spells
46
47 //Not much choise about these. We have to make own defintion on the direction/start-end point
48 SPELL_BREATH_NORTH_TO_SOUTH = 17086, // 20x in "array"
49 SPELL_BREATH_SOUTH_TO_NORTH = 18351, // 11x in "array"
50
51 SPELL_BREATH_EAST_TO_WEST = 18576, // 7x in "array"
52 SPELL_BREATH_WEST_TO_EAST = 18609, // 7x in "array"
53
54 SPELL_BREATH_SE_TO_NW = 18564, // 12x in "array"
55 SPELL_BREATH_NW_TO_SE = 18584, // 12x in "array"
56 SPELL_BREATH_SW_TO_NE = 18596, // 12x in "array"
57 SPELL_BREATH_NE_TO_SW = 18617, // 12x in "array"
58
59 //SPELL_BREATH = 21131, // 8x in "array", different initial cast than the other arrays
60
61 // Phase 3 spells
63};
64
78
86
88{
89 {0, 1, SPELL_BREATH_WEST_TO_EAST, -33.5561f, -182.682f, -56.9457f}, //west
90 {1, 0, SPELL_BREATH_EAST_TO_WEST, -31.4963f, -250.123f, -55.1278f}, //east
91 {2, 4, SPELL_BREATH_NW_TO_SE, 6.8951f, -180.246f, -55.896f}, //north-west
92 {3, 5, SPELL_BREATH_NE_TO_SW, 10.2191f, -247.912f, -55.896f}, //north-east
93 {4, 2, SPELL_BREATH_SE_TO_NW, -63.5156f, -240.096f, -55.477f}, //south-east
94 {5, 3, SPELL_BREATH_SW_TO_NE, -58.2509f, -189.020f, -55.790f}, //south-west
95 {6, 7, SPELL_BREATH_SOUTH_TO_NORTH, -65.8444f, -213.809f, -55.2985f}, //south
96 {7, 6, SPELL_BREATH_NORTH_TO_SOUTH, 22.8763f, -217.152f, -55.0548f}, //north
97};
98
99Position const MiddleRoomLocation = {-23.6155f, -215.357f, -55.7344f, 0.0f};
100
101Position const Phase2Location = {-80.924f, -214.299f, -82.942f, 0.0f};
102
104{
105 //Whelps
106 {-30.127f, -254.463f, -89.440f, 0.0f},
107 {-30.817f, -177.106f, -89.258f, 0.0f},
108 //Lair Guard
109 {-145.950f, -212.831f, -68.659f, 0.0f}
110};
111
113{
114public:
115 boss_onyxia() : CreatureScript("boss_onyxia") { }
116
117 struct boss_onyxiaAI : public BossAI
118 {
120 {
121 Reset();
122 }
123
125 {
127 SetCombatMovement(true);
128
130 MovePoint = std::rand() % 5;
132 Summons.DespawnAll();
134 IsMoving = false;
135
136 if (instance)
137 {
138 instance->SetBossState(DATA_ONYXIA, NOT_STARTED);
141 }
142 }
143
144 void EnterCombat(Unit* /*who*/) OVERRIDE
145 {
147 me->SetInCombatWithZone();
148
149 events.ScheduleEvent(EVENT_FLAME_BREATH, std::rand() % 20000 + 10000);
150 events.ScheduleEvent(EVENT_TAIL_SWEEP, std::rand() % 20000 + 15000);
151 events.ScheduleEvent(EVENT_CLEAVE, std::rand() % 5000 + 2000);
152 events.ScheduleEvent(EVENT_WING_BUFFET, std::rand() % 20000 + 10000);
153
154 if (instance)
155 {
156 instance->SetBossState(DATA_ONYXIA, IN_PROGRESS);
158 }
159 }
160
161 void JustDied(Unit* /*killer*/) OVERRIDE
162 {
163 if (instance)
164 instance->SetBossState(DATA_ONYXIA, DONE);
165
166 Summons.DespawnAll();
167 }
168
170 {
171 summoned->SetInCombatWithZone();
172 if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
173 summoned->AI()->AttackStart(target);
174
175 switch (summoned->GetEntry())
176 {
177 case NPC_WHELP:
179 break;
180 case NPC_LAIRGUARD:
181 summoned->setActive(true);
182 break;
183 }
184 Summons.Summon(summoned);
185 }
186
188 {
189 Summons.Despawn(summon);
190 }
191
192 void KilledUnit(Unit* /*victim*/) OVERRIDE
193 {
194 Talk(SAY_KILL);
195 }
196
197 void SpellHit(Unit* /*pCaster*/, const SpellInfo* Spell) OVERRIDE
198 {
199 if (Spell->Id == SPELL_BREATH_EAST_TO_WEST ||
205 {
207 MovePoint = PointData->LocIdEnd;
208
209 me->SetSpeed(MOVE_FLIGHT, 1.5f);
210 me->GetMotionMaster()->MovePoint(8, MiddleRoomLocation);
211 }
212 }
213
215 {
216 if (type == POINT_MOTION_TYPE)
217 {
218 switch (id)
219 {
220 case 8:
222 if (PointData)
223 {
224 me->SetSpeed(MOVE_FLIGHT, 1.0f);
225 me->GetMotionMaster()->MovePoint(PointData->LocId, PointData->fX, PointData->fY, PointData->fZ);
226 }
227 break;
228 case 9:
229 me->GetMotionMaster()->MoveChase(me->GetVictim());
230 events.ScheduleEvent(EVENT_BELLOWING_ROAR, 1000);
231 break;
232 case 10:
233 me->SetCanFly(true);
234 me->GetMotionMaster()->MovePoint(11, Phase2Location.GetPositionX(), Phase2Location.GetPositionY(), Phase2Location.GetPositionZ()+25);
235 me->SetSpeed(MOVE_FLIGHT, 1.0f);
237 if (instance)
239 events.ScheduleEvent(EVENT_WHELP_SPAWN, 5000);
240 events.ScheduleEvent(EVENT_LAIR_GUARD, 15000);
241 break;
242 case 11:
243 if (PointData)
244 me->GetMotionMaster()->MovePoint(PointData->LocId, PointData->fX, PointData->fY, PointData->fZ);
245 me->GetMotionMaster()->Clear(false);
246 me->GetMotionMaster()->MoveIdle();
247 break;
248 default:
249 IsMoving = false;
250 break;
251 }
252 }
253 }
254
256 {
257 //Workaround - Couldn't find a way to group this spells (All Eruption)
258 if (((Spell->Id >= 17086 && Spell->Id <= 17095) ||
259 (Spell->Id == 17097) ||
260 (Spell->Id >= 18351 && Spell->Id <= 18361) ||
261 (Spell->Id >= 18564 && Spell->Id <= 18576) ||
262 (Spell->Id >= 18578 && Spell->Id <= 18607) ||
263 (Spell->Id == 18609) ||
264 (Spell->Id >= 18611 && Spell->Id <= 18628) ||
265 (Spell->Id >= 21132 && Spell->Id <= 21133) ||
266 (Spell->Id >= 21135 && Spell->Id <= 21139) ||
267 (Spell->Id >= 22191 && Spell->Id <= 22202) ||
268 (Spell->Id >= 22267 && Spell->Id <= 22268)) &&
269 (target->GetTypeId() == TypeID::TYPEID_PLAYER))
270 {
271 if (instance)
272 {
274 }
275 }
276 }
277
279 {
280 uint8 MaxCount = sizeof(MoveData)/sizeof(OnyxMove);
281
282 for (uint8 i = 0; i < MaxCount; ++i)
283 {
284 if (MoveData[i].LocId == MovePoint)
285 return &MoveData[i];
286 }
287
288 return NULL;
289 }
290
292 {
293 uint8 MaxCount = sizeof(MoveData)/sizeof(OnyxMove);
294
295 uint8 iTemp = std::rand() % (MaxCount-1);
296
297 if (iTemp >= MovePoint)
298 ++iTemp;
299
300 MovePoint = iTemp;
301 }
302
304 {
305 if (!UpdateVictim())
306 return;
307
308 //Common to PHASE_START && PHASE_END
309 if (Phase == PHASE_START || Phase == PHASE_END)
310 {
311 //Specific to PHASE_START || PHASE_END
312 if (Phase == PHASE_START)
313 {
314 if (HealthBelowPct(60))
315 {
316 SetCombatMovement(false);
318 events.ScheduleEvent(EVENT_DEEP_BREATH, 85000);
319 events.ScheduleEvent(EVENT_MOVEMENT, 14000);
320 events.ScheduleEvent(EVENT_FIREBALL, 15000);
321 events.ScheduleEvent(EVENT_LAIR_GUARD, 60000);
322 events.ScheduleEvent(EVENT_WHELP_SPAWN, 60000);
323 me->GetMotionMaster()->MovePoint(10, Phase2Location);
324 return;
325 }
326 }
327
328 events.Update(diff);
329
330 while (uint32 eventId = events.ExecuteEvent())
331 {
332 switch (eventId)
333 {
334 case EVENT_BELLOWING_ROAR: // Phase PHASE_END
335 {
337 // Eruption
338 GameObject* Floor = NULL;
339 Skyfire::GameObjectInRangeCheck check(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 15);
341 me->VisitNearbyGridObject(30, searcher);
342 if (instance && Floor)
343 instance->SetData64(DATA_FLOOR_ERUPTION_GUID, Floor->GetGUID());
344 events.ScheduleEvent(EVENT_BELLOWING_ROAR, 30000);
345 break;
346 }
347 case EVENT_FLAME_BREATH: // Phase PHASE_START and PHASE_END
349 events.ScheduleEvent(EVENT_FLAME_BREATH, std::rand() % 20000 + 10000);
350 break;
351 case EVENT_TAIL_SWEEP: // Phase PHASE_START and PHASE_END
353 events.ScheduleEvent(EVENT_TAIL_SWEEP, std::rand() % 20000 + 15000);
354 break;
355 case EVENT_CLEAVE: // Phase PHASE_START and PHASE_END
357 events.ScheduleEvent(EVENT_CLEAVE, std::rand() % 5000 + 2000);
358 break;
359 case EVENT_WING_BUFFET: // Phase PHASE_START and PHASE_END
361 events.ScheduleEvent(EVENT_WING_BUFFET, std::rand() % 30000 + 15000);
362 break;
363 default:
364 break;
365 }
366 }
368 }
369 else
370 {
371 if (HealthBelowPct(40))
372 {
374 if (instance)
377
378 SetCombatMovement(true);
379 me->SetCanFly(false);
380 IsMoving = false;
381 me->GetMotionMaster()->MovePoint(9, me->GetHomePosition());
382 events.ScheduleEvent(EVENT_BELLOWING_ROAR, 30000);
383 return;
384 }
385
386 events.Update(diff);
387
388 while (uint32 eventId = events.ExecuteEvent())
389 {
390 switch (eventId)
391 {
392 case EVENT_DEEP_BREATH: // Phase PHASE_BREATH
393 if (!IsMoving)
394 {
395 if (me->IsNonMeleeSpellCasted(false))
396 me->InterruptNonMeleeSpells(false);
397
399 if (PointData)
400 DoCast(me, PointData->SpellId);
401 events.ScheduleEvent(EVENT_DEEP_BREATH, 70000);
402 }
403 break;
404 case EVENT_MOVEMENT: // Phase PHASE_BREATH
405 if (!IsMoving && !(me->HasUnitState(UNIT_STATE_CASTING)))
406 {
409
410 if (!PointData)
411 return;
412
413 me->GetMotionMaster()->MovePoint(PointData->LocId, PointData->fX, PointData->fY, PointData->fZ);
414 IsMoving = true;
415 events.ScheduleEvent(EVENT_MOVEMENT, 25000);
416 }
417 break;
418 case EVENT_FIREBALL: // Phase PHASE_BREATH
419 if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE)
420 {
421 if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
422 DoCast(target, SPELL_FIREBALL);
423
424 events.ScheduleEvent(EVENT_FIREBALL, 8000);
425 }
426 break;
427 case EVENT_LAIR_GUARD: // Phase PHASE_BREATH
429 events.ScheduleEvent(EVENT_LAIR_GUARD, 30000);
430 break;
431 case EVENT_WHELP_SPAWN: // Phase PHASE_BREATH
434 if (SummonWhelpCount >= RAID_MODE(20, 40))
435 {
437 events.ScheduleEvent(EVENT_WHELP_SPAWN, 90000);
438 }
439 else
440 events.ScheduleEvent(EVENT_WHELP_SPAWN, 500);
441 break;
442 default:
443 break;
444 }
445 }
446 }
447 }
448
449 private:
456 };
457
459 {
460 return new boss_onyxiaAI(creature);
461 }
462};
463
465{
466 new boss_onyxia();
467}
@ ACHIEVEMENT_TIMED_TYPE_EVENT
Definition DBCEnums.h:158
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
#define OVERRIDE
Definition Define.h:60
@ IN_PROGRESS
@ FAIL
@ DONE
@ NOT_STARTED
@ POINT_MOTION_TYPE
@ TYPEID_PLAYER
Definition Object.h:55
@ TEMPSUMMON_CORPSE_DESPAWN
Definition Object.h:72
@ UNIT_STATE_CASTING
Definition Unit.h:527
@ MOVE_FLIGHT
Definition Unit.h:561
@ SELECT_TARGET_RANDOM
Definition UnitAI.h:23
@ SPELL_CLEAVE
@ EVENT_CLEAVE
@ SPELL_FIREBALL
#define SAY_AGGRO
#define SAY_KILL
@ SPELL_WING_BUFFET
@ EVENT_FLAME_BREATH
Definition boss_gyth.cpp:31
@ EMOTE_BREATH
@ SPELL_BELLOWING_ROAR
@ SPELL_TAIL_SWEEP
@ SAY_AGGRO
@ SAY_PHASE_2_TRANS
@ EMOTE_BREATH
@ SAY_KILL
@ SAY_PHASE_3_TRANS
@ SPELL_CLEAVE
@ SPELL_BREATH_NE_TO_SW
@ SPELL_WING_BUFFET
@ SPELL_BELLOWING_ROAR
@ SPELL_BREATH_SE_TO_NW
@ SPELL_FLAME_BREATH
@ SPELL_BREATH_SOUTH_TO_NORTH
@ SPELL_BREATH_NORTH_TO_SOUTH
@ SPELL_FIREBALL
@ SPELL_DEEP_BREATH
@ SPELL_BREATH_WEST_TO_EAST
@ SPELL_BREATH_SW_TO_NE
@ SPELL_BREATH_EAST_TO_WEST
@ SPELL_BREATH_NW_TO_SE
@ SPELL_TAIL_SWEEP
static OnyxMove MoveData[8]
Position const MiddleRoomLocation
Position const Phase2Location
Position const SpawnLocations[3]
void AddSC_boss_onyxia()
@ EVENT_FLAME_BREATH
@ EVENT_TAIL_SWEEP
@ EVENT_MOVEMENT
@ EVENT_FIREBALL
@ EVENT_WING_BUFFET
@ EVENT_LAIR_GUARD
@ EVENT_DEEP_BREATH
@ EVENT_BELLOWING_ROAR
@ EVENT_WHELP_SPAWN
@ EVENT_CLEAVE
@ EVENT_FIREBALL
@ EVENT_WING_BUFFET
InstanceScript *const instance
EventMap events
BossAI(Creature *creature, uint32 bossId)
void Talk(uint8 id, WorldObject const *whisperTarget=NULL)
bool UpdateVictim()
CreatureScript(const char *name)
uint64 GetGUID() const
Definition Object.h:119
Definition Spell.h:289
void DoMeleeAttackIfReady()
Definition UnitAI.cpp:28
void DoCast(uint32 spellId)
Definition UnitAI.cpp:110
Unit * SelectTarget(SelectAggroTarget targetType, uint32 position=0, float dist=0.0f, bool playerOnly=false, int32 aura=0)
Definition UnitAI.cpp:66
void DoCastVictim(uint32 spellId, bool triggered=false)
Definition UnitAI.cpp:168
void DoCastAOE(uint32 spellId, bool triggered=false)
Definition UnitAI.cpp:176
Definition Unit.h:1367
CreatureAI * GetAI(Creature *creature) const OVERRIDE
@ PHASE_BREATH
@ PHASE_START
@ PHASE_END
@ DATA_FLOOR_ERUPTION_GUID
@ DATA_SHE_DEEP_BREATH_MORE
@ DATA_ONYXIA_PHASE
@ NPC_WHELP
@ NPC_LAIRGUARD
@ ACHIEV_TIMED_START_EVENT
@ DATA_ONYXIA
uint8 LocIdEnd
uint8 LocId
uint32 SpellId
void SetCombatMovement(bool allowMovement)
Creature * me
bool IsCombatMovementAllowed() const
bool HealthBelowPct(uint32 pct) const
const T & RAID_MODE(const T &normal10, const T &normal25) const
void MovementInform(uint32 type, uint32 id) OVERRIDE
void SpellHit(Unit *, const SpellInfo *Spell) OVERRIDE
boss_onyxiaAI(Creature *creature)
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
void KilledUnit(Unit *) OVERRIDE
void EnterCombat(Unit *) OVERRIDE
void JustDied(Unit *) OVERRIDE
void JustSummoned(Creature *summoned) OVERRIDE
void SummonedCreatureDespawn(Creature *summon) OVERRIDE
void SpellHitTarget(Unit *target, const SpellInfo *Spell) OVERRIDE
static Location SpawnLocations[]