/* * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2 * Copyright (C) 2008-2016 TrinityCore * Copyright (C) 2005-2009 MaNGOS */ #ifndef SCRIPTEDCREATURE_H_ #define SCRIPTEDCREATURE_H_ #include "Creature.h" #include "CreatureAI.h" #include "CreatureAIImpl.h" #include "InstanceScript.h" #define CAST_AI(a, b) (dynamic_cast(b)) class InstanceScript; class SummonList { public: typedef std::list 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(); template 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 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_; Trinity::Containers::RandomResizeList(listCopy, predicate, max); for (StorageType::iterator i = listCopy.begin(); i != listCopy.end(); ++i) { Creature* summon = ObjectAccessor::GetCreature(*me, *i); if (summon) { if (summon->IsAIEnabled) summon->AI()->DoAction(info); } else storage_.remove(*i); } } void DoZoneInCombat(uint32 entry = 0); void RemoveNotExisting(); bool HasEntry(uint32 entry) const; uint32 GetEntryCount(uint32 entry) const; void Respawn(); Creature* GetCreatureWithEntry(uint32 entry) const; private: Creature* me; StorageType storage_; }; class EntryCheckPredicate { public: EntryCheckPredicate(uint32 entry) : _entry(entry) {} bool operator()(uint64 guid) { return GUID_ENPART(guid) == _entry; } private: uint32 _entry; }; class PlayerOrPetCheck { public: bool operator() (WorldObject* unit) const { if (unit->GetTypeId() != TYPEID_PLAYER) if (!IS_PLAYER_GUID(unit->ToUnit()->GetOwnerGUID())) return true; return false; } }; struct ScriptedAI : public CreatureAI { explicit ScriptedAI(Creature* creature); virtual ~ScriptedAI() {} // ************* //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*/) {} //Called at World update tick virtual void UpdateAI(uint32 diff); //Called at creature death void JustDied(Unit* /*killer*/) {} //Called at creature killing another unit void KilledUnit(Unit* /*victim*/) {} // Called when the creature summon successfully other creature void JustSummoned(Creature* /*summon*/) {} // Called when a summoned creature is despawned void SummonedCreatureDespawn(Creature* /*summon*/) {} // Called when hit by a spell void SpellHit(Unit* /*caster*/, SpellInfo const* /*spell*/) {} // Called when spell hits a target void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) {} //Called at waypoint reached or PointMovement end void MovementInform(uint32 /*type*/, uint32 /*id*/) {} // Called when AI is temporarily replaced or put back when possess is applied or removed void OnPossess(bool /*apply*/) {} // ************* // Variables // ************* //Pointer to creature we are manipulating Creature* me; //For fleeing bool IsFleeing; // ************* //Pure virtual functions // ************* //Called at creature reset either by death or evade void Reset() {} //Called at creature aggro either by MoveInLOS or Attack Start void EnterCombat(Unit* /*victim*/) {} // Called before EnterCombat even before the creature is in combat. void AttackStart(Unit* /*target*/); // ************* //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); //Drops all threat to 0%. Does not remove players from the threat list void DoResetThreat(); float DoGetThreat(Unit* unit); void DoModifyThreatPercent(Unit* unit, int32 pct); //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 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, uint32 despawntime); 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); // 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 EnterEvadeIfOutOfCombatArea(); 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 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 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 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; private: Difficulty _difficulty; uint32 _evadeCheckCooldown; bool _isCombatMovementAllowed; bool _isHeroic; }; class BossAI : public ScriptedAI { public: BossAI(Creature* creature, uint32 bossId); virtual ~BossAI() {} InstanceScript* const instance; BossBoundaryMap const* GetBoundary() const { return _boundary; } void JustSummoned(Creature* summon); void SummonedCreatureDespawn(Creature* summon); virtual void UpdateAI(uint32 diff); // 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() { _Reset(); } void EnterCombat(Unit* /*who*/) { _EnterCombat(); } void JustDied(Unit* /*killer*/) { _JustDied(); } void JustReachedHome() { _JustReachedHome(); } protected: void _Reset(); void _EnterCombat(); void _JustDied(); void _JustReachedHome() { me->setActive(false); } bool CheckInRoom() { if (CheckBoundary(me)) return true; EnterEvadeMode(); return false; } bool CheckBoundary(Unit* who); void TeleportCheaters(); EventMap events; SummonList summons; private: BossBoundaryMap const* const _boundary; uint32 const _bossId; }; class WorldBossAI : public ScriptedAI { public: WorldBossAI(Creature* creature); virtual ~WorldBossAI() {} void JustSummoned(Creature* summon); void SummonedCreatureDespawn(Creature* summon); virtual void UpdateAI(uint32 diff); // 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() { _Reset(); } void EnterCombat(Unit* /*who*/) { _EnterCombat(); } void JustDied(Unit* /*killer*/) { _JustDied(); } protected: void _Reset(); void _EnterCombat(); 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); void GetCreatureListWithEntryInGrid(std::list& list, WorldObject* source, uint32 entry, float maxSearchRange); void GetGameObjectListWithEntryInGrid(std::list& list, WorldObject* source, uint32 entry, float maxSearchRange); #endif // SCRIPTEDCREATURE_H_