/*
* This file is part of the TrinityCore 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 .
*/
#ifndef TRINITY_SCRIPTEDCREATURE_H
#define TRINITY_SCRIPTEDCREATURE_H
#include "CreatureAI.h"
#include "Creature.h" // convenience include for scripts, all uses of ScriptedCreature also need Creature (except ScriptedCreature itself doesn't need Creature)
#include "DBCEnums.h"
#include "EventMap.h"
#include "TaskScheduler.h"
class InstanceScript;
enum SelectTargetType : uint8;
enum SelectEffect : uint8;
class TC_GAME_API 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();
}
// Clear the underlying storage. This does NOT despawn the creatures - use DespawnAll for that!
void clear()
{
_storage.clear();
}
void Summon(Creature const* summon);
void Despawn(Creature const* summon);
void DespawnEntry(uint32 entry);
void DespawnAll();
template
void DespawnIf(T const& predicate)
{
_storage.remove_if(predicate);
}
template
void DoAction(int32 info, Predicate&& predicate, uint16 max = 0)
{
// We need to use a copy of SummonList here, otherwise original SummonList would be modified
StorageType listCopy;
std::copy_if(std::begin(_storage), std::end(_storage), std::inserter(listCopy, std::end(listCopy)), predicate);
DoActionImpl(info, listCopy, max);
}
void DoZoneInCombat(uint32 entry = 0);
void RemoveNotExisting();
bool HasEntry(uint32 entry) const;
private:
void DoActionImpl(int32 action, StorageType& summons, uint16 max);
Creature* _me;
StorageType _storage;
};
class TC_GAME_API EntryCheckPredicate
{
public:
EntryCheckPredicate(uint32 entry) : _entry(entry) { }
bool operator()(ObjectGuid const& guid) const { return guid.GetEntry() == _entry; }
private:
uint32 _entry;
};
class TC_GAME_API DummyEntryCheckPredicate
{
public:
bool operator()(ObjectGuid const&) const { return true; }
};
struct TC_GAME_API ScriptedAI : public CreatureAI
{
public:
explicit ScriptedAI(Creature* creature, uint32 scriptId = 0) noexcept;
virtual ~ScriptedAI() { }
// *************
// CreatureAI Functions
// *************
void AttackStartNoMove(Unit* target);
// Called at World update tick
virtual void UpdateAI(uint32 diff) override;
// *************
// Variables
// *************
// *************
// Pure virtual functions
// *************
// 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);
// Add specified amount of threat directly to victim (ignores redirection effects) - also puts victim in combat and engages them if necessary
void AddThreat(Unit* victim, float amount, Unit* who = nullptr);
// Adds/removes the specified percentage from the specified victim's threat (to who, or me if not specified)
void ModifyThreatByPercent(Unit* victim, int32 pct, Unit* who = nullptr);
// Resets the victim's threat level to who (or me if not specified) to zero
void ResetThreat(Unit* victim, Unit* who = nullptr);
// Resets the specified unit's threat list (me if not specified) - does not delete entries, just sets their threat to zero
void ResetThreatList(Unit* who = nullptr);
// Returns the threat level of victim towards who (or me if not specified)
float GetThreat(Unit const* victim, Unit const* who = nullptr);
// Stops combat, ignoring restrictions, for the given creature
void ForceCombatStop(Creature* who, bool reset = true);
// Stops combat, ignoring restrictions, for the found creatures
void ForceCombatStopForCreatureEntry(uint32 entry, float maxSearchRange = 250.0f, bool samePhase = true, bool reset = true);
// Stops combat, ignoring restrictions, for the found creatures
void ForceCombatStopForCreatureEntry(std::vector creatureEntries, float maxSearchRange = 250.0f, bool samePhase = true, bool reset = true);
void DoTeleportTo(float x, float y, float z, uint32 time = 0);
void DoTeleportTo(float const pos[4]);
// Teleports a player without dropping threat (only teleports to same map)
void DoTeleportPlayer(Unit* unit, float x, float y, float z, float o);
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 friendly unit with hp pct below specified and with specified entry
Unit* DoSelectBelowHpPctFriendlyWithEntry(uint32 entry, float range, uint8 hpPct = 1, bool excludeSelf = true);
// Returns a list of friendly CC'd units within range
std::list DoFindFriendlyCC(float range);
// Returns a list of all friendly units missing a specific buff within range
std::list 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, Milliseconds despawntime);
bool HealthBelowPct(uint32 pct) const;
bool HealthAbovePct(uint32 pct) const;
// Returns spells that meet the specified criteria from the creatures spell list
SpellInfo const* SelectSpell(Unit* target, uint32 school, uint32 mechanic, SelectTargetType targets, 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);
// Used to control if MoveChase() is to be used or not in AttackStart(). Some creatures does not chase victims
// NOTE: If you use SetCombatMovement while the creature is in combat, it will do NOTHING - This only affects AttackStart
// You should make the necessary to make it happen so.
// Remember that if you modified _isCombatMovementAllowed (e.g: using SetCombatMovement) it will not be reset at Reset().
// It will keep the last value you set.
void SetCombatMovement(bool allowMovement);
bool IsCombatMovementAllowed() const { return _isCombatMovementAllowed; }
bool IsLFR() const;
bool IsNormal() const;
bool IsHeroic() const;
bool IsMythic() const;
bool IsMythicPlus() const;
bool IsHeroicOrHigher() const;
bool IsTimewalking() const;
// 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 == DIFFICULTY_25_N || _difficulty == DIFFICULTY_25_HC; }
template
inline T const& DUNGEON_MODE(T const& normal5, T const& heroic10) const
{
switch (_difficulty)
{
case DIFFICULTY_NORMAL:
return normal5;
case DIFFICULTY_HEROIC:
return heroic10;
default:
break;
}
return heroic10;
}
template
inline T const& RAID_MODE(T const& normal10, T const& normal25) const
{
switch (_difficulty)
{
case DIFFICULTY_10_N:
return normal10;
case DIFFICULTY_25_N:
return normal25;
default:
break;
}
return normal25;
}
template
inline T const& RAID_MODE(T const& normal10, T const& normal25, T const& heroic10, T const& heroic25) const
{
switch (_difficulty)
{
case DIFFICULTY_10_N:
return normal10;
case DIFFICULTY_25_N:
return normal25;
case DIFFICULTY_10_HC:
return heroic10;
case DIFFICULTY_25_HC:
return heroic25;
default:
break;
}
return heroic25;
}
private:
Difficulty _difficulty;
bool _isCombatMovementAllowed;
};
class TC_GAME_API BossAI : public ScriptedAI
{
public:
explicit BossAI(Creature* creature, uint32 bossId) noexcept;
virtual ~BossAI();
InstanceScript* const instance;
void JustSummoned(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override;
virtual 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*/) { }
virtual void ScheduleTasks() { }
void Reset() override { _Reset(); }
void JustEngagedWith(Unit* who) override { _JustEngagedWith(who); }
void JustDied(Unit* /*killer*/) override { _JustDied(); }
void JustReachedHome() override { _JustReachedHome(); }
bool CanAIAttack(Unit const* target) const override;
uint32 GetBossId() const { return _bossId; }
protected:
void _Reset();
void _JustEngagedWith(Unit* who);
void _JustDied();
void _JustReachedHome();
void _DespawnAtEvade(Seconds delayToRespawn = 30s, Creature* who = nullptr);
void TeleportCheaters();
EventMap events;
SummonList summons;
TaskScheduler scheduler;
private:
uint32 const _bossId;
};
class TC_GAME_API WorldBossAI : public ScriptedAI
{
public:
explicit WorldBossAI(Creature* creature) noexcept;
virtual ~WorldBossAI();
void JustSummoned(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override;
virtual 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.
inline Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive = true)
{
return source->FindNearestCreature(entry, maxSearchRange, alive);
}
inline Creature* GetClosestCreatureWithOptions(WorldObject* source, float maxSearchRange, FindCreatureOptions const& options)
{
return source->FindNearestCreatureWithOptions(maxSearchRange, options);
}
inline GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool spawnedOnly = true)
{
return source->FindNearestGameObject(entry, maxSearchRange, spawnedOnly);
}
template
inline void GetCreatureListWithEntryInGrid(Container& container, WorldObject* source, uint32 entry, float maxSearchRange)
{
source->GetCreatureListWithEntryInGrid(container, entry, maxSearchRange);
}
template
inline void GetCreatureListWithOptionsInGrid(Container& container, WorldObject* source, float maxSearchRange, FindCreatureOptions const& options)
{
source->GetCreatureListWithOptionsInGrid(container, maxSearchRange, options);
}
template
inline void GetGameObjectListWithEntryInGrid(Container& container, WorldObject* source, uint32 entry, float maxSearchRange)
{
source->GetGameObjectListWithEntryInGrid(container, entry, maxSearchRange);
}
template
inline void GetPlayerListInGrid(Container& container, WorldObject* source, float maxSearchRange, bool alive = true)
{
source->GetPlayerListInGrid(container, maxSearchRange, alive);
}
#endif // TRINITY_SCRIPTEDCREATURE_H