TrinityCore
boss_lurker_below.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/* ScriptData
19SDName: boss_the_lurker_below
20SD%Complete: 80
21SDComment: Coilfang Frenzy, find out how could we fishing in the strangepool
22SDCategory: The Lurker Below
23EndScriptData */
24
25#include "ScriptMgr.h"
26#include "GameObject.h"
27#include "GameObjectAI.h"
28#include "InstanceScript.h"
29#include "Map.h"
30#include "MotionMaster.h"
31#include "Player.h"
32#include "ScriptedCreature.h"
33#include "serpent_shrine.h"
34#include "TemporarySummon.h"
35
37{
38 SPELL_SPOUT = 37433,
41 SPELL_GEYSER = 37478,
42 SPELL_WHIRL = 37660,
45 SPELL_EMERGE = 20568,
46
47 // Ambusher spells
49 SPELL_SHOOT = 37770,
50 // Guardian spells
51 SPELL_ARCINGSMASH = 38761, // Wrong SpellId. Can't find the right one.
52 SPELL_HAMSTRING = 26211
53};
54
55enum Misc
56{
57 EMOTE_SPOUT = 0, // "The Lurker Below takes a deep breath."
58 SPOUT_DIST = 100
59};
60
62{
65};
66
67float AddPos[9][3] =
68{
69 // MOVE_AMBUSHER_1 X, Y, Z
70 {2.8553810f, -459.823914f, -19.182686f},
71 {12.400000f, -466.042267f, -19.182686f},
72 {51.366653f, -460.836060f, -19.182686f},
73 {62.597980f, -457.433044f, -19.182686f},
74 {77.607452f, -384.302765f, -19.182686f},
75 {63.897900f, -378.984924f, -19.182686f},
76 {34.447250f, -387.333618f, -19.182686f},
77 {14.388216f, -423.468018f, -19.625271f},
78 {42.471519f, -445.115295f, -19.769423f}
79};
80
82{
84 {
85 Initialize();
86 SetCombatMovement(false);
87 }
88
90 {
91 SpoutAnimTimer = 1000;
92 RotTimer = 0;
93 WaterboltTimer = 15000; // give time to get in range when fight starts
94 SpoutTimer = 45000;
95 WhirlTimer = 18000; // after avery spout
96 PhaseTimer = 120000;
97 GeyserTimer = rand32() % 5000 + 15000;
98 CheckTimer = 15000; // give time to get in range when fight starts
99 WaitTimer = 60000; // never reached
100 WaitTimer2 = 60000; // never reached
101
102 Submerged = true; // will be false at combat start
103 Spawned = false;
104 InRange = false;
105 CanStartEvent = false;
106 }
107
122
123 bool CheckCanStart()//check if players fished
124 {
126 return false;
127 return true;
128 }
129
130 void Reset() override
131 {
132 me->SetSwim(true);
133 me->SetDisableGravity(true);
134 Initialize();
135
136 _Reset();
138 DoCast(me, SPELL_SUBMERGE); // submerge anim
139 me->SetVisible(false); // we start invis under water, submerged
140 me->SetUninteractible(true);
141 me->SetImmuneToPC(true);
142 me->SetCanMelee(true);
143 }
144
145 void JustDied(Unit* /*killer*/) override
146 {
147 _JustDied();
149 }
150
151 void MoveInLineOfSight(Unit* who) override
152 {
153 if (!CanStartEvent) // boss is invisible, don't attack
154 return;
155 if (!me->GetVictim() && who->IsValidAttackTarget(me))
156 {
157 float attackRadius = me->GetAttackDistance(who);
158 if (me->IsWithinDistInMap(who, attackRadius))
159 AttackStart(who);
160 }
161 }
162
163 void MovementInform(uint32 type, uint32 /*id*/) override
164 {
165 if (type == ROTATE_MOTION_TYPE)
167 }
168
169 void UpdateAI(uint32 diff) override
170 {
171 if (!CanStartEvent) // boss is invisible, don't attack
172 {
173 if (CheckCanStart())
174 {
175 if (Submerged)
176 {
177 me->SetVisible(true);
178 me->SetCanMelee(true);
179 Submerged = false;
180 WaitTimer2 = 500;
181 }
182 else if (WaitTimer2 <= diff) // wait 500ms before emerge anim
183 {
186 DoCast(me, SPELL_EMERGE, false);
187 WaitTimer2 = 60000; // never reached
188 WaitTimer = 3000;
189 }
190 else
191 WaitTimer2 -= diff;
192
193 if (WaitTimer <= diff) // wait 3secs for emerge anim, then attack
194 {
195 WaitTimer = 3000;
196 CanStartEvent = true; // fresh fished from pool
197 me->SetImmuneToPC(false);
198 me->SetUninteractible(false);
199 }
200 else
201 WaitTimer -= diff;
202 }
203 return;
204 }
205
206 if (!me->IsThreatened()) // check if should evade
207 {
208 if (me->IsEngaged())
210 return;
211 }
212 if (!Submerged)
213 {
214 if (PhaseTimer <= diff)
215 {
217 me->SetCanMelee(false);
219 PhaseTimer = 60000; // 60secs submerged
220 Submerged = true;
221 }
222 else
223 PhaseTimer -= diff;
224
225 if (SpoutTimer <= diff)
226 {
231 SpoutTimer = 45000;
232 WhirlTimer = 20000; // whirl directly after spout
233 RotTimer = 20000;
234 return;
235 }
236 else
237 SpoutTimer -= diff;
238
239 // Whirl directly after a Spout and at random times
240 if (WhirlTimer <= diff)
241 {
242 WhirlTimer = 18000;
244 }
245 else
246 WhirlTimer -= diff;
247
248 if (CheckTimer <= diff) // check if there are players in melee range
249 {
250 InRange = false;
251 Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers();
252 if (!PlayerList.isEmpty())
253 {
254 for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
255 {
256 if (me->IsWithinMeleeRange(i->GetSource()))
257 InRange = true;
258 }
259 }
260 CheckTimer = 2000;
261 }
262 else
263 CheckTimer -= diff;
264
265 if (RotTimer)
266 {
267 if (SpoutAnimTimer <= diff)
268 {
269 DoCast(me, SPELL_SPOUT, true);
270 SpoutAnimTimer = 200;
271 } else SpoutAnimTimer -= diff;
272
273 if (RotTimer <= diff)
274 {
275 RotTimer = 0;
276 }
277 else
278 RotTimer -= diff;
279 return;
280 }
281
282 if (GeyserTimer <= diff)
283 {
285 if (!target && me->GetVictim())
286 target = me->GetVictim();
287 if (target)
288 DoCast(target, SPELL_GEYSER, true);
289 GeyserTimer = rand32() % 5000 + 15000;
290 }
291 else
292 GeyserTimer -= diff;
293
294 if (!InRange) // if on players in melee range cast Waterbolt
295 {
296 if (WaterboltTimer <= diff)
297 {
299 if (!target && me->GetVictim())
300 target = me->GetVictim();
301 if (target)
302 DoCast(target, SPELL_WATERBOLT, true);
303 WaterboltTimer = 3000;
304 }
305 else
306 WaterboltTimer -= diff;
307 }
308
309 if (!UpdateVictim())
310 return;
311 }
312 else // submerged
313 {
314 if (PhaseTimer <= diff)
315 {
316 Submerged = false;
317 me->InterruptNonMeleeSpells(false); // shouldn't be any
319 me->SetImmuneToPC(false);
321 me->SetCanMelee(true);
322 DoCast(me, SPELL_EMERGE, true);
323 Spawned = false;
324 SpoutTimer = 3000; // directly cast Spout after emerging!
325 PhaseTimer = 120000;
326 return;
327 }
328 else
329 PhaseTimer -= diff;
330
331 if (!me->IsThreatened()) // check if should evade
332 {
334 return;
335 }
336
337 if (!me->IsInCombat())
339
340 if (!Spawned)
341 {
343 // spawn adds
344 for (uint8 i = 0; i < 9; ++i)
346 Spawned = true;
347 }
348 }
349 }
350 };
351
353{
355 {
356 Initialize();
357 SetCombatMovement(false);
358 }
359
361 {
362 MultiShotTimer = 10000;
363 ShootBowTimer = 4000;
364 }
365
368
369 void Reset() override
370 {
371 Initialize();
372 }
373
374 void MoveInLineOfSight(Unit* who) override
375
376 {
377 if (!who || me->GetVictim())
378 return;
379
380 if (who->isInAccessiblePlaceFor(me) && me->IsValidAttackTarget(who) && me->IsWithinDistInMap(who, 45))
381 AttackStart(who);
382 }
383
384 void UpdateAI(uint32 diff) override
385 {
386 if (MultiShotTimer <= diff)
387 {
388 if (me->GetVictim())
390
391 MultiShotTimer = 10000 + rand32() % 10000;
392 ShootBowTimer += 1500; // add global cooldown
393 } else MultiShotTimer -= diff;
394
395 if (ShootBowTimer <= diff)
396 {
398 me->CastSpell(target, SPELL_SHOOT, CastSpellExtraArgs(TRIGGERED_FULL_MASK).AddSpellBP0(1100));
399 ShootBowTimer = 4000 + rand32() % 5000;
400 MultiShotTimer += 1500; // add global cooldown
401 } else ShootBowTimer -= diff;
402 }
403};
404
406{
407 go_strange_pool(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { }
408
410
411 bool OnGossipHello(Player* player) override
412 {
413 // 25%
414 if (!urand(0, 3))
415 {
417 {
418 me->CastSpell(player, 54587);
420 }
421 return true;
422 }
423
424 return false;
425 }
426};
427
429{
433}
#define M_PI
Definition: Common.h:115
uint8_t uint8
Definition: Define.h:144
uint32_t uint32
Definition: Define.h:142
@ IN_PROGRESS
@ NOT_STARTED
@ ROTATE_DIRECTION_RIGHT
@ ROTATE_DIRECTION_LEFT
@ ROTATE_MOTION_TYPE
@ TEMPSUMMON_CORPSE_DESPAWN
Definition: ObjectDefines.h:67
Spells
Definition: PlayerAI.cpp:32
uint32 urand(uint32 min, uint32 max)
Definition: Random.cpp:42
uint32 rand32()
Definition: Random.cpp:70
@ EMOTE_ONESHOT_NONE
@ TRIGGERED_FULL_MASK
Used when doing CastSpell with triggered == true.
Definition: SpellDefines.h:266
@ REACT_PASSIVE
Definition: UnitDefines.h:506
@ REACT_AGGRESSIVE
Definition: UnitDefines.h:508
@ UNIT_FLAG_IMMUNE_TO_PC
Definition: UnitDefines.h:152
@ NPC_COILFANG_GUARDIAN
@ NPC_COILFANG_AMBUSHER
void AddSC_boss_the_lurker_below()
@ SPOUT_DIST
@ EMOTE_SPOUT
@ SPELL_SPREAD_SHOT
@ SPELL_HAMSTRING
@ SPELL_SHOOT
@ SPELL_WATERBOLT
@ SPELL_ARCINGSMASH
@ SPELL_KNOCKBACK
@ SPELL_EMERGE
@ SPELL_SPOUT
@ SPELL_SPOUT_BREATH
@ SPELL_WHIRL
@ SPELL_SUBMERGE
@ SPELL_GEYSER
float AddPos[9][3]
InstanceScript *const instance
void _JustDied()
void DoZoneInCombat()
Definition: CreatureAI.h:161
virtual void EnterEvadeMode(EvadeReason why=EvadeReason::Other)
Definition: CreatureAI.cpp:219
void Talk(uint8 id, WorldObject const *whisperTarget=nullptr)
Definition: CreatureAI.cpp:56
bool UpdateVictim()
Definition: CreatureAI.cpp:245
Creature *const me
Definition: CreatureAI.h:61
void SetCanMelee(bool canMelee, bool fleeFromMelee=false)
Definition: Creature.cpp:2822
float GetAttackDistance(Unit const *player) const
Definition: Creature.cpp:2153
void SetImmuneToPC(bool apply) override
Definition: Creature.h:170
bool IsEngaged() const override
Definition: Creature.cpp:3601
void SetReactState(ReactStates st)
Definition: Creature.h:160
GameObject *const me
Definition: GameObjectAI.h:50
bool isEmpty() const
Definition: LinkedList.h:110
iterator end()
Definition: MapRefManager.h:35
iterator begin()
Definition: MapRefManager.h:34
PlayerList const & GetPlayers() const
Definition: Map.h:367
void MoveRotate(uint32 id, RotateDirection direction, Optional< Milliseconds > time={}, Optional< float > turnSpeed={}, Optional< float > totalTurnAngle={})
Makes the Unit turn in place.
SpellCastResult DoCastVictim(uint32 spellId, CastSpellExtraArgs const &args={})
Definition: UnitAI.cpp:180
Unit * SelectTarget(SelectTargetMethod targetType, uint32 offset=0, float dist=0.0f, bool playerOnly=false, bool withTank=true, int32 aura=0)
Definition: UnitAI.cpp:79
SpellCastResult DoCast(uint32 spellId)
Definition: UnitAI.cpp:89
Definition: Unit.h:627
bool IsWithinMeleeRange(Unit const *obj) const
Definition: Unit.h:699
void SetVisible(bool x)
Definition: Unit.cpp:8351
void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid=0, bool withInstant=true)
Definition: Unit.cpp:3089
void ReplaceAllUnitFlags(UnitFlags flags)
Definition: Unit.h:835
MotionMaster * GetMotionMaster()
Definition: Unit.h:1652
bool SetDisableGravity(bool disable, bool updateAnimTier=true)
Definition: Unit.cpp:12725
void SetUninteractible(bool apply)
Definition: Unit.cpp:8147
bool isInAccessiblePlaceFor(Creature const *c) const
Definition: Unit.cpp:3165
bool IsThreatened() const
Definition: Unit.cpp:8163
bool SetSwim(bool enable)
Definition: Unit.cpp:12801
void SetEmoteState(Emote emote)
Definition: Unit.h:852
Unit * GetVictim() const
Definition: Unit.h:715
void RemoveAllAuras()
Definition: Unit.cpp:4242
bool IsInCombat() const
Definition: Unit.h:1043
Map * GetMap() const
Definition: Object.h:624
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition: Object.cpp:2896
bool IsValidAttackTarget(WorldObject const *target, SpellInfo const *bySpell=nullptr) const
Definition: Object.cpp:2991
TempSummon * SummonCreature(uint32 entry, Position const &pos, TempSummonType despawnType=TEMPSUMMON_MANUAL_DESPAWN, Milliseconds despawnTime=0s, uint32 vehId=0, uint32 spellId=0, ObjectGuid privateObjectOwner=ObjectGuid::Empty)
Definition: Object.cpp:2025
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition: Object.cpp:1147
virtual uint32 GetData(uint32) const
Definition: ZoneScript.h:91
virtual void SetData(uint32, uint32)
Definition: ZoneScript.h:92
@ DATA_STRANGE_POOL
#define RegisterSerpentshrineCavernGameObjectAI(ai_name)
@ BOSS_THE_LURKER_BELOW
#define RegisterSerpentshrineCavernCreatureAI(ai_name)
void AttackStart(Unit *) override
== Triggered Actions Requested ==================
void SetCombatMovement(bool allowMovement)
void MovementInform(uint32 type, uint32) override
void UpdateAI(uint32 diff) override
void MoveInLineOfSight(Unit *who) override
void JustDied(Unit *) override
boss_the_lurker_below(Creature *creature)
bool OnGossipHello(Player *player) override
InstanceScript * instance
go_strange_pool(GameObject *go)
void UpdateAI(uint32 diff) override
void MoveInLineOfSight(Unit *who) override
npc_coilfang_ambusher(Creature *creature)