Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
boss_lurker_below.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_the_lurker_below
8SD%Complete: 80
9SDComment: Coilfang Frenzy, find out how could we fishing in the strangepool
10SDCategory: The Lurker Below
11EndScriptData */
12
13#include "ScriptMgr.h"
14#include "ScriptedCreature.h"
15#include "serpent_shrine.h"
16#include "Spell.h"
17#include "Player.h"
18
20{
21 SPELL_SPOUT = 37433,
25 SPELL_GEYSER = 37478,
26 SPELL_WHIRL = 37660,
29 SPELL_EMERGE = 20568,
30
31
32 // Ambusher spells
34 SPELL_SHOOT = 37770,
35 // Guardian spells
36 SPELL_ARCINGSMASH = 38761, // Wrong SpellId. Can't find the right one.
38};
39
45
46#define EMOTE_SPOUT "The Lurker Below takes a deep breath."
47
48#define SPOUT_DIST 100
49
50float AddPos[9][3] =
51{
52 // MOVE_AMBUSHER_1 X, Y, Z
53 {2.8553810f, -459.823914f, -19.182686f},
54 {12.400000f, -466.042267f, -19.182686f},
55 {51.366653f, -460.836060f, -19.182686f},
56 {62.597980f, -457.433044f, -19.182686f},
57 {77.607452f, -384.302765f, -19.182686f},
58 {63.897900f, -378.984924f, -19.182686f},
59 {34.447250f, -387.333618f, -19.182686f},
60 {14.388216f, -423.468018f, -19.625271f},
61 {42.471519f, -445.115295f, -19.769423f}
62};
63
65{
66public:
67 boss_the_lurker_below() : CreatureScript("boss_the_lurker_below") { }
68
70 {
71 return new boss_the_lurker_belowAI(creature);
72 }
73
75 {
77 {
78 SetCombatMovement(false);
79 instance = creature->GetInstanceScript();
80 }
81
84
85 bool Spawned;
87 bool InRange;
99
100 bool CheckCanStart()//check if players fished
101 {
103 return false;
104 return true;
105 }
107 {
108 me->SetSwim(true);
109 me->SetDisableGravity(true);
110 SpoutAnimTimer = 1000;
111 RotTimer = 0;
112 WaterboltTimer = 15000; // give time to get in range when fight starts
113 SpoutTimer = 45000;
114 WhirlTimer = 18000; // after avery spout
115 PhaseTimer = 120000;
116 GeyserTimer = rand()%5000 + 15000;
117 CheckTimer = 15000; // give time to get in range when fight starts
118 WaitTimer = 60000; // never reached
119 WaitTimer2 = 60000; // never reached
120
121 Submerged = true; // will be false at combat start
122 Spawned = false;
123 InRange = false;
124 CanStartEvent = false;
125
126 Summons.DespawnAll();
127
128 if (instance)
129 {
132 }
133 DoCast(me, SPELL_SUBMERGE); // submerge anim
134 me->SetVisible(false); // we start invis under water, submerged
137 }
138
139 void JustDied(Unit* /*killer*/) OVERRIDE
140 {
141 if (instance)
142 {
145 }
146
147 Summons.DespawnAll();
148 }
149
150 void EnterCombat(Unit* /*who*/) OVERRIDE
151 {
152 if (instance)
154 }
155
157
158 {
159 if (!CanStartEvent) // boss is invisible, don't attack
160 return;
161 if (!me->GetVictim() && who->IsValidAttackTarget(me))
162 {
163 float attackRadius = me->GetAttackDistance(who);
164 if (me->IsWithinDistInMap(who, attackRadius))
165 AttackStart(who);
166 }
167 }
168
170 {
171 if (type == ROTATE_MOTION_TYPE)
172 me->SetReactState(REACT_AGGRESSIVE);
173 }
174
176 {
177 if (!CanStartEvent) // boss is invisible, don't attack
178 {
179 if (CheckCanStart())
180 {
181 if (Submerged)
182 {
183 me->SetVisible(true);
184 Submerged = false;
185 WaitTimer2 = 500;
186 }
187
188 if (!Submerged && WaitTimer2 <= diff) // wait 500ms before emerge anim
189 {
190 me->RemoveAllAuras();
191 me->SetUInt32Value(UNIT_FIELD_NPC_EMOTESTATE, 0);
192 DoCast(me, SPELL_EMERGE, false);
193 WaitTimer2 = 60000; // never reached
194 WaitTimer = 3000;
195 }
196 else
197 WaitTimer2 -= diff;
198
199 if (WaitTimer <= diff) // wait 3secs for emerge anim, then attack
200 {
201 WaitTimer = 3000;
202 CanStartEvent = true; // fresh fished from pool
205 }
206 else
207 WaitTimer -= diff;
208 }
209 return;
210 }
211
212 if (me->getThreatManager().getThreatList().empty()) // check if should evade
213 {
214 if (me->IsInCombat())
216 return;
217 }
218 if (!Submerged)
219 {
220 if (PhaseTimer <= diff)
221 {
222 me->InterruptNonMeleeSpells(false);
224 PhaseTimer = 60000; // 60secs submerged
225 Submerged = true;
226 } else PhaseTimer-=diff;
227
228 if (SpoutTimer <= diff)
229 {
230 me->MonsterTextEmote(EMOTE_SPOUT, 0, true);
231 me->SetReactState(REACT_PASSIVE);
232 me->GetMotionMaster()->MoveRotate(20000, std::rand() % 1 ? ROTATE_DIRECTION_LEFT : ROTATE_DIRECTION_RIGHT);
233 SpoutTimer = 45000;
234 WhirlTimer = 20000; // whirl directly after spout
235 RotTimer = 20000;
236 return;
237 } else SpoutTimer -= diff;
238
239 // Whirl directly after a Spout and at random times
240 if (WhirlTimer <= diff)
241 {
242 WhirlTimer = 18000;
244 } else WhirlTimer -= diff;
245
246 if (CheckTimer <= diff)//check if there are players in melee range
247 {
248 InRange = false;
249 Map* map = me->GetMap();
250 Map::PlayerList const &PlayerList = map->GetPlayers();
251 if (!PlayerList.isEmpty())
252 {
253 for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
254 {
255 if (me->IsWithinMeleeRange(i->GetSource()))
256 InRange = true;
257 }
258 }
259 CheckTimer = 2000;
260 } else CheckTimer -= diff;
261
262 if (RotTimer)
263 {
264 Map* map = me->GetMap();
265 if (map->IsDungeon())
266 {
267 Map::PlayerList const &PlayerList = map->GetPlayers();
268 for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
269 {
270 if (i->GetSource() && i->GetSource()->IsAlive() && me->HasInArc(float(diff/20000*M_PI*2), i->GetSource()) && me->IsWithinDist(i->GetSource(), SPOUT_DIST) && !i->GetSource()->IsInWater())
271 DoCast(i->GetSource(), SPELL_SPOUT, true); // only knock back players in arc, in 100yards, not in water
272 }
273 }
274
275 if (SpoutAnimTimer <= diff)
276 {
277 DoCast(me, SPELL_SPOUT_ANIM, true);
278 SpoutAnimTimer = 1000;
279 } else SpoutAnimTimer -= diff;
280
281 if (RotTimer <= diff)
282 {
283 RotTimer = 0;
284 } else RotTimer -= diff;
285 return;
286 }
287
288 if (GeyserTimer <= diff)
289 {
291 if (!target && me->GetVictim())
292 target = me->GetVictim();
293 if (target)
294 DoCast(target, SPELL_GEYSER, true);
295 GeyserTimer = rand()%5000 + 15000;
296 } else GeyserTimer -= diff;
297
298 if (!InRange) // if on players in melee range cast Waterbolt
299 {
300 if (WaterboltTimer <= diff)
301 {
303 if (!target && me->GetVictim())
304 target = me->GetVictim();
305 if (target)
306 DoCast(target, SPELL_WATERBOLT, true);
307 WaterboltTimer = 3000;
308 } else WaterboltTimer -= diff;
309 }
310
311 if (!UpdateVictim())
312 return;
313
315 }
316 else // submerged
317 {
318 if (PhaseTimer <= diff)
319 {
320 Submerged = false;
321 me->InterruptNonMeleeSpells(false); // shouldn't be any
322 me->RemoveAllAuras();
325 DoCast(me, SPELL_EMERGE, true);
326 Spawned = false;
327 SpoutTimer = 3000; // directly cast Spout after emerging!
328 PhaseTimer = 120000;
329 return;
330 } else PhaseTimer-=diff;
331
332 if (me->getThreatManager().getThreatList().empty()) // check if should evade
333 {
335 return;
336 }
337
338 if (!me->IsInCombat())
340
341 if (!Spawned)
342 {
344 // spawn adds
345 for (uint8 i = 0; i < 9; ++i)
346 if (Creature* summoned = me->SummonCreature(i < 6 ? NPC_COILFANG_AMBUSHER : NPC_COILFANG_GUARDIAN, AddPos[i][0], AddPos[i][1], AddPos[i][2], 0, TempSummonType::TEMPSUMMON_CORPSE_DESPAWN, 0))
347 Summons.Summon(summoned);
348 Spawned = true;
349 }
350 }
351 }
352 };
353};
354
356{
357public:
358 npc_coilfang_ambusher() : CreatureScript("npc_coilfang_ambusher") { }
359
361 {
362 return new npc_coilfang_ambusherAI(creature);
363 }
364
366 {
368 {
369 SetCombatMovement(false);
370 }
371
374
376 {
377 MultiShotTimer = 10000;
378 ShootBowTimer = 4000;
379 }
380
382
383 {
384 if (!who || me->GetVictim())
385 return;
386
387 if (who->isInAccessiblePlaceFor(me) && me->IsValidAttackTarget(who) && me->IsWithinDistInMap(who, 45))
388 AttackStart(who);
389 }
390
392 {
393 if (MultiShotTimer <= diff)
394 {
395 if (me->GetVictim())
397
398 MultiShotTimer = 10000+rand()%10000;
399 ShootBowTimer += 1500; // add global cooldown
400 } else MultiShotTimer -= diff;
401
402 if (ShootBowTimer <= diff)
403 {
404 int bp0 = 1100;
405 if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
406 me->CastCustomSpell(target, SPELL_SHOOT, &bp0, NULL, NULL, true);
407 ShootBowTimer = 4000+rand()%5000;
408 MultiShotTimer += 1500; // add global cooldown
409 } else ShootBowTimer -= diff;
410 }
411 };
412};
413
415{
416 public:
417 go_strange_pool() : GameObjectScript("go_strange_pool") { }
418
420 {
421 // 25%
422 if (InstanceScript* instanceScript = go->GetInstanceScript())
423 if (!(std::rand() % 3))
424 {
425 if (instanceScript->GetData(DATA_STRANGE_POOL) == NOT_STARTED)
426 {
427 go->CastSpell(player, 54587);
428 instanceScript->SetData(DATA_STRANGE_POOL, IN_PROGRESS);
429 }
430 return true;
431 }
432
433 return false;
434 }
435};
436
#define M_PI
Definition Common.h:192
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
#define OVERRIDE
Definition Define.h:60
@ IN_PROGRESS
@ DONE
@ NOT_STARTED
@ ROTATE_DIRECTION_RIGHT
@ ROTATE_DIRECTION_LEFT
@ ROTATE_MOTION_TYPE
@ TEMPSUMMON_CORPSE_DESPAWN
Definition Object.h:72
@ EMOTE_STATE_SUBMERGED
@ REACT_PASSIVE
Definition Unit.h:1156
@ REACT_AGGRESSIVE
Definition Unit.h:1158
@ UNIT_FLAG_NOT_SELECTABLE
Definition Unit.h:650
@ UNIT_FLAG_IMMUNE_TO_PC
Definition Unit.h:633
@ SELECT_TARGET_RANDOM
Definition UnitAI.h:23
@ UNIT_FIELD_FLAGS
@ UNIT_FIELD_NPC_EMOTESTATE
Creatures
@ SPELL_SUBMERGE
@ SPELL_WATERBOLT
@ SPELL_ARCINGSMASH
@ NPC_COILFANG_GUARDIAN
@ NPC_COILFANG_AMBUSHER
void AddSC_boss_the_lurker_below()
@ SPELL_SPREAD_SHOT
@ SPELL_SHOOT
@ SPELL_WATERBOLT
@ SPELL_SPOUT_ANIM
@ SPELL_EMERGE
@ SPELL_SPOUT
@ SPELL_SPOUT_BREATH
@ SPELL_WHIRL
@ SPELL_SUBMERGE
@ SPELL_GEYSER
float AddPos[9][3]
#define EMOTE_SPOUT
#define SPOUT_DIST
void DoZoneInCombat(Creature *creature=NULL, float maxRangeToNearestTarget=50.0f)
bool UpdateVictim()
virtual void EnterEvadeMode()
CreatureScript(const char *name)
GameObjectScript(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
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
Definition Unit.h:1367
InstanceScript * GetInstanceScript()
Definition Object.cpp:1612
CreatureAI * GetAI(Creature *creature) const OVERRIDE
bool OnGossipHello(Player *player, GameObject *go) OVERRIDE
CreatureAI * GetAI(Creature *creature) const OVERRIDE
@ DATA_STRANGE_POOL
@ DATA_THELURKERBELOWEVENT
void SetCombatMovement(bool allowMovement)
Creature * me
ScriptedAI(Creature *creature)
void AttackStart(Unit *) OVERRIDE
void MovementInform(uint32 type, uint32) OVERRIDE
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================
void UpdateAI(uint32 diff) OVERRIDE
== Triggered Actions Requested ==================