Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
boss_gothik.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#include "ScriptMgr.h"
7#include "ScriptedCreature.h"
8#include "SpellScript.h"
9#include "GridNotifiers.h"
10#include "CombatAI.h"
11#include "naxxramas.h"
12
20
21//Gothik
36
47
48struct Waves { uint32 entry, time, mode; };
49// wave setups are not the same in heroic and normal difficulty,
50// mode is 0 only normal, 1 both and 2 only heroic
51// but this is handled in DoGothikSummon function
52const Waves waves[] =
53{
54 {NPC_LIVE_TRAINEE, 20000, 1},
55 {NPC_LIVE_TRAINEE, 20000, 1},
56 {NPC_LIVE_TRAINEE, 10000, 1},
57 {NPC_LIVE_KNIGHT, 10000, 1},
58 {NPC_LIVE_TRAINEE, 15000, 1},
59 {NPC_LIVE_KNIGHT, 5000, 1},
60 {NPC_LIVE_TRAINEE, 20000, 1},
61 {NPC_LIVE_TRAINEE, 0, 1},
62 {NPC_LIVE_KNIGHT, 10000, 1},
63 {NPC_LIVE_TRAINEE, 10000, 2},
64 {NPC_LIVE_RIDER, 10000, 0},
65 {NPC_LIVE_RIDER, 5000, 2},
66 {NPC_LIVE_TRAINEE, 5000, 0},
67 {NPC_LIVE_TRAINEE, 15000, 2},
68 {NPC_LIVE_KNIGHT, 15000, 0},
69 {NPC_LIVE_TRAINEE, 0, 0},
70 {NPC_LIVE_RIDER, 10000, 1},
71 {NPC_LIVE_KNIGHT, 10000, 1},
72 {NPC_LIVE_TRAINEE, 10000, 0},
73 {NPC_LIVE_RIDER, 10000, 2},
74 {NPC_LIVE_TRAINEE, 0, 2},
75 {NPC_LIVE_RIDER, 5000, 1},
76 {NPC_LIVE_TRAINEE, 0, 2},
77 {NPC_LIVE_KNIGHT, 5000, 1},
78 {NPC_LIVE_RIDER, 0, 2},
79 {NPC_LIVE_TRAINEE, 20000, 1},
80 {NPC_LIVE_RIDER, 0, 1},
81 {NPC_LIVE_KNIGHT, 0, 1},
82 {NPC_LIVE_TRAINEE, 25000, 2},
83 {NPC_LIVE_TRAINEE, 15000, 0},
84 {NPC_LIVE_TRAINEE, 25000, 0},
85 {0, 0, 1},
86};
87
88#define POS_Y_GATE -3360.78f
89#define POS_Y_WEST -3285.0f
90#define POS_Y_EAST -3434.0f
91#define POS_X_NORTH 2750.49f
92#define POS_X_SOUTH 2633.84f
93
94#define IN_LIVE_SIDE(who) (who->GetPositionY() < POS_Y_GATE)
95
104enum Pos
105{
108};
109
111{
112 {2669.7f, -3428.76f, 268.56f, 1.6f},
113 {2692.1f, -3428.76f, 268.56f, 1.6f},
114 {2714.4f, -3428.76f, 268.56f, 1.6f},
115 {2669.7f, -3431.67f, 268.56f, 1.6f},
116 {2692.1f, -3431.67f, 268.56f, 1.6f},
117 {2714.4f, -3431.67f, 268.56f, 1.6f},
118};
119
121{
122 {2725.1f, -3310.0f, 268.85f, 3.4f},
123 {2699.3f, -3322.8f, 268.60f, 3.3f},
124 {2733.1f, -3348.5f, 268.84f, 3.1f},
125 {2682.8f, -3304.2f, 268.85f, 3.9f},
126 {2664.8f, -3340.7f, 268.23f, 3.7f},
127};
128
129float const PosGroundLiveSide[4] = {2691.2f, -3387.0f, 267.68f, 1.52f};
130float const PosGroundDeadSide[4] = {2693.5f, -3334.6f, 267.68f, 4.67f};
131float const PosPlatform[4] = {2640.5f, -3360.6f, 285.26f, 0.0f};
132
133// Predicate function to check that the r efzr unit is NOT on the same side as the source.
134struct NotOnSameSide : public SF_UNARY_FUNCTION<Unit*, bool>
135{
137
138 bool operator() (Unit const* target)
139 {
140 return (_onLiveSide != IN_LIVE_SIDE(target));
141 }
142
143 private:
145};
146
148{
149 public:
150 boss_gothik() : CreatureScript("boss_gothik") { }
151
152 struct boss_gothikAI : public BossAI
153 {
154 boss_gothikAI(Creature* creature) : BossAI(creature, BOSS_GOTHIK) { }
155
157 typedef std::vector<Creature*> TriggerVct;
162
163 std::vector<uint64> LiveTriggerGUID;
164 std::vector<uint64> DeadTriggerGUID;
165
167 {
168 LiveTriggerGUID.clear();
169 DeadTriggerGUID.clear();
170
171 me->SetReactState(REACT_PASSIVE);
172 if (instance)
174 _Reset();
175 mergedSides = false;
176 phaseTwo = false;
177 thirtyPercentReached = false;
178 }
179
180 void EnterCombat(Unit* /*who*/) OVERRIDE
181 {
182 for (uint32 i = 0; i < POS_LIVE; ++i)
183 if (Creature* trigger = DoSummon(WORLD_TRIGGER, PosSummonLive[i]))
184 LiveTriggerGUID.push_back(trigger->GetGUID());
185 for (uint32 i = 0; i < POS_DEAD; ++i)
186 if (Creature* trigger = DoSummon(WORLD_TRIGGER, PosSummonDead[i]))
187 DeadTriggerGUID.push_back(trigger->GetGUID());
188
189 if (LiveTriggerGUID.size() < POS_LIVE || DeadTriggerGUID.size() < POS_DEAD)
190 {
191 SF_LOG_ERROR("scripts", "Script Gothik: cannot summon triggers!");
193 return;
194 }
195
196 _EnterCombat();
197 waveCount = 0;
198 events.ScheduleEvent(EVENT_SUMMON, 30000);
201 if (instance)
203 }
204
206 {
207 if (summon->GetEntry() == WORLD_TRIGGER)
208 summon->setActive(true);
209 else if (!mergedSides)
210 {
211 summon->AI()->DoAction(me->HasReactState(REACT_PASSIVE) ? 1 : 0);
212 summon->AI()->EnterEvadeMode();
213 }
214 else
215 {
216 summon->AI()->DoAction(0);
217 summon->AI()->DoZoneInCombat();
218 }
219 summons.Summon(summon);
220 }
221
223 {
224 summons.Despawn(summon);
225 }
226
227 void KilledUnit(Unit* /*victim*/) OVERRIDE
228 {
229 if (!(rand()%5))
230 Talk(SAY_KILL);
231 }
232
233 void JustDied(Unit* /*killer*/) OVERRIDE
234 {
235 LiveTriggerGUID.clear();
236 DeadTriggerGUID.clear();
237 _JustDied();
239 if (instance)
241 }
242
244 {
246 {
247 switch (entry)
248 {
249 case NPC_LIVE_TRAINEE:
250 {
253 if (Creature* liveTrigger1 = Unit::GetCreature(*me, LiveTriggerGUID[1]))
254 DoSummon(NPC_LIVE_TRAINEE, liveTrigger1, 1);
255 if (Creature* liveTrigger2 = Unit::GetCreature(*me, LiveTriggerGUID[2]))
256 DoSummon(NPC_LIVE_TRAINEE, liveTrigger2, 1);
257 break;
258 }
259 case NPC_LIVE_KNIGHT:
260 {
261 if (Creature* liveTrigger3 = Unit::GetCreature(*me, LiveTriggerGUID[3]))
262 DoSummon(NPC_LIVE_KNIGHT, liveTrigger3, 1);
263 if (Creature* liveTrigger5 = Unit::GetCreature(*me, LiveTriggerGUID[5]))
264 DoSummon(NPC_LIVE_KNIGHT, liveTrigger5, 1);
265 break;
266 }
267 case NPC_LIVE_RIDER:
268 {
269 if (Creature* liveTrigger4 = Unit::GetCreature(*me, LiveTriggerGUID[4]))
270 DoSummon(NPC_LIVE_RIDER, liveTrigger4, 1);
271 break;
272 }
273 }
274 }
275 else
276 {
277 switch (entry)
278 {
279 case NPC_LIVE_TRAINEE:
280 {
283 if (Creature* liveTrigger2 = Unit::GetCreature(*me, LiveTriggerGUID[4]))
284 DoSummon(NPC_LIVE_TRAINEE, liveTrigger2, 1);
285 break;
286 }
287 case NPC_LIVE_KNIGHT:
288 {
289 if (Creature* liveTrigger5 = Unit::GetCreature(*me, LiveTriggerGUID[4]))
290 DoSummon(NPC_LIVE_KNIGHT, liveTrigger5, 1);
291 break;
292 }
293 case NPC_LIVE_RIDER:
294 {
295 if (Creature* liveTrigger4 = Unit::GetCreature(*me, LiveTriggerGUID[4]))
296 DoSummon(NPC_LIVE_RIDER, liveTrigger4, 1);
297 break;
298 }
299 }
300 }
301 }
302
304 {
305 Map* map = me->GetMap();
306 if (map && map->IsDungeon())
307 {
308 Map::PlayerList const &PlayerList = map->GetPlayers();
309 if (!PlayerList.isEmpty())
310 {
311 bool checklife = false;
312 bool checkdead = false;
313 for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
314 {
315 if (i->GetSource() && i->GetSource()->IsAlive() &&
316 i->GetSource()->GetPositionX() <= POS_X_NORTH &&
317 i->GetSource()->GetPositionX() >= POS_X_SOUTH &&
318 i->GetSource()->GetPositionY() <= POS_Y_GATE &&
319 i->GetSource()->GetPositionY() >= POS_Y_EAST)
320 {
321 checklife = true;
322 }
323 else if (i->GetSource() && i->GetSource()->IsAlive() &&
324 i->GetSource()->GetPositionX() <= POS_X_NORTH &&
325 i->GetSource()->GetPositionX() >= POS_X_SOUTH &&
326 i->GetSource()->GetPositionY() >= POS_Y_GATE &&
327 i->GetSource()->GetPositionY() <= POS_Y_WEST)
328 {
329 checkdead = true;
330 }
331
332 if (checklife && checkdead)
333 return true;
334 }
335 }
336 }
337
338 return false;
339 }
340
341 void SpellHit(Unit* /*caster*/, SpellInfo const* spell) OVERRIDE
342 {
343 uint32 spellId = 0;
344 switch (spell->Id)
345 {
349 }
350 if (spellId && me->IsInCombat())
351 {
352 me->HandleEmoteCommand(EMOTE_ONESHOT_SPELL_CAST);
353 if (Creature* pRandomDeadTrigger = Unit::GetCreature(*me, DeadTriggerGUID[rand() % POS_DEAD]))
354 me->CastSpell(pRandomDeadTrigger, spellId, true);
355 }
356 }
357
358 void DamageTaken(Unit* /*who*/, uint32& damage) OVERRIDE
359 {
360 if (!phaseTwo)
361 damage = 0;
362 }
363
364 void SpellHitTarget(Unit* target, SpellInfo const* spell) OVERRIDE
365 {
366 if (!me->IsInCombat())
367 return;
368
369 switch (spell->Id)
370 {
372 DoSummon(NPC_DEAD_TRAINEE, target, 0);
373 break;
375 DoSummon(NPC_DEAD_KNIGHT, target, 0);
376 break;
378 DoSummon(NPC_DEAD_RIDER, target, 1.0f);
379 DoSummon(NPC_DEAD_HORSE, target, 1.0f);
380 break;
381 }
382 }
383
385 {
386 if (!UpdateVictim() || !CheckInRoom())
387 return;
388
389 events.Update(diff);
390
392 {
394 if (instance)
396 }
397
398 if (me->HasUnitState(UNIT_STATE_CASTING))
399 return;
400
401 while (uint32 eventId = events.ExecuteEvent())
402 {
403 switch (eventId)
404 {
405 case EVENT_SUMMON:
406 if (waves[waveCount].entry)
407 {
408 if ((waves[waveCount].mode == 2) && (GetDifficulty() == DIFFICULTY_25MAN_NORMAL))
410 else if ((waves[waveCount].mode == 0) && (GetDifficulty() == DIFFICULTY_10MAN_NORMAL))
412 else if (waves[waveCount].mode == 1)
414
415 // if group is not splitted, open gate and merge both sides at ~ 2 minutes (wave 11)
416 if (waveCount == 11)
417 {
418 if (!CheckGroupSplitted())
419 {
420 if (instance)
423 summons.DoAction(0, pred);
424 summons.DoZoneInCombat();
425 mergedSides = true;
426 }
427 }
428
429 if (waves[waveCount].mode == 1)
430 events.ScheduleEvent(EVENT_SUMMON, waves[waveCount].time);
431 else if ((waves[waveCount].mode == 2) && (GetDifficulty() == DIFFICULTY_25MAN_NORMAL))
432 events.ScheduleEvent(EVENT_SUMMON, waves[waveCount].time);
433 else if ((waves[waveCount].mode == 0) && (GetDifficulty() == DIFFICULTY_10MAN_NORMAL))
434 events.ScheduleEvent(EVENT_SUMMON, waves[waveCount].time);
435 else
436 events.ScheduleEvent(EVENT_SUMMON, 0);
437
438 ++waveCount;
439 }
440 else
441 {
442 phaseTwo = true;
445 me->SetReactState(REACT_AGGRESSIVE);
447 summons.DoAction(0, pred);
448 summons.DoZoneInCombat();
449 events.ScheduleEvent(EVENT_BOLT, 1000);
450 events.ScheduleEvent(EVENT_HARVEST, std::rand() % 15000 + 3000);
451 events.ScheduleEvent(EVENT_TELEPORT, 20000);
452 }
453 break;
454 case EVENT_BOLT:
456 events.ScheduleEvent(EVENT_BOLT, 1000);
457 break;
458 case EVENT_HARVEST:
460 events.ScheduleEvent(EVENT_HARVEST, std::rand() % 25000 + 20000);
461 break;
462 case EVENT_TELEPORT:
464 {
465 me->AttackStop();
466 if (IN_LIVE_SIDE(me))
468 else
470
471 me->getThreatManager().resetAggro(NotOnSameSide(me));
472 if (Unit* target = SelectTarget(SELECT_TARGET_NEAREST, 0))
473 {
474 me->getThreatManager().addThreat(target, 100.0f);
475 AttackStart(target);
476 }
477
478 events.ScheduleEvent(EVENT_TELEPORT, 20000);
479 }
480 break;
481 }
482 }
483
484 if (!phaseTwo)
486 }
487 };
488
490 {
491 return new boss_gothikAI(creature);
492 }
493};
494
496{
497 public:
498 npc_gothik_minion() : CreatureScript("npc_gothik_minion") { }
499
501 {
502 npc_gothik_minionAI(Creature* creature) : CombatAI(creature)
503 {
505 }
506
509
510 bool isOnSameSide(Unit const* who) const
511 {
512 return (liveSide == IN_LIVE_SIDE(who));
513 }
514
516 {
517 gateClose = param;
518 }
519
520 void DamageTaken(Unit* attacker, uint32 &damage) OVERRIDE
521 {
522 if (gateClose && !isOnSameSide(attacker))
523 damage = 0;
524 }
525
526 void JustDied(Unit* /*killer*/) OVERRIDE
527 {
528 if (me->IsSummon())
529 if (Unit* owner = me->ToTempSummon()->GetSummoner())
530 CombatAI::JustDied(owner);
531 }
532
534 {
535 if (!gateClose)
536 {
538 return;
539 }
540
541 if (!_EnterEvadeMode())
542 return;
543
544 Map* map = me->GetMap();
545 if (map->IsDungeon())
546 {
547 Map::PlayerList const &PlayerList = map->GetPlayers();
548 if (!PlayerList.isEmpty())
549 {
550 for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
551 {
552 if (i->GetSource() && i->GetSource()->IsAlive() && isOnSameSide(i->GetSource()))
553 {
554 AttackStart(i->GetSource());
555 return;
556 }
557 }
558 }
559 }
560
561 me->GetMotionMaster()->MoveIdle();
562 Reset();
563 }
564
566 {
567 if (gateClose && (!isOnSameSide(me) || (me->GetVictim() && !isOnSameSide(me->GetVictim()))))
568 {
570 return;
571 }
572
573 CombatAI::UpdateAI(diff);
574 }
575 };
576
578 {
579 return new npc_gothik_minionAI(creature);
580 }
581};
582
608
610{
611 new boss_gothik();
612 new npc_gothik_minion();
614}
@ DIFFICULTY_25MAN_NORMAL
Definition DBCEnums.h:335
@ DIFFICULTY_10MAN_NORMAL
Definition DBCEnums.h:334
std::int32_t int32
Definition Define.h:73
std::uint32_t uint32
Definition Define.h:77
#define OVERRIDE
Definition Define.h:60
@ GO_STATE_ACTIVE
Definition GameObject.h:576
@ GO_STATE_READY
Definition GameObject.h:577
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
@ EFFECT_0
@ EMOTE_ONESHOT_SPELL_CAST
@ TARGET_UNIT_SRC_AREA_ENEMY
#define SpellObjectAreaTargetSelectFn(F, I, N)
#define SF_UNARY_FUNCTION
#define WORLD_TRIGGER
Definition Unit.h:20
@ UNIT_STATE_CASTING
Definition Unit.h:527
@ REACT_PASSIVE
Definition Unit.h:1156
@ REACT_AGGRESSIVE
Definition Unit.h:1158
@ SELECT_TARGET_NEAREST
Definition UnitAI.h:26
Creatures
@ EVENT_BOLT
@ SAY_TELEPORT
#define SAY_DEATH
#define SAY_KILL
@ EVENT_NONE
@ SPELL_SHADOW_BOLT
@ EVENT_SUMMON
float const PosPlatform[4]
float const PosGroundLiveSide[4]
#define POS_Y_EAST
@ NPC_DEAD_HORSE
@ NPC_DEAD_TRAINEE
@ NPC_LIVE_TRAINEE
@ NPC_DEAD_RIDER
@ NPC_DEAD_KNIGHT
@ NPC_LIVE_KNIGHT
@ NPC_LIVE_RIDER
@ SAY_DEATH
@ SAY_TELEPORT
@ SAY_SPEECH
@ SAY_KILL
#define POS_X_SOUTH
const Position PosSummonLive[POS_LIVE]
@ SPELL_INFORM_LIVE_TRAINEE
@ H_SPELL_SHADOW_BOLT
@ SPELL_INFORM_LIVE_KNIGHT
@ SPELL_SHADOW_BOLT
@ SPELL_HARVEST_SOUL
@ SPELL_INFORM_DEAD_RIDER
@ SPELL_INFORM_LIVE_RIDER
@ SPELL_SHADOW_MARK
@ SPELL_INFORM_DEAD_TRAINEE
@ SPELL_INFORM_DEAD_KNIGHT
void AddSC_boss_gothik()
const Waves waves[]
float const PosGroundDeadSide[4]
@ POS_LIVE
@ POS_DEAD
#define POS_X_NORTH
#define IN_LIVE_SIDE(who)
const Position PosSummonDead[POS_DEAD]
#define POS_Y_GATE
#define POS_Y_WEST
@ EVENT_TELEPORT
@ EVENT_BOLT
@ EVENT_SUMMON
@ EVENT_HARVEST
InstanceScript *const instance
bool CheckInRoom()
SummonList summons
EventMap events
BossAI(Creature *creature, uint32 bossId)
void _EnterCombat()
void JustDied(Unit *killer) OVERRIDE
Definition CombatAI.cpp:65
CombatAI(Creature *c)
Definition CombatAI.h:29
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
Definition CombatAI.cpp:83
void Reset() OVERRIDE
Definition CombatAI.cpp:60
Creature * DoSummon(uint32 entry, Position const &pos, uint32 despawnTime=30000, TempSummonType summonType=TempSummonType::TEMPSUMMON_CORPSE_TIMED_DESPAWN)
bool _EnterEvadeMode()
void Talk(uint8 id, WorldObject const *whisperTarget=NULL)
bool UpdateVictim()
Creature *const me
Definition CreatureAI.h:56
virtual void EnterEvadeMode()
CreatureScript(const char *name)
bool isEmpty() const
Definition LinkedList.h:86
Definition Map.h:238
bool IsDungeon() const
Definition Map.h:369
MapRefManager PlayerList
Definition Map.h:406
PlayerList const & GetPlayers() const
Definition Map.h:407
iterator end()
iterator begin()
LinkedListHead::Iterator< MapReference const > const_iterator
HookList< ObjectAreaTargetSelectHandler > OnObjectAreaTargetSelect
SpellScriptLoader(const char *name)
void DoMeleeAttackIfReady()
Definition UnitAI.cpp:28
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
virtual void AttackStart(Unit *)
Definition UnitAI.cpp:16
Definition Unit.h:1367
static Creature * GetCreature(WorldObject &object, uint64 guid)
Definition Unit.cpp:4905
CreatureAI * GetAI(Creature *creature) const OVERRIDE
CreatureAI * GetAI(Creature *creature) const OVERRIDE
SpellScript * GetSpellScript() const OVERRIDE
@ SPELL_HARVEST_SOUL
@ BOSS_GOTHIK
Definition naxxramas.h:24
@ DATA_GOTHIK_GATE
Definition naxxramas.h:33
NotOnSameSide(Unit *source)
bool operator()(Unit const *target)
DifficultyID GetDifficulty() const
Creature * me
void DoTeleportTo(float x, float y, float z, uint32 time=0)
bool HealthBelowPct(uint32 pct) const
const T & RAID_MODE(const T &normal10, const T &normal25) const
void AttackStart(Unit *) OVERRIDE
uint32 time
uint32 entry
uint32 mode
void SpellHit(Unit *, SpellInfo const *spell) OVERRIDE
std::vector< uint64 > DeadTriggerGUID
void EnterCombat(Unit *) OVERRIDE
void JustSummoned(Creature *summon) OVERRIDE
void DoGothikSummon(uint32 entry)
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
void DamageTaken(Unit *, uint32 &damage) OVERRIDE
std::vector< uint64 > LiveTriggerGUID
boss_gothikAI(Creature *creature)
std::vector< Creature * > TriggerVct
void SummonedCreatureDespawn(Creature *summon) OVERRIDE
void SpellHitTarget(Unit *target, SpellInfo const *spell) OVERRIDE
void KilledUnit(Unit *) OVERRIDE
void JustDied(Unit *) OVERRIDE
void DoAction(int32 param) OVERRIDE
void DamageTaken(Unit *attacker, uint32 &damage) OVERRIDE
bool isOnSameSide(Unit const *who) const
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================