1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
|
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTEDCREATURE_H_
#define SCRIPTEDCREATURE_H_
#include "Creature.h"
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "EventMap.h"
#include "InstanceScript.h"
#include "TaskScheduler.h"
typedef std::list<WorldObject*> ObjectList;
class InstanceScript;
class SummonList
{
public:
typedef GuidList StorageType;
typedef StorageType::iterator iterator;
typedef StorageType::const_iterator const_iterator;
typedef StorageType::size_type size_type;
typedef StorageType::value_type value_type;
explicit SummonList(Creature* creature)
: me(creature)
{ }
// And here we see a problem of original inheritance approach. People started
// to exploit presence of std::list members, so I have to provide wrappers
iterator begin()
{
return storage_.begin();
}
const_iterator begin() const
{
return storage_.begin();
}
iterator end()
{
return storage_.end();
}
const_iterator end() const
{
return storage_.end();
}
iterator erase(iterator i)
{
return storage_.erase(i);
}
bool empty() const
{
return storage_.empty();
}
size_type size() const
{
return storage_.size();
}
void clear()
{
storage_.clear();
}
void Summon(Creature const* summon) { storage_.push_back(summon->GetGUID()); }
void Despawn(Creature const* summon) { storage_.remove(summon->GetGUID()); }
void DespawnEntry(uint32 entry);
void DespawnAll(Milliseconds delay = 0ms);
bool IsAnyCreatureAlive() const;
bool IsAnyCreatureWithEntryAlive(uint32 entry) const;
bool IsAnyCreatureInCombat() const;
template <typename T>
void DespawnIf(T const& predicate)
{
storage_.remove_if(predicate);
}
void DoAction(int32 info, uint16 max = 0)
{
if (max)
RemoveNotExisting(); // pussywizard: when max is set, non existing can be chosen and nothing will happen
StorageType listCopy = storage_;
for (StorageType::const_iterator i = listCopy.begin(); i != listCopy.end(); ++i)
{
if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
if (summon->IsAIEnabled)
summon->AI()->DoAction(info);
}
}
template <class Predicate>
void DoAction(int32 info, Predicate&& predicate, uint16 max = 0)
{
if (max)
RemoveNotExisting(); // pussywizard: when max is set, non existing can be chosen and nothing will happen
// We need to use a copy of SummonList here, otherwise original SummonList would be modified
StorageType listCopy = storage_;
Acore::Containers::RandomResize<StorageType, Predicate>(listCopy, std::forward<Predicate>(predicate), max);
for (auto const& guid : listCopy)
{
Creature* summon = ObjectAccessor::GetCreature(*me, guid);
if (summon && summon->IsAIEnabled)
{
summon->AI()->DoAction(info);
}
else if (!summon)
{
storage_.remove(guid);
}
}
}
void DoForAllSummons(std::function<void(WorldObject*)> exec)
{
// We need to use a copy of SummonList here, otherwise original SummonList would be modified
StorageType listCopy = storage_;
for (auto const& guid : listCopy)
{
if (WorldObject* summon = ObjectAccessor::GetWorldObject(*me, guid))
{
exec(summon);
}
}
}
void DoZoneInCombat(uint32 entry = 0);
void RemoveNotExisting();
bool HasEntry(uint32 entry) const;
uint32 GetEntryCount(uint32 entry) const;
void Respawn();
Creature* GetCreatureWithEntry(uint32 entry) const;
Creature* GetRandomCreatureWithEntry(uint32 entry) const;
private:
Creature* me;
StorageType storage_;
};
class EntryCheckPredicate
{
public:
EntryCheckPredicate(uint32 entry) : _entry(entry) {}
bool operator()(ObjectGuid guid) { return guid.GetEntry() == _entry; }
private:
uint32 _entry;
};
class PlayerOrPetCheck
{
public:
bool operator() (WorldObject* unit) const
{
if (!unit->IsPlayer())
if (!unit->ToUnit()->GetOwnerGUID().IsPlayer())
return true;
return false;
}
};
struct ScriptedAI : public CreatureAI
{
explicit ScriptedAI(Creature* creature);
~ScriptedAI() override {}
// *************
//CreatureAI Functions
// *************
void AttackStartNoMove(Unit* target);
// Called at any Damage from any attacker (before damage apply)
void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override;
//Called at World update tick
void UpdateAI(uint32 diff) override;
//Called at creature death
void JustDied(Unit* /*killer*/) override {}
//Called at creature killing another unit
void KilledUnit(Unit* /*victim*/) override {}
// Called when the creature summon successfully other creature
void JustSummoned(Creature* /*summon*/) override {}
// Called when a summoned creature is despawned
void SummonedCreatureDespawn(Creature* /*summon*/) override {}
// Called when hit by a spell
void SpellHit(Unit* /*caster*/, SpellInfo const* /*spell*/) override {}
// Called when spell hits a target
void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) override {}
//Called at waypoint reached or PointMovement end
void MovementInform(uint32 /*type*/, uint32 /*id*/) override {}
// Called when AI is temporarily replaced or put back when possess is applied or removed
void OnPossess(bool /*apply*/) {}
enum class Axis
{
AXIS_X,
AXIS_Y
};
/* This is called for bosses whenever an encounter is happening.
* - Arguments:
* - Position has to be passed as a constant pointer (&Position)
* - Axis is the X or Y axis that is used to decide the position threshold
* - Above decides if the boss position should be above the passed position
* or below.
* Example:
* Hodir is in room until his Y position is below the Door position:
* IsInRoom(doorPosition, AXIS_Y, false);
*/
bool IsInRoom(const Position* pos, Axis axis, bool above)
{
if (!pos)
{
return true;
}
switch (axis)
{
case Axis::AXIS_X:
if ((!above && me->GetPositionX() < pos->GetPositionX()) || me->GetPositionX() > pos->GetPositionX())
{
EnterEvadeMode();
return false;
}
break;
case Axis::AXIS_Y:
if ((!above && me->GetPositionY() < pos->GetPositionY()) || me->GetPositionY() > pos->GetPositionY())
{
EnterEvadeMode();
return false;
}
break;
}
return true;
}
// *************
// Variables
// *************
//Pointer to creature we are manipulating
Creature* me;
// *************
//Pure virtual functions
// *************
//Called at creature reset either by death or evade
void Reset() override {}
//Called at creature aggro either by MoveInLOS or Attack Start
void JustEngagedWith(Unit* /*who*/) override {}
// Called before JustEngagedWith even before the creature is in combat.
void AttackStart(Unit* /*target*/) override;
// *************
//AI Helper Functions
// *************
//Start movement toward victim
void DoStartMovement(Unit* target, float distance = 0.0f, float angle = 0.0f);
//Start no movement on victim
void DoStartNoMovement(Unit* target);
//Stop attack of current victim
void DoStopAttack();
//Cast spell by spell info
void DoCastSpell(Unit* target, SpellInfo const* spellInfo, bool triggered = false);
//Plays a sound to all nearby players
void DoPlaySoundToSet(WorldObject* source, uint32 soundId);
//Plays music for all players in the zone (zone = true) or the area (zone = false)
void DoPlayMusic(uint32 soundId, bool zone);
// Add specified amount of threat directly to victim (ignores redirection effects) - also puts victim in combat and engages them if necessary
void DoAddThreat(Unit* unit, float amount);
// Adds/removes the specified percentage from the specified victim's threat (to who, or me if not specified)
void DoModifyThreatByPercent(Unit* unit, int32 pct);
//Drops all threat to 0%. Does not remove players from the threat list
void DoResetThreat(Unit* unit);
// Resets the specified unit's threat list (me if not specified) - does not delete entries, just sets their threat to zero
void DoResetThreatList();
// Returns the threat level of victim towards who (or me if not specified)
float DoGetThreat(Unit* unit);
//Teleports a player without dropping threat (only teleports to same map)
void DoTeleportPlayer(Unit* unit, float x, float y, float z, float o);
void DoTeleportPlayer(Unit* unit, Position pos) { DoTeleportPlayer(unit, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); };
void DoTeleportAll(float x, float y, float z, float o);
//Returns friendly unit with the most amount of hp missing from max hp
Unit* DoSelectLowestHpFriendly(float range, uint32 minHPDiff = 1);
//Returns a list of friendly CC'd units within range
std::list<Creature*> DoFindFriendlyCC(float range);
//Returns a list of all friendly units missing a specific buff within range
std::list<Creature*> DoFindFriendlyMissingBuff(float range, uint32 spellId);
//Return a player with at least minimumRange from me
Player* GetPlayerAtMinimumRange(float minRange);
//Spawns a creature relative to me
Creature* DoSpawnCreature(uint32 entry, float offsetX, float offsetY, float offsetZ, float angle, uint32 type, uint32 despawntime);
bool IsUniqueTimedEventDone(uint32 id) const { return _uniqueTimedEvents.find(id) != _uniqueTimedEvents.end(); }
void SetUniqueTimedEventDone(uint32 id) { _uniqueTimedEvents.insert(id); }
void ResetUniqueTimedEvent(uint32 id) { _uniqueTimedEvents.erase(id); }
void ClearUniqueTimedEventsDone() { _uniqueTimedEvents.clear(); }
// Schedules a timed event using task scheduler.
void ScheduleTimedEvent(Milliseconds timerMin, Milliseconds timerMax, std::function<void()> exec, Milliseconds repeatMin, Milliseconds repeatMax = 0ms, uint32 uniqueId = 0);
void ScheduleTimedEvent(Milliseconds timerMax, std::function<void()> exec, Milliseconds repeatMin, Milliseconds repeatMax = 0ms, uint32 uniqueId = 0) { ScheduleTimedEvent(0ms, timerMax, exec, repeatMin, repeatMax, uniqueId); };
// Schedules a timed event using task scheduler that never repeats. Requires an unique non-zero ID.
void ScheduleUniqueTimedEvent(Milliseconds timer, std::function<void()> exec, uint32 uniqueId) { ScheduleTimedEvent(0ms, timer, exec, 0ms, 0ms, uniqueId); };
bool HealthBelowPct(uint32 pct) const { return me->HealthBelowPct(pct); }
bool HealthAbovePct(uint32 pct) const { return me->HealthAbovePct(pct); }
//Returns spells that meet the specified criteria from the creatures spell list
SpellInfo const* SelectSpell(Unit* target, uint32 school, uint32 mechanic, SelectTargetType targets, uint32 powerCostMin, uint32 powerCostMax, float rangeMin, float rangeMax, SelectEffect effect);
void SetEquipmentSlots(bool loadDefault, int32 mainHand = EQUIP_NO_CHANGE, int32 offHand = EQUIP_NO_CHANGE, int32 ranged = EQUIP_NO_CHANGE);
virtual bool CheckEvadeIfOutOfCombatArea() const { return false; }
// return true for heroic mode. i.e.
// - for dungeon in mode 10-heroic,
// - for raid in mode 10-Heroic
// - for raid in mode 25-heroic
// DO NOT USE to check raid in mode 25-normal.
bool IsHeroic() const { return _isHeroic; }
// return the dungeon or raid difficulty
Difficulty GetDifficulty() const { return _difficulty; }
// return true for 25 man or 25 man heroic mode
bool Is25ManRaid() const { return _difficulty & RAID_DIFFICULTY_MASK_25MAN; }
template<class T> inline
const T& DUNGEON_MODE(const T& normal5, const T& heroic10) const
{
switch (_difficulty)
{
case DUNGEON_DIFFICULTY_NORMAL:
return normal5;
case DUNGEON_DIFFICULTY_HEROIC:
return heroic10;
default:
break;
}
return heroic10;
}
template<class T> inline
const T& RAID_MODE(const T& normal10, const T& normal25) const
{
switch (_difficulty)
{
case RAID_DIFFICULTY_10MAN_NORMAL:
return normal10;
case RAID_DIFFICULTY_25MAN_NORMAL:
return normal25;
default:
break;
}
return normal25;
}
template<class T> inline
const T& RAID_MODE(const T& normal10, const T& normal25, const T& heroic10, const T& heroic25) const
{
switch (_difficulty)
{
case RAID_DIFFICULTY_10MAN_NORMAL:
return normal10;
case RAID_DIFFICULTY_25MAN_NORMAL:
return normal25;
case RAID_DIFFICULTY_10MAN_HEROIC:
return heroic10;
case RAID_DIFFICULTY_25MAN_HEROIC:
return heroic25;
default:
break;
}
return heroic25;
}
Player* SelectTargetFromPlayerList(float maxdist, uint32 excludeAura = 0, bool mustBeInLOS = false) const;
// Allows dropping to 1 HP but prevents creature from dying.
void SetInvincibility(bool apply) { _invincible = apply; };
[[nodiscard]] bool IsInvincible() const { return _invincible; };
// Disables creature auto attacks.
void SetAutoAttackAllowed(bool allow) { _canAutoAttack = allow; };
[[nodiscard]] bool IsAutoAttackAllowed() const { return _canAutoAttack; };
private:
Difficulty _difficulty;
bool _isHeroic;
bool _invincible;
bool _canAutoAttack;
std::unordered_set<uint32> _uniqueTimedEvents;
};
enum HealthCheckStatus
{
HEALTH_CHECK_PROCESSED,
HEALTH_CHECK_SCHEDULED,
HEALTH_CHECK_PENDING
};
struct HealthCheckEventData
{
HealthCheckEventData(uint8 healthPct, std::function<void()> exec, uint8 status = HEALTH_CHECK_SCHEDULED, bool allowedWhileCasting = true, Milliseconds Delay = 0ms) : _healthPct(healthPct), _exec(exec), _status(status), _allowedWhileCasting(allowedWhileCasting), _delay(Delay) { };
uint8 _healthPct;
std::function<void()> _exec;
uint8 _status;
bool _allowedWhileCasting;
Milliseconds _delay;
[[nodiscard]] bool HasBeenProcessed() const { return _status == HEALTH_CHECK_PROCESSED; };
[[nodiscard]] bool IsPending() const { return _status == HEALTH_CHECK_PENDING; };
[[nodiscard]] Milliseconds GetDelay() const { return _delay; };
void UpdateStatus(uint8 status) { _status = status; };
};
class BossAI : public ScriptedAI
{
public:
BossAI(Creature* creature, uint32 bossId);
~BossAI() override {}
float callForHelpRange;
InstanceScript* const instance;
bool CanRespawn() override;
void OnSpellCastFinished(SpellInfo const* spell, SpellFinishReason reason) override;
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override;
void JustSummoned(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override;
void SummonedCreatureDespawnAll() override;
void UpdateAI(uint32 diff) override;
void ScheduleHealthCheckEvent(uint32 healthPct, std::function<void()> exec, bool allowedWhileCasting = true);
void ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, std::function<void()> exec, bool allowedWhileCasting = true);
void ProcessHealthCheck();
// @brief Casts the spell after the fixed time and says the text id if provided. Timer will run even if the creature is casting or out of combat.
// @param spellId The spell to cast.
// @param timer The time to wait before casting the spell.
// @param textId The text id to say.
void ScheduleEnrageTimer(uint32 spellId, Milliseconds timer, uint8 textId = 0);
// Hook used to execute events scheduled into EventMap without the need
// to override UpdateAI
// note: You must re-schedule the event within this method if the event
// is supposed to run more than once
virtual void ExecuteEvent(uint32 /*eventId*/) { }
virtual void ScheduleTasks() { }
void Reset() override { _Reset(); }
void JustEngagedWith(Unit* /*who*/) override { _JustEngagedWith(); }
void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER) override { _EnterEvadeMode(why); }
void JustDied(Unit* /*killer*/) override { _JustDied(); }
void JustReachedHome() override { _JustReachedHome(); }
protected:
void _Reset();
void _JustEngagedWith();
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
void _EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
void TeleportCheaters();
SummonList summons;
private:
uint32 const _bossId;
std::list<HealthCheckEventData> _healthCheckEvents;
HealthCheckEventData _nextHealthCheck;
};
class WorldBossAI : public ScriptedAI
{
public:
WorldBossAI(Creature* creature);
~WorldBossAI() override {}
void JustSummoned(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override;
void UpdateAI(uint32 diff) override;
// Hook used to execute events scheduled into EventMap without the need
// to override UpdateAI
// note: You must re-schedule the event within this method if the event
// is supposed to run more than once
virtual void ExecuteEvent(uint32 /*eventId*/) { }
void Reset() override { _Reset(); }
void JustEngagedWith(Unit* /*who*/) override { _JustEngagedWith(); }
void JustDied(Unit* /*killer*/) override { _JustDied(); }
protected:
void _Reset();
void _JustEngagedWith();
void _JustDied();
EventMap events;
SummonList summons;
};
// SD2 grid searchers.
Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive = true);
GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool onlySpawned = false);
void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
void GetDeadCreatureListInGrid(std::list<Creature*>& list, WorldObject* source, float maxSearchRange, bool alive = false);
#endif // SCRIPTEDCREATURE_H_
|