aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/CoreAI/GameObjectAI.h9
-rwxr-xr-xsrc/server/game/AI/CreatureAIRegistry.cpp1
-rwxr-xr-xsrc/server/game/AI/SmartAI/SmartAI.cpp190
-rwxr-xr-xsrc/server/game/AI/SmartAI/SmartAI.h367
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp900
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.h254
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp1949
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.h244
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp776
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h1276
-rw-r--r--src/server/game/CMakeLists.txt2
-rwxr-xr-xsrc/server/game/Chat/Chat.cpp1
-rwxr-xr-xsrc/server/game/Chat/Chat.h1
-rwxr-xr-xsrc/server/game/Chat/Commands/Level3.cpp9
-rwxr-xr-xsrc/server/game/Scripting/ScriptLoader.cpp3
-rwxr-xr-xsrc/server/game/Server/Protocol/Handlers/MiscHandler.cpp7
-rwxr-xr-xsrc/server/game/Server/Protocol/Handlers/QuestHandler.cpp3
-rwxr-xr-xsrc/server/game/Server/Protocol/Handlers/SpellHandler.cpp3
-rwxr-xr-xsrc/server/game/Spells/SpellEffects.cpp3
-rwxr-xr-xsrc/server/game/Texts/CreatureTextMgr.cpp4
-rwxr-xr-xsrc/server/game/World/World.cpp7
-rwxr-xr-xsrc/server/shared/Database/Implementation/WorldDatabase.cpp2
-rwxr-xr-xsrc/server/shared/Database/Implementation/WorldDatabase.h2
-rw-r--r--src/server/worldserver/CMakeLists.txt1
24 files changed, 5454 insertions, 560 deletions
diff --git a/src/server/game/AI/CoreAI/GameObjectAI.h b/src/server/game/AI/CoreAI/GameObjectAI.h
index 631b61a9250..6329d0e1662 100644
--- a/src/server/game/AI/CoreAI/GameObjectAI.h
+++ b/src/server/game/AI/CoreAI/GameObjectAI.h
@@ -40,6 +40,15 @@ class GameObjectAI
virtual void Reset() {};
static int Permissible(const GameObject* go);
+
+ virtual bool GossipHello(Player* player) {return false;}
+ virtual bool GossipSelect(Player* player, uint32 sender, uint32 action) {return false;}
+ virtual bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/) {return false;}
+ virtual bool QuestAccept(Player* player, Quest const* quest) {return false;}
+ virtual bool QuestReward(Player* player, Quest const* quest, uint32 opt) {return false;}
+ uint32 GetDialogStatus(Player* /*player*/) {return 100;}
+ virtual void Destroyed(Player* player, uint32 eventId) {}
+ virtual void SetData(uint32 id, uint32 value) {}
};
class NullGameObjectAI : public GameObjectAI
diff --git a/src/server/game/AI/CreatureAIRegistry.cpp b/src/server/game/AI/CreatureAIRegistry.cpp
index d343bdce0eb..0c443a0ed4c 100755
--- a/src/server/game/AI/CreatureAIRegistry.cpp
+++ b/src/server/game/AI/CreatureAIRegistry.cpp
@@ -53,6 +53,7 @@ namespace AIRegistry
(new CreatureAIFactory<SmartAI>("SmartAI"))->RegisterSelf();
(new GameObjectAIFactory<GameObjectAI>("GameObjectAI"))->RegisterSelf();
+ (new GameObjectAIFactory<SmartGameObjectAI>("SmartGameObjectAI"))->RegisterSelf();
(new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf();
(new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf();
diff --git a/src/server/game/AI/SmartAI/SmartAI.cpp b/src/server/game/AI/SmartAI/SmartAI.cpp
deleted file mode 100755
index 37e9847d0e5..00000000000
--- a/src/server/game/AI/SmartAI/SmartAI.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
- *
- * 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/>.
- */
-
-#include "Common.h"
-#include "DatabaseEnv.h"
-#include "SQLStorage.h"
-#include "SmartAI.h"
-#include "ObjectMgr.h"
-#include "ProgressBar.h"
-#include "ObjectDefines.h"
-#include "GridDefines.h"
-#include "ConditionMgr.h"
-#include "CreatureTextMgr.h"
-
-void SmartAIMgr::LoadSmartAIFromDB()
-{
- //Drop Existing SmartAI List
-}
-
-SmartAI::SmartAI(Creature *c) : CreatureAI(c)
-{
- // copy script to local (pretection for table reload)
-}
-
-int SmartAI::Permissible(const Creature* creature)
-{
- if (creature->GetAIName() == "SmartAI")
- return PERMIT_BASE_SPECIAL;
- return PERMIT_BASE_NO;
-}
-
-void SmartAI::UpdateAI(const uint32 /*diff*/)
-{
-}
-
-void SmartAI::JustRespawned()
-{
-}
-
-void SmartAI::Reset()
-{
-}
-
-void SmartAI::JustReachedHome()
-{
-}
-
-void SmartAI::EnterCombat(Unit* /*enemy*/)
-{
-}
-
-void SmartAI::EnterEvadeMode()
-{
-}
-
-void SmartAI::JustDied(Unit* /*killer*/)
-{
-}
-
-void SmartAI::KilledUnit(Unit* /*victim*/)
-{
-}
-
-void SmartAI::JustSummoned(Creature* /*pUnit*/)
-{
-}
-
-void SmartAI::AttackStart(Unit* who)
-{
- if (who && me->Attack(who, true))
- me->GetMotionMaster()->MoveChase(who);
-}
-
-void SmartAI::MoveInLineOfSight(Unit* /*who*/)
-{
- //CanAIAttack
-}
-
-void SmartAI::SpellHit(Unit* /*pUnit*/, const SpellEntry* /*pSpell*/)
-{
-}
-
-void SmartAI::SpellHitTarget(Unit* /*target*/, const SpellEntry* /*pSpell*/)
-{
-}
-
-void SmartAI::DamageTaken(Unit* /*done_by*/, uint32& /*damage*/, DamageEffectType /*damagetype*/)
-{
-}
-
-void SmartAI::HealReceived(Unit* /*done_by*/, uint32& /*addhealth*/)
-{
-}
-
-void SmartAI::ReceiveEmote(Player* /*pPlayer*/, uint32 /*text_emote*/)
-{
-}
-
-void SmartAI::MovementInform(uint32 /*MovementType*/, uint32 /*Data*/)
-{
-}
-
-void SmartAI::IsSummonedBy(Unit* /*summoner*/)
-{
-}
-
-void SmartAI::DamageDealt(Unit* /*done_to*/, uint32& /*damage*/)
-{
-}
-
-void SmartAI::SummonedCreatureDespawn(Creature* /*unit*/)
-{
-}
-
-void SmartAI::UpdateAIWhileCharmed(const uint32 /*diff*/)
-{
-}
-
-void SmartAI::CorpseRemoved(uint32& /*respawnDelay*/)
-{
-}
-
-void SmartAI::PassengerBoarded(Unit* /*who*/, int8 /*seatId*/, bool /*apply*/)
-{
-}
-
-void SmartAI::InitializeAI()
-{
-}
-
-void SmartAI::OnCharmed(bool /*apply*/)
-{
-}
-
-bool SmartAI::CanAIAttack(const Unit* /*who*/) const
-{
- return true;
-}
-
-void SmartAI::DoAction(const int32 /*param*/)
-{
-}
-
-uint32 SmartAI::GetData(uint32 /*id*/)
-{
- return 0;
-}
-
-void SmartAI::SetData(uint32 /*id*/, uint32 /*value*/)
-{
-}
-
-void SmartAI::SetGUID(const uint64& /*guid*/, int32 /*id*/)
-{
-}
-
-uint64 SmartAI::GetGUID(int32 /*id*/)
-{
- return 0;
-}
-
-void SmartAI::MovepointReached(uint32 /*id*/)
-{
-}
-
-void SmartAI::MovepointStart(uint32 /*id*/)
-{
-}
-
-void SmartAI::SetRun(bool /*run*/)
-{
-}
-
-void SmartAI::SetMovePathEndAction(SMARTAI_ACTION /*action*/)
-{
-}
diff --git a/src/server/game/AI/SmartAI/SmartAI.h b/src/server/game/AI/SmartAI/SmartAI.h
deleted file mode 100755
index d71a2d786a9..00000000000
--- a/src/server/game/AI/SmartAI/SmartAI.h
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
- *
- * 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 TRINITY_SMARTAI_H
-#define TRINITY_SMARTAI_H
-
-#include "Common.h"
-#include "Creature.h"
-#include "CreatureAI.h"
-#include "Unit.h"
-
-/*
-N O T E S
-
-
-movepoints:
- create ingame commands
-
-commands:
- smartai
- smartai movepoints
- smartai movepoints start (id)
- smartai movepoints add
- smartai movepoints end
- smartai movepoints show (id)
- smartai reload
- smartai create [reload creature]
-
-
-void passenger removed
-void setcanrun
-void setcanfly
-void setcanswim
-void setinhabittype
-
-event change flags
-
-need a local SendChat()
-
-*/
-
-//temp copied from eai, will be modded
-enum SMARTAI_EVENT
-{
- SMART_EVENT_TIMER_IC = 0, // InitialMin, InitialMax, RepeatMin, RepeatMax
- SMART_EVENT_TIMER_OOC = 1, // InitialMin, InitialMax, RepeatMin, RepeatMax
- SMART_EVENT_HP = 2, // HPMax%, HPMin%, RepeatMin, RepeatMax
- SMART_EVENT_MANA = 3, // ManaMax%,ManaMin% RepeatMin, RepeatMax
- SMART_EVENT_AGGRO = 4, // NONE
- SMART_EVENT_KILL = 5, // RepeatMin, RepeatMax
- SMART_EVENT_DEATH = 6, // NONE
- SMART_EVENT_EVADE = 7, // NONE
- SMART_EVENT_SPELLHIT = 8, // SpellID, School, RepeatMin, RepeatMax
- SMART_EVENT_RANGE = 9, // MinDist, MaxDist, RepeatMin, RepeatMax
- SMART_EVENT_OOC_LOS = 10, // NoHostile, MaxRnage, RepeatMin, RepeatMax
- SMART_EVENT_SPAWNED = 11, // Condition, CondValue1
- SMART_EVENT_TARGET_HP = 12, // HPMax%, HPMin%, RepeatMin, RepeatMax
- SMART_EVENT_TARGET_CASTING = 13, // RepeatMin, RepeatMax
- SMART_EVENT_FRIENDLY_HP = 14, // HPDeficit, Radius, RepeatMin, RepeatMax
- SMART_EVENT_FRIENDLY_IS_CC = 15, // DispelType, Radius, RepeatMin, RepeatMax
- SMART_EVENT_FRIENDLY_MISSING_BUFF = 16, // SpellId, Radius, RepeatMin, RepeatMax
- SMART_EVENT_SUMMONED_UNIT = 17, // CreatureId, RepeatMin, RepeatMax
- SMART_EVENT_TARGET_MANA = 18, // ManaMax%, ManaMin%, RepeatMin, RepeatMax
- SMART_EVENT_QUEST_ACCEPT = 19, // QuestID
- SMART_EVENT_QUEST_COMPLETE = 20, //
- SMART_EVENT_REACHED_HOME = 21, // NONE
- SMART_EVENT_RECEIVE_EMOTE = 22, // EmoteId, Condition, CondValue1, CondValue2
- SMART_EVENT_BUFFED = 23, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max
- SMART_EVENT_TARGET_BUFFED = 24, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max
- SMART_EVENT_RESET = 35, // Is it called after combat, when the creature respawn and spawn. -- TRINITY ONLY
-
- SMART_EVENT_END = 36,
-};
-
-//temp copied from eai, will be modded
-enum SMARTAI_ACTION
-{
- SMART_ACTION_NONE = 0, // No action
- SMART_ACTION_TEXT = 1, // TextId1, optionally -TextId2, optionally -TextId3(if -TextId2 exist). If more than just -TextId1 is defined, randomize. Negative values.
- SMART_ACTION_SET_FACTION = 2, // FactionId (or 0 for default)
- SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL = 3, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph)
- SMART_ACTION_SOUND = 4, // SoundId
- SMART_ACTION_EMOTE = 5, // EmoteId
- SMART_ACTION_RANDOM_SAY = 6, // UNUSED
- SMART_ACTION_RANDOM_YELL = 7, // UNUSED
- SMART_ACTION_RANDOM_TEXTEMOTE = 8, // UNUSED
- SMART_ACTION_RANDOM_SOUND = 9, // SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field)
- SMART_ACTION_RANDOM_EMOTE = 10, // EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field)
- SMART_ACTION_CAST = 11, // SpellId, Target, CastFlags
- SMART_ACTION_SUMMON = 12, // CreatureID, Target, Duration in ms
- SMART_ACTION_THREAT_SINGLE_PCT = 13, // Threat%, Target
- SMART_ACTION_THREAT_ALL_PCT = 14, // Threat%
- SMART_ACTION_QUEST_EVENT = 15, // QuestID, Target
- SMART_ACTION_CAST_EVENT = 16, // QuestID, SpellId, Target - must be removed as hack?
- SMART_ACTION_SET_UNIT_FIELD = 17, // Field_Number, Value, Target
- SMART_ACTION_SET_UNIT_FLAG = 18, // Flags (may be more than one field OR'd together), Target
- SMART_ACTION_REMOVE_UNIT_FLAG = 19, // Flags (may be more than one field OR'd together), Target
- SMART_ACTION_AUTO_ATTACK = 20, // AllowAttackState (0 = stop attack, anything else means continue attacking)
- SMART_ACTION_COMBAT_MOVEMENT = 21, // AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
- SMART_ACTION_SET_PHASE = 22, // Phase
- SMART_ACTION_INC_PHASE = 23, // Value (may be negative to decrement phase, should not be 0)
- SMART_ACTION_EVADE = 24, // No Params
- SMART_ACTION_FLEE_FOR_ASSIST = 25, // No Params
- SMART_ACTION_QUEST_EVENT_ALL = 26, // QuestID
- SMART_ACTION_CAST_EVENT_ALL = 27, // CreatureId, SpellId
- SMART_ACTION_REMOVEAURASFROMSPELL = 28, // Target, Spellid
- SMART_ACTION_RANGED_MOVEMENT = 29, // Distance, Angle
- SMART_ACTION_RANDOM_PHASE = 30, // PhaseId1, PhaseId2, PhaseId3
- SMART_ACTION_RANDOM_PHASE_RANGE = 31, // PhaseMin, PhaseMax
- SMART_ACTION_SUMMON_ID = 32, // CreatureId, Target, SpawnId
- SMART_ACTION_KILLED_MONSTER = 33, // CreatureId, Target
- SMART_ACTION_SET_INST_DATA = 34, // Field, Data
- SMART_ACTION_SET_INST_DATA64 = 35, // Field, Target
- SMART_ACTION_UPDATE_TEMPLATE = 36, // Entry, Team
- SMART_ACTION_DIE = 37, // No Params
- SMART_ACTION_ZONE_COMBAT_PULSE = 38, // No Params
- SMART_ACTION_CALL_FOR_HELP = 39, // Radius
- SMART_ACTION_SET_SHEATH = 40, // Sheath (0-passive,1-melee,2-ranged)
- SMART_ACTION_FORCE_DESPAWN = 41, // No Params
- SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL = 42, // MinHpValue, format(0-flat,1-percent from max health)
- SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL = 43, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to unmount)
-
- SMART_ACTION_SET_PHASE_MASK = 97,
- SMART_ACTION_SET_STAND_STATE = 98,
- SMART_ACTION_MOVE_RANDOM_POINT = 99,
- SMART_ACTION_SET_VISIBILITY = 100,
- SMART_ACTION_SET_ACTIVE = 101, //Apply
- SMART_ACTION_SET_AGGRESSIVE = 102, //Apply
- SMART_ACTION_ATTACK_START_PULSE = 103, //Distance
- SMART_ACTION_SUMMON_GO = 104, //GameObjectID, DespawnTime in ms
-
- SMART_ACTION_END = 105,
-};
-
-//temp copied from eai, will be modded
-enum SMARTAI_TARGETS
-{
- SMART_TARGET_SELF = 0, //Self cast
-
- //Hostile targets (if pet then returns pet owner)
- SMART_TARGET_VICTIM = 1, //Our current target (ie: highest aggro)
- SMART_TARGET_HOSTILE_SECOND_AGGRO = 2, //Second highest aggro (generaly used for cleaves and some special attacks)
- SMART_TARGET_HOSTILE_LAST_AGGRO = 3, //Dead last on aggro (no idea what this could be used for)
- SMART_TARGET_HOSTILE_RANDOM = 4, //Just any random target on our threat list
- SMART_TARGET_HOSTILE_RANDOM_NOT_TOP = 5, //Any random target except top threat
-
- //Invoker targets (if pet then returns pet owner)
- SMART_TARGET_ACTION_INVOKER = 6, //Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF)
-
- //Hostile targets (including pets)
- SMART_TARGET_HOSTILE_WPET = 7, //Current target (can be a pet)
- SMART_TARGET_HOSTILE_WPET_SECOND_AGGRO = 8, //Second highest aggro (generaly used for cleaves and some special attacks)
- SMART_TARGET_HOSTILE_WPET_LAST_AGGRO = 9, //Dead last on aggro (no idea what this could be used for)
- SMART_TARGET_HOSTILE_WPET_RANDOM = 10, //Just any random target on our threat list
- SMART_TARGET_HOSTILE_WPET_RANDOM_NOT_TOP = 11, //Any random target except top threat
-
- SMART_TARGET_ACTION_INVOKER_WPET = 12,
-
- SMART_TARGET_END = 13,
-};
-
-#define SMARTAI_EVENT_PARAM_COUNT 6
-#define SMARTAI_ACTION_PARAM_COUNT 6
-
-struct MovePoint
-{
- MovePoint(uint32 _id, float _x, float _y, float _z)
- {
- id = _id;
- x = _x;
- y = _y;
- z = _z;
- }
-
- uint32 id;
- float x;
- float y;
- float z;
-};
-
-// one line in DB is one event
-struct SMARTAI_EVENT_HOLDER
-{
- uint32 event_id;
- SMARTAI_EVENT event_type;
- uint32 event_phase_mask;
- uint32 event_chance;
- uint32 event_flags;
- int32 event_param[SMARTAI_EVENT_PARAM_COUNT];
-
- SMARTAI_ACTION action_type;
- int32 action_param[SMARTAI_ACTION_PARAM_COUNT];
-
- float param_x;
- float param_y;
- float param_z;
- float param_o;
-};
-
-// all events for a single entry
-typedef std::vector<SMARTAI_EVENT_HOLDER> SmartAIEventList;
-
-// all events for all entries
-typedef UNORDERED_MAP<int32, SmartAIEventList> SmartAIEventMap;
-
-class SmartAIMgr
-{
- SmartAIMgr(){};
- public:
- ~SmartAIMgr(){};
-
- void LoadSmartAIFromDB();
-
- // only use this after EntryExists() check
- SmartAIEventList const& GetEventList(int32 entry) const
- {
- SmartAIEventMap::const_iterator sList = m_EventMap.find(entry);
- return (*sList).second;
- }
- SmartAIEventMap const& GetEventMap() const { return m_EventMap; }
-
- // do we have entry's eventList from DB?
- bool EntryExists(int32 entry)
- {
- SmartAIEventMap::const_iterator sList = m_EventMap.find(entry);
- if (sList != m_EventMap.end())
- return true;
- return false;
- }
-
- private:
- SmartAIEventMap m_EventMap;
-};
-
-class SmartAI : public CreatureAI
-{
- public:
- ~SmartAI(){};
- explicit SmartAI(Creature *c);
-
- // Called when creature is spawned or respawned
- void JustRespawned();
-
- // Called after InitializeAI(), EnterEvadeMode() for resetting variables
- void Reset();
-
- // Called at reaching home after evade
- void JustReachedHome();
-
- // Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
- void EnterCombat(Unit *enemy);
-
- // Called for reaction at stopping attack at no attackers or targets
- void EnterEvadeMode();
-
- // Called when the creature is killed
- void JustDied(Unit* killer);
-
- // Called when the creature kills a unit
- void KilledUnit(Unit* victim);
-
- // Called when the creature summon successfully other creature
- void JustSummoned(Creature* pUnit);
-
- // Tell creature to attack and follow the victim
- void AttackStart(Unit *who);
-
- // Called if IsVisible(Unit *who) is true at each *who move, reaction at visibility zone enter
- void MoveInLineOfSight(Unit *who);
-
- // Called when hit by a spell
- void SpellHit(Unit* pUnit, const SpellEntry* pSpell);
-
- // Called when spell hits a target
- void SpellHitTarget(Unit* target, const SpellEntry*);
-
- // Called at any Damage from any attacker (before damage apply)
- void DamageTaken(Unit* done_by, uint32& damage, DamageEffectType damagetype);
-
- // Called when the creature receives heal
- void HealReceived(Unit* done_by, uint32& addhealth);
-
- // Called at World update tick
- void UpdateAI(const uint32 diff);
-
- // Called at text emote receive from player
- void ReceiveEmote(Player* pPlayer, uint32 text_emote);
-
- // Called at waypoint reached or point movement finished
- void MovementInform(uint32 MovementType, uint32 Data);
-
- // Called when creature is summoned by another unit
- void IsSummonedBy(Unit* summoner);
-
- // Called at any Damage to any victim (before damage apply)
- void DamageDealt(Unit * done_to, uint32 & damage);
-
- // Called when a summoned creature dissapears (UnSommoned)
- void SummonedCreatureDespawn(Creature* unit);
-
- // called when the corpse of this creature gets removed
- void CorpseRemoved(uint32 & respawnDelay);
-
- // Called at World update tick if creature is charmed
- void UpdateAIWhileCharmed(const uint32 diff);
-
- // Called when a Player/Creature enters the creature (vehicle)
- void PassengerBoarded(Unit* who, int8 seatId, bool apply);
-
- // Called when gets initialized, when creature is added to world
- void InitializeAI();
-
- // Called when creature gets charmed by another unit
- void OnCharmed(bool apply);
-
- // Called when victim is in line of sight
- bool CanAIAttack(const Unit* who) const;
-
- // Used in scripts to share variables
- void DoAction(const int32 param = 0);
-
- // Used in scripts to share variables
- uint32 GetData(uint32 id = 0);
-
- // Used in scripts to share variables
- void SetData(uint32 id, uint32 value);
-
- // Used in scripts to share variables
- void SetGUID(const uint64 &guid, int32 id = 0);
-
- // Used in scripts to share variables
- uint64 GetGUID(int32 id = 0);
-
- //core related
- static int Permissible(const Creature *);
-
- // Called at movepoint reached
- void MovepointReached(uint32 id);
-
- // Start moving to the desired MovePoint
- void MovepointStart(uint32 id);
-
- // Makes the creature run/walk
- void SetRun(bool bRun = true);
-
- // Sets the action that will be called when creature reaches the last movepoint
- void SetMovePathEndAction(SMARTAI_ACTION action = SMART_ACTION_FORCE_DESPAWN);
-
- private:
-};
-
-#define sSmartAIMgr (*ACE_Singleton<SmartAIMgr, ACE_Null_Mutex>::instance())
-#endif
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
new file mode 100644
index 00000000000..998f13a4540
--- /dev/null
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -0,0 +1,900 @@
+/*
+ * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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/>.
+ */
+
+
+#include "DatabaseEnv.h"
+#include "SQLStorage.h"
+#include "ObjectMgr.h"
+#include "ProgressBar.h"
+#include "ObjectDefines.h"
+#include "GridDefines.h"
+#include "GridNotifiers.h"
+#include "SpellMgr.h"
+#include "GridNotifiersImpl.h"
+#include "Cell.h"
+#include "CellImpl.h"
+#include "InstanceScript.h"
+#include "ScriptedCreature.h"
+
+#include "SmartAI.h"
+#include "ScriptPCH.h"
+
+SmartAI::SmartAI(Creature *c) : CreatureAI(c)
+{
+ // copy script to local (pretection for table reload)
+
+ mWayPoints = NULL;
+ mEscortState = SMART_ESCORT_NONE;
+ mCurrentWPID = 0;//first wp id is 1 !!
+ mWPReached = false;
+ mWPPauseTimer = 0;
+ mLastWP = NULL;
+
+ mCanRepeatPath = false;
+
+ // spawn in run mode
+ me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
+ mRun = true;
+
+ me->GetPosition(&mLastOOCPos);
+
+ mCanAutoAttack = true;
+ mCanCombatMove = true;
+
+ mForcedPaused = false;
+ mLastWPIDReached = 0;
+
+ mEscortQuestID = 0;
+
+ mDespawnTime = 0;
+ mDespawnState = 0;
+
+ mEscortInvokerCheckTimer = 1000;
+ mFollowGuid = 0;
+ mFollowDist = 0;
+ mFollowAngle = 0;
+ mFollowCredit = 0;
+ mFollowArrivedEntry = 0;
+ mFollowCreditType = 0;
+}
+
+void SmartAI::UpdateDespawn(const uint32 diff)
+{
+ if (mDespawnState <= 1 || mDespawnState > 3) return;
+ if (mDespawnTime < diff)
+ {
+ if (mDespawnState == 2)
+ {
+ me->SetVisibility(VISIBILITY_OFF);
+ mDespawnTime = 5000;
+ mDespawnState++;
+ }
+ else
+ me->ForcedDespawn();
+ } else mDespawnTime -= diff;
+}
+
+void SmartAI::Reset()
+{
+ SetRun(true);
+ GetScript()->OnReset();
+}
+
+WayPoint* SmartAI::GetNextWayPoint()
+{
+ if (!mWayPoints || mWayPoints->empty())
+ return NULL;
+
+ mCurrentWPID++;
+ WPPath::const_iterator itr = mWayPoints->find(mCurrentWPID);
+ if (itr != mWayPoints->end())
+ {
+ mLastWP = (*itr).second;
+ if (mLastWP->id != mCurrentWPID)
+ {
+ sLog.outError("SmartAI::GetNextWayPoint: Got not expected waypoint id %u, expected %u", mLastWP->id, mCurrentWPID);
+ }
+ return (*itr).second;
+ }
+ return NULL;
+}
+
+void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* invoker)
+{
+ if (me->isInCombat())// no wp movement in combat
+ {
+ sLog.outError("SmartAI::StartPath: Creature entry %u wanted to start waypoint movement while in combat, ignoring.", me->GetEntry());
+ return;
+ }
+ if (HasEscortState(SMART_ESCORT_ESCORTING))
+ StopPath();
+ if (path)
+ if (!LoadPath(path))
+ return;
+ if (!mWayPoints || mWayPoints->empty())
+ return;
+
+ AddEscortState(SMART_ESCORT_ESCORTING);
+ mCanRepeatPath = repeat;
+
+ SetRun(run);
+
+ WayPoint* wp = GetNextWayPoint();
+ if (wp)
+ {
+ me->GetPosition(&mLastOOCPos);
+ me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z);
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, NULL, wp->id, GetScript()->GetPathId());
+ }
+}
+
+bool SmartAI::LoadPath(uint32 entry)
+{
+ if (HasEscortState(SMART_ESCORT_ESCORTING))
+ return false;
+ mWayPoints = sSmartWaypointMgr.GetPath(entry);
+ if (!mWayPoints)
+ {
+ GetScript()->SetPathId(0);
+ return false;
+ }
+ GetScript()->SetPathId(entry);
+ return true;
+}
+
+void SmartAI::PausePath(uint32 delay, bool forced)
+{
+ if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ return;
+ if (HasEscortState(SMART_ESCORT_PAUSED))
+ {
+ sLog.outError("SmartAI::StartPath: Creature entry %u wanted to pause waypoint movement while already paused, ignoring.", me->GetEntry());
+ return;
+ }
+ mForcedPaused = forced;
+ me->GetPosition(&mLastOOCPos);
+ AddEscortState(SMART_ESCORT_PAUSED);
+ mWPPauseTimer = delay;
+ if (forced)
+ {
+ SetRun(mRun);
+ me->StopMoving();//force stop
+ me->GetMotionMaster()->MoveIdle();//force stop
+ }
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, NULL, mLastWP->id, GetScript()->GetPathId());
+}
+
+void SmartAI::StopPath(uint32 DespawnTime, uint32 quest, bool fail)
+{
+ if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ return;
+
+ if (quest)
+ mEscortQuestID = quest;
+ SetDespawnTime(DespawnTime);
+ //mDespawnTime = DespawnTime;
+
+ me->GetPosition(&mLastOOCPos);
+ me->StopMoving();//force stop
+ me->GetMotionMaster()->MoveIdle();
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, NULL, mLastWP->id, GetScript()->GetPathId());
+ EndPath(fail);
+}
+
+void SmartAI::EndPath(bool fail)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, NULL, mLastWP->id, GetScript()->GetPathId());
+
+ RemoveEscortState(SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING);
+ mWayPoints = NULL;
+ mCurrentWPID = 0;
+ mWPPauseTimer = 0;
+ mLastWP = NULL;
+
+ if (mCanRepeatPath)
+ StartPath(mRun, GetScript()->GetPathId(), mCanRepeatPath);
+ else
+ GetScript()->SetPathId(0);
+
+
+ ObjectList* targets = GetScript()->GetTargetList(SMART_ESCORT_TARGETS);
+ if (targets && mEscortQuestID)
+ {
+ if (targets->size() == 1 && GetScript()->IsPlayer((*targets->begin())))
+ {
+ Player* plr = (*targets->begin())->ToPlayer();
+ if(!fail && plr->IsAtGroupRewardDistance(me) && !plr->GetCorpse())
+ plr->GroupEventHappens(mEscortQuestID, me);
+
+ if(fail && plr->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE)
+ plr->FailQuest(mEscortQuestID);
+
+ if (Group *pGroup = plr->GetGroup())
+ {
+ for (GroupReference *gr = pGroup->GetFirstMember(); gr != NULL; gr = gr->next())
+ {
+ Player *pGroupGuy = gr->getSource();
+
+ if(!fail && pGroupGuy->IsAtGroupRewardDistance(me) && !pGroupGuy->GetCorpse())
+ pGroupGuy->AreaExploredOrEventHappens(mEscortQuestID);
+ if(fail && pGroupGuy->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE)
+ pGroupGuy->FailQuest(mEscortQuestID);
+ }
+ }
+ }else
+ {
+ for (ObjectList::iterator iter = targets->begin(); iter != targets->end(); iter++)
+ {
+ if (GetScript()->IsPlayer((*iter)))
+ {
+ Player* plr = (*iter)->ToPlayer();
+ if(!fail && plr->IsAtGroupRewardDistance(me) && !plr->GetCorpse())
+ plr->AreaExploredOrEventHappens(mEscortQuestID);
+ if(fail && plr->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE)
+ plr->FailQuest(mEscortQuestID);
+ }
+ }
+ }
+ }
+ if (mDespawnState == 1)
+ StartDespawn();
+}
+
+void SmartAI::ResumePath()
+{
+ //mWPReached = false;
+ SetRun(mRun);
+ if (mLastWP)
+ me->GetMotionMaster()->MovePoint(mLastWP->id, mLastWP->x, mLastWP->y, mLastWP->z);
+}
+
+void SmartAI::ReturnToLastOOCPos()
+{
+ SetRun(mRun);
+ me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, mLastOOCPos);
+}
+
+void SmartAI::UpdatePath(const uint32 diff)
+{
+ if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ return;
+ if (mEscortInvokerCheckTimer < diff)
+ {
+ if (!IsEscortInvokerInRange())
+ {
+ StopPath(mDespawnTime, mEscortQuestID, true);
+ }
+ mEscortInvokerCheckTimer = 1000;
+ } else mEscortInvokerCheckTimer -= diff;
+ // handle pause
+ if (HasEscortState(SMART_ESCORT_PAUSED))
+ {
+ if (mWPPauseTimer < diff)
+ {
+ if (!me->isInCombat() && !HasEscortState(SMART_ESCORT_RETURNING) && (mWPReached || mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT || mForcedPaused))
+ {
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_RESUMED, NULL, mLastWP->id, GetScript()->GetPathId());
+ RemoveEscortState(SMART_ESCORT_PAUSED);
+ if (mForcedPaused)// if paused between 2 wps resend movement
+ {
+ ResumePath();
+ mWPReached = false;
+ mForcedPaused = false;
+ }
+ if (mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT)
+ mWPReached = true;
+ }
+ mWPPauseTimer = 0;
+ } else {
+ mWPPauseTimer -= diff;
+
+ }
+ }
+ if (HasEscortState(SMART_ESCORT_RETURNING))
+ {
+ if (mWPReached)//reached OOC WP
+ {
+ RemoveEscortState(SMART_ESCORT_RETURNING);
+ if (!HasEscortState(SMART_ESCORT_PAUSED))
+ ResumePath();
+ mWPReached = false;
+ }
+ }
+ if (me->isInCombat() || HasEscortState(SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING))
+ return;
+ // handle next wp
+ if (mWPReached)//reached WP
+ {
+ mWPReached = false;
+ if (mCurrentWPID == GetWPCount())
+ {
+ EndPath();
+ } else {
+ WayPoint* wp = GetNextWayPoint();
+ if (wp)
+ {
+ SetRun(mRun);
+ me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z);
+ }
+ }
+
+ }
+}
+
+void SmartAI::UpdateAI(const uint32 diff)
+{
+ GetScript()->OnUpdate(diff);
+ UpdatePath(diff);
+ UpdateDespawn(diff);
+
+ //TODO move to void
+ if (mFollowGuid)
+ {
+ if (mFollowArrivedTimer < diff)
+ {
+ if (Creature* target = me->FindNearestCreature(mFollowArrivedEntry,INTERACTION_DISTANCE, true))
+ {
+ if (Player* plr = me->GetPlayer(*me, mFollowGuid))
+ {
+ if (!mFollowCreditType)
+ plr->RewardPlayerAndGroupAtEvent(mFollowCredit, me);
+ else
+ plr->GroupEventHappens(mFollowCredit, me);
+ }
+ mFollowGuid = 0;
+ mFollowDist = 0;
+ mFollowAngle = 0;
+ mFollowCredit = 0;
+ mFollowArrivedTimer = 1000;
+ mFollowArrivedEntry = 0;
+ mFollowCreditType = 0;
+ SetDespawnTime(5000);
+ me->StopMoving();
+ me->GetMotionMaster()->MoveIdle();
+ StartDespawn();
+ GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COPMLETE);
+ return;
+ }
+ mFollowArrivedTimer = 1000;
+ } else mFollowArrivedTimer -= diff;
+ }
+
+ if (!UpdateVictim())
+ return;
+
+ if(mCanAutoAttack)
+ DoMeleeAttackIfReady();
+}
+
+bool SmartAI::IsEscortInvokerInRange()
+{
+ ObjectList* targets = GetScript()->GetTargetList(SMART_ESCORT_TARGETS);
+ if (targets)
+ {
+ if (targets->size() == 1 && GetScript()->IsPlayer((*targets->begin())))
+ {
+ Player* plr = (*targets->begin())->ToPlayer();
+ if (me->GetDistance(plr) <= SMART_ESCORT_MAX_PLAYER_DIST)
+ return true;
+
+ if (Group *pGroup = plr->GetGroup())
+ {
+ for (GroupReference *gr = pGroup->GetFirstMember(); gr != NULL; gr = gr->next())
+ {
+ Player *pGroupGuy = gr->getSource();
+
+ if (me->GetDistance(pGroupGuy) <= SMART_ESCORT_MAX_PLAYER_DIST)
+ return true;
+ }
+ }
+ }else
+ {
+ for (ObjectList::iterator iter = targets->begin(); iter != targets->end(); iter++)
+ {
+ if (GetScript()->IsPlayer((*iter)))
+ {
+ if (me->GetDistance((*iter)->ToPlayer()) <= SMART_ESCORT_MAX_PLAYER_DIST)
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void SmartAI::MovepointReached(uint32 id)
+{
+ if (id != SMART_ESCORT_LAST_OOC_POINT)
+ GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, NULL, id);
+
+ mLastWPIDReached = id;
+ mWPReached = true;
+}
+
+void SmartAI::MovementInform(uint32 MovementType, uint32 Data)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_MOVEMENTINFORM, NULL, MovementType, Data);
+ if (MovementType != POINT_MOTION_TYPE || !HasEscortState(SMART_ESCORT_ESCORTING))
+ return;
+ MovepointReached(Data);
+}
+
+void SmartAI::EnterEvadeMode()
+{
+ if (!me->isAlive())
+ return;
+
+ me->RemoveAllAuras();
+ me->DeleteThreatList();
+ me->CombatStop(true);
+ me->LoadCreaturesAddon();
+ me->SetLootRecipient(NULL);
+ me->ResetPlayerDamageReq();
+
+ GetScript()->ProcessEventsFor(SMART_EVENT_EVADE);//must be after aura clear so we can cast spells from db
+
+ SetRun(mRun);
+ if (HasEscortState(SMART_ESCORT_ESCORTING))
+ {
+ AddEscortState(SMART_ESCORT_RETURNING);
+ ReturnToLastOOCPos();
+ } else if (mFollowGuid){
+ if (Unit* target = me->GetUnit(*me, mFollowGuid))
+ me->GetMotionMaster()->MoveFollow(target, mFollowDist, mFollowAngle);
+ } else {
+ me->GetMotionMaster()->MoveTargetedHome();
+ }
+
+ Reset();
+}
+
+void SmartAI::MoveInLineOfSight(Unit* who)
+{
+ if (!who) return;
+ GetScript()->OnMoveInLineOfSight(who);
+ //HasEscortState(SMART_ESCORT_ESCORTING) ||
+ if (me->HasReactState(REACT_PASSIVE) || AssistPlayerInCombat(who))
+ return;
+
+ if (!CanAIAttack(who))
+ return;
+
+ if (me->IsHostileTo(who))
+ {
+ float fAttackRadius = me->GetAttackDistance(who);
+ if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who))
+ {
+ if (!me->getVictim())
+ {
+ who->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
+ AttackStart(who);
+ }
+ else/* if (me->GetMap()->IsDungeon())*/
+ {
+ who->SetInCombatWith(me);
+ me->AddThreat(who, 0.0f);
+ }
+ }
+ }
+
+ //if (me->canStartAttack(who, false))
+ // AttackStart(who);
+}
+
+bool SmartAI::CanAIAttack(const Unit* who) const
+{
+ if (me->GetReactState() == REACT_PASSIVE)
+ return false;
+ return true;
+}
+
+bool SmartAI::AssistPlayerInCombat(Unit* pWho)
+{
+ if (!pWho || !pWho->getVictim())
+ return false;
+
+ //experimental (unknown) flag not present
+ if (!(me->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
+ return false;
+
+ //not a player
+ if (!pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
+ return false;
+
+ //never attack friendly
+ if (me->IsFriendlyTo(pWho))
+ return false;
+
+ //too far away and no free sight?
+ if (me->IsWithinDistInMap(pWho, SMART_MAX_AID_DIST) && me->IsWithinLOSInMap(pWho))
+ {
+ //already fighting someone?
+ if (!me->getVictim())
+ {
+ AttackStart(pWho);
+ return true;
+ }
+ else
+ {
+ pWho->SetInCombatWith(me);
+ me->AddThreat(pWho, 0.0f);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SmartAI::JustRespawned()
+{
+ mDespawnTime = 0;
+ mDespawnState = 0;
+ mEscortState = SMART_ESCORT_NONE;
+ me->SetVisibility(VISIBILITY_ON);
+ if (me->getFaction() != me->GetCreatureInfo()->faction_A)
+ me->RestoreFaction();
+ GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
+ Reset();
+ mFollowGuid = 0;//do not reset follower on Reset(), we need it after combat evade
+ mFollowDist = 0;
+ mFollowAngle = 0;
+ mFollowCredit = 0;
+ mFollowArrivedTimer = 1000;
+ mFollowArrivedEntry = 0;
+ mFollowCreditType = 0;
+}
+
+int SmartAI::Permissible(const Creature* creature)
+{
+ if (creature->GetAIName() == "SmartAI")
+ return PERMIT_BASE_SPECIAL;
+ return PERMIT_BASE_NO;
+}
+
+void SmartAI::JustReachedHome()
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_REACHED_HOME);
+}
+
+void SmartAI::EnterCombat(Unit* enemy)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_AGGRO, enemy);
+ me->GetPosition(&mLastOOCPos);
+}
+
+void SmartAI::JustDied(Unit* killer)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer);
+ if (HasEscortState(SMART_ESCORT_ESCORTING))
+ EndPath(true);
+}
+
+void SmartAI::KilledUnit(Unit* victim)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_KILL, victim);
+}
+
+void SmartAI::JustSummoned(Creature* pUnit)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_SUMMONED_UNIT, pUnit);
+}
+
+void SmartAI::AttackStart(Unit* who)
+{
+ if (who && me->Attack(who, true))
+ {
+ SetRun(mRun);
+ if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
+ me->GetMotionMaster()->MovementExpired();
+
+ if (mCanCombatMove)
+ me->GetMotionMaster()->MoveChase(who);
+
+ me->GetPosition(&mLastOOCPos);
+ }
+}
+
+void SmartAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_SPELLHIT, pUnit, 0, 0, false, pSpell);
+}
+
+void SmartAI::SpellHitTarget(Unit* target, const SpellEntry* pSpell)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_SPELLHIT_TARGET, target, 0, 0, false, pSpell);
+}
+
+void SmartAI::DamageTaken(Unit* done_by, uint32& damage, DamageEffectType /*damagetype*/)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_DAMAGED, done_by, damage);
+}
+
+void SmartAI::HealReceived(Unit* done_by, uint32& addhealth)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_RECEIVE_HEAL, done_by, addhealth);
+}
+
+void SmartAI::ReceiveEmote(Player* pPlayer, uint32 text_emote)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_RECEIVE_EMOTE, pPlayer, text_emote);
+}
+
+void SmartAI::IsSummonedBy(Unit* summoner)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_JUST_SUMMONED, summoner);
+}
+
+void SmartAI::DamageDealt(Unit* done_to, uint32& damage)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_DAMAGED_TARGET, done_to, damage);
+}
+
+void SmartAI::SummonedCreatureDespawn(Creature* unit)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_SUMMON_DESPAWNED, unit);
+}
+
+void SmartAI::UpdateAIWhileCharmed(const uint32 diff)
+{
+}
+
+void SmartAI::CorpseRemoved(uint32& respawnDelay)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_CORPSE_REMOVED, NULL, respawnDelay);
+}
+
+void SmartAI::PassengerBoarded(Unit* who, int8 seatId, bool apply)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_PASSENGER_BOARDED, who, (uint32)seatId, 0, apply);
+}
+
+void SmartAI::InitializeAI()
+{
+ GetScript()->OnInitialize(me);
+ if (!me->isDead())
+ Reset();
+ GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
+}
+
+void SmartAI::OnCharmed(bool apply)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_CHARMED, NULL, 0, 0, apply);
+}
+
+void SmartAI::DoAction(const int32 param)
+{
+}
+
+uint32 SmartAI::GetData(uint32 id)
+{
+ return 0;
+}
+
+void SmartAI::SetData(uint32 id, uint32 value)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_DATA_SET, NULL, id, value);
+}
+
+void SmartAI::SetGUID(const uint64& guid, int32 id)
+{
+}
+
+uint64 SmartAI::GetGUID(int32 id)
+{
+ return 0;
+}
+
+void SmartAI::SetRun(bool run)
+{
+ if (run)
+ me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING);
+ else
+ me->AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
+ mRun = run;
+}
+
+void SmartAI::SetFly(bool bFly)
+{
+ me->SetFlying(bFly);
+}
+
+void SmartAI::SetSwimm(bool bSwimm)
+{
+ if (bSwimm)
+ me->AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
+ else
+ me->RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING);
+}
+
+void SmartAI::sGossipHello(Player* player)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_HELLO, player);
+}
+
+void SmartAI::sGossipSelect(Player* player, uint32 sender, uint32 action)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_SELECT, player, sender, action);
+}
+
+void SmartAI::sGossipSelectCode(Player* player, uint32 sender, uint32 action, const char* code)
+{
+}
+
+void SmartAI::sQuestAccept(Player* player, Quest const* quest)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_ACCEPTED_QUEST, player, quest->GetQuestId());
+}
+
+void SmartAI::sQuestReward(Player* player, Quest const* quest, uint32 opt)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_REWARD_QUEST, player, quest->GetQuestId(), opt);
+}
+void SmartAI::SetCombatMove(bool on)
+{
+ if (mCanCombatMove == on)
+ return;
+ mCanCombatMove = on;
+ if (!HasEscortState(SMART_ESCORT_ESCORTING))
+ {
+ if (on && me->getVictim())
+ {
+ if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE)
+ {
+ SetRun(mRun);
+ me->GetMotionMaster()->MoveChase(me->getVictim());
+ me->CastStop();
+ }
+ }
+ else
+ {
+ me->StopMoving();
+ me->GetMotionMaster()->MoveIdle();
+ }
+ }
+}
+
+void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, uint32 end, uint32 creditType)
+{
+ if (!target)
+ return;
+ SetRun(mRun);
+ mFollowGuid = target->GetGUID();
+ mFollowDist = dist;
+ mFollowAngle = angle;
+ mFollowArrivedTimer = 1000;
+ mFollowCredit = credit;
+ mFollowArrivedEntry = end;
+ me->GetMotionMaster()->MoveFollow(target, dist, angle);
+ mFollowCreditType = creditType;
+}
+/*
+SMART_EVENT_UPDATE_OOC
+SMART_EVENT_SPELLHIT
+SMART_EVENT_RANGE
+SMART_EVENT_RESPAWN
+SMART_EVENT_SUMMONED_UNIT
+SMART_EVENT_ACCEPTED_QUEST
+SMART_EVENT_REWARD_QUEST
+SMART_EVENT_TARGET_BUFFED
+SMART_EVENT_SUMMON_DESPAWNED
+SMART_EVENT_AI_INIT
+SMART_EVENT_DATA_SET
+SMART_EVENT_TEXT_OVER
+SMART_EVENT_TIMED_EVENT_TRIGGERED
+SMART_EVENT_UPDATE
+SMART_EVENT_LINK
+SMART_EVENT_GOSSIP_SELECT
+SMART_EVENT_JUST_CREATED
+SMART_EVENT_GOSSIP_HELLO
+SMART_EVENT_DEATH
+*/
+
+int SmartGameObjectAI::Permissible(const GameObject* g)
+{
+ if (g->GetAIName() == "SmartGameObjectAI")
+ return PERMIT_BASE_SPECIAL;
+ return PERMIT_BASE_NO;
+}
+
+void SmartGameObjectAI::UpdateAI(const uint32 diff)
+{
+ GetScript()->OnUpdate(diff);
+}
+
+void SmartGameObjectAI::InitializeAI()
+{
+ GetScript()->OnInitialize(go);
+ GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
+ //Reset();
+}
+
+void SmartGameObjectAI::Reset()
+{
+ GetScript()->OnReset();
+}
+
+// Called when a player opens a gossip dialog with the gameobject.
+bool SmartGameObjectAI::GossipHello(Player* player)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_HELLO, player, 0 ,0 , false, NULL, go);
+ return false;
+}
+
+// Called when a player selects a gossip item in the gameobject's gossip menu.
+bool SmartGameObjectAI::GossipSelect(Player* player, uint32 sender, uint32 action)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_SELECT, player, sender, action, false, NULL, go);
+ return false;
+}
+
+// Called when a player selects a gossip with a code in the gameobject's gossip menu.
+bool SmartGameObjectAI::GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/)
+{
+ return false;
+}
+
+// Called when a player accepts a quest from the gameobject.
+bool SmartGameObjectAI::QuestAccept(Player* player, Quest const* quest)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_ACCEPTED_QUEST, player, quest->GetQuestId() ,0 , false, NULL, go);
+ return false;
+}
+
+// Called when a player selects a quest reward.
+bool SmartGameObjectAI::QuestReward(Player* player, Quest const* quest, uint32 opt)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_REWARD_QUEST, player, quest->GetQuestId() ,opt , false, NULL, go);
+ return false;
+}
+
+// Called when the dialog status between a player and the gameobject is requested.
+uint32 SmartGameObjectAI::GetDialogStatus(Player* /*player*/) { return 100; }
+
+// Called when the gameobject is destroyed (destructible buildings only).
+void SmartGameObjectAI::Destroyed(Player* player, uint32 eventId)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, player, eventId ,0 , false, NULL, go);
+}
+
+void SmartGameObjectAI::SetData(uint32 id, uint32 value)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_DATA_SET, NULL, id, value);
+}
+
+class SmartTrigger : public AreaTriggerScript
+{
+ public:
+
+ SmartTrigger()
+ : AreaTriggerScript("SmartTrigger")
+ {
+ }
+
+ bool OnTrigger(Player* player, AreaTriggerEntry const* trigger)
+ {
+ sLog.outDebug("AreaTrigger %u is using SmartTrigger script", trigger->id);
+ SmartScript script;
+ script.OnInitialize(NULL, trigger);
+ script.ProcessEventsFor(SMART_EVENT_AREATRIGGER_ONTRIGGER, player, trigger->id);
+ return true;
+ }
+};
+
+void AddSC_SmartSCripts()
+{
+ new SmartTrigger();
+} \ No newline at end of file
diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h
new file mode 100644
index 00000000000..6bc49b24f9f
--- /dev/null
+++ b/src/server/game/AI/SmartScripts/SmartAI.h
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 TRINITY_SMARTAI_H
+#define TRINITY_SMARTAI_H
+
+#include "Common.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "Unit.h"
+#include "ConditionMgr.h"
+#include "CreatureTextMgr.h"
+#include "Spell.h"
+
+#include "SmartScript.h"
+#include "SmartScriptMgr.h"
+#include "GameObjectAI.h"
+
+enum SmartEscortState
+{
+ SMART_ESCORT_NONE = 0x000, //nothing in progress
+ SMART_ESCORT_ESCORTING = 0x001, //escort is in progress
+ SMART_ESCORT_RETURNING = 0x002, //escort is returning after being in combat
+ SMART_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed
+};
+
+enum SmartEscortVars
+{
+ SMART_ESCORT_MAX_PLAYER_DIST = 50,
+ SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2,
+};
+
+class SmartAI : public CreatureAI
+{
+ public:
+ ~SmartAI(){};
+ explicit SmartAI(Creature *c);
+
+ // Start moving to the desired MovePoint
+ void StartPath(bool run = false, uint32 path = 0, bool repeat = false, Unit* invoker = NULL);
+ bool LoadPath(uint32 entry);
+ void PausePath(uint32 delay, bool forced = false);
+ void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false);
+ void EndPath(bool fail = false);
+ void ResumePath();
+ WayPoint* GetNextWayPoint();
+ bool HasEscortState(uint32 uiEscortState) { return (mEscortState & uiEscortState); }
+ void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; }
+ void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
+ void SetAutoAttack(bool on) { mCanAutoAttack = on; }
+ void SetCombatMove(bool on);
+ void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0);
+
+ SmartScript* GetScript() { return &mScript; }
+ bool IsEscortInvokerInRange();
+
+ // Called when creature is spawned or respawned
+ void JustRespawned();
+
+ // Called after InitializeAI(), EnterEvadeMode() for resetting variables
+ void Reset();
+
+ // Called at reaching home after evade
+ void JustReachedHome();
+
+ // Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
+ void EnterCombat(Unit *enemy);
+
+ // Called for reaction at stopping attack at no attackers or targets
+ void EnterEvadeMode();
+
+ // Called when the creature is killed
+ void JustDied(Unit* killer);
+
+ // Called when the creature kills a unit
+ void KilledUnit(Unit* victim);
+
+ // Called when the creature summon successfully other creature
+ void JustSummoned(Creature* pUnit);
+
+ // Tell creature to attack and follow the victim
+ void AttackStart(Unit *who);
+
+ // Called if IsVisible(Unit *who) is true at each *who move, reaction at visibility zone enter
+ void MoveInLineOfSight(Unit *who);
+
+ // Called when hit by a spell
+ void SpellHit(Unit* pUnit, const SpellEntry* pSpell);
+
+ // Called when spell hits a target
+ void SpellHitTarget(Unit* target, const SpellEntry* pSpell);
+
+ // Called at any Damage from any attacker (before damage apply)
+ void DamageTaken(Unit* done_by, uint32& damage, DamageEffectType damagetype);
+
+ // Called when the creature receives heal
+ void HealReceived(Unit* done_by, uint32& addhealth);
+
+ // Called at World update tick
+ void UpdateAI(const uint32 diff);
+
+ // Called at text emote receive from player
+ void ReceiveEmote(Player* pPlayer, uint32 text_emote);
+
+ // Called at waypoint reached or point movement finished
+ void MovementInform(uint32 MovementType, uint32 Data);
+
+ // Called when creature is summoned by another unit
+ void IsSummonedBy(Unit* summoner);
+
+ // Called at any Damage to any victim (before damage apply)
+ void DamageDealt(Unit * done_to, uint32 & damage);
+
+ // Called when a summoned creature dissapears (UnSommoned)
+ void SummonedCreatureDespawn(Creature* unit);
+
+ // called when the corpse of this creature gets removed
+ void CorpseRemoved(uint32 & respawnDelay);
+
+ // Called at World update tick if creature is charmed
+ void UpdateAIWhileCharmed(const uint32 diff);
+
+ // Called when a Player/Creature enters the creature (vehicle)
+ void PassengerBoarded(Unit* who, int8 seatId, bool apply);
+
+ // Called when gets initialized, when creature is added to world
+ void InitializeAI();
+
+ // Called when creature gets charmed by another unit
+ void OnCharmed(bool apply);
+
+ // Called when victim is in line of sight
+ bool CanAIAttack(const Unit* who) const;
+
+ // Used in scripts to share variables
+ void DoAction(const int32 param = 0);
+
+ // Used in scripts to share variables
+ uint32 GetData(uint32 id = 0);
+
+ // Used in scripts to share variables
+ void SetData(uint32 id, uint32 value);
+
+ // Used in scripts to share variables
+ void SetGUID(const uint64 &guid, int32 id = 0);
+
+ // Used in scripts to share variables
+ uint64 GetGUID(int32 id = 0);
+
+ //core related
+ static int Permissible(const Creature *);
+
+ // Called at movepoint reached
+ void MovepointReached(uint32 id);
+
+ // Makes the creature run/walk
+ void SetRun(bool bRun = true);
+
+ void SetFly(bool bFly = true);
+
+ void SetSwimm(bool bSwimm = true);
+
+ void sGossipHello(Player* player);
+ void sGossipSelect(Player* player, uint32 sender, uint32 action);
+ void sGossipSelectCode(Player* player, uint32 sender, uint32 action, const char* code);
+ void sQuestAccept(Player* player, Quest const* quest);
+ //void sQuestSelect(Player* player, Quest const* quest);
+ //void sQuestComplete(Player* player, Quest const* quest);
+ void sQuestReward(Player* player, Quest const* quest, uint32 opt);
+
+ uint32 mEscortQuestID;
+
+ void SetDespawnTime (uint32 t)
+ {
+ mDespawnTime = t;
+ mDespawnState = t ? 1 : 0;
+ }
+ void StartDespawn() { mDespawnState = 2; }
+
+ private:
+ uint32 mFollowCreditType;
+ uint32 mFollowArrivedTimer;
+ uint32 mFollowCredit;
+ uint32 mFollowArrivedEntry;
+ uint64 mFollowGuid;
+ float mFollowDist;
+ float mFollowAngle;
+
+ void ReturnToLastOOCPos();
+ void UpdatePath(const uint32 diff);
+ SmartScript mScript;
+ WPPath* mWayPoints;
+ uint32 mEscortState;
+ uint32 mCurrentWPID;
+ uint32 mLastWPIDReached;
+ bool mWPReached;
+ uint32 mWPPauseTimer;
+ WayPoint* mLastWP;
+ Position mLastOOCPos;//set on enter combat
+ uint32 GetWPCount() { return mWayPoints ? mWayPoints->size() : 0; }
+ bool mCanRepeatPath;
+ bool mRun;
+ bool mCanAutoAttack;
+ bool mCanCombatMove;
+ bool mForcedPaused;
+
+ bool AssistPlayerInCombat(Unit* pWho);
+
+ uint32 mDespawnTime;
+ uint32 mDespawnState;
+ void UpdateDespawn(const uint32 diff);
+ uint32 mEscortInvokerCheckTimer;
+};
+
+class SmartGameObjectAI : public GameObjectAI
+{
+public:
+ SmartGameObjectAI(GameObject *g) : go(g), GameObjectAI(g) {}
+ ~SmartGameObjectAI() {}
+
+ void UpdateAI(const uint32 diff);
+ void InitializeAI();
+ void Reset();
+ SmartScript* GetScript() { return &mScript; }
+ static int Permissible(const GameObject* g);
+
+ bool GossipHello(Player* player) ;
+ bool GossipSelect(Player* player, uint32 sender, uint32 action);
+ bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/);
+ bool QuestAccept(Player* player, Quest const* quest);
+ bool QuestReward(Player* player, Quest const* quest, uint32 opt);
+ uint32 GetDialogStatus(Player* /*player*/);
+ void Destroyed(Player* player, uint32 eventId);
+ void SetData(uint32 id, uint32 value);
+
+protected:
+ GameObject * const go;
+ SmartScript mScript;
+};
+#endif
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
new file mode 100644
index 00000000000..4da6dc85a5d
--- /dev/null
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -0,0 +1,1949 @@
+/*
+ * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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/>.
+ */
+
+
+#include "DatabaseEnv.h"
+#include "SQLStorage.h"
+#include "ObjectMgr.h"
+#include "ProgressBar.h"
+#include "ObjectDefines.h"
+#include "GridDefines.h"
+#include "GridNotifiers.h"
+#include "SpellMgr.h"
+#include "GridNotifiersImpl.h"
+#include "Cell.h"
+#include "CellImpl.h"
+#include "InstanceScript.h"
+#include "ScriptedCreature.h"
+#include "GossipDef.h"
+#include "ScriptedCreature.h"
+#include "SmartScript.h"
+#include "SmartAI.h"
+
+SmartScript::SmartScript()
+{
+ go = NULL;
+ me = NULL;
+ mEventPhase = 0;
+ mInvinceabilityHpLevel = 0;
+ mPathId = 0;
+ mTargetStorage = new ObjectListMap();
+ mStoredEvents.clear();
+ mTextTimer = 0;
+ mLastTextID = 0;
+ mTextGUID = 0;
+ mUseTextTimer = false;
+ mTemplate = SMARTAI_TEMPLATE_BASIC;
+ meOrigGUID = 0;
+ goOrigGUID = 0;
+}
+
+void SmartScript::OnReset()
+{
+ SetPhase(0);
+ ResetBaseObject();
+ for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
+ {
+ if ((*i).GetEventType() == SMART_EVENT_UPDATE_OOC || (*i).GetEventType() == SMART_EVENT_UPDATE)
+ RecalcTimer((*i), (*i).event.minMaxRepeat.min, (*i).event.minMaxRepeat.max);
+ (*i).runOnce = false;
+ }
+ ProcessEventsFor(SMART_EVENT_RESET);
+}
+
+void SmartScript::ProcessEventsFor(SMART_EVENT e, Unit* unit, uint32 var0, uint32 var1, bool bvar, const SpellEntry* spell, GameObject* gob)
+{
+ for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
+ {
+ if ((*i).GetEventType() == SMART_EVENT_LINK)//special handling
+ continue;
+ if ((*i).GetEventType() == e/* && (!(*i).event.event_phase_mask || IsInPhase((*i).event.event_phase_mask)) && !((*i).event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE && (*i).runOnce)*/)
+ ProcessEvent(*i, unit, var0, var1, bvar, spell, gob);
+ }
+}
+
+void SmartScript::ProcessAction(SmartScriptHolder &e, Unit* unit, uint32 var0, uint32 var1, bool bvar, const SpellEntry* spell, GameObject* gob)
+{
+ //calc random
+ if (e.GetEventType() != SMART_EVENT_LINK && e.event.event_chance < 100 && e.event.event_chance)
+ {
+ uint32 rnd = urand(0, 100);
+ if (e.event.event_chance <= rnd)
+ return;
+ }
+ e.runOnce = true;//used for repeat check
+
+ if (e.link && e.link != e.event_id)
+ {
+ SmartScriptHolder linked = FindLinkedEvent(e.link);
+ if (linked.GetActionType() && linked.GetEventType() == SMART_EVENT_LINK)
+ {
+ ProcessEvent(linked, unit, var0, var1, bvar, spell, gob);
+ }else{
+ sLog.outErrorDb("SmartScript::ProcessAction: Entry %d SourceType %u, Event %u, Link Event %u not found or invalid, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.link);
+ }
+ }
+
+ switch (e.GetActionType())
+ {
+ case SMART_ACTION_TALK:
+ if (!me) return;
+ mLastTextID = e.action.talk.textGroupID1;
+ mTextTimer = sCreatureTextMgr.SendChat(me, uint8(e.action.talk.textGroupID1), IsPlayer(unit)? unit->GetGUID() : NULL);
+ mTextGUID = IsPlayer(unit)? unit->GetGUID() : NULL;
+ if (e.action.talk.textGroupID2) mTextIDs.push_back(e.action.talk.textGroupID2);
+ if (e.action.talk.textGroupID3) mTextIDs.push_back(e.action.talk.textGroupID3);
+ if (e.action.talk.textGroupID4) mTextIDs.push_back(e.action.talk.textGroupID4);
+ if (e.action.talk.textGroupID5) mTextIDs.push_back(e.action.talk.textGroupID5);
+ if (e.action.talk.textGroupID6) mTextIDs.push_back(e.action.talk.textGroupID6);
+ if (!mTextIDs.empty())
+ mUseTextTimer = true;
+ break;
+ case SMART_ACTION_PLAY_EMOTE:
+ if (me)
+ me->HandleEmoteCommand(e.action.emote.emote);
+ break;
+ case SMART_ACTION_SOUND:
+ if (me)
+ sCreatureTextMgr.SendSound(me, e.action.sound.sound, CHAT_TYPE_SAY, 0, TextRange(e.action.sound.range), Team(NULL), false);
+ break;
+ case SMART_ACTION_SET_FACTION:
+ {
+ if (!me) return;
+ if (e.action.faction.factionID)
+ me->setFaction(e.action.faction.factionID);
+ else
+ {
+ if (CreatureInfo const* ci = GetCreatureTemplateStore(me->GetEntry()))
+ {
+ if (me->getFaction() != ci->faction_A)
+ me->setFaction(ci->faction_A);
+ }
+ }
+ break;
+ }
+ case SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL:
+ {
+ if (!me) return;
+ if (e.action.morphOrMount.creature || e.action.morphOrMount.model)
+ {
+ //set model based on entry from creature_template
+ if (e.action.morphOrMount.creature)
+ {
+ if (CreatureInfo const* ci = GetCreatureTemplateStore(e.action.morphOrMount.creature))
+ {
+ uint32 display_id = sObjectMgr.ChooseDisplayId(0, ci);
+ me->SetDisplayId(display_id);
+ }
+ }
+ //if no param1, then use value from param2 (modelId)
+ else
+ me->SetDisplayId(e.action.morphOrMount.model);
+ }
+ else
+ me->DeMorph();
+ break;
+ }
+ case SMART_ACTION_FAIL_QUEST:
+ {
+ if (!unit || !unit->ToPlayer()) return;//return if no player
+ unit->ToPlayer()->FailQuest(e.action.quest.quest);
+ break;
+ }
+ case SMART_ACTION_ADD_QUEST:
+ {
+ if (!unit || !unit->ToPlayer()) return;//return if no player
+ if (const Quest* q = sObjectMgr.GetQuestTemplate(e.action.quest.quest))
+ unit->ToPlayer()->AddQuest(q, NULL);
+ break;
+ }
+ case SMART_ACTION_SET_REACT_STATE:
+ {
+ if (!me) return;
+ me->SetReactState(ReactStates(e.action.react.state));
+ break;
+ }
+ case SMART_ACTION_RANDOM_EMOTE:
+ {
+ if (!me) return;
+ uint32 emotes[SMART_ACTION_PARAM_COUNT];
+ emotes[0] = e.action.randomEmote.emote1;
+ emotes[1] = e.action.randomEmote.emote2;
+ emotes[2] = e.action.randomEmote.emote3;
+ emotes[3] = e.action.randomEmote.emote4;
+ emotes[4] = e.action.randomEmote.emote5;
+ emotes[5] = e.action.randomEmote.emote6;
+ uint32 temp[SMART_ACTION_PARAM_COUNT];
+ uint32 count = 0;
+ for (uint8 i = 0; i < SMART_ACTION_PARAM_COUNT; i++)
+ {
+ if (emotes[i])
+ {
+ temp[count] = emotes[i];
+ count++;
+ }
+ }
+ me->HandleEmoteCommand(temp[urand(0, count)]);
+ break;
+ }
+ case SMART_ACTION_THREAT_ALL_PCT:
+ {
+ if (!me) return;
+ std::list<HostileReference*>& threatList = me->getThreatManager().getThreatList();
+ for (std::list<HostileReference*>::iterator i = threatList.begin(); i != threatList.end(); ++i)
+ if (Unit* Temp = Unit::GetUnit(*me,(*i)->getUnitGuid()))
+ me->getThreatManager().modifyThreatPercent(Temp, e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
+ break;
+ }
+ case SMART_ACTION_THREAT_SINGLE_PCT:
+ {
+ if (!me) return;
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ if (IsUnit((*itr)))
+ me->getThreatManager().modifyThreatPercent((*itr)->ToUnit(), e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
+ break;
+ }
+ case SMART_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS:
+ {
+ //if (!me) return;
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ if (IsPlayer((*itr)))
+ (*itr)->ToPlayer()->AreaExploredOrEventHappens(e.action.quest.quest);
+ break;
+ }
+ case SMART_ACTION_SEND_CASTCREATUREORGO:
+ {
+ if (!me) return;
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ if (IsPlayer((*itr)))
+ (*itr)->ToPlayer()->CastedCreatureOrGO(e.action.castedCreatureOrGO.creature, me->GetGUID(), e.action.castedCreatureOrGO.spell);
+ break;
+ }
+ case SMART_ACTION_CAST:
+ {
+ if (!me) return;
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ if (IsUnit((*itr)))
+ {
+ if (e.action.cast.flags & SMARTCAST_INTERRUPT_PREVIOUS)
+ me->InterruptNonMeleeSpells(false);
+ me->CastSpell((*itr)->ToUnit(), e.action.cast.spell,(e.action.cast.flags & SMARTCAST_TRIGGERED) ? true : false);
+ }
+ break;
+ }
+ case SMART_ACTION_ADD_AURA:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ if (IsUnit((*itr)))
+ {
+ (*itr)->ToUnit()->AddAura(e.action.cast.spell, (*itr)->ToUnit());
+ }
+ break;
+ }
+ case SMART_ACTION_ACTIVATE_GOBJECT:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ if (IsGameObject((*itr)))
+ {
+ // Activate
+ (*itr)->ToGameObject()->SetLootState(GO_READY);
+ (*itr)->ToGameObject()->UseDoorOrButton();
+ }
+ break;
+ }
+ case SMART_ACTION_RESET_GOBJECT:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ if (IsGameObject((*itr)))
+ (*itr)->ToGameObject()->ResetDoorOrButton();
+ break;
+ }
+ case SMART_ACTION_SET_EMOTE_STATE:
+ {
+ if (!me) return;
+ me->SetUInt32Value(UNIT_NPC_EMOTESTATE, e.action.emote.emote);
+ break;
+ }
+ case SMART_ACTION_SET_UNIT_FLAG:
+ {
+ if (!me) return;
+ me->SetFlag(UNIT_FIELD_FLAGS, e.action.unitFlag.flag);
+ break;
+ }
+ case SMART_ACTION_REMOVE_UNIT_FLAG:
+ {
+ if (!me) return;
+ me->RemoveFlag(UNIT_FIELD_FLAGS, e.action.unitFlag.flag);
+ break;
+ }
+ case SMART_ACTION_AUTO_ATTACK:
+ {
+ if (!IsSmart()) return;
+ CAST_AI(SmartAI, me->AI())->SetAutoAttack(e.action.autoAttack.attack ? true : false);
+ break;
+ }
+ case SMART_ACTION_ALLOW_COMBAT_MOVEMENT:
+ {
+ if (!IsSmart()) return;
+ bool move = e.action.combatMove.move ? true : false;
+ CAST_AI(SmartAI, me->AI())->SetCombatMove(move);
+ break;
+ }
+ case SMART_ACTION_SET_EVENT_PHASE:
+ {
+ SetPhase(e.action.setEventPhase.phase);
+ break;
+ }
+ case SMART_ACTION_INC_EVENT_PHASE:
+ {
+ IncPhase(e.action.incEventPhase.inc);
+ DecPhase(e.action.incEventPhase.dec);
+ break;
+ }
+ case SMART_ACTION_EVADE:
+ {
+ if (me) me->AI()->EnterEvadeMode();
+ return;
+ }
+ case SMART_ACTION_FLEE_FOR_ASSIST:
+ {
+ if (me) me->DoFleeToGetAssistance();
+ break;
+ }
+ case SMART_ACTION_CALL_GROUPEVENTHAPPENS:
+ {
+ if (IsPlayer(unit) && GetBaseObject())
+ unit->ToPlayer()->GroupEventHappens(e.action.quest.quest, GetBaseObject());
+ break;
+ }
+ case SMART_ACTION_CALL_CASTEDCREATUREORGO:
+ {
+ if (!me) return;
+ std::list<HostileReference*>& threatList = me->getThreatManager().getThreatList();
+ for (std::list<HostileReference*>::iterator i = threatList.begin(); i != threatList.end(); ++i)
+ if (Unit* Temp = Unit::GetUnit(*me,(*i)->getUnitGuid()))
+ if (IsPlayer(Temp))
+ Temp->ToPlayer()->CastedCreatureOrGO(e.action.castedCreatureOrGO.creature, me->GetGUID(), e.action.castedCreatureOrGO.spell);
+ break;
+ }
+ case SMART_ACTION_REMOVEAURASFROMSPELL:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(!IsUnit((*itr))) continue;
+ (*itr)->ToUnit()->RemoveAurasDueToSpell(e.action.removeAura.spell);
+ }
+ break;
+ }
+ case SMART_ACTION_FOLLOW:
+ {
+ if (!IsSmart()) return;
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if (IsUnit((*itr)))
+ {
+ CAST_AI(SmartAI, me->AI())->SetFollow((*itr)->ToUnit(), (float)e.action.follow.dist, (float)e.action.follow.angle, e.action.follow.credit, e.action.follow.entry, e.action.follow.creditType);
+ return;
+ }
+ }
+ break;
+ }
+ case SMART_ACTION_RANDOM_PHASE:
+ {
+ uint32 phases[SMART_ACTION_PARAM_COUNT];
+ phases[0] = e.action.randomEmote.emote1;
+ phases[1] = e.action.randomEmote.emote2;
+ phases[2] = e.action.randomEmote.emote3;
+ phases[3] = e.action.randomEmote.emote4;
+ phases[4] = e.action.randomEmote.emote5;
+ phases[5] = e.action.randomEmote.emote6;
+ uint32 temp[SMART_ACTION_PARAM_COUNT];
+ uint32 count = 0;
+ for (uint8 i = 0; i < SMART_ACTION_PARAM_COUNT; i++)
+ {
+ if (phases[i] > 0)
+ {
+ temp[count] = phases[i];
+ count++;
+ }
+ }
+ SetPhase(temp[urand(0, count)]);
+ break;
+ }
+ case SMART_ACTION_RANDOM_PHASE_RANGE:
+ {
+ SetPhase(urand(e.action.randomPhaseRange.phaseMin, e.action.randomPhaseRange.phaseMax));
+ break;
+ }
+ case SMART_ACTION_CALL_KILLEDMONSTER:
+ {
+ Player* pPlayer = NULL;
+ if (me)
+ pPlayer = me->GetLootRecipient();
+ if (me && pPlayer)
+ pPlayer->RewardPlayerAndGroupAtEvent(e.action.killedMonster.creature, pPlayer);
+ else if (GetBaseObject())
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(!IsPlayer((*itr))) continue;
+ (*itr)->ToPlayer()->RewardPlayerAndGroupAtEvent(e.action.killedMonster.creature, (*itr)->ToPlayer());
+ }
+ }else if (trigger && IsPlayer(unit))
+ {
+ unit->ToPlayer()->RewardPlayerAndGroupAtEvent(e.action.killedMonster.creature, unit);
+ }
+ break;
+ }
+ case SMART_ACTION_SET_INST_DATA:
+ {
+ WorldObject* obj = GetBaseObject();
+ if (!obj)
+ obj = unit;
+ if (obj) return;
+ InstanceScript* pInst = (InstanceScript*)obj->GetInstanceScript();
+ if (!pInst)
+ {
+ sLog.outErrorDb("SmartScript: Event %u attempt to set instance data without instance script. EntryOrGuid %d", e.GetEventType(), e.entryOrGuid);
+ return;
+ }
+ pInst->SetData(e.action.setInstanceData.field, e.action.setInstanceData.data);
+ break;
+ }
+ case SMART_ACTION_SET_INST_DATA64:
+ {
+ WorldObject* obj = GetBaseObject();
+ if (!obj)
+ obj = unit;
+ if (obj) return;
+ InstanceScript* pInst = (InstanceScript*)obj->GetInstanceScript();
+ if (!pInst)
+ {
+ sLog.outErrorDb("SmartScript: Event %u attempt to set instance data without instance script. EntryOrGuid %d", e.GetEventType(), e.entryOrGuid);
+ return;
+ }
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ pInst->SetData64(e.action.setInstanceData64.field, (*itr)->GetGUID());
+ return;
+ }
+
+ break;
+ }
+ case SMART_ACTION_UPDATE_TEMPLATE:
+ {
+ if (!me || me->GetEntry() == e.action.updateTemplate.creature)
+ return;
+ me->UpdateEntry(e.action.updateTemplate.creature, e.action.updateTemplate.team ? HORDE : ALLIANCE);
+ break;
+ }
+ case SMART_ACTION_DIE:
+ {
+ if (me && !me->isDead())
+ me->Kill(me);
+ break;
+ }
+ case SMART_ACTION_SET_IN_COMBAT_WITH_ZONE:
+ {
+ if (me)
+ me->SetInCombatWithZone();
+ break;
+ }
+ case SMART_ACTION_CALL_FOR_HELP:
+ {
+ if (me)
+ me->CallForHelp((float)e.action.callHelp.range);
+ break;
+ }
+ case SMART_ACTION_SET_SHEATH:
+ {
+ if (me)
+ me->SetSheath(SheathState(e.action.setSheath.sheath));
+ break;
+ }
+ case SMART_ACTION_FORCE_DESPAWN:
+ {
+ if (!IsSmart()) return;
+ CAST_AI(SmartAI, me->AI())->SetDespawnTime(e.action.forceDespawn.delay + 1);//next tick
+ CAST_AI(SmartAI, me->AI())->StartDespawn();
+ break;
+ }
+ case SMART_ACTION_SET_INGAME_PHASE_MASK:
+ {
+ if (GetBaseObject())
+ GetBaseObject()->SetPhaseMask(e.action.ingamePhaseMask.mask, true);
+ break;
+ }
+ case SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL:
+ {
+ if (!me) return;
+ if (e.action.morphOrMount.creature || e.action.morphOrMount.model)
+ {
+ if (e.action.morphOrMount.creature > 0)
+ {
+ if (CreatureInfo const* cInfo = GetCreatureTemplateStore(e.action.morphOrMount.creature))
+ {
+ uint32 display_id = sObjectMgr.ChooseDisplayId(0, cInfo);
+ me->Mount(display_id);
+ }
+ }
+ else
+ me->Mount(e.action.morphOrMount.model);
+ }
+ else
+ me->Unmount();
+ break;
+ }
+ case SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL:
+ {
+ if (!GetBaseObject()) return;
+ if (e.action.invincHP.minHP)
+ mInvinceabilityHpLevel = me->CountPctFromMaxHealth(e.action.invincHP.minHP);
+ else
+ mInvinceabilityHpLevel = e.action.invincHP.minHP;
+ break;
+ }
+ case SMART_ACTION_SET_DATA:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if (IsCreature((*itr)))
+ (*itr)->ToCreature()->AI()->SetData(e.action.setData.field, e.action.setData.data);
+ if (IsGameObject((*itr)))
+ (*itr)->ToGameObject()->AI()->SetData(e.action.setData.field, e.action.setData.data);
+ return;
+ }
+ break;
+ }
+ case SMART_ACTION_MOVE_FORWARD:
+ {
+ if (!me) return;
+ float x,y,z;
+ me->GetClosePoint(x, y, z, me->GetObjectSize() / 3, (float)e.action.moveRandom.distance);
+ me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT,x,y,z);
+ break;
+ }
+ case SMART_ACTION_SET_VISIBILITY:
+ {
+ if (me)
+ me->SetVisibility(e.action.visibility.state ? VISIBILITY_ON : VISIBILITY_OFF);
+ break;
+ }
+ case SMART_ACTION_SET_ACTIVE:
+ {
+ if (GetBaseObject())
+ GetBaseObject()->setActive(true);
+ break;
+ }
+ case SMART_ACTION_ATTACK_START:
+ {
+ if (!me) return;
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if (IsUnit((*itr)))
+ me->AI()->AttackStart((*itr)->ToUnit());
+ return;
+ }
+ break;
+ }
+ case SMART_ACTION_SUMMON_CREATURE:
+ {
+ WorldObject* obj = GetBaseObject();
+ if (!obj)
+ obj = unit;
+ float x,y,z,o;;
+ ObjectList* targets = GetTargets(e, unit);
+ if (targets)
+ {
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(!IsUnit((*itr))) continue;
+ (*itr)->GetPosition(x,y,z,o);
+ if (Creature* summon = GetBaseObject()->SummonCreature(e.action.summonCreature.creature, x, y, z, o, (TempSummonType)e.action.summonCreature.type, e.action.summonCreature.duration))
+ {
+ if (unit && e.action.summonCreature.attackInvoker)
+ {
+ summon->AI()->AttackStart((*itr)->ToUnit());
+ }
+ }
+ }
+ }
+ if (e.GetTargetType() != SMART_TARGET_POSITION)
+ return;
+ if (Creature* summon = GetBaseObject()->SummonCreature(e.action.summonCreature.creature, e.target.x, e.target.y, e.target.z, e.target.o, (TempSummonType)e.action.summonCreature.type, e.action.summonCreature.duration))
+ {
+ if (unit && e.action.summonCreature.attackInvoker)
+ summon->AI()->AttackStart(unit);
+ }
+ break;
+ }
+ case SMART_ACTION_SUMMON_GO:
+ {
+ if (!GetBaseObject()) return;
+ float x,y,z,o;
+ ObjectList* targets = GetTargets(e, unit);
+ if (targets)
+ {
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(!IsUnit((*itr))) continue;
+ (*itr)->GetPosition(x,y,z,o);
+ GetBaseObject()->SummonGameObject(e.action.summonGO.entry, x, y, z, o, 0, 0, 0, 0, e.action.summonGO.despawnTime);
+ }
+ }
+ if (e.GetTargetType() != SMART_TARGET_POSITION)
+ return;
+ GetBaseObject()->SummonGameObject(e.action.summonGO.entry, e.target.x, e.target.y, e.target.z, e.target.o, 0, 0, 0, 0, e.action.summonGO.despawnTime);
+ break;
+ }
+ case SMART_ACTION_KILL_UNIT:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(!IsUnit((*itr))) continue;
+ (*itr)->ToUnit()->Kill((*itr)->ToUnit());
+ }
+ break;
+ }
+ case SMART_ACTION_INSTALL_AI_TEMPLATE:
+ {
+ InstallTemplate(e);
+ break;
+ }
+ case SMART_ACTION_ADD_ITEM:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(!IsPlayer((*itr))) continue;
+ (*itr)->ToPlayer()->AddItem(e.action.item.entry, e.action.item.count);
+ }
+ break;
+ }
+ case SMART_ACTION_REMOVE_ITEM:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(!IsPlayer((*itr))) continue;
+ (*itr)->ToPlayer()->DestroyItemCount(e.action.item.entry, e.action.item.count, true);
+ }
+ break;
+ }
+ case SMART_ACTION_STORE_VARIABLE_DECIMAL:
+ {
+ if(mStoredDecimals.find(e.action.storeVar.id) != mStoredDecimals.end())
+ mStoredDecimals.erase(e.action.storeVar.id);
+ mStoredDecimals[e.action.storeVar.id] = e.action.storeVar.number;
+ break;
+ }
+ case SMART_ACTION_STORE_TARGET_LIST:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ StoreTargetList(targets, e.action.storeTargets.id);
+ break;
+ }
+ case SMART_ACTION_TELEPORT:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ uint32 map = e.action.teleport.mapID;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(!IsPlayer((*itr))) continue;
+ (*itr)->ToPlayer()->TeleportTo(e.action.teleport.mapID, e.target.x, e.target.y, e.target.z, e.target.o);
+ }
+ break;
+ }
+ case SMART_ACTION_SET_FLY:
+ {
+ if (!IsSmart()) return;
+ CAST_AI(SmartAI, me->AI())->SetFly(e.action.setFly.fly ? true : false);
+ break;
+ }
+ case SMART_ACTION_SET_RUN:
+ {
+ if (!IsSmart()) return;
+ CAST_AI(SmartAI, me->AI())->SetRun(e.action.setRun.run ? true : false);
+ break;
+ }
+
+ case SMART_ACTION_SET_SWIMM:
+ {
+ if (!IsSmart()) return;
+ CAST_AI(SmartAI, me->AI())->SetSwimm(e.action.setSwimm.swimm ? true : false);
+ break;
+ }
+ case SMART_ACTION_WP_LOAD:
+ {
+ if (!me) return;
+ uint32 entry = e.action.wpLoad.id;
+ break;
+ }
+ case SMART_ACTION_WP_START:
+ {
+ if (!IsSmart()) return;
+ bool run = e.action.wpStart.run ? true : false;
+ uint32 entry = e.action.wpStart.pathID;
+ bool repeat = e.action.wpStart.repeat ? true : false;
+ ObjectList* targets = GetTargets(e, unit);
+ StoreTargetList(targets, SMART_ESCORT_TARGETS);
+ me->SetReactState((ReactStates)e.action.wpStart.reactState);
+ CAST_AI(SmartAI, me->AI())->StartPath(run, entry, repeat, unit);
+
+ uint32 quest = e.action.wpStart.quest;
+ uint32 DespawnTime = e.action.wpStart.despawnTime;
+ CAST_AI(SmartAI, me->AI())->mEscortQuestID = quest;
+ CAST_AI(SmartAI, me->AI())->SetDespawnTime(DespawnTime);
+ break;
+ }
+ case SMART_ACTION_WP_PAUSE:
+ {
+ if (!IsSmart()) return;
+ uint32 delay = e.action.wpPause.delay;
+ CAST_AI(SmartAI, me->AI())->PausePath(delay, e.GetEventType() == SMART_EVENT_WAYPOINT_REACHED ? false : true);
+ break;
+ }
+ case SMART_ACTION_WP_STOP:
+ {
+ if (!IsSmart()) return;
+ uint32 DespawnTime = e.action.wpStop.despawnTime;
+ uint32 quest = e.action.wpStop.quest;
+ bool fail = e.action.wpStop.fail ? true : false;
+ CAST_AI(SmartAI, me->AI())->StopPath(DespawnTime, quest, fail);
+ break;
+ }
+ case SMART_ACTION_WP_RESUME:
+ {
+ if (!IsSmart()) return;
+ CAST_AI(SmartAI, me->AI())->ResumePath();
+ break;
+ }
+ case SMART_ACTION_SET_ORIENTATION:
+ {
+ if (!me) return;
+ ObjectList* targets = GetTargets(e, unit);
+ if (e.GetTargetType() == SMART_TARGET_POSITION)
+ me->SetFacing(e.target.o, NULL);
+ else if (targets && !targets->empty())
+ me->SetFacing(0, (*targets->begin()));
+ break;
+ }
+ case SMART_ACTION_PLAYMOVIE:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(!IsPlayer((*itr))) continue;
+ (*itr)->ToPlayer()->SendMovieStart(e.action.movie.entry);
+ }
+
+ break;
+ }
+ case SMART_ACTION_MOVE_TO_POS:
+ {
+ if (!IsSmart()) return;
+ bool run = e.action.setRun.run ? true : false;
+ CAST_AI(SmartAI, me->AI())->SetRun(run);
+ me->GetMotionMaster()->MovePoint(0, e.target.x, e.target.y , e.target.z);
+ break;
+ }
+ case SMART_ACTION_RESPAWN_TARGET:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(IsCreature((*itr)))
+ (*itr)->ToCreature()->Respawn();
+ if(IsGameObject((*itr)))
+ (*itr)->ToGameObject()->Respawn();
+ }
+ break;
+ }
+ case SMART_ACTION_CLOSE_GOSSIP:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(IsPlayer((*itr)))
+ (*itr)->ToPlayer()->PlayerTalkClass->CloseGossip();
+ }
+ break;
+ }
+ case SMART_ACTION_EQUIP:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if(Creature* npc = (*itr)->ToCreature())
+ {
+ if (e.action.equip.entry && !e.action.equip.slot1 && !e.action.equip.slot2 && !e.action.equip.slot3)
+ npc->LoadEquipment(e.action.equip.entry, true);
+ else
+ {
+ npc->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, e.action.equip.slot1);
+ npc->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, e.action.equip.slot2);
+ npc->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, e.action.equip.slot3);
+ }
+ }
+ }
+ break;
+ }
+ case SMART_ACTION_CREATE_TIMED_EVENT:
+ {
+ SmartEvent ne;
+ ne.type = (SMART_EVENT)SMART_EVENT_UPDATE;
+ ne.event_chance = e.action.timeEvent.chance;
+ if (!ne.event_chance) ne.event_chance = 100;
+
+ ne.minMaxRepeat.min = e.action.timeEvent.min;
+ ne.minMaxRepeat.max = e.action.timeEvent.max;
+ ne.minMaxRepeat.repeatMin = e.action.timeEvent.repeatMin;
+ ne.minMaxRepeat.repeatMax = e.action.timeEvent.repeatMax;
+
+ if (!ne.minMaxRepeat.repeatMin && !ne.minMaxRepeat.repeatMax)
+ ne.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
+
+ SmartAction ac;
+ ac.type = (SMART_ACTION)SMART_ACTION_TRIGGER_TIMED_EVENT;
+ ac.timeEvent.id = e.action.timeEvent.id;
+
+ SmartScriptHolder ev;
+ ev.event = ne;
+ ev.event_id = e.action.timeEvent.id;
+ ev.target = e.target;
+ ev.action = ac;
+ InitTimer(ev);
+ mStoredEvents.push_back(ev);
+
+ break;
+ }
+ case SMART_ACTION_TRIGGER_TIMED_EVENT:
+ {
+ ProcessEventsFor((SMART_EVENT)SMART_EVENT_TIMED_EVENT_TRIGGERED, NULL, e.action.timeEvent.id);
+ break;
+ }
+ case SMART_ACTION_REMOVE_TIMED_EVENT:
+ {
+ mRemIDs.push_back(e.action.timeEvent.id);
+ break;
+ }
+ case SMART_ACTION_OVERRIDE_SCRIPT_BASE_OBJECT:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if (IsCreature(*itr))
+ {
+ if (!meOrigGUID)
+ meOrigGUID = me?me->GetGUID():0;
+ if (!goOrigGUID)
+ goOrigGUID = go?go->GetGUID():0;
+ go = NULL;
+ me = (*itr)->ToCreature();
+ return;
+ }else if (IsGameObject((*itr)))
+ {
+ if (!meOrigGUID)
+ meOrigGUID = me?me->GetGUID():0;
+ if (!goOrigGUID)
+ goOrigGUID = go?go->GetGUID():0;
+ go = (*itr)->ToGameObject();
+ me = NULL;
+ return;
+ }
+ }
+ break;
+ }
+ case SMART_ACTION_RESET_SCRIPT_BASE_OBJECT:
+ ResetBaseObject();
+ break;
+ case SMART_ACTION_CALL_SCRIPT_RESET:
+ OnReset();
+ break;
+ default:
+ sLog.outErrorDb("SmartScript::ProcessAction: Unhandled Action type %u", e.GetActionType());
+ break;
+ }
+}
+
+void SmartScript::InstallTemplate(SmartScriptHolder e)
+{
+ if (!GetBaseObject())
+ return;
+ if (mTemplate)
+ {
+ sLog.outErrorDb("SmartScript::InstallTemplate: Entry %d SourceType %u AI Template can not be set more then once, skipped.", e.entryOrGuid, e.GetScriptType());
+ return;
+ }
+ mTemplate = (SMARTAI_TEMPLATE)e.action.installTtemplate.id;
+ switch ((SMARTAI_TEMPLATE)e.action.installTtemplate.id)
+ {
+ case SMARTAI_TEMPLATE_CASTER:
+ {
+ AddEvent(SMART_EVENT_UPDATE_IC,0, 0,0,e.action.installTtemplate.param2,e.action.installTtemplate.param3,SMART_ACTION_CAST,e.action.installTtemplate.param1,e.target.raw.param1,0,0,0,0,SMART_TARGET_VICTIM,0,0,0,1);
+ AddEvent(SMART_EVENT_RANGE,0, e.action.installTtemplate.param4,300,0,0,SMART_ACTION_ALLOW_COMBAT_MOVEMENT,1,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,1);
+ AddEvent(SMART_EVENT_RANGE,0, 0,e.action.installTtemplate.param4>10?e.action.installTtemplate.param4-10:0,0,0,SMART_ACTION_ALLOW_COMBAT_MOVEMENT,0,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,1);
+ AddEvent(SMART_EVENT_MANA_PCT,0,e.action.installTtemplate.param5-15>100?100:e.action.installTtemplate.param5+15,100,1000,1000,SMART_ACTION_SET_EVENT_PHASE,1,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,0);
+ AddEvent(SMART_EVENT_MANA_PCT,0,0,e.action.installTtemplate.param5,1000,1000,SMART_ACTION_SET_EVENT_PHASE,0,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,0);
+ AddEvent(SMART_EVENT_MANA_PCT,0,0,e.action.installTtemplate.param5,1000,1000,SMART_ACTION_ALLOW_COMBAT_MOVEMENT,1,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,0);
+ break;
+ }
+ case SMARTAI_TEMPLATE_TURRET:
+ {
+ AddEvent(SMART_EVENT_UPDATE_IC,0, 0,0,e.action.installTtemplate.param2,e.action.installTtemplate.param3,SMART_ACTION_CAST,e.action.installTtemplate.param1,e.target.raw.param1,0,0,0,0,SMART_TARGET_VICTIM,0,0,0,0);
+ AddEvent(SMART_EVENT_JUST_CREATED,0, 0,0,0,0,SMART_ACTION_ALLOW_COMBAT_MOVEMENT,0,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,0);
+ break;
+ }
+ case SMARTAI_TEMPLATE_CAGED_NPC_PART:
+ {
+ if (!me) return;
+ //store cage as id1
+ AddEvent(SMART_EVENT_DATA_SET,0,0,0,0,0,SMART_ACTION_STORE_TARGET_LIST,1,0,0,0,0,0,SMART_TARGET_CLOSEST_GAMEOBJECT,e.action.installTtemplate.param1,10,0,0);
+
+ //reset(close) cage on hostage(me) respawn
+ AddEvent(SMART_EVENT_UPDATE,SMART_EVENT_FLAG_NOT_REPEATABLE,0,0,0,0,SMART_ACTION_RESET_GOBJECT,0,0,0,0,0,0,SMART_TARGET_GAMEOBJECT_DISTANCE,e.action.installTtemplate.param1,5,0,0);
+
+ AddEvent(SMART_EVENT_DATA_SET,0,0,0,0,0,SMART_ACTION_SET_RUN,e.action.installTtemplate.param3,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,0);
+ AddEvent(SMART_EVENT_DATA_SET,0,0,0,0,0,SMART_ACTION_SET_EVENT_PHASE,1,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,0);
+
+ AddEvent(SMART_EVENT_UPDATE,SMART_EVENT_FLAG_NOT_REPEATABLE,1000,1000,0,0,SMART_ACTION_MOVE_FORWARD,e.action.installTtemplate.param4,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,1);
+ //phase 1: give quest credit on movepoint reached
+ AddEvent(SMART_EVENT_MOVEMENTINFORM,0, POINT_MOTION_TYPE,SMART_RANDOM_POINT,0,0,SMART_ACTION_SET_DATA,0,0,0,0,0,0,SMART_TARGET_STORED,1,0,0,1);
+ //phase 1: despawn after time on movepoint reached
+ AddEvent(SMART_EVENT_MOVEMENTINFORM,0, POINT_MOTION_TYPE,SMART_RANDOM_POINT,0,0,SMART_ACTION_FORCE_DESPAWN,e.action.installTtemplate.param2,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,1);
+
+ if (sCreatureTextMgr.TextExist(me->GetEntry(), (uint8)e.action.installTtemplate.param5))
+ AddEvent(SMART_EVENT_MOVEMENTINFORM,0, POINT_MOTION_TYPE,SMART_RANDOM_POINT,0,0,SMART_ACTION_TALK,e.action.installTtemplate.param5,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,1);
+ break;
+ }
+ case SMARTAI_TEMPLATE_CAGED_GO_PART:
+ {
+ if (!go) return;
+ //store hostage as id1
+ AddEvent(SMART_EVENT_GOSSIP_HELLO,0,0,0,0,0,SMART_ACTION_STORE_TARGET_LIST,1,0,0,0,0,0,SMART_TARGET_CLOSEST_CREATURE,e.action.installTtemplate.param1,10,0,0);
+ //store invoker as id2
+ AddEvent(SMART_EVENT_GOSSIP_HELLO,0,0,0,0,0,SMART_ACTION_STORE_TARGET_LIST,2,0,0,0,0,0,SMART_TARGET_NONE,0,0,0,0);
+ //signal hostage
+ AddEvent(SMART_EVENT_GOSSIP_HELLO,0,0,0,0,0,SMART_ACTION_SET_DATA,0,0,0,0,0,0,SMART_TARGET_STORED,1,0,0,0);
+ //when hostage raeched end point, give credit to invoker
+ if (e.action.installTtemplate.param2)
+ AddEvent(SMART_EVENT_DATA_SET,0,0,0,0,0,SMART_ACTION_CALL_KILLEDMONSTER,e.action.installTtemplate.param1,0,0,0,0,0,SMART_TARGET_STORED,2,0,0,0);
+ else
+ AddEvent(SMART_EVENT_GOSSIP_HELLO,0,0,0,0,0,SMART_ACTION_CALL_KILLEDMONSTER,e.action.installTtemplate.param1,0,0,0,0,0,SMART_TARGET_STORED,2,0,0,0);
+ break;
+ }
+ case SMARTAI_TEMPLATE_BASIC:
+ default:
+ return;
+ }
+}
+
+void SmartScript::AddEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 phaseMask)
+{
+ mInstallEvents.push_back(CreateEvent(e, event_flags, event_param1, event_param2, event_param3, event_param4, action, action_param1, action_param2, action_param3, action_param4, action_param5, action_param6, t, target_param1, target_param2, target_param3, phaseMask));
+}
+
+SmartScriptHolder SmartScript::CreateEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 phaseMask)
+{
+ SmartScriptHolder script;
+ script.event.type = e;
+ script.event.raw.param1 = event_param1;
+ script.event.raw.param2 = event_param2;
+ script.event.raw.param3 = event_param3;
+ script.event.raw.param4 = event_param4;
+ script.event.event_phase_mask = phaseMask;
+ script.event.event_flags = event_flags;
+
+ script.action.type = action;
+ script.action.raw.param1 = action_param1;
+ script.action.raw.param2 = action_param2;
+ script.action.raw.param3 = action_param3;
+ script.action.raw.param4 = action_param4;
+ script.action.raw.param5 = action_param5;
+ script.action.raw.param6 = action_param6;
+
+ script.target.type = t;
+ script.target.raw.param1 = target_param1;
+ script.target.raw.param2 = target_param2;
+ script.target.raw.param3 = target_param3;
+
+ script.source_type = SMART_SCRIPT_TYPE_CREATURE;
+ InitTimer(script);
+ return script;
+}
+
+ObjectList* SmartScript::GetTargets(SmartScriptHolder e, Unit* invoker)
+{
+ ObjectList* l = new ObjectList();
+ switch (e.GetTargetType())
+ {
+ case SMART_TARGET_SELF:
+ if (GetBaseObject())
+ l->push_back(GetBaseObject());
+ break;
+ case SMART_TARGET_VICTIM:
+ if (me && me->getVictim())
+ l->push_back(me->getVictim());
+ break;
+ case SMART_TARGET_HOSTILE_SECOND_AGGRO:
+ if (!me) return NULL;
+ if(Unit* u = me->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1))
+ l->push_back(u);
+ break;
+ case SMART_TARGET_HOSTILE_LAST_AGGRO:
+ if (!me) return NULL;
+ if(Unit* u = me->AI()->SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0))
+ l->push_back(u);
+ break;
+ case SMART_TARGET_HOSTILE_RANDOM:
+ if (!me) return NULL;
+ if(Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0))
+ l->push_back(u);
+ break;
+ case SMART_TARGET_HOSTILE_RANDOM_NOT_TOP:
+ if (!me) return NULL;
+ if(Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1))
+ l->push_back(u);
+ break;
+ case SMART_TARGET_NONE:
+ case SMART_TARGET_ACTION_INVOKER:
+ if (invoker)
+ {
+ l->push_back(invoker);
+ }
+ break;
+ case SMART_TARGET_INVOKER_PARTY:
+ if (invoker)
+ {
+ l->push_back(invoker);
+ if (Player* plr = invoker->ToPlayer())
+ {
+ if (Group *pGroup = plr->GetGroup())
+ {
+ for (GroupReference *gr = pGroup->GetFirstMember(); gr != NULL; gr = gr->next())
+ {
+ if (Player *pGroupGuy = gr->getSource())
+ l->push_back(pGroupGuy);
+ }
+ }
+ }
+ }
+ break;
+ case SMART_TARGET_CREATURE_RANGE:
+ {
+ ObjectList* units = GetWorldObjectsInDist((float)e.target.unitRange.maxDist);
+ if (!units) return NULL;
+ for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); itr++)
+ {
+ if(!IsCreature((*itr)))
+ continue;
+ if (me && me == (*itr))
+ continue;
+ if (((e.target.unitRange.creature && (*itr)->ToCreature()->GetEntry() == e.target.unitRange.creature) || !e.target.unitRange.creature) && GetBaseObject()->IsInRange((*itr), (float)e.target.unitRange.minDist, (float)e.target.unitRange.maxDist))
+ l->push_back((*itr));
+ }
+ break;
+ }
+ case SMART_TARGET_CREATURE_DISTANCE:
+ {
+ ObjectList* units = GetWorldObjectsInDist((float)e.target.unitDistance.dist);
+ if (!units) return NULL;
+ for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); itr++)
+ {
+ if(!IsCreature((*itr)))
+ continue;
+ if (me && me == (*itr))
+ continue;
+ if ((e.target.unitDistance.creature && (*itr)->ToCreature()->GetEntry() == e.target.unitDistance.creature) || !e.target.unitDistance.creature)
+ {
+ l->push_back((*itr));
+ }
+ }
+ break;
+ }
+ case SMART_TARGET_GAMEOBJECT_DISTANCE:
+ {
+ ObjectList* units = GetWorldObjectsInDist((float)e.target.goDistance.dist);
+ if (!units) return NULL;
+ for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); itr++)
+ {
+ if(!IsGameObject((*itr)))
+ continue;
+ if (go && go == (*itr))
+ continue;
+ if ((e.target.goDistance.entry && (*itr)->ToGameObject()->GetEntry() == e.target.goDistance.entry) || !e.target.goDistance.entry)
+ l->push_back((*itr));
+ }
+ break;
+ }
+ case SMART_TARGET_GAMEOBJECT_RANGE:
+ {
+ ObjectList* units = GetWorldObjectsInDist((float)e.target.goRange.maxDist);
+ if (!units) return NULL;
+ for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); itr++)
+ {
+ if(!IsGameObject((*itr)))
+ continue;
+ if (go && go == (*itr))
+ continue;
+ if (((e.target.goRange.entry && IsGameObject((*itr)) && (*itr)->ToGameObject()->GetEntry() == e.target.goRange.entry) || !e.target.goRange.entry) && GetBaseObject()->IsInRange((*itr), (float)e.target.goRange.minDist, (float)e.target.goRange.maxDist))
+ l->push_back((*itr));
+ }
+ break;
+ }
+ case SMART_TARGET_CREATURE_GUID:
+ {
+ Creature* target = NULL;
+ if (e.target.unitGUID.entry)
+ {
+ uint64 guid = MAKE_NEW_GUID(e.target.unitGUID.guid, e.target.unitGUID.entry, HIGHGUID_UNIT);
+ target = HashMapHolder<Creature>::Find(guid);
+ } else
+ {
+ if (!invoker)
+ {
+ sLog.outError("SMART_TARGET_CREATURE_GUID can not be used without invoker and without entry");
+ return NULL;
+ }
+ target = FindCreatureNear(invoker, e.target.unitGUID.guid);
+ }
+ if (target)
+ {
+ l->push_back(target);
+ }
+ break;
+ }
+ case SMART_TARGET_GAMEOBJECT_GUID:
+ {
+ GameObject* target = NULL;
+ if (e.target.unitGUID.entry)
+ {
+ uint64 guid = MAKE_NEW_GUID(e.target.goGUID.guid, e.target.goGUID.entry, HIGHGUID_GAMEOBJECT);
+ target = HashMapHolder<GameObject>::Find(guid);
+ } else
+ {
+ if (!invoker)
+ {
+ sLog.outError("SMART_TARGET_GAMEOBJECT_GUID can not be used without invoker and without entry");
+ return NULL;
+ }
+ target = FindGameObjectNear(invoker, e.target.goGUID.guid);
+ }
+ if (target)
+ {
+ l->push_back(target);
+ }
+ break;
+ }
+ case SMART_TARGET_PLAYER_RANGE:
+ {
+ ObjectList* units = GetWorldObjectsInDist((float)e.target.playerRange.maxDist);
+ if (!units || !GetBaseObject()) return NULL;
+ for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); itr++)
+ {
+ if(IsPlayer((*itr)) && GetBaseObject()->IsInRange((*itr), (float)e.target.playerRange.minDist, (float)e.target.playerRange.maxDist))
+ l->push_back((*itr));
+ }
+ break;
+ }
+ case SMART_TARGET_PLAYER_DISTANCE:
+ {
+ ObjectList* units = GetWorldObjectsInDist((float)e.target.playerDistance.dist);
+ if (!units) return NULL;
+ for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); itr++)
+ {
+ if(IsPlayer((*itr)))
+ l->push_back((*itr));
+ }
+ break;
+ }
+ case SMART_TARGET_STORED:
+ {
+ ObjectListMap::iterator itr = mTargetStorage->find(e.target.stored.id);
+ if (itr != mTargetStorage->end())
+ return itr->second;
+ return l;
+ }
+ case SMART_TARGET_CLOSEST_CREATURE:
+ {
+ Creature* target = GetClosestCreatureWithEntry(GetBaseObject(),e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100), e.target.closest.dead ? false : true);
+ if (target)
+ l->push_back(target);
+ break;
+ }
+ case SMART_TARGET_CLOSEST_GAMEOBJECT:
+ {
+ GameObject* target = GetClosestGameObjectWithEntry(GetBaseObject(),e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100));
+ if (target)
+ l->push_back(target);
+ break;
+ }
+ case SMART_TARGET_POSITION:
+ default:
+ return NULL;
+ }
+ return l;
+}
+
+ObjectList* SmartScript::GetWorldObjectsInDist(float dist)
+{
+ ObjectList* targets = new ObjectList();
+ WorldObject* obj = GetBaseObject();
+ if (obj)
+ {
+ Trinity::AllWorldObjectsInRange u_check(obj, dist);
+ Trinity::WorldObjectListSearcher<Trinity::AllWorldObjectsInRange> searcher(obj, *targets, u_check);
+ obj->VisitNearbyObject(dist, searcher);
+ }
+ return targets;
+}
+
+void SmartScript::ProcessEvent(SmartScriptHolder &e, Unit* unit, uint32 var0, uint32 var1, bool bvar, const SpellEntry* spell, GameObject* gob)
+{
+ if (!e.active && e.GetEventType() != SMART_EVENT_LINK)
+ return;
+
+ if ((e.event.event_phase_mask && !IsInPhase(e.event.event_phase_mask)) || ((e.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE) && e.runOnce))
+ return;
+
+ switch (e.GetEventType())
+ {
+ case SMART_EVENT_LINK://special handling
+ ProcessAction(e, unit, var0, var1, bvar, spell, gob);
+ break;
+ //called from Update tick
+ case SMART_EVENT_UPDATE:
+ RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
+ ProcessAction(e);
+ break;
+ case SMART_EVENT_UPDATE_OOC:
+ if(me && me->isInCombat())
+ return;
+ RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
+ ProcessAction(e);
+ break;
+ case SMART_EVENT_UPDATE_IC:
+ if(!me || !me->isInCombat())
+ return;
+ RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
+ ProcessAction(e);
+ break;
+ case SMART_EVENT_HEALT_PCT:
+ {
+ if (!me || !me->isInCombat() || !me->GetMaxHealth())
+ return;
+ uint32 perc = (uint32)me->GetHealthPct();
+ if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
+ return;
+ RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
+ ProcessAction(e);
+ break;
+ }
+ case SMART_EVENT_TARGET_HEALTH_PCT:
+ {
+ if (!me || !me->isInCombat() || !me->getVictim() || !me->getVictim()->GetMaxHealth())
+ return;
+ uint32 perc = (uint32)me->getVictim()->GetHealthPct();
+ if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
+ return;
+ RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
+ ProcessAction(e, me->getVictim());
+ break;
+ }
+ case SMART_EVENT_MANA_PCT:
+ {
+ if (!me || !me->isInCombat() || !me->GetMaxPower(POWER_MANA))
+ return;
+ uint32 perc = uint32(100.0f * me->GetPower(POWER_MANA) / me->GetMaxPower(POWER_MANA));
+ if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
+ return;
+ RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
+ ProcessAction(e);
+ break;
+ }
+ case SMART_EVENT_TARGET_MANA_PCT:
+ {
+ if (!me || !me->isInCombat() || !me->getVictim() || !me->getVictim()->GetMaxPower(POWER_MANA))
+ return;
+ uint32 perc = uint32(100.0f * me->getVictim()->GetPower(POWER_MANA) / me->getVictim()->GetMaxPower(POWER_MANA));
+ if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
+ return;
+ RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
+ ProcessAction(e, me->getVictim());
+ break;
+ }
+ case SMART_EVENT_RANGE:
+ {
+ if (!GetBaseObject()) return;
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets) return;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); itr++)
+ {
+ if (!IsUnit((*itr)))
+ continue;
+ if (GetBaseObject()->IsInMap((*itr)))
+ if (GetBaseObject()->IsInRange((*itr),(float)e.event.minMaxRepeat.min,(float)e.event.minMaxRepeat.max))
+ {
+ ProcessAction(e, (*itr)->ToUnit());
+ RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
+ }
+ }
+ break;
+ }
+ case SMART_EVENT_TARGET_CASTING:
+ {
+ if (!me || !me->isInCombat() || !me->getVictim() || !me->getVictim()->IsNonMeleeSpellCasted(false, false, true))
+ return;
+ ProcessAction(e, me->getVictim());
+ RecalcTimer(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax);
+ }
+ case SMART_EVENT_FRIENDLY_HEALTH:
+ {
+ if (!me || !me->isInCombat())
+ return;
+
+ Unit* pUnit = DoSelectLowestHpFriendly((float)e.event.friendlyHealt.radius, e.event.friendlyHealt.hpDeficit);
+ if (!pUnit)
+ return;
+ ProcessAction(e, pUnit);
+ RecalcTimer(e, e.event.friendlyHealt.repeatMin, e.event.friendlyHealt.repeatMax);
+ break;
+ }
+ case SMART_EVENT_FRIENDLY_IS_CC:
+ {
+ if (!me || !me->isInCombat())
+ return;
+
+ std::list<Creature*> pList;
+ DoFindFriendlyCC(pList, (float)e.event.friendlyCC.radius);
+ if (pList.empty())
+ return;
+ ProcessAction(e, *(pList.begin()));
+ RecalcTimer(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax);
+ break;
+ }
+ case SMART_EVENT_FRIENDLY_MISSING_BUFF:
+ {
+ std::list<Creature*> pList;
+ DoFindFriendlyMissingBuff(pList, (float)e.event.missingBuff.radius, e.event.missingBuff.spell);
+
+ if (pList.empty())
+ return;
+ ProcessAction(e, *(pList.begin()));
+ RecalcTimer(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax);
+ break;
+ }
+ case SMART_EVENT_HAS_AURA:
+ {
+ if (!me) return;
+ uint32 count = me->GetAuraCount(e.event.aura.spell);
+ if (count < e.event.aura.count)
+ return;
+ ProcessAction(e);
+ RecalcTimer(e, e.event.aura.repeatMin, e.event.aura.repeatMax);
+ break;
+ }
+ case SMART_EVENT_TARGET_BUFFED:
+ {
+ if (!me || !me->getVictim()) return;
+ if (!me) return;
+ uint32 count = me->getVictim()->GetAuraCount(e.event.aura.spell);
+ if (count < e.event.aura.count)
+ return;
+ ProcessAction(e);
+ RecalcTimer(e, e.event.aura.repeatMin, e.event.aura.repeatMax);
+ break;
+ }
+ //no params
+ case SMART_EVENT_AGGRO:
+ case SMART_EVENT_DEATH:
+ case SMART_EVENT_EVADE:
+ case SMART_EVENT_REACHED_HOME:
+ case SMART_EVENT_CHARMED:
+ case SMART_EVENT_CHARMED_TARGET:
+ case SMART_EVENT_CORPSE_REMOVED:
+ case SMART_EVENT_AI_INIT:
+ case SMART_EVENT_TRANSPORT_ADDPLAYER:
+ case SMART_EVENT_TRANSPORT_REMOVE_PLAYER:
+ case SMART_EVENT_QUEST_ACCEPTED:
+ case SMART_EVENT_QUEST_OBJ_COPLETETION:
+ case SMART_EVENT_QUEST_COMPLETION:
+ case SMART_EVENT_QUEST_REWARDED:
+ case SMART_EVENT_QUEST_FAIL:
+ case SMART_EVENT_JUST_SUMMONED:
+ case SMART_EVENT_RESET:
+ case SMART_EVENT_JUST_CREATED:
+ case SMART_EVENT_GOSSIP_HELLO:
+ case SMART_EVENT_FOLLOW_COPMLETE:
+ ProcessAction(e, unit, var0, var1, bvar, spell, gob);
+ break;
+ case SMART_EVENT_RECEIVE_EMOTE:
+ if (e.event.emote.emote == var0)
+ {
+ ProcessAction(e, unit);
+ RecalcTimer(e, e.event.emote.cooldownMin, e.event.emote.cooldownMax);
+ }
+ break;
+ case SMART_EVENT_KILL:
+ {
+ if (!me || !unit) return;
+ if (e.event.kill.playerOnly && unit->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if (e.event.kill.creature && unit->GetEntry() != e.event.kill.creature)
+ return;
+ ProcessAction(e, unit);
+ RecalcTimer(e, e.event.kill.cooldownMin, e.event.kill.cooldownMax);
+ break;
+ }
+ case SMART_EVENT_SPELLHIT_TARGET:
+ case SMART_EVENT_SPELLHIT:
+ {
+ if (!spell) return;
+ if (!e.event.spellHit.spell || spell->Id == e.event.spellHit.spell)
+ if (!e.event.spellHit.school || (spell->SchoolMask & e.event.spellHit.school))
+ {
+ ProcessAction(e, unit, 0, 0, bvar, spell);
+ RecalcTimer(e, e.event.spellHit.cooldownMin, e.event.spellHit.cooldownMax);
+ }
+ break;
+ }
+ case SMART_EVENT_OOC_LOS:
+ {
+ if (!me || me->isInCombat()) return;
+ //can trigger if closer than fMaxAllowedRange
+ float range = (float)e.event.los.maxDist;
+
+ //if range is ok and we are actually in LOS
+ if (me->IsWithinDistInMap(unit, range) && me->IsWithinLOSInMap(unit))
+ {
+ //if friendly event&&who is not hostile OR hostile event&&who is hostile
+ if ((e.event.los.noHostile && !me->IsHostileTo(unit)) ||
+ (!e.event.los.noHostile && me->IsHostileTo(unit)))
+ {
+ ProcessAction(e, unit);
+ RecalcTimer(e, e.event.los.cooldownMin, e.event.los.cooldownMax);
+ }
+ }
+ break;
+ }
+ case SMART_EVENT_IC_LOS:
+ {
+ if (!me || !me->isInCombat()) return;
+ //can trigger if closer than fMaxAllowedRange
+ float range = (float)e.event.los.maxDist;
+
+ //if range is ok and we are actually in LOS
+ if (me->IsWithinDistInMap(unit, range) && me->IsWithinLOSInMap(unit))
+ {
+ //if friendly event&&who is not hostile OR hostile event&&who is hostile
+ if ((e.event.los.noHostile && !me->IsHostileTo(unit)) ||
+ (!e.event.los.noHostile && me->IsHostileTo(unit)))
+ {
+ ProcessAction(e, unit);
+ RecalcTimer(e, e.event.los.cooldownMin, e.event.los.cooldownMax);
+ }
+ }
+ break;
+ }
+ case SMART_EVENT_RESPAWN:
+ {
+ if (!GetBaseObject()) return;
+ if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_MAP && GetBaseObject()->GetMapId() != e.event.respawn.map)
+ return;
+ if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && GetBaseObject()->GetZoneId() != e.event.respawn.area)
+ return;
+ ProcessAction(e);
+ break;
+ }
+ case SMART_EVENT_SUMMONED_UNIT:
+ {
+ if (!IsCreature(unit)) return;
+ if (e.event.summoned.creature && unit->GetEntry() != e.event.summoned.creature)
+ return;
+ ProcessAction(e, unit);
+ RecalcTimer(e, e.event.summoned.cooldownMin, e.event.summoned.cooldownMax);
+ break;
+ }
+ case SMART_EVENT_RECEIVE_HEAL:
+ case SMART_EVENT_DAMAGED:
+ case SMART_EVENT_DAMAGED_TARGET:
+ {
+ if (var0 > e.event.minMaxRepeat.max || var0 < e.event.minMaxRepeat.min)
+ return;
+ ProcessAction(e, unit);
+ RecalcTimer(e, e.event.minMaxRepeat.repeatMin,e.event.minMaxRepeat.repeatMax);
+ break;
+ }
+ case SMART_EVENT_MOVEMENTINFORM:
+ {
+ if ((e.event.movementInform.type && var0 != e.event.movementInform.type) || (e.event.movementInform.id && var1 != e.event.movementInform.id))
+ return;
+ ProcessAction(e, unit, var0, var1);
+ break;
+ }
+ case SMART_EVENT_TRANSPORT_RELOCATE:
+ case SMART_EVENT_WAYPOINT_START:
+ {
+ if (e.event.waypoint.pathID && var0 != e.event.waypoint.pathID)
+ return;
+ ProcessAction(e, unit, var0);
+ break;
+ }
+ case SMART_EVENT_WAYPOINT_REACHED:
+ case SMART_EVENT_WAYPOINT_RESUMED:
+ case SMART_EVENT_WAYPOINT_PAUSED:
+ case SMART_EVENT_WAYPOINT_STOPPED:
+ case SMART_EVENT_WAYPOINT_ENDED:
+ {
+ if (!me || (e.event.waypoint.pointID && var0 != e.event.waypoint.pointID) || (e.event.waypoint.pathID && GetPathId() != e.event.waypoint.pathID))
+ return;
+ ProcessAction(e, unit);
+ break;
+ }
+ case SMART_EVENT_SUMMON_DESPAWNED:
+ case SMART_EVENT_INSTANCE_PLAYER_ENTER:
+ {
+ if (e.event.instancePlayerEnter.team && var0 != e.event.instancePlayerEnter.team)
+ return;
+ ProcessAction(e, unit, var0);
+ RecalcTimer(e, e.event.instancePlayerEnter.cooldownMin, e.event.instancePlayerEnter.cooldownMax);
+ break;
+ }
+ case SMART_EVENT_ACCEPTED_QUEST:
+ case SMART_EVENT_REWARD_QUEST:
+ {
+ if (e.event.quest.quest && var0 != e.event.quest.quest)
+ return;
+ ProcessAction(e, unit, var0);
+ break;
+ }
+ case SMART_EVENT_TRANSPORT_ADDCREATURE:
+ {
+ if (e.event.transportAddCreature.creature && var0 != e.event.transportAddCreature.creature)
+ return;
+ ProcessAction(e, unit, var0);
+ break;
+ }
+ case SMART_EVENT_AREATRIGGER_ONTRIGGER:
+ {
+ if (e.event.areatrigger.id && var0 != e.event.areatrigger.id)
+ return;
+ ProcessAction(e, unit, var0);
+ break;
+ }
+ case SMART_EVENT_TEXT_OVER:
+ {
+ if (e.event.textOver.textGroupID && var0 != e.event.textOver.textGroupID)
+ return;
+ ProcessAction(e, unit, var0);
+ break;
+ }
+ case SMART_EVENT_DATA_SET:
+ {
+ if (e.event.dataSet.id != var0 || e.event.dataSet.value != var1)
+ return;
+ ProcessAction(e, unit, var0, var1);
+ RecalcTimer(e, e.event.dataSet.cooldownMin, e.event.dataSet.cooldownMax);
+ break;
+ }
+ case SMART_EVENT_PASSENGER_REMOVED:
+ case SMART_EVENT_PASSENGER_BOARDED:
+ {
+ if (!unit) return;
+ ProcessAction(e, unit);
+ RecalcTimer(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax);
+ break;
+ }
+ case SMART_EVENT_TIMED_EVENT_TRIGGERED:
+ {
+ if (e.event.timedEvent.id == var0)
+ ProcessAction(e,unit);
+ break;
+ }
+ case SMART_EVENT_GOSSIP_SELECT:
+ {
+ if ((e.event.gossip.sender != var0 || e.event.gossip.action != var1))
+ return;
+ ProcessAction(e, unit, var0, var1);
+ break;
+ }
+ default:
+ sLog.outErrorDb("SmartScript::ProcessEvent: Unhandled Event type %u", e.GetEventType());
+ break;
+ }
+}
+
+void SmartScript::InitTimer(SmartScriptHolder &e)
+{
+ switch (e.GetEventType())
+ {//set only events which have initial timers
+ case SMART_EVENT_UPDATE:
+ case SMART_EVENT_UPDATE_IC:
+ case SMART_EVENT_UPDATE_OOC:
+ case SMART_EVENT_OOC_LOS:
+ case SMART_EVENT_IC_LOS:
+ RecalcTimer(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max);
+ break;
+ default:
+ e.active = true;
+ break;
+ }
+}
+void SmartScript::RecalcTimer(SmartScriptHolder &e, uint32 min, uint32 max)
+{
+ // min/max was checked at loading!
+ e.timer = urand(uint32(min), uint32(max));
+ e.active = e.timer ? false : true;
+}
+
+void SmartScript::UpdateTimer(SmartScriptHolder &e, const uint32 diff)
+{
+ if (e.GetEventType() == SMART_EVENT_LINK)
+ return;
+ if (e.event.event_phase_mask && !IsInPhase(e.event.event_phase_mask))
+ return;
+ if (e.timer < diff)
+ {
+ e.active = true;//activate events with cooldown
+ switch (e.GetEventType())//process ONLY timed events
+ {
+ case SMART_EVENT_UPDATE:
+ case SMART_EVENT_UPDATE_OOC:
+ case SMART_EVENT_UPDATE_IC:
+ case SMART_EVENT_HEALT_PCT:
+ case SMART_EVENT_TARGET_HEALTH_PCT:
+ case SMART_EVENT_MANA_PCT:
+ case SMART_EVENT_TARGET_MANA_PCT:
+ case SMART_EVENT_RANGE:
+ case SMART_EVENT_TARGET_CASTING:
+ case SMART_EVENT_FRIENDLY_HEALTH:
+ case SMART_EVENT_FRIENDLY_IS_CC:
+ case SMART_EVENT_FRIENDLY_MISSING_BUFF:
+ case SMART_EVENT_HAS_AURA:
+ case SMART_EVENT_TARGET_BUFFED:
+ ProcessEvent(e);
+ break;
+ }
+ } else e.timer -= diff;
+}
+
+bool SmartScript::CheckTimer(SmartScriptHolder &e)
+{
+ return e.active;
+}
+
+void SmartScript::InstallEvents()
+{
+ if (!mInstallEvents.empty())
+ {
+ for (SmartAIEventList::iterator i = mInstallEvents.begin(); i != mInstallEvents.end(); ++i)
+ {
+ mEvents.push_back((*i));//must be before UpdateTimers
+ }
+ mInstallEvents.clear();
+ }
+}
+
+void SmartScript::OnUpdate(const uint32 diff)
+{
+ if ((mScriptType == SMART_SCRIPT_TYPE_CREATURE || mScriptType == SMART_SCRIPT_TYPE_GAMEOBJECT) && !GetBaseObject())
+ return;
+ InstallEvents();//before UpdateTimers
+
+ for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
+ UpdateTimer((*i), diff);
+
+ if (!mStoredEvents.empty())
+ {
+ for (SmartAIEventList::iterator i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
+ {
+ UpdateTimer((*i), diff);
+ }
+ }
+ if (!mRemIDs.empty())
+ {
+ for (std::list<uint32>::iterator i = mRemIDs.begin(); i != mRemIDs.end(); ++i)
+ {
+ RemoveStoredEvent((*i));
+ }
+ }
+ if (mUseTextTimer && me)
+ {
+ if (mTextTimer < diff)
+ {
+ ProcessEventsFor(SMART_EVENT_TEXT_OVER, NULL, mLastTextID);
+ if (!mTextIDs.empty())
+ {
+ mLastTextID = (*mTextIDs.begin());
+ mTextIDs.erase(mTextIDs.begin());
+ mTextTimer = sCreatureTextMgr.SendChat(me, (uint8)mLastTextID, mTextGUID);
+ }else{
+ mLastTextID = 0;
+ mTextTimer = 0;
+ mUseTextTimer = false;
+ }
+ } else mTextTimer -= diff;
+ }
+}
+
+void SmartScript::FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEntry const* at)
+{
+ if (e.empty())
+ {
+ if (obj)
+ sLog.outErrorDb("SmartScript: EventMap for Entry %u is empty but is using SmartScript.", obj->GetEntry());
+ if (at)
+ sLog.outErrorDb("SmartScript: EventMap for AreaTrigger %u is empty but is using SmartScript.", at->id);
+ return;
+ }
+ for (SmartAIEventList::iterator i = e.begin(); i != e.end(); ++i)
+ {
+ #ifndef TRINITY_DEBUG
+ if ((*i).event_flags & EFLAG_DEBUG_ONLY)
+ continue;
+ #endif
+
+ if ((*i).event.event_flags & SMART_EVENT_FLAG_DIFFICULTY_ALL)//if has instance flag add only if in it
+ {
+ if (obj && obj->GetMap()->IsDungeon())
+ {
+ if ((1 << (obj->GetMap()->GetSpawnMode()+1)) & (*i).event.event_flags)
+ {
+ mEvents.push_back((*i));
+ }
+ }
+ continue;
+ }
+ mEvents.push_back((*i));//NOTE: 'world(0)' events still get processed in ANY instance mode
+ }
+ if (mEvents.empty() && obj)
+ sLog.outErrorDb("SmartScript: Entry %u has events but no events added to list because of instance flags.", obj->GetEntry());
+ if (mEvents.empty() && at)
+ sLog.outErrorDb("SmartScript: AreaTrigger %u has events but no events added to list because of instance flags. NOTE: triggers can not handle any instance flags.", at->id);
+}
+
+void SmartScript::GetScript()
+{
+ SmartAIEventList e;
+ if (me)
+ {
+ e = sSmartScriptMgr.GetScript(-((int32)me->GetDBTableGUIDLow()), mScriptType);
+ if (e.empty())
+ e = sSmartScriptMgr.GetScript((int32)me->GetEntry(), mScriptType);
+ FillScript(e, me, NULL);
+ }
+ else if (go)
+ {
+ e = sSmartScriptMgr.GetScript(-((int32)go->GetDBTableGUIDLow()), mScriptType);
+ if (e.empty())
+ e = sSmartScriptMgr.GetScript((int32)go->GetEntry(), mScriptType);
+ FillScript(e, go, NULL);
+ }
+ else if (trigger)
+ {
+ e = sSmartScriptMgr.GetScript((int32)trigger->id, mScriptType);
+ FillScript(e, NULL, trigger);
+ }
+}
+
+void SmartScript::OnInitialize(WorldObject* obj, AreaTriggerEntry const* at)
+{
+ if (obj)//handle object based scripts
+ {
+ switch (obj->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ mScriptType = SMART_SCRIPT_TYPE_CREATURE;
+ me = obj->ToCreature();
+ sLog.outDebug("SmartScript::OnInitialize: source is Creature %u", me->GetEntry());
+ break;
+ case TYPEID_GAMEOBJECT:
+ mScriptType = SMART_SCRIPT_TYPE_GAMEOBJECT;
+ go = obj->ToGameObject();
+ sLog.outDebug("SmartScript::OnInitialize: source is GameObject %u", go->GetEntry());
+ break;
+ default:
+ sLog.outError("SmartScript::OnInitialize: Unhandled TypeID !WARNING!");
+ return;
+ }
+ } else if (at)
+ {
+ mScriptType = SMART_SCRIPT_TYPE_AREATRIGGER;
+ trigger = at;
+ sLog.outDebug("SmartScript::OnInitialize: source is AreaTrigger %u", trigger->id);
+ }
+ else
+ {
+ sLog.outError("SmartScript::OnInitialize: !WARNING! Initialized objects are NULL.");
+ return;
+ }
+
+ GetScript();//load copy of script
+
+ for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
+ InitTimer((*i));//calculate timers for first time use
+
+ ProcessEventsFor(SMART_EVENT_AI_INIT);
+ InstallEvents();
+ ProcessEventsFor(SMART_EVENT_JUST_CREATED);
+}
+
+void SmartScript::OnMoveInLineOfSight(Unit* who)
+{
+ ProcessEventsFor(SMART_EVENT_OOC_LOS, who);
+
+ if(!me) return;
+ if (me->getVictim())
+ return;
+
+ ProcessEventsFor(SMART_EVENT_IC_LOS, who);
+
+}
+
+/*
+void SmartScript::UpdateAIWhileCharmed(const uint32 diff)
+{
+}
+
+
+void SmartScript::DoAction(const int32 param)
+{
+}
+
+uint32 SmartScript::GetData(uint32 id)
+{
+ return 0;
+}
+
+void SmartScript::SetData(uint32 id, uint32 value)
+{
+}
+
+void SmartScript::SetGUID(const uint64& guid, int32 id)
+{
+}
+
+uint64 SmartScript::GetGUID(int32 id)
+{
+ return 0;
+}
+
+void SmartScript::MovepointStart(uint32 id)
+{
+}
+
+void SmartScript::SetRun(bool run)
+{
+}
+
+void SmartScript::SetMovePathEndAction(SMART_ACTION action)
+{
+}
+
+uint32 SmartScript::DoChat(int8 id, uint64 whisperGuid)
+{
+ return 0;
+}*/
+// SmartScript end
+
+
+Unit* SmartScript::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff)
+{
+ if (!me) return NULL;
+ CellPair p(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ Unit* pUnit = NULL;
+
+ Trinity::MostHPMissingInRange u_check(me, range, MinHPDiff);
+ Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(me, pUnit, u_check);
+
+ TypeContainerVisitor<Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
+ cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, range);
+ return pUnit;
+}
+
+void SmartScript::DoFindFriendlyCC(std::list<Creature*>& _list, float range)
+{
+ if (!me) return;
+ CellPair p(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ Trinity::FriendlyCCedInRange u_check(me, range);
+ Trinity::CreatureListSearcher<Trinity::FriendlyCCedInRange> searcher(me, _list, u_check);
+
+ TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::FriendlyCCedInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ cell.Visit(p, grid_creature_searcher, *me->GetMap());
+}
+
+void SmartScript::DoFindFriendlyMissingBuff(std::list<Creature*>& _list, float range, uint32 spellid)
+{
+ if (!me) return;
+ CellPair p(Trinity::ComputeCellPair(me->GetPositionX(), me->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ Trinity::FriendlyMissingBuffInRange u_check(me, range, spellid);
+ Trinity::CreatureListSearcher<Trinity::FriendlyMissingBuffInRange> searcher(me, _list, u_check);
+
+ TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::FriendlyMissingBuffInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ cell.Visit(p, grid_creature_searcher, *me->GetMap());
+} \ No newline at end of file
diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h
new file mode 100644
index 00000000000..bb7722e136c
--- /dev/null
+++ b/src/server/game/AI/SmartScripts/SmartScript.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 TRINITY_SMARTSCRIPT_H
+#define TRINITY_SMARTSCRIPT_H
+
+#include "Common.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "Unit.h"
+#include "ConditionMgr.h"
+#include "CreatureTextMgr.h"
+#include "Spell.h"
+#include "GridNotifiers.h"
+
+#include "SmartScriptMgr.h"
+//#include "SmartAI.h"
+
+class SmartScript
+{
+ public:
+ ~SmartScript(){};
+ SmartScript();
+
+ void OnInitialize(WorldObject* obj, AreaTriggerEntry const* at = NULL);
+ void GetScript();
+ void FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEntry const* at);
+
+ void ProcessEventsFor(SMART_EVENT e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellEntry* spell = NULL, GameObject* gob = NULL);
+ void ProcessEvent(SmartScriptHolder &e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellEntry* spell = NULL, GameObject* gob = NULL);
+ bool CheckTimer(SmartScriptHolder &e);
+ void RecalcTimer(SmartScriptHolder &e, uint32 min, uint32 max);
+ void UpdateTimer(SmartScriptHolder &e, const uint32 diff);
+ void InitTimer(SmartScriptHolder &e);
+ void ProcessAction(SmartScriptHolder &e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellEntry* spell = NULL, GameObject* gob = NULL);
+ ObjectList* GetTargets(SmartScriptHolder e, Unit* invoker = NULL);
+ ObjectList* GetWorldObjectsInDist(float dist);
+ void InstallTemplate(SmartScriptHolder e);
+ SmartScriptHolder CreateEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 phaseMask = 0);
+ void AddEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 phaseMask = 0);
+ void SetPathId(uint32 id) { mPathId = id; }
+ uint32 GetPathId() { return mPathId; }
+ WorldObject* GetBaseObject()
+ {
+ WorldObject* obj = NULL;
+ if (me)
+ obj = me;
+ else if (go)
+ obj = go;
+ return obj;
+ }
+ bool IsUnit(WorldObject* obj)
+ {
+ return obj && (obj->GetTypeId() == TYPEID_UNIT || obj->GetTypeId() == TYPEID_PLAYER);
+ }
+ bool IsPlayer(WorldObject* obj)
+ {
+ return obj && obj->GetTypeId() == TYPEID_PLAYER;
+ }
+ bool IsCreature(WorldObject* obj)
+ {
+ return obj && obj->GetTypeId() == TYPEID_UNIT;
+ }
+ bool IsGameObject(WorldObject* obj)
+ {
+ return obj && obj->GetTypeId() == TYPEID_GAMEOBJECT;
+ }
+ bool ConditionValid(Unit* u, int32 c, int32 v1, int32 v2, int32 v3)
+ {
+ if (c == 0) return true;
+ if (!u || !u->ToPlayer()) return false;
+ Condition cond;
+ cond.mConditionType = ConditionType(uint32(c));
+ cond.mConditionValue1 = uint32(v1);
+ cond.mConditionValue1 = uint32(v2);
+ cond.mConditionValue1 = uint32(v3);
+ return cond.Meets(u->ToPlayer());
+ }
+
+ void OnUpdate(const uint32 diff);
+ void OnMoveInLineOfSight(Unit *who);
+
+ Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff);
+ void DoFindFriendlyCC(std::list<Creature*>& _list, float range);
+ void DoFindFriendlyMissingBuff(std::list<Creature*>& _list, float range, uint32 spellid);
+
+ void StoreTargetList(ObjectList* targets, uint32 id)
+ {
+ if (!targets) return;
+ if(mTargetStorage->find(id) != mTargetStorage->end())
+ mTargetStorage->erase(id);
+ (*mTargetStorage)[id] = targets;
+ }
+ bool IsSmart(Creature* c = NULL)
+ {
+ if (c && c->GetAIName() != "SmartAI") return false;
+ if (!me || me->GetAIName() != "SmartAI") return false;
+ return true;
+ }
+ ObjectList* GetTargetList(uint32 id)
+ {
+ ObjectListMap::iterator itr = mTargetStorage->find(id);
+ if(itr != mTargetStorage->end())
+ return (*itr).second;
+ return NULL;
+ }
+
+ inline GameObject* FindGameObjectNear(WorldObject* pSearchObject, uint32 guid) const
+ {
+ GameObject *pGameObject = NULL;
+
+ CellPair p(Trinity::ComputeCellPair(pSearchObject->GetPositionX(), pSearchObject->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ Trinity::GameObjectWithDbGUIDCheck goCheck(*pSearchObject, guid);
+ Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck> checker(pSearchObject, pGameObject, goCheck);
+
+ TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > objectChecker(checker);
+ cell.Visit(p, objectChecker, *pSearchObject->GetMap());
+
+ return pGameObject;
+ }
+
+ inline Creature* FindCreatureNear(WorldObject* pSearchObject, uint32 guid) const
+ {
+ Creature *crea = NULL;
+ CellPair p(Trinity::ComputeCellPair(pSearchObject->GetPositionX(), pSearchObject->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ Trinity::CreatureWithDbGUIDCheck target_check(pSearchObject, guid);
+ Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(pSearchObject, crea, target_check);
+
+ TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker);
+ cell.Visit(p, unit_checker, *pSearchObject->GetMap());
+
+ return crea;
+ }
+
+ ObjectListMap* mTargetStorage;
+ void ResetTexts() { mTextIDs.clear(); }
+
+ void OnReset();
+ void ResetBaseObject()
+ {
+ if (meOrigGUID)
+ {
+ if (Creature* m = HashMapHolder<Creature>::Find(meOrigGUID))
+ {
+ me = m;
+ go = NULL;
+ }
+ }
+ if (goOrigGUID)
+ {
+ if (GameObject* o = HashMapHolder<GameObject>::Find(goOrigGUID))
+ {
+ me = NULL;
+ go = o;
+ }
+ }
+ goOrigGUID = 0;
+ meOrigGUID = 0;
+ }
+
+ private:
+ void IncPhase(int32 p = 1) { p >= 0 ? mEventPhase += (uint32)p : DecPhase(abs(p)); }
+ void DecPhase(int32 p = 1) { mEventPhase -= (mEventPhase < (uint32)p ? (uint32)p - mEventPhase : (uint32)p); }
+ bool IsInPhase(uint32 p) { return mEventPhase & p; }
+ void SetPhase(uint32 p = 0) { mEventPhase = p; }
+
+ SmartAIEventList mEvents;
+ SmartAIEventList mInstallEvents;
+ Creature* me;
+ uint64 meOrigGUID;
+ GameObject* go;
+ uint64 goOrigGUID;
+ AreaTriggerEntry const* trigger;
+ SmartScriptType mScriptType;
+ uint32 mEventPhase;
+
+ uint32 mInvinceabilityHpLevel;
+ UNORDERED_MAP<int32, int32> mStoredDecimals;
+ uint32 mPathId;
+ SmartAIEventList mStoredEvents;
+ std::list<uint32>mRemIDs;
+
+ std::vector<uint32>mTextIDs;
+ uint32 mTextTimer;
+ uint32 mLastTextID;
+ uint64 mTextGUID;
+ bool mUseTextTimer;
+ SMARTAI_TEMPLATE mTemplate;
+ void InstallEvents();
+
+ void RemoveStoredEvent (uint32 id)
+ {
+ if (!mStoredEvents.empty())
+ {
+ for (SmartAIEventList::iterator i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
+ {
+ if (i->event_id = id)
+ {
+ mStoredEvents.erase(i);
+ return;
+ }
+
+ }
+ }
+ }
+ SmartScriptHolder FindLinkedEvent (uint32 link)
+ {
+ if (!mEvents.empty())
+ {
+ for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
+ {
+ if (i->event_id == link)
+ {
+ return (*i);
+ }
+
+ }
+ }
+ SmartScriptHolder s;
+ return s;
+ }
+};
+
+#endif
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
new file mode 100644
index 00000000000..a6a4ba9a23e
--- /dev/null
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -0,0 +1,776 @@
+/*
+ * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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/>.
+ */
+
+
+#include "DatabaseEnv.h"
+#include "SQLStorage.h"
+#include "ObjectMgr.h"
+#include "ProgressBar.h"
+#include "ObjectDefines.h"
+#include "GridDefines.h"
+#include "GridNotifiers.h"
+#include "SpellMgr.h"
+#include "GridNotifiersImpl.h"
+#include "Cell.h"
+#include "CellImpl.h"
+#include "InstanceScript.h"
+#include "ScriptedCreature.h"
+
+#include "SmartScriptMgr.h"
+
+void SmartWaypointMgr::LoadFromDB()
+{
+ waypoint_map.clear();
+
+ PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_LOAD_SMARTAI_WP);
+ PreparedQueryResult result = WorldDatabase.Query(stmt);
+
+ if (!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+ sLog.outString();
+ sLog.outString(">> Loaded 0 SmartAI Waypoint Paths. DB table `waypoints` is empty.");
+ return;
+ }
+
+ WPPath* path = NULL;
+ uint32 last_entry = 0;
+ uint32 last_id = 1;
+ barGoLink bar(result->GetRowCount());
+ uint32 count = 0;
+ uint32 total = 0;
+
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+ uint32 entry = fields[0].GetUInt32();
+ uint32 id = fields[1].GetUInt32();
+ float x,y,z;
+ x = fields[2].GetFloat();
+ y = fields[3].GetFloat();
+ z = fields[4].GetFloat();
+
+
+ WayPoint *wp = new WayPoint(id, x, y, z);
+
+ if (last_entry != entry)
+ {
+ path = new WPPath;
+ last_id = 1;
+ }
+ if (last_id != id)
+ {
+ sLog.outErrorDb("SmartWaypointMgr::LoadFromDB: Path entry %u, unexpected point id %u, expected %u.", entry, id, last_id);
+ }
+ last_id++;
+ (*path)[id] = wp;
+
+ if (last_entry != entry)
+ {
+ count++;
+ waypoint_map[entry] = path;
+ }
+ last_entry = entry;
+ total++;
+ } while (result->NextRow());
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u SmartAI Waypoint Paths, total %u waypoints.", count, total);
+}
+
+void SmartAIMgr::LoadSmartAIFromDB()
+{
+ for (uint8 i = 0; i < SMART_SCRIPT_TYPE_MAX; i++)
+ mEventMap[i].clear(); //Drop Existing SmartAI List
+
+ PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_LOAD_SMART_SCRIPTS);
+ PreparedQueryResult result = WorldDatabase.Query(stmt);
+
+ if (!result)
+ {
+ barGoLink bar(1);
+ bar.step();
+ sLog.outString();
+ sLog.outString(">> Loaded 0 SmartAI scripts. DB table `smartai_scripts` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+ uint32 ScriptCount = 0;
+
+ do
+ {
+ bar.step();
+ Field* fields = result->Fetch();
+
+ SmartScriptHolder temp;
+
+ temp.entryOrGuid = fields[0].GetInt32();
+ SmartScriptType source_type = (SmartScriptType)fields[1].GetUInt32();
+ if (source_type >= SMART_SCRIPT_TYPE_MAX)
+ {
+ sLog.outErrorDb("SmartAIMgr::LoadSmartAIFromDB: invalid source_type (%u), skipped loading.", uint32(source_type));
+ continue;
+ }
+ if (temp.entryOrGuid >= 0)
+ {
+ switch(source_type)
+ {
+ case SMART_SCRIPT_TYPE_CREATURE:
+ {
+ if (!sCreatureStorage.LookupEntry<CreatureInfo>((uint32)temp.entryOrGuid))
+ {
+ sLog.outErrorDb("SmartAIMgr::LoadSmartAIFromDB: Creature entry (%u) does not exist, skipped loading.", uint32(temp.entryOrGuid));
+ continue;
+ }
+ break;
+ }
+ case SMART_SCRIPT_TYPE_GAMEOBJECT:
+ {
+ if (!sGOStorage.LookupEntry<GameObjectInfo>((uint32)temp.entryOrGuid))
+ {
+ sLog.outErrorDb("SmartAIMgr::LoadSmartAIFromDB: GameObject entry (%u) does not exist, skipped loading.", uint32(temp.entryOrGuid));
+ continue;
+ }
+ break;
+ }
+ case SMART_SCRIPT_TYPE_AREATRIGGER:
+ {
+ if (!sAreaTriggerStore.LookupEntry((uint32)temp.entryOrGuid))
+ {
+ sLog.outErrorDb("SmartAIMgr::LoadSmartAIFromDB: AreaTrigger entry (%u) does not exist, skipped loading.", uint32(temp.entryOrGuid));
+ continue;
+ }
+ break;
+ }
+ default:
+ sLog.outErrorDb("SmartAIMgr::LoadSmartAIFromDB: not yet implemented source_type %u", (uint32)source_type);
+ continue;
+ }
+ }else
+ {
+ if (!sObjectMgr.GetCreatureData(uint32(abs(temp.entryOrGuid))))
+ {
+ sLog.outErrorDb("SmartAIMgr::LoadSmartAIFromDB: Creature guid (%u) does not exist, skipped loading.", uint32(abs(temp.entryOrGuid)));
+ continue;
+ }
+ }
+ temp.source_type = source_type;
+ temp.event_id = fields[2].GetUInt32();
+ temp.link = fields[3].GetUInt32();
+ temp.event.type = (SMART_EVENT)fields[4].GetUInt32();
+ temp.event.event_phase_mask = fields[5].GetUInt32();
+ temp.event.event_chance = fields[6].GetUInt32();
+ temp.event.event_flags = fields[7].GetUInt32();
+
+ temp.event.raw.param1 = fields[8].GetUInt32();
+ temp.event.raw.param2 = fields[9].GetUInt32();
+ temp.event.raw.param3 = fields[10].GetUInt32();
+ temp.event.raw.param4 = fields[11].GetUInt32();
+
+ temp.action.type = (SMART_ACTION)fields[12].GetUInt32();
+
+ temp.action.raw.param1 = fields[13].GetUInt32();
+ temp.action.raw.param2 = fields[14].GetUInt32();
+ temp.action.raw.param3 = fields[15].GetUInt32();
+ temp.action.raw.param4 = fields[16].GetUInt32();
+ temp.action.raw.param5 = fields[17].GetUInt32();
+ temp.action.raw.param6 = fields[18].GetUInt32();
+
+ temp.target.type = (SMARTAI_TARGETS)fields[19].GetUInt32();
+ temp.target.raw.param1 = fields[20].GetUInt32();
+ temp.target.raw.param2 = fields[21].GetUInt32();
+ temp.target.raw.param3 = fields[22].GetUInt32();
+ temp.target.x = fields[23].GetFloat();
+ temp.target.y = fields[24].GetFloat();
+ temp.target.z = fields[25].GetFloat();
+ temp.target.o = fields[26].GetFloat();
+
+ //check target
+ if (!IsTargetValid(temp))
+ continue;
+
+ // check all event and action params
+ if (!IsEventValid(temp))
+ continue;
+
+ // creature entry / guid not found in storage, create empty event list for it and increase counters
+ if (mEventMap[source_type].find(temp.entryOrGuid) == mEventMap[source_type].end())
+ {
+ ++ScriptCount;
+ SmartAIEventList eventList;
+ mEventMap[source_type][temp.entryOrGuid] = eventList;
+ }
+ // store the new event
+ mEventMap[source_type][temp.entryOrGuid].push_back(temp);
+ } while (result->NextRow());
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u SmartAI scripts.", ScriptCount);
+}
+
+bool SmartAIMgr::IsTargetValid(SmartScriptHolder e)
+{
+ if (e.GetActionType() == SMART_ACTION_INSTALL_AI_TEMPLATE)
+ return true; //AI template has special handling
+ switch (e.GetTargetType())
+ {
+ case SMART_TARGET_CREATURE_DISTANCE:
+ case SMART_TARGET_CREATURE_RANGE:
+ {
+ if (e.target.unitDistance.creature && !sCreatureStorage.LookupEntry<CreatureInfo>(e.target.unitDistance.creature))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Creature entry %u as target_param1, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.target.unitDistance.creature);
+ return false;
+ }
+ break;
+ }
+ case SMART_TARGET_GAMEOBJECT_DISTANCE:
+ case SMART_TARGET_GAMEOBJECT_RANGE:
+ {
+ if (e.target.goDistance.entry && !sGOStorage.LookupEntry<GameObjectInfo>(e.target.goDistance.entry))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent GameObject entry %u as target_param1, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.target.goDistance.entry);
+ return false;
+ }
+ break;
+ }
+ case SMART_TARGET_CREATURE_GUID:
+ {
+ if (e.target.unitGUID.entry && !IsCreatureValid(e, e.target.unitGUID.entry)) return false;
+ break;
+ }
+ case SMART_TARGET_GAMEOBJECT_GUID:
+ {
+ if (e.target.goGUID.entry && !IsGameObjectValid(e, e.target.goGUID.entry)) return false;
+ break;
+ }
+ case SMART_TARGET_PLAYER_RANGE:
+ case SMART_TARGET_PLAYER_DISTANCE:
+ case SMART_TARGET_SELF:
+ case SMART_TARGET_VICTIM:
+ case SMART_TARGET_HOSTILE_SECOND_AGGRO:
+ case SMART_TARGET_HOSTILE_LAST_AGGRO:
+ case SMART_TARGET_HOSTILE_RANDOM:
+ case SMART_TARGET_HOSTILE_RANDOM_NOT_TOP:
+ case SMART_TARGET_ACTION_INVOKER:
+ case SMART_TARGET_POSITION:
+ case SMART_TARGET_NONE:
+ case SMART_TARGET_CLOSEST_CREATURE:
+ case SMART_TARGET_CLOSEST_GAMEOBJECT:
+ case SMART_TARGET_CLOSEST_PLAYER:
+ break;
+ default:
+ sLog.outErrorDb("SmartAIMgr: Not handled target_type(%u), Entry %d SourceType %u Event %u Action %u, skipped.", e.GetTargetType(), e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+ return true;
+}
+
+bool SmartAIMgr::IsEventValid(SmartScriptHolder e)
+{
+ if (e.event.type >= SMART_EVENT_END)
+ {
+ sLog.outErrorDb("SmartAIMgr: EntryOrGuid %d using event(%u) has invalid event type (%u), skipped.", e.entryOrGuid, e.event_id, e.GetEventType());
+ return false;
+ }
+ if (!(SmartAIEventMask[e.event.type][1] & SmartAITypeMask[e.GetScriptType()][1]))
+ {
+ sLog.outErrorDb("SmartAIMgr: EntryOrGuid %d, event type %u can not be used for Script type %u", e.entryOrGuid, e.GetEventType(), e.GetScriptType());
+ return false;
+ }
+ if (e.action.type >= SMART_ACTION_END)
+ {
+ sLog.outErrorDb("SmartAIMgr: EntryOrGuid %d using event(%u) has invalid action type (%u), skipped.", e.entryOrGuid, e.event_id, e.GetActionType());
+ return false;
+ }
+ if (e.event.event_phase_mask > SMART_EVENT_PHASE_ALL)
+ {
+ sLog.outErrorDb("SmartAIMgr: EntryOrGuid %d using event(%u) has invalid phase mask (%u), skipped.", e.entryOrGuid, e.event_id, e.event.event_phase_mask);
+ return false;
+ }
+ switch (e.event.type)
+ {
+ case SMART_EVENT_UPDATE:
+ case SMART_EVENT_UPDATE_IC:
+ case SMART_EVENT_UPDATE_OOC:
+ case SMART_EVENT_HEALT_PCT:
+ case SMART_EVENT_MANA_PCT:
+ case SMART_EVENT_TARGET_HEALTH_PCT:
+ case SMART_EVENT_TARGET_MANA_PCT:
+ case SMART_EVENT_RANGE:
+ case SMART_EVENT_DAMAGED:
+ case SMART_EVENT_DAMAGED_TARGET:
+ case SMART_EVENT_RECEIVE_HEAL:
+ if (!IsMinMaxValid(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max)) return false;
+ if (!IsMinMaxValid(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax)) return false;
+ break;
+ case SMART_EVENT_SPELLHIT:
+ case SMART_EVENT_SPELLHIT_TARGET:
+ if (e.event.spellHit.spell)
+ {
+ SpellEntry const* pSpell = sSpellStore.LookupEntry(e.event.spellHit.spell);
+ if (!pSpell)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Spell entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell);
+ return false;
+ }
+ if (e.event.spellHit.school && (e.event.spellHit.school & pSpell->SchoolMask) != pSpell->SchoolMask)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses Spell entry %u with invalid school mask, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell);
+ return false;
+ }
+ }
+ if (!IsMinMaxValid(e, e.event.spellHit.cooldownMin, e.event.spellHit.cooldownMax)) return false;
+ break;
+ case SMART_EVENT_OOC_LOS:
+ case SMART_EVENT_IC_LOS:
+ if (!IsMinMaxValid(e, e.event.los.cooldownMin, e.event.los.cooldownMax)) return false;
+ break;
+ case SMART_EVENT_RESPAWN:
+ if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_MAP && !sMapStore.LookupEntry(e.event.respawn.map))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.map);
+ return false;
+ }
+ if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !GetAreaEntryByAreaID(e.event.respawn.area))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Area entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.area);
+ return false;
+ }
+ break;
+ case SMART_EVENT_FRIENDLY_HEALTH:
+ if (!NotNULL(e, e.event.friendlyHealt.radius)) return false;
+ if (!IsMinMaxValid(e, e.event.friendlyHealt.repeatMin, e.event.friendlyHealt.repeatMax)) return false;
+ break;
+ case SMART_EVENT_FRIENDLY_IS_CC:
+ if (!IsMinMaxValid(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax)) return false;
+ break;
+ case SMART_EVENT_FRIENDLY_MISSING_BUFF:
+ {
+ if (!IsSpellValid(e, e.event.missingBuff.spell)) return false;
+ if (!NotNULL(e, e.event.missingBuff.radius)) return false;
+ if (!IsMinMaxValid(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax)) return false;
+ break;
+ }
+ case SMART_EVENT_KILL:
+ if (!IsMinMaxValid(e, e.event.kill.cooldownMin, e.event.kill.cooldownMax)) return false;
+ if (e.event.kill.creature && !IsCreatureValid(e, e.event.kill.creature)) return false;
+ break;
+ case SMART_EVENT_TARGET_CASTING:
+ case SMART_EVENT_PASSENGER_BOARDED:
+ case SMART_EVENT_PASSENGER_REMOVED:
+ if (!IsMinMaxValid(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax)) return false;
+ break;
+ case SMART_EVENT_SUMMON_DESPAWNED:
+ case SMART_EVENT_SUMMONED_UNIT:
+ if (e.event.summoned.creature && !IsCreatureValid(e, e.event.summoned.creature)) return false;
+ if (!IsMinMaxValid(e, e.event.summoned.cooldownMin, e.event.summoned.cooldownMax)) return false;
+ break;
+ case SMART_EVENT_ACCEPTED_QUEST:
+ case SMART_EVENT_REWARD_QUEST:
+ if (!IsQuestValid(e, e.event.quest.quest)) return false;
+ break;
+ case SMART_EVENT_RECEIVE_EMOTE:
+ {
+ if (e.event.emote.emote && !IsEmoteValid(e, e.event.emote.emote)) return false;
+ if (!IsMinMaxValid(e, e.event.emote.cooldownMin, e.event.emote.cooldownMax)) return false;
+ break;
+ }
+ case SMART_EVENT_HAS_AURA:
+ case SMART_EVENT_TARGET_BUFFED:
+ {
+ if (!IsSpellValid(e, e.event.aura.spell)) return false;
+ if (!IsMinMaxValid(e, e.event.aura.repeatMin, e.event.aura.repeatMax)) return false;
+ break;
+ }
+ case SMART_EVENT_TRANSPORT_ADDCREATURE:
+ {
+ if (e.event.transportAddCreature.creature && !IsCreatureValid(e, e.event.transportAddCreature.creature)) return false;
+ break;
+ }
+ case SMART_EVENT_MOVEMENTINFORM:
+ {
+ if (e.event.movementInform.type > NULL_MOTION_TYPE)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid Motion type %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.movementInform.type);
+ return false;
+ }
+ break;
+ }
+ case SMART_EVENT_DATA_SET:
+ {
+ if (!IsMinMaxValid(e, e.event.dataSet.cooldownMin, e.event.dataSet.cooldownMax)) return false;
+ break;
+ }
+ case SMART_EVENT_AREATRIGGER_ONTRIGGER:
+ {
+ if (e.event.areatrigger.id && !IsAreaTriggerValid(e, e.event.areatrigger.id)) return false;
+ break;
+ }
+ case SMART_EVENT_TEXT_OVER:
+ if (e.event.textOver.textGroupID && !IsTextValid(e, e.event.textOver.textGroupID)) return false;
+ break;
+ case SMART_EVENT_LINK:
+ {
+ if (e.link && e.link == e.event_id)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u, Event %u, Link Event is linking self (infinite loop), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id);
+ return false;
+ }
+ break;
+ }
+ case SMART_EVENT_TIMED_EVENT_TRIGGERED:
+ case SMART_EVENT_INSTANCE_PLAYER_ENTER:
+ case SMART_EVENT_TRANSPORT_RELOCATE:
+ case SMART_EVENT_CHARMED:
+ case SMART_EVENT_CHARMED_TARGET:
+ case SMART_EVENT_CORPSE_REMOVED:
+ case SMART_EVENT_AI_INIT:
+ case SMART_EVENT_TRANSPORT_ADDPLAYER:
+ case SMART_EVENT_TRANSPORT_REMOVE_PLAYER:
+ case SMART_EVENT_AGGRO:
+ case SMART_EVENT_DEATH:
+ case SMART_EVENT_EVADE:
+ case SMART_EVENT_REACHED_HOME:
+ case SMART_EVENT_RESET:
+ case SMART_EVENT_QUEST_ACCEPTED:
+ case SMART_EVENT_QUEST_OBJ_COPLETETION:
+ case SMART_EVENT_QUEST_COMPLETION:
+ case SMART_EVENT_QUEST_REWARDED:
+ case SMART_EVENT_QUEST_FAIL:
+ case SMART_EVENT_JUST_SUMMONED:
+ case SMART_EVENT_WAYPOINT_START:
+ case SMART_EVENT_WAYPOINT_REACHED:
+ case SMART_EVENT_WAYPOINT_PAUSED:
+ case SMART_EVENT_WAYPOINT_RESUMED:
+ case SMART_EVENT_WAYPOINT_STOPPED:
+ case SMART_EVENT_WAYPOINT_ENDED:
+ case SMART_ACTION_PLAYMOVIE:
+ case SMART_EVENT_GOSSIP_SELECT:
+ case SMART_EVENT_GOSSIP_HELLO:
+ case SMART_EVENT_JUST_CREATED:
+ case SMART_EVENT_FOLLOW_COPMLETE:
+ break;
+ default:
+ sLog.outErrorDb("SmartAIMgr: Not handled event_type(%u), Entry %d SourceType %u Event %u Action %u, skipped.", e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+ switch (e.GetActionType())
+ {
+ case SMART_ACTION_TALK:
+ if (!IsTextValid(e, e.action.talk.textGroupID1)) return false;
+ if (e.action.talk.textGroupID2 && !IsTextValid(e, e.action.talk.textGroupID2)) return false;
+ if (e.action.talk.textGroupID3 && !IsTextValid(e, e.action.talk.textGroupID3)) return false;
+ if (e.action.talk.textGroupID4 && !IsTextValid(e, e.action.talk.textGroupID4)) return false;
+ if (e.action.talk.textGroupID5 && !IsTextValid(e, e.action.talk.textGroupID5)) return false;
+ if (e.action.talk.textGroupID6 && !IsTextValid(e, e.action.talk.textGroupID6)) return false;
+
+ break;
+ case SMART_ACTION_SET_FACTION:
+ if (e.action.faction.factionID && !sFactionStore.LookupEntry(e.action.faction.factionID))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Faction %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.faction.factionID);
+ return false;
+ }
+ break;
+ case SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL:
+ case SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL:
+ if (e.action.morphOrMount.creature || e.action.morphOrMount.model)
+ {
+ if (e.action.morphOrMount.creature > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(e.action.morphOrMount.creature))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Creature entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.morphOrMount.creature);
+ return false;
+ }
+
+ if (e.action.morphOrMount.model)
+ {
+ if (e.action.morphOrMount.creature)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u has ModelID set with also set CreatureId, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+ else if (!sCreatureDisplayInfoStore.LookupEntry(e.action.morphOrMount.model))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Model id %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.morphOrMount.model);
+ return false;
+ }
+ }
+ }
+ break;
+ case SMART_ACTION_SOUND:
+ if (!IsSoundValid(e, e.action.sound.sound)) return false;
+ if (e.action.sound.range > TEXT_RANGE_WORLD)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid Text Range %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.sound.range);
+ return false;
+ }
+ break;
+ case SMART_ACTION_SET_EMOTE_STATE:
+ case SMART_ACTION_PLAY_EMOTE:
+ if (!IsEmoteValid(e, e.action.emote.emote)) return false;
+ break;
+ case SMART_ACTION_FAIL_QUEST:
+ case SMART_ACTION_ADD_QUEST:
+ if (e.action.quest.quest && !IsQuestValid(e, e.action.quest.quest)) return false;
+ break;
+ case SMART_ACTION_RANDOM_EMOTE:
+ if (e.action.randomEmote.emote1 && !IsEmoteValid(e, e.action.randomEmote.emote1)) return false;
+ if (e.action.randomEmote.emote2 && !IsEmoteValid(e, e.action.randomEmote.emote2)) return false;
+ if (e.action.randomEmote.emote3 && !IsEmoteValid(e, e.action.randomEmote.emote3)) return false;
+ if (e.action.randomEmote.emote4 && !IsEmoteValid(e, e.action.randomEmote.emote4)) return false;
+ if (e.action.randomEmote.emote5 && !IsEmoteValid(e, e.action.randomEmote.emote5)) return false;
+ if (e.action.randomEmote.emote6 && !IsEmoteValid(e, e.action.randomEmote.emote6)) return false;
+ break;
+ case SMART_ACTION_ADD_AURA:
+ case SMART_ACTION_CAST:
+ if (!IsSpellValid(e, e.action.cast.spell)) return false;
+ break;
+ case SMART_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS:
+ case SMART_ACTION_CALL_GROUPEVENTHAPPENS:
+ if (Quest const* qid = sObjectMgr.GetQuestTemplate(e.action.quest.quest))
+ {
+ if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u SpecialFlags for Quest entry %u does not include FLAGS_EXPLORATION_OR_EVENT(2), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.quest.quest);
+ return false;
+ }
+ }
+ else
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Quest entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.quest.quest);
+ return false;
+ }
+ break;
+ case SMART_ACTION_SEND_CASTCREATUREORGO:
+ if (!IsQuestValid(e, e.action.castCreatureOrGO.quest)) return false;
+ if (!IsSpellValid(e, e.action.castCreatureOrGO.spell)) return false;
+ break;
+
+
+
+ case SMART_ACTION_SET_EVENT_PHASE:
+ if (e.action.setEventPhase.phase >= SMART_EVENT_PHASE_MAX)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to set phase %u. Phase mask cannot be used past phase %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setEventPhase.phase, SMART_EVENT_PHASE_MAX-1);
+ return false;
+ }
+ break;
+ case SMART_ACTION_INC_EVENT_PHASE:
+ if (!e.action.incEventPhase.inc && !e.action.incEventPhase.dec)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u is incrementing phase by 0, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+ else if (e.action.incEventPhase.inc > SMART_EVENT_PHASE_MAX || e.action.incEventPhase.dec > SMART_EVENT_PHASE_MAX)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to increment phase by too large value, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+ break;
+ case SMART_ACTION_CALL_CASTEDCREATUREORGO:
+ if (!IsCreatureValid(e, e.action.castedCreatureOrGO.creature)) return false;
+ if (!IsSpellValid(e, e.action.castedCreatureOrGO.spell)) return false;
+ break;
+ case SMART_ACTION_REMOVEAURASFROMSPELL:
+ if (!IsSpellValid(e, e.action.removeAura.spell)) return false;
+ break;
+ case SMART_ACTION_RANDOM_PHASE:
+ {
+ if (e.action.randomPhase.phase1 >= SMART_EVENT_PHASE_MAX ||
+ e.action.randomPhase.phase2 >= SMART_EVENT_PHASE_MAX ||
+ e.action.randomPhase.phase3 >= SMART_EVENT_PHASE_MAX ||
+ e.action.randomPhase.phase4 >= SMART_EVENT_PHASE_MAX ||
+ e.action.randomPhase.phase5 >= SMART_EVENT_PHASE_MAX ||
+ e.action.randomPhase.phase6 >= SMART_EVENT_PHASE_MAX)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+ }
+ break;
+ case SMART_ACTION_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax
+ {
+ if (e.action.randomPhaseRange.phaseMin >= SMART_EVENT_PHASE_MAX ||
+ e.action.randomPhaseRange.phaseMax >= SMART_EVENT_PHASE_MAX)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+ if (!IsMinMaxValid(e, e.action.randomPhaseRange.phaseMin, e.action.randomPhaseRange.phaseMax)) return false;
+ break;
+ }
+ case SMART_ACTION_SUMMON_CREATURE:
+ if (!IsCreatureValid(e, e.action.summonCreature.creature)) return false;
+ if (e.action.summonCreature.type > TEMPSUMMON_MANUAL_DESPAWN)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses incorrect TempSummonType %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonCreature.type);
+ return false;
+ }
+ break;
+ case SMART_ACTION_CALL_KILLEDMONSTER:
+ if (!IsCreatureValid(e, e.action.killedMonster.creature)) return false;
+ break;
+ case SMART_ACTION_UPDATE_TEMPLATE:
+ if (e.action.updateTemplate.creature && !IsCreatureValid(e, e.action.updateTemplate.creature)) return false;
+ break;
+ case SMART_ACTION_SET_SHEATH:
+ if (e.action.setSheath.sheath && e.action.setSheath.sheath >= MAX_SHEATH_STATE)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses incorrect Sheath state %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setSheath.sheath);
+ return false;
+ }
+ break;
+ case SMART_ACTION_SET_REACT_STATE:
+ {
+ if (e.action.react.state > REACT_AGGRESSIVE)
+ {
+ sLog.outErrorDb("SmartAIMgr: Creature %d Event %u Action %u uses invalid React State %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.react.state);
+ return false;
+ }
+ break;
+ }
+ case SMART_ACTION_SUMMON_GO:
+ if (!IsGameObjectValid(e, e.action.summonGO.entry)) return false;
+ break;
+ case SMART_ACTION_WP_LOAD:
+ if (!sSmartWaypointMgr.GetPath(e.action.wpLoad.id))
+ {
+ sLog.outErrorDb("SmartAIMgr: Creature %d Event %u Action %u uses non-existent WaypointPath id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpLoad.id);
+ return false;
+ }
+ break;
+ case SMART_ACTION_ADD_ITEM:
+ case SMART_ACTION_REMOVE_ITEM:
+ if (!IsItemValid(e, e.action.item.entry)) return false;
+ if (!NotNULL(e, e.action.item.count)) return false;
+ break;
+ case SMART_ACTION_TELEPORT:
+ if (!sMapStore.LookupEntry(e.action.teleport.mapID))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.teleport.mapID);
+ return false;
+ }
+ break;
+ case SMART_ACTION_INSTALL_AI_TEMPLATE:
+ if (e.action.installTtemplate.id >= SMARTAI_TEMPLATE_END)
+ {
+ sLog.outErrorDb("SmartAIMgr: Creature %d Event %u Action %u uses non-existent AI template id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.installTtemplate.id);
+ return false;
+ }
+ break;
+ case SMART_ACTION_WP_STOP:
+ if (e.action.wpStop.quest && !IsQuestValid(e, e.action.wpStop.quest)) return false;
+ break;
+ case SMART_ACTION_WP_START:
+ {
+ if (e.action.wpStart.quest && !IsQuestValid(e, e.action.wpStart.quest)) return false;
+ if (e.action.wpStart.reactState > REACT_AGGRESSIVE)
+ {
+ sLog.outErrorDb("SmartAIMgr: Creature %d Event %u Action %u uses invalid React State %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.reactState);
+ return false;
+ }
+ break;
+ }
+ case SMART_ACTION_CREATE_TIMED_EVENT:
+ {
+ if (!IsMinMaxValid(e, e.action.timeEvent.min, e.action.timeEvent.max)) return false;
+ if (!IsMinMaxValid(e, e.action.timeEvent.repeatMin, e.action.timeEvent.repeatMax)) return false;
+ break;
+ }
+ case SMART_ACTION_FOLLOW:
+ case SMART_ACTION_SET_ORIENTATION:
+ case SMART_ACTION_STORE_TARGET_LIST:
+ case SMART_ACTION_EVADE:
+ case SMART_ACTION_FLEE_FOR_ASSIST:
+ case SMART_ACTION_DIE:
+ case SMART_ACTION_SET_IN_COMBAT_WITH_ZONE:
+ case SMART_ACTION_SET_ACTIVE:
+ case SMART_ACTION_STORE_VARIABLE_DECIMAL:
+ case SMART_ACTION_WP_RESUME:
+ case SMART_ACTION_KILL_UNIT:
+ case SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL:
+ case SMART_ACTION_RESET_GOBJECT:
+ case SMART_ACTION_ATTACK_START:
+ case SMART_ACTION_THREAT_ALL_PCT:
+ case SMART_ACTION_THREAT_SINGLE_PCT:
+ case SMART_ACTION_SET_INST_DATA:
+ case SMART_ACTION_SET_INST_DATA64:
+ case SMART_ACTION_AUTO_ATTACK:
+ case SMART_ACTION_ALLOW_COMBAT_MOVEMENT:
+ case SMART_ACTION_CALL_FOR_HELP:
+ case SMART_ACTION_SET_DATA:
+ case SMART_ACTION_MOVE_FORWARD:
+ case SMART_ACTION_SET_VISIBILITY:
+ case SMART_ACTION_WP_PAUSE:
+ case SMART_ACTION_SET_FLY:
+ case SMART_ACTION_SET_RUN:
+ case SMART_ACTION_SET_SWIMM:
+ case SMART_ACTION_FORCE_DESPAWN:
+ case SMART_ACTION_SET_INGAME_PHASE_MASK:
+ case SMART_ACTION_SET_UNIT_FLAG:
+ case SMART_ACTION_REMOVE_UNIT_FLAG:
+ case SMART_ACTION_PLAYMOVIE:
+ case SMART_ACTION_MOVE_TO_POS:
+ case SMART_ACTION_RESPAWN_TARGET:
+ case SMART_ACTION_CLOSE_GOSSIP:
+ case SMART_ACTION_EQUIP:
+ case SMART_ACTION_TRIGGER_TIMED_EVENT:
+ case SMART_ACTION_REMOVE_TIMED_EVENT:
+ case SMART_ACTION_OVERRIDE_SCRIPT_BASE_OBJECT:
+ case SMART_ACTION_RESET_SCRIPT_BASE_OBJECT:
+ case SMART_ACTION_ACTIVATE_GOBJECT:
+ case SMART_ACTION_CALL_SCRIPT_RESET:
+ case SMART_ACTION_NONE:
+ break;
+ default:
+ sLog.outErrorDb("SmartAIMgr: Not handled action_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
+ return false;
+ }
+
+ return true;
+}
+
+bool SmartAIMgr::IsTextValid(SmartScriptHolder e, uint32 id)
+{
+ bool error = false;
+ uint32 entry = 0;
+ if (e.entryOrGuid >= 0)
+ entry = uint32(e.entryOrGuid);
+ else {
+ entry = uint32(abs(e.entryOrGuid));
+ CreatureData const* data = sObjectMgr.GetCreatureData(entry);
+ if (!data)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u using non-existent Creature guid %d, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
+ return false;
+ }
+ else
+ entry = data->id;
+ }
+ if (!entry || !sCreatureTextMgr.TextExist(entry, uint8(id)))
+ error = true;
+ if (error)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u using non-existent Text id %d, skipped.", e.entryOrGuid, e.GetScriptType(), e.source_type, e.GetActionType(), id);
+ return false;
+ }
+ return true;
+}
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
new file mode 100644
index 00000000000..6d979c6d94f
--- /dev/null
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -0,0 +1,1276 @@
+/*
+ * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 TRINITY_SMARTSCRIPTMGR_H
+#define TRINITY_SMARTSCRIPTMGR_H
+
+#include "Common.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "Unit.h"
+#include "ConditionMgr.h"
+#include "CreatureTextMgr.h"
+#include "Spell.h"
+
+//#include "SmartScript.h"
+//#include "SmartAI.h"
+
+struct WayPoint
+{
+ WayPoint(uint32 _id, float _x, float _y, float _z)
+ {
+ id = _id;
+ x = _x;
+ y = _y;
+ z = _z;
+ }
+
+ uint32 id;
+ float x;
+ float y;
+ float z;
+};
+
+enum SMART_EVENT_PHASE
+{
+ SMART_EVENT_PHASE_ALWAYS = 0,
+ SMART_EVENT_PHASE_1 = 1,
+ SMART_EVENT_PHASE_2 = 2,
+ SMART_EVENT_PHASE_3 = 3,
+ SMART_EVENT_PHASE_4 = 4,
+ SMART_EVENT_PHASE_5 = 5,
+ SMART_EVENT_PHASE_6 = 6,
+ SMART_EVENT_PHASE_MAX = 7,
+
+ SMART_EVENT_PHASE_COUNT = 6,
+};
+
+enum SMART_EVENT_PHASE_BITS
+{
+ SMART_EVENT_PHASE_ALWAYS_BIT = 0,
+ SMART_EVENT_PHASE_1_BIT = 1,
+ SMART_EVENT_PHASE_2_BIT = 2,
+ SMART_EVENT_PHASE_3_BIT = 4,
+ SMART_EVENT_PHASE_4_BIT = 8,
+ SMART_EVENT_PHASE_5_BIT = 16,
+ SMART_EVENT_PHASE_6_BIT = 32,
+ SMART_EVENT_PHASE_ALL = SMART_EVENT_PHASE_1_BIT + SMART_EVENT_PHASE_2_BIT + SMART_EVENT_PHASE_3_BIT + SMART_EVENT_PHASE_4_BIT + SMART_EVENT_PHASE_5_BIT + SMART_EVENT_PHASE_6_BIT,
+};
+
+const uint32 SmartPhaseMask[SMART_EVENT_PHASE_COUNT][2] =
+{
+ {SMART_EVENT_PHASE_1, SMART_EVENT_PHASE_1_BIT },
+ {SMART_EVENT_PHASE_2, SMART_EVENT_PHASE_2_BIT },
+ {SMART_EVENT_PHASE_3, SMART_EVENT_PHASE_3_BIT },
+ {SMART_EVENT_PHASE_4, SMART_EVENT_PHASE_4_BIT },
+ {SMART_EVENT_PHASE_5, SMART_EVENT_PHASE_5_BIT },
+ {SMART_EVENT_PHASE_6, SMART_EVENT_PHASE_6_BIT },
+};
+
+enum SMART_EVENT
+{
+ SMART_EVENT_UPDATE_IC = 0, //1 // InitialMin, InitialMax, RepeatMin, RepeatMax
+ SMART_EVENT_UPDATE_OOC = 1, //1 // InitialMin, InitialMax, RepeatMin, RepeatMax
+ SMART_EVENT_HEALT_PCT = 2, //1 // HPMin%, HPMax%, RepeatMin, RepeatMax
+ SMART_EVENT_MANA_PCT = 3, //1 // ManaMin%, ManaMax%, RepeatMin, RepeatMax
+ SMART_EVENT_AGGRO = 4, //1 // NONE
+ SMART_EVENT_KILL = 5, //1 // CooldownMin0, CooldownMax1,playerOnly2,else creature entry3
+ SMART_EVENT_DEATH = 6, //1 // NONE
+ SMART_EVENT_EVADE = 7, //1 // NONE
+ SMART_EVENT_SPELLHIT = 8, //1 // SpellID, School, CooldownMin, CooldownMax
+ SMART_EVENT_RANGE = 9, //1 // MinDist, MaxDist, RepeatMin, RepeatMax
+ SMART_EVENT_OOC_LOS = 10, //1 // NoHostile, MaxRnage, CooldownMin, CooldownMax
+ SMART_EVENT_RESPAWN = 11, //1 // type, MapId,ZoneId
+ SMART_EVENT_TARGET_HEALTH_PCT = 12, //1 // HPMin%, HPMax%, RepeatMin, RepeatMax
+ SMART_EVENT_TARGET_CASTING = 13, //1 // RepeatMin, RepeatMax
+ SMART_EVENT_FRIENDLY_HEALTH = 14, //1 // HPDeficit, Radius, RepeatMin, RepeatMax
+ SMART_EVENT_FRIENDLY_IS_CC = 15, //1 // Radius, RepeatMin, RepeatMax
+ SMART_EVENT_FRIENDLY_MISSING_BUFF = 16, //1 // SpellId, Radius, RepeatMin, RepeatMax
+ SMART_EVENT_SUMMONED_UNIT = 17, //1 // CreatureId(0 all), CooldownMin, CooldownMax
+ SMART_EVENT_TARGET_MANA_PCT = 18, //1 // ManaMin%, ManaMax%, RepeatMin, RepeatMax
+ SMART_EVENT_ACCEPTED_QUEST = 19, //1 // QuestID(0any)
+ SMART_EVENT_REWARD_QUEST = 20, //1 // QuestID(0any)
+ SMART_EVENT_REACHED_HOME = 21, //1 // NONE
+ SMART_EVENT_RECEIVE_EMOTE = 22, //1 // EmoteId, CooldownMin, CooldownMax, condition, val1,val2,val3
+ SMART_EVENT_HAS_AURA = 23, //1 // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 RepeatMin, RepeatMax
+ SMART_EVENT_TARGET_BUFFED = 24, //1 // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 RepeatMin, RepeatMax
+ SMART_EVENT_RESET = 25, //1 // Called after combat, when the creature respawn and spawn.
+
+ SMART_EVENT_IC_LOS = 26, //1 // NoHostile, MaxRnage, CooldownMin, CooldownMax
+ SMART_EVENT_PASSENGER_BOARDED = 27, //1 // CooldownMin, CooldownMax
+ SMART_EVENT_PASSENGER_REMOVED = 28, //1 // CooldownMin, CooldownMax
+ SMART_EVENT_CHARMED = 29, //1 // NONE
+ SMART_EVENT_CHARMED_TARGET = 30, //1 // NONE
+ SMART_EVENT_SPELLHIT_TARGET = 31, //1 // SpellID, School, CooldownMin, CooldownMax
+ SMART_EVENT_DAMAGED = 32, //1 // MinDmg, MaxDmg, CooldownMin, CooldownMax
+ SMART_EVENT_DAMAGED_TARGET = 33, //1 // MinDmg, MaxDmg, CooldownMin, CooldownMax
+ SMART_EVENT_MOVEMENTINFORM = 34, //1 // MovementType(any), PointID
+ SMART_EVENT_SUMMON_DESPAWNED = 35, //1 // Entry, CooldownMin, CooldownMax
+ SMART_EVENT_CORPSE_REMOVED = 36, //1 // NONE
+ SMART_EVENT_AI_INIT = 37, //1 // NONE
+ SMART_EVENT_DATA_SET = 38, //1 // Id, Value, CooldownMin, CooldownMax
+ SMART_EVENT_WAYPOINT_START = 39, //1 // PointId(0any), pathID(0any)
+ SMART_EVENT_WAYPOINT_REACHED = 40, //1 // PointId(0any), pathID(0any)
+ SMART_EVENT_TRANSPORT_ADDPLAYER = 41, //1 // NONE
+ SMART_EVENT_TRANSPORT_ADDCREATURE = 42, //1 // Entry (0 any)
+ SMART_EVENT_TRANSPORT_REMOVE_PLAYER = 43, //1 // NONE
+ SMART_EVENT_TRANSPORT_RELOCATE = 44, //1 // PointId
+ SMART_EVENT_INSTANCE_PLAYER_ENTER = 45, //1 // Team (0 any), CooldownMin, CooldownMax
+ SMART_EVENT_AREATRIGGER_ONTRIGGER = 46, //1 // TriggerId(0 any)
+ SMART_EVENT_QUEST_ACCEPTED = 47, //1 // none
+ SMART_EVENT_QUEST_OBJ_COPLETETION = 48, //1 // none
+ SMART_EVENT_QUEST_COMPLETION = 49, //1 // none
+ SMART_EVENT_QUEST_REWARDED = 50, //1 // none
+ SMART_EVENT_QUEST_FAIL = 51, //1 // none
+ SMART_EVENT_TEXT_OVER = 52, //1 // GroupId from creature_text (0 any)
+ SMART_EVENT_RECEIVE_HEAL = 53, //1 // MinHeal, MaxHeal, CooldownMin, CooldownMax
+ SMART_EVENT_JUST_SUMMONED = 54, //1 // none
+ SMART_EVENT_WAYPOINT_PAUSED = 55, //1 // PointId(0any), pathID(0any)
+ SMART_EVENT_WAYPOINT_RESUMED = 56, //1 // PointId(0any), pathID(0any)
+ SMART_EVENT_WAYPOINT_STOPPED = 57, //1 // PointId(0any), pathID(0any)
+ SMART_EVENT_WAYPOINT_ENDED = 58, //1 // PointId(0any), pathID(0any)
+ SMART_EVENT_TIMED_EVENT_TRIGGERED = 59, //1 // id
+ SMART_EVENT_UPDATE = 60, //1 // InitialMin, InitialMax, RepeatMin, RepeatMax
+ SMART_EVENT_LINK = 61, //1 // INTERNAL USAGE, no params, used to link together multiple events, does not use any extra resources to iterate event lists needlessly
+ SMART_EVENT_GOSSIP_SELECT = 62, //1 // sender, action
+ SMART_EVENT_JUST_CREATED = 63, //1 // none
+ SMART_EVENT_GOSSIP_HELLO = 64, //1 // none
+ SMART_EVENT_FOLLOW_COPMLETE = 65, //1 // none
+
+ SMART_EVENT_END = 66,
+};
+
+struct SmartEvent
+{
+ SMART_EVENT type;
+ uint32 event_phase_mask;
+ uint32 event_chance;
+ uint32 event_flags;
+ union
+ {
+ struct
+ {
+ uint32 min;
+ uint32 max;
+ uint32 repeatMin;
+ uint32 repeatMax;
+ } minMaxRepeat;
+
+ struct
+ {
+ uint32 cooldownMin;
+ uint32 cooldownMax;
+ uint32 playerOnly;
+ uint32 creature;
+ } kill;
+
+ struct
+ {
+ uint32 spell;
+ uint32 school;
+ uint32 cooldownMin;
+ uint32 cooldownMax;
+ } spellHit;
+
+ struct
+ {
+ uint32 noHostile;
+ uint32 maxDist;
+ uint32 cooldownMin;
+ uint32 cooldownMax;
+ } los;
+
+ struct
+ {
+ uint32 type;
+ uint32 map;
+ uint32 area;
+ } respawn;
+
+ struct
+ {
+ uint32 repeatMin;
+ uint32 repeatMax;
+ } minMax;
+
+ struct
+ {
+ uint32 hpDeficit;
+ uint32 radius;
+ uint32 repeatMin;
+ uint32 repeatMax;
+ } friendlyHealt;
+
+ struct
+ {
+ uint32 radius;
+ uint32 repeatMin;
+ uint32 repeatMax;
+ } friendlyCC;
+
+ struct
+ {
+ uint32 spell;
+ uint32 radius;
+ uint32 repeatMin;
+ uint32 repeatMax;
+ } missingBuff;
+
+ struct
+ {
+ uint32 creature;
+ uint32 cooldownMin;
+ uint32 cooldownMax;
+ } summoned;
+
+ struct
+ {
+ uint32 quest;
+ } quest;
+
+ struct
+ {
+ uint32 emote;
+ uint32 cooldownMin;
+ uint32 cooldownMax;
+ } emote;
+
+ struct
+ {
+ uint32 spell;
+ uint32 count;
+ uint32 repeatMin;
+ uint32 repeatMax;
+ } aura;
+
+ struct
+ {
+ uint32 spell;
+ uint32 count;
+ uint32 repeatMin;
+ uint32 repeatMax;
+ } targetAura;
+
+ struct
+ {
+ uint32 type;
+ uint32 id;
+ } movementInform;
+
+ struct
+ {
+ uint32 id;
+ uint32 value;
+ uint32 cooldownMin;
+ uint32 cooldownMax;
+ } dataSet;
+
+ struct
+ {
+ uint32 pointID;
+ uint32 pathID;
+ } waypoint;
+
+ struct
+ {
+ uint32 creature;
+ } transportAddCreature;
+
+ struct
+ {
+ uint32 pointID;
+ } transportRelocate;
+
+ struct
+ {
+ uint32 team;
+ uint32 cooldownMin;
+ uint32 cooldownMax;
+ } instancePlayerEnter;
+
+ struct
+ {
+ uint32 id;
+ } areatrigger;
+
+ struct
+ {
+ uint32 textGroupID;
+ } textOver;
+
+ struct
+ {
+ uint32 id;
+ } timedEvent;
+
+ struct
+ {
+ uint32 sender;
+ uint32 action;
+ } gossip;
+
+ struct
+ {
+ uint32 param1;
+ uint32 param2;
+ uint32 param3;
+ uint32 param4;
+ } raw;
+ };
+};
+
+enum SMART_SCRIPT_RESPAWN_CONDITION
+{
+ SMART_SCRIPT_RESPAWN_CONDITION_NONE = 0,
+ SMART_SCRIPT_RESPAWN_CONDITION_MAP = 1,
+ SMART_SCRIPT_RESPAWN_CONDITION_AREA = 2,
+ SMART_SCRIPT_RESPAWN_CONDITION_END = 3,
+};
+
+enum SMART_ACTION
+{
+ SMART_ACTION_NONE = 0, //1 // No action
+ SMART_ACTION_TALK = 1, //1 // groupID from creature_text
+ SMART_ACTION_SET_FACTION = 2, //1 // FactionId (or 0 for default)
+ SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL = 3, //1 // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph)
+ SMART_ACTION_SOUND = 4, //1 // SoundId, TextRange
+ SMART_ACTION_PLAY_EMOTE = 5, //1 // EmoteId
+ SMART_ACTION_FAIL_QUEST = 6, //1 //QuestID
+ SMART_ACTION_ADD_QUEST = 7, //1 //QuestID
+ SMART_ACTION_SET_REACT_STATE = 8, //1 // state
+ SMART_ACTION_ACTIVATE_GOBJECT = 9, //1 // Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_RANDOM_EMOTE = 10, //1 // EmoteId1, EmoteId2, EmoteId3...
+ SMART_ACTION_CAST = 11, //1 // SpellId, CastFlags, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_SUMMON_CREATURE = 12, //1 // CreatureID,summonType, duration in ms, storageID, attackInvoker, Target(place), TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_THREAT_SINGLE_PCT = 13, //1 // Threat%, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_THREAT_ALL_PCT = 14, //1 // Threat%
+ SMART_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS = 15, //1 // QuestID, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_SEND_CASTCREATUREORGO = 16, //1 // QuestID, SpellId, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_SET_EMOTE_STATE = 17, //1 // emoteID
+ SMART_ACTION_SET_UNIT_FLAG = 18, //1 // Flags (may be more than one field OR'd together), Target
+ SMART_ACTION_REMOVE_UNIT_FLAG = 19, //1 // Flags (may be more than one field OR'd together), Target
+ SMART_ACTION_AUTO_ATTACK = 20, //1 // AllowAttackState (0 = stop attack, anything else means continue attacking)
+ SMART_ACTION_ALLOW_COMBAT_MOVEMENT = 21, //1 // AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
+ SMART_ACTION_SET_EVENT_PHASE = 22, //1 // Phase
+ SMART_ACTION_INC_EVENT_PHASE = 23, //1 // Value (may be negative to decrement phase, should not be 0)
+ SMART_ACTION_EVADE = 24, //1 // No Params
+ SMART_ACTION_FLEE_FOR_ASSIST = 25, //1 // No Params
+ SMART_ACTION_CALL_GROUPEVENTHAPPENS = 26, //1 // QuestID
+ SMART_ACTION_CALL_CASTEDCREATUREORGO = 27, //1 // CreatureId, SpellId
+ SMART_ACTION_REMOVEAURASFROMSPELL = 28, //1 // Spellid, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_FOLLOW = 29, //1 // Distance, Angle, EndCreatureEntry, credit, creditType (0monsterkill,1event) Target(uses first found), TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_RANDOM_PHASE = 30, //1 // PhaseId1, PhaseId2, PhaseId3...
+ SMART_ACTION_RANDOM_PHASE_RANGE = 31, //1 // PhaseMin, PhaseMax
+ SMART_ACTION_RESET_GOBJECT = 32, //1 // Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_CALL_KILLEDMONSTER = 33, //1 // CreatureId, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_SET_INST_DATA = 34, //1 // Field, Data
+ SMART_ACTION_SET_INST_DATA64 = 35, //1 // Field, Target(uses first found), TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_UPDATE_TEMPLATE = 36, //1 // Entry, Team
+ SMART_ACTION_DIE = 37, //1 // No Params
+ SMART_ACTION_SET_IN_COMBAT_WITH_ZONE = 38, //1 // No Params
+ SMART_ACTION_CALL_FOR_HELP = 39, //1 // Radius
+ SMART_ACTION_SET_SHEATH = 40, //1 // Sheath (0-passive,1-melee,2-ranged)
+ SMART_ACTION_FORCE_DESPAWN = 41, //1 // timer
+ SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL = 42, //1 // MinHpValue(+pct, -flat)
+ SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL = 43, //1 // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to unmount)
+ SMART_ACTION_SET_INGAME_PHASE_MASK = 44, //1 // mask
+
+ SMART_ACTION_SET_DATA = 45, //1 // Field, Data (only creature TODO)
+ SMART_ACTION_MOVE_FORWARD = 46, //1 // distance
+ SMART_ACTION_SET_VISIBILITY = 47, //1 // on/off
+ SMART_ACTION_SET_ACTIVE = 48, //1 // No Params
+ SMART_ACTION_ATTACK_START = 49, //1 // Target(uses first found), TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_SUMMON_GO = 50, //1 // GameObjectID, DespawnTime in ms, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_KILL_UNIT = 51, //1 // Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_WP_LOAD = 52, //1 // pathID
+ SMART_ACTION_WP_START = 53, //1 // run/walk, pathID, canRepeat, Target(used for escort quests), TargetVar1, TargetVar2, TargetVar3 + uses xyzo, quest, despawntime
+ SMART_ACTION_WP_PAUSE = 54, //1 // time
+ SMART_ACTION_WP_STOP = 55, //1 // despawnTime, quest, fail?
+ SMART_ACTION_ADD_ITEM = 56, //1 // itemID, count, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_REMOVE_ITEM = 57, //1 // itemID, count, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_INSTALL_AI_TEMPLATE = 58, //1 // AITemplateID
+ SMART_ACTION_SET_RUN = 59, //1 // 0/1
+ SMART_ACTION_SET_FLY = 60, //1 // 0/1
+ SMART_ACTION_SET_SWIMM = 61, //1 // 0/1
+ SMART_ACTION_TELEPORT = 62, //1 // mapID, Target, TargetVar1, TargetVar2, TargetVar3 + uses xyzo
+ SMART_ACTION_STORE_VARIABLE_DECIMAL = 63, //1 // varID, number
+ SMART_ACTION_STORE_TARGET_LIST = 64, //1 // varID, Target, TargetVar1, TargetVar2, TargetVar3
+ SMART_ACTION_WP_RESUME = 65, //1 // none
+ SMART_ACTION_SET_ORIENTATION = 66, //1 // Target, TargetVar1, TargetVar2, TargetVar3, orientation (used if target -1 or 0)
+
+ SMART_ACTION_CREATE_TIMED_EVENT = 67, //1 // id, InitialMin, InitialMax, RepeatMin(only if it repeats), RepeatMax(only if it repeats), chance
+ SMART_ACTION_PLAYMOVIE = 68, //1 // entry
+ SMART_ACTION_MOVE_TO_POS = 69, //1 // xyz
+ SMART_ACTION_RESPAWN_TARGET = 70, //1 // Target, TargetVar1, TargetVar2, TargetVar3,
+ SMART_ACTION_EQUIP = 71, //1 // entry slot1,slot2,slot3
+ SMART_ACTION_CLOSE_GOSSIP = 72, //1 // none
+ SMART_ACTION_TRIGGER_TIMED_EVENT = 73, //1 // id(>1)
+ SMART_ACTION_REMOVE_TIMED_EVENT = 74, //1 // id(>1)
+ SMART_ACTION_ADD_AURA = 75, //1 // spellid, targets
+ SMART_ACTION_OVERRIDE_SCRIPT_BASE_OBJECT = 76, //1 // target(first found used), WARNING: CAN CRASH CORE, do not use if you dont know what you are doing
+ SMART_ACTION_RESET_SCRIPT_BASE_OBJECT = 77, //1 // none
+ SMART_ACTION_CALL_SCRIPT_RESET = 78, //1 // none
+ SMART_ACTION_END = 79,
+};
+
+struct SmartAction
+{
+ SMART_ACTION type;
+
+ union
+ {
+ struct
+ {
+ uint32 textGroupID1;
+ uint32 textGroupID2;
+ uint32 textGroupID3;
+ uint32 textGroupID4;
+ uint32 textGroupID5;
+ uint32 textGroupID6;
+ } talk;
+
+ struct
+ {
+ uint32 factionID;
+ } faction;
+
+ struct
+ {
+ uint32 creature;
+ uint32 model;
+ } morphOrMount;
+
+ struct
+ {
+ uint32 sound;
+ uint32 range;
+ } sound;
+
+ struct
+ {
+ uint32 emote;
+ } emote;
+
+ struct
+ {
+ uint32 quest;
+ } quest;
+
+ struct
+ {
+ uint32 state;
+ } react;
+
+ struct
+ {
+
+ uint32 emote1;
+ uint32 emote2;
+ uint32 emote3;
+ uint32 emote4;
+ uint32 emote5;
+ uint32 emote6;
+ } randomEmote;
+
+ struct
+ {
+ uint32 spell;
+ uint32 flags;
+ } cast;
+
+ struct
+ {
+ uint32 creature;
+ uint32 type;
+ uint32 duration;
+ uint32 storageID;
+ uint32 attackInvoker;
+ } summonCreature;
+
+ struct
+ {
+ uint32 threatINC;
+ uint32 threatDEC;
+ } threatPCT;
+
+ struct
+ {
+ uint32 quest;
+ uint32 spell;
+ } castCreatureOrGO;
+
+ struct
+ {
+ uint32 flag1;
+ uint32 flag2;
+ uint32 flag3;
+ uint32 flag4;
+ uint32 flag5;
+ uint32 flag6;
+ } addUnitFlag;
+
+ struct
+ {
+ uint32 flag1;
+ uint32 flag2;
+ uint32 flag3;
+ uint32 flag4;
+ uint32 flag5;
+ uint32 flag6;
+ } removeUnitFlag;
+
+ struct
+ {
+ uint32 attack;
+ } autoAttack;
+
+ struct
+ {
+ uint32 move;
+ } combatMove;
+
+ struct
+ {
+ uint32 phase;
+ } setEventPhase;
+
+ struct
+ {
+ uint32 inc;
+ uint32 dec;
+ } incEventPhase;
+
+ struct
+ {
+ uint32 creature;
+ uint32 spell;
+ } castedCreatureOrGO;
+
+ struct
+ {
+ uint32 spell;
+ } removeAura;
+
+ struct
+ {
+ uint32 dist;
+ uint32 angle;
+ uint32 entry;
+ uint32 credit;
+ uint32 creditType;
+ } follow;
+
+ struct
+ {
+ uint32 phase1;
+ uint32 phase2;
+ uint32 phase3;
+ uint32 phase4;
+ uint32 phase5;
+ uint32 phase6;
+ } randomPhase;
+
+ struct
+ {
+ uint32 phaseMin;
+ uint32 phaseMax;
+ } randomPhaseRange;
+
+ struct
+ {
+ uint32 creature;
+ } killedMonster;
+
+ struct
+ {
+ uint32 field;
+ uint32 data;
+ } setInstanceData;
+
+ struct
+ {
+ uint32 field;
+ } setInstanceData64;
+
+ struct
+ {
+ uint32 creature;
+ uint32 team;
+ } updateTemplate;
+
+ struct
+ {
+ uint32 range;
+ } callHelp;
+
+ struct
+ {
+ uint32 sheath;
+ } setSheath;
+
+ struct
+ {
+ uint32 delay;
+ } forceDespawn;
+
+ struct
+ {
+ uint32 minHP;
+ uint32 percent;
+ } invincHP;
+
+ struct
+ {
+ uint32 mask;
+ } ingamePhaseMask;
+
+ struct
+ {
+ uint32 field;
+ uint32 data;
+ } setData;
+
+ struct
+ {
+ uint32 distance;
+ } moveRandom;
+
+ struct
+ {
+ uint32 state;
+ } visibility;
+
+ struct
+ {
+ uint32 entry;
+ uint32 despawnTime;
+ } summonGO;
+
+ struct
+ {
+ uint32 id;
+ } wpLoad;
+
+
+ struct
+ {
+ uint32 run;
+ uint32 pathID;
+ uint32 repeat;
+ uint32 quest;
+ uint32 despawnTime;
+ uint32 reactState;
+ } wpStart;
+
+ struct
+ {
+ uint32 delay;
+ } wpPause;
+
+ struct
+ {
+ uint32 despawnTime;
+ uint32 quest;
+ uint32 fail;
+ } wpStop;
+
+ struct
+ {
+ uint32 entry;
+ uint32 count;
+ } item;
+
+ struct
+ {
+ uint32 id;
+ uint32 param1;
+ uint32 param2;
+ uint32 param3;
+ uint32 param4;
+ uint32 param5;
+ } installTtemplate;
+
+ struct
+ {
+ uint32 run;
+ } setRun;
+
+ struct
+ {
+ uint32 fly;
+ } setFly;
+
+ struct
+ {
+ uint32 swimm;
+ } setSwimm;
+
+ struct
+ {
+ uint32 mapID;
+ } teleport;
+
+ struct
+ {
+ uint32 id;
+ uint32 number;
+ } storeVar;
+
+ struct
+ {
+ uint32 id;
+ } storeTargets;
+
+
+ struct
+ {
+ uint32 id;
+ uint32 min;
+ uint32 max;
+ uint32 repeatMin;
+ uint32 repeatMax;
+ uint32 chance;
+ } timeEvent;
+
+ struct
+ {
+ uint32 entry;
+ } movie;
+
+ struct
+ {
+ uint32 entry;
+ uint32 slot1;
+ uint32 slot2;
+ uint32 slot3;
+ } equip;
+
+ struct
+ {
+ uint32 flag;
+ } unitFlag;
+
+ struct
+ {
+ uint32 param1;
+ uint32 param2;
+ uint32 param3;
+ uint32 param4;
+ uint32 param5;
+ uint32 param6;
+ } raw;
+ };
+};
+
+enum SMARTAI_TEMPLATE
+{
+ SMARTAI_TEMPLATE_BASIC = 0, //nothing is preset
+ SMARTAI_TEMPLATE_CASTER = 1, //spellid, repeatMin, repeatMax, range, manaPCT +JOIN: target_param1 as castFlag
+ SMARTAI_TEMPLATE_TURRET = 2, //spellid, repeatMin, repeatMax +JOIN: target_param1 as castFlag
+ SMARTAI_TEMPLATE_PASSIVE = 3,
+ SMARTAI_TEMPLATE_CAGED_GO_PART = 4, //creatureID, give credit at point end?,
+ SMARTAI_TEMPLATE_CAGED_NPC_PART = 5, //gameObjectID, despawntime, run?, dist, TextGroupID
+ SMARTAI_TEMPLATE_END = 6,
+};
+
+enum SMARTAI_TARGETS
+{
+ SMART_TARGET_NONE = 0, // NONE, defaulting to invoket
+ SMART_TARGET_SELF = 1, // Self cast
+ SMART_TARGET_VICTIM = 2, // Our current target (ie: highest aggro)
+ SMART_TARGET_HOSTILE_SECOND_AGGRO = 3, // Second highest aggro
+ SMART_TARGET_HOSTILE_LAST_AGGRO = 4, // Dead last on aggro
+ SMART_TARGET_HOSTILE_RANDOM = 5, // Just any random target on our threat list
+ SMART_TARGET_HOSTILE_RANDOM_NOT_TOP = 6, // Any random target except top threat
+ SMART_TARGET_ACTION_INVOKER = 7, // Unit who caused this Event to occur
+ SMART_TARGET_POSITION = 8, // use xyz from event params
+ SMART_TARGET_CREATURE_RANGE = 9, // CreatureEntry(0any), minDist, maxDist
+ SMART_TARGET_CREATURE_GUID = 10, // guid, entry
+ SMART_TARGET_CREATURE_DISTANCE = 11, // CreatureEntry(0any), maxDist
+ SMART_TARGET_STORED = 12, // id, uses pre-stored target(list)
+ SMART_TARGET_GAMEOBJECT_RANGE = 13, // entry(0any), min, max
+ SMART_TARGET_GAMEOBJECT_GUID = 14, // guid, entry
+ SMART_TARGET_GAMEOBJECT_DISTANCE = 15, // entry(0any), maxDist
+ SMART_TARGET_INVOKER_PARTY = 16, // invoker's party members
+ SMART_TARGET_PLAYER_RANGE = 17, // min, max
+ SMART_TARGET_PLAYER_DISTANCE = 18, // maxDist
+ SMART_TARGET_CLOSEST_CREATURE = 19, // CreatureEntry(0any)
+ SMART_TARGET_CLOSEST_GAMEOBJECT = 20, // entry(0any)
+ SMART_TARGET_CLOSEST_PLAYER = 21, // none
+ SMART_TARGET_END = 22,
+};
+
+struct SmartTarget
+{
+ SmartTarget (SMARTAI_TARGETS t = SMART_TARGET_NONE, uint32 p1 = 0, uint32 p2 = 0, uint32 p3 = 0)
+ {
+ type = t;
+ raw.param1 = p1;
+ raw.param2 = p2;
+ raw.param3 = p3;
+ }
+ SMARTAI_TARGETS type;
+ float x,y,z,o;
+ union
+ {
+ struct
+ {
+ uint32 creature;
+ uint32 minDist;
+ uint32 maxDist;
+ } unitRange;
+
+ struct
+ {
+ uint32 guid;
+ uint32 entry;
+ } unitGUID;
+
+ struct
+ {
+ uint32 creature;
+ uint32 dist;
+ } unitDistance;
+
+ struct
+ {
+ uint32 dist;
+ } playerDistance;
+
+ struct
+ {
+ uint32 minDist;
+ uint32 maxDist;
+ } playerRange;
+
+ struct
+ {
+ uint32 id;
+ } stored;
+
+ struct
+ {
+ uint32 entry;
+ uint32 minDist;
+ uint32 maxDist;
+ } goRange;
+
+ struct
+ {
+ uint32 guid;
+ uint32 entry;
+ } goGUID;
+
+ struct
+ {
+ uint32 entry;
+ uint32 dist;
+ } goDistance;
+
+ struct
+ {
+ uint32 map;
+ } position;
+
+ struct
+ {
+ uint32 entry;
+ uint32 dist;
+ uint32 dead;
+ } closest;
+
+ struct
+ {
+ uint32 param1;
+ uint32 param2;
+ uint32 param3;
+ } raw;
+ };
+};
+
+enum eSmartAI
+{
+ SMART_EVENT_PARAM_COUNT = 4,
+ SMART_ACTION_PARAM_COUNT = 6,
+ SMART_SUMMON_COUNTER = 0xFFFFFF,
+ SMART_ESCORT_LAST_OOC_POINT = 0xFFFFFF,
+ SMART_RANDOM_POINT = 0xFFFFFE,
+ SMART_ESCORT_TARGETS = 0xFFFFFF
+};
+
+enum SmartScriptType
+{
+ SMART_SCRIPT_TYPE_CREATURE = 0,//done
+ SMART_SCRIPT_TYPE_GAMEOBJECT = 1,//done
+ SMART_SCRIPT_TYPE_AREATRIGGER = 2,//done
+ SMART_SCRIPT_TYPE_EVENT = 3,//
+ SMART_SCRIPT_TYPE_GOSSIP = 4,//
+ SMART_SCRIPT_TYPE_QUEST = 5,//
+ SMART_SCRIPT_TYPE_SPELL = 6,//
+ SMART_SCRIPT_TYPE_TRANSPORT = 7,//
+ SMART_SCRIPT_TYPE_INSTANCE = 8,//
+ SMART_SCRIPT_TYPE_MAX = 9
+};
+
+enum SmartAITypeMaskId
+{
+ SMART_SCRIPT_TYPE_MASK_CREATURE = 1,
+ SMART_SCRIPT_TYPE_MASK_GAMEOBJECT = 2,
+ SMART_SCRIPT_TYPE_MASK_AREATRIGGER = 4,
+ SMART_SCRIPT_TYPE_MASK_EVENT = 8,
+ SMART_SCRIPT_TYPE_MASK_GOSSIP = 16,
+ SMART_SCRIPT_TYPE_MASK_QUEST = 32,
+ SMART_SCRIPT_TYPE_MASK_SPELL = 64,
+ SMART_SCRIPT_TYPE_MASK_TRANSPORT = 128,
+ SMART_SCRIPT_TYPE_MASK_INSTANCE = 256,
+};
+
+const uint32 SmartAITypeMask[SMART_SCRIPT_TYPE_MAX][2] =
+{
+ {SMART_SCRIPT_TYPE_CREATURE, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_SCRIPT_TYPE_GAMEOBJECT, SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_SCRIPT_TYPE_AREATRIGGER, SMART_SCRIPT_TYPE_MASK_AREATRIGGER },
+ {SMART_SCRIPT_TYPE_EVENT, SMART_SCRIPT_TYPE_MASK_EVENT },
+ {SMART_SCRIPT_TYPE_GOSSIP, SMART_SCRIPT_TYPE_MASK_GOSSIP },
+ {SMART_SCRIPT_TYPE_QUEST, SMART_SCRIPT_TYPE_MASK_QUEST },
+ {SMART_SCRIPT_TYPE_SPELL, SMART_SCRIPT_TYPE_MASK_SPELL },
+ {SMART_SCRIPT_TYPE_TRANSPORT, SMART_SCRIPT_TYPE_MASK_TRANSPORT },
+ {SMART_SCRIPT_TYPE_INSTANCE, SMART_SCRIPT_TYPE_MASK_INSTANCE }
+};
+
+const uint32 SmartAIEventMask[SMART_EVENT_END][2] =
+{
+ {SMART_EVENT_UPDATE_IC, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_UPDATE_OOC, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT + SMART_SCRIPT_TYPE_MASK_INSTANCE },
+ {SMART_EVENT_HEALT_PCT, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_MANA_PCT, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_AGGRO, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_KILL, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_DEATH, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_EVADE, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_SPELLHIT, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_RANGE, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_OOC_LOS, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_RESPAWN, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_TARGET_HEALTH_PCT, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_TARGET_CASTING, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_FRIENDLY_HEALTH, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_FRIENDLY_IS_CC, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_FRIENDLY_MISSING_BUFF, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_SUMMONED_UNIT, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_TARGET_MANA_PCT, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_ACCEPTED_QUEST, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_REWARD_QUEST, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_REACHED_HOME, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_RECEIVE_EMOTE, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_HAS_AURA, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_TARGET_BUFFED, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_RESET, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_IC_LOS, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_PASSENGER_BOARDED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_PASSENGER_REMOVED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_CHARMED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_CHARMED_TARGET, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_SPELLHIT_TARGET, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_DAMAGED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_DAMAGED_TARGET, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_MOVEMENTINFORM, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_SUMMON_DESPAWNED, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_CORPSE_REMOVED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_AI_INIT, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_DATA_SET, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_WAYPOINT_START, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_WAYPOINT_REACHED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_TRANSPORT_ADDPLAYER, SMART_SCRIPT_TYPE_MASK_TRANSPORT },
+ {SMART_EVENT_TRANSPORT_ADDCREATURE, SMART_SCRIPT_TYPE_MASK_TRANSPORT },
+ {SMART_EVENT_TRANSPORT_REMOVE_PLAYER, SMART_SCRIPT_TYPE_MASK_TRANSPORT },
+ {SMART_EVENT_TRANSPORT_RELOCATE, SMART_SCRIPT_TYPE_MASK_TRANSPORT },
+ {SMART_EVENT_INSTANCE_PLAYER_ENTER, SMART_SCRIPT_TYPE_MASK_INSTANCE },
+ {SMART_EVENT_AREATRIGGER_ONTRIGGER, SMART_SCRIPT_TYPE_MASK_AREATRIGGER },
+ {SMART_EVENT_QUEST_ACCEPTED, SMART_SCRIPT_TYPE_MASK_QUEST },
+ {SMART_EVENT_QUEST_OBJ_COPLETETION, SMART_SCRIPT_TYPE_MASK_QUEST },
+ {SMART_EVENT_QUEST_REWARDED, SMART_SCRIPT_TYPE_MASK_QUEST },
+ {SMART_EVENT_QUEST_COMPLETION, SMART_SCRIPT_TYPE_MASK_QUEST },
+ {SMART_EVENT_QUEST_FAIL, SMART_SCRIPT_TYPE_MASK_QUEST },
+ {SMART_EVENT_TEXT_OVER, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_RECEIVE_HEAL, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_JUST_SUMMONED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_WAYPOINT_PAUSED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_WAYPOINT_RESUMED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_WAYPOINT_STOPPED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_WAYPOINT_ENDED, SMART_SCRIPT_TYPE_MASK_CREATURE },
+ {SMART_EVENT_TIMED_EVENT_TRIGGERED, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_UPDATE, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_LINK, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT + SMART_SCRIPT_TYPE_MASK_AREATRIGGER + SMART_SCRIPT_TYPE_MASK_EVENT + SMART_SCRIPT_TYPE_MASK_GOSSIP + SMART_SCRIPT_TYPE_MASK_QUEST + SMART_SCRIPT_TYPE_MASK_SPELL + SMART_SCRIPT_TYPE_MASK_TRANSPORT + SMART_SCRIPT_TYPE_MASK_INSTANCE },
+ {SMART_EVENT_GOSSIP_SELECT, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_JUST_CREATED, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+ {SMART_EVENT_GOSSIP_HELLO, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
+
+};
+
+enum SmartEventFlags
+{
+ SMART_EVENT_FLAG_NOT_REPEATABLE = 0x01, //Event can not repeat
+ SMART_EVENT_FLAG_DIFFICULTY_0 = 0x02, //Event only occurs in instance difficulty 0
+ SMART_EVENT_FLAG_DIFFICULTY_1 = 0x04, //Event only occurs in instance difficulty 1
+ SMART_EVENT_FLAG_DIFFICULTY_2 = 0x08, //Event only occurs in instance difficulty 2
+ SMART_EVENT_FLAG_DIFFICULTY_3 = 0x10, //Event only occurs in instance difficulty 3
+ SMART_EVENT_FLAG_RESERVED_5 = 0x20,
+ SMART_EVENT_FLAG_RESERVED_6 = 0x40,
+ SMART_EVENT_FLAG_DEBUG_ONLY = 0x80, //Event only occurs in debug build
+
+ SMART_EVENT_FLAG_DIFFICULTY_ALL = (SMART_EVENT_FLAG_DIFFICULTY_0|SMART_EVENT_FLAG_DIFFICULTY_1|SMART_EVENT_FLAG_DIFFICULTY_2|SMART_EVENT_FLAG_DIFFICULTY_3)
+};
+
+enum SmartCastFlags
+{
+ SMARTCAST_INTERRUPT_PREVIOUS = 0x01, //Interrupt any spell casting
+ SMARTCAST_TRIGGERED = 0x02, //Triggered (this makes spell cost zero mana and have no cast time)
+ //CAST_FORCE_CAST = 0x04, //Forces cast even if creature is out of mana or out of range
+ //CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range
+ //CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself
+ //CAST_AURA_NOT_PRESENT = 0x20, //Only casts the spell if the target does not have an aura from the spell
+};
+
+// one line in DB is one event
+struct SmartScriptHolder
+{
+ SmartScriptHolder()
+ {
+ timer = 0;
+ active = false;
+ runOnce = false;
+ link = 0;
+ entryOrGuid = 0;
+ link = 0;
+ event_id = 0;
+ }
+ int32 entryOrGuid;
+ SmartScriptType source_type;
+ uint32 event_id;
+ uint32 link;
+
+ SmartEvent event;
+ SmartAction action;
+ SmartTarget target;
+
+ public:
+ uint32 GetScriptType() { return (uint32)source_type; }
+ uint32 GetEventType() { return (uint32)event.type; }
+ uint32 GetActionType() { return (uint32)action.type; }
+ uint32 GetTargetType() { return (uint32)target.type; }
+
+ uint32 timer;
+ bool active;
+ bool runOnce;
+
+
+};
+
+typedef UNORDERED_MAP<uint32, WayPoint*> WPPath;
+
+typedef std::list<WorldObject*> ObjectList;
+typedef UNORDERED_MAP<uint32, ObjectList*> ObjectListMap;
+
+class SmartWaypointMgr
+{
+ friend class ACE_Singleton<SmartWaypointMgr, ACE_Null_Mutex>;
+ SmartWaypointMgr(){};
+ public:
+ ~SmartWaypointMgr(){};
+
+ void LoadFromDB();
+
+ WPPath* GetPath(uint32 id)
+ {
+ if (waypoint_map.find(id) != waypoint_map.end())
+ return waypoint_map[id];
+ else return 0;
+ }
+
+ private:
+ UNORDERED_MAP<uint32, WPPath*> waypoint_map;
+};
+
+// all events for a single entry
+typedef std::vector<SmartScriptHolder> SmartAIEventList;
+
+// all events for all entries / guids
+typedef UNORDERED_MAP<int32, SmartAIEventList> SmartAIEventMap;
+
+class SmartAIMgr
+{
+ friend class ACE_Singleton<SmartAIMgr, ACE_Null_Mutex>;
+ SmartAIMgr(){};
+ public:
+ ~SmartAIMgr(){};
+
+ void LoadSmartAIFromDB();
+
+ SmartAIEventList GetScript(int32 entry, SmartScriptType type)
+ {
+ SmartAIEventList temp;
+ if (mEventMap[uint32(type)].find(entry) != mEventMap[uint32(type)].end())
+ return mEventMap[uint32(type)][entry];
+ else
+ {
+ if(entry > 0)//first search is for guid (negative), do not drop error if not found
+ sLog.outError("SmartAIMgr::GetScript: Could not load Script for Entry %d AIType %u.", entry, uint32(type));
+ return temp;
+ }
+ }
+
+ private:
+ //event stores
+ SmartAIEventMap mEventMap[SMART_SCRIPT_TYPE_MAX];
+
+ bool IsEventValid(SmartScriptHolder e);
+ bool IsTargetValid(SmartScriptHolder e);
+ /*inline bool IsTargetValid(SmartScriptHolder e, int32 target)
+ {
+ if (target < SMART_TARGET_NONE || target >= SMART_TARGET_END)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid Target type %d, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), target);
+ return false;
+ }
+ return true;
+ }*/
+ inline bool IsMinMaxValid(SmartScriptHolder e, uint32 min, uint32 max)
+ {
+ if (max < min)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses min/max params wrong (%u/%u), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), min, max);
+ return false;
+ }
+ return true;
+ }
+ /*inline bool IsPercentValid(SmartScriptHolder e, int32 pct)
+ {
+ if (pct < -100 || pct > 100)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u has invalid Percent set (%d), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), pct);
+ return false;
+ }
+ return true;
+ }*/
+ inline bool NotNULL(SmartScriptHolder e, uint32 data)
+ {
+ if (!data)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u Parameter can not be NULL, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+ return true;
+ }
+ inline bool IsCreatureValid(SmartScriptHolder e, uint32 entry)
+ {
+ if (!sCreatureStorage.LookupEntry<CreatureInfo>(entry))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Creature entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
+ return false;
+ }
+ return true;
+ }
+ inline bool IsQuestValid(SmartScriptHolder e, uint32 entry)
+ {
+ if (!sObjectMgr.GetQuestTemplate(entry))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Quest entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
+ return false;
+ }
+ return true;
+ }
+ inline bool IsGameObjectValid(SmartScriptHolder e, uint32 entry)
+ {
+ if (!sGOStorage.LookupEntry<GameObjectInfo>(uint32(entry)))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent GameObject entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
+ return false;
+ }
+ return true;
+ }
+ inline bool IsSpellValid(SmartScriptHolder e, uint32 entry)
+ {
+ if (!sSpellStore.LookupEntry(entry))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Spell entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
+ return false;
+ }
+ return true;
+ }
+ inline bool IsItemValid(SmartScriptHolder e, uint32 entry)
+ {
+ if (!sItemStore.LookupEntry(entry))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Item entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
+ return false;
+ }
+ return true;
+ }
+ inline bool IsConditionValid(SmartScriptHolder e, uint32 t, uint32 v1, uint32 v2, uint32 v3)
+ {
+ bool error = false;
+ if (t > 0 && v1 >= 0 && v2 >= 0 && v3 >= 0)
+ {
+ Condition cond;
+ cond.mConditionType = ConditionType(t);
+ cond.mConditionValue1 = v1;
+ cond.mConditionValue2 = v2;
+ cond.mConditionValue3 = v3;
+ if (!sConditionMgr.isConditionTypeValid(&cond))
+ error = true;
+ }
+ if (error)
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid Condition, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
+ return false;
+ }
+ return true;
+ }
+ inline bool IsEmoteValid(SmartScriptHolder e, uint32 entry)
+ {
+ if (!sEmotesTextStore.LookupEntry(entry))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Emote entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
+ return false;
+ }
+ return true;
+ }
+ inline bool IsAreaTriggerValid(SmartScriptHolder e, uint32 entry)
+ {
+ if (!sAreaTriggerStore.LookupEntry(entry))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent AreaTrigger entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
+ return false;
+ }
+ return true;
+ }
+ inline bool IsSoundValid(SmartScriptHolder e, uint32 entry)
+ {
+ if (!sSoundEntriesStore.LookupEntry(entry))
+ {
+ sLog.outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Sound entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
+ return false;
+ }
+ return true;
+ }
+ bool IsTextValid(SmartScriptHolder e, uint32 id);
+};
+
+#define sSmartScriptMgr (*ACE_Singleton<SmartAIMgr, ACE_Null_Mutex>::instance())
+#define sSmartWaypointMgr (*ACE_Singleton<SmartWaypointMgr, ACE_Null_Mutex>::instance())
+#endif
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index fc5a6819808..323a3ac9404 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -130,7 +130,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/AI/CoreAI
${CMAKE_CURRENT_SOURCE_DIR}/AI/EventAI
${CMAKE_CURRENT_SOURCE_DIR}/AI/ScriptedAI
- ${CMAKE_CURRENT_SOURCE_DIR}/AI/SmartAI
+ ${CMAKE_CURRENT_SOURCE_DIR}/AI/SmartScripts
${CMAKE_CURRENT_SOURCE_DIR}/AuctionHouse
${CMAKE_CURRENT_SOURCE_DIR}/Battlegrounds
${CMAKE_CURRENT_SOURCE_DIR}/Battlegrounds/Zones
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index a84a17e2fd6..3439b123b2f 100755
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -527,6 +527,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "reserved_name", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadReservedNameCommand, "", NULL },
{ "reputation_reward_rate", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadReputationRewardRateCommand, "", NULL },
{ "reputation_spillover_template",SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadReputationRewardRateCommand, "", NULL },
+ { "smartai_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSmartAI, "", NULL },
{ "skill_discovery_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillDiscoveryTemplateCommand, "", NULL },
{ "skill_extra_item_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillExtraItemTemplateCommand, "", NULL },
{ "skill_fishing_base_level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSkillFishingBaseLevelCommand, "", NULL },
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index 370dc55bdce..b3f20a0c0ae 100755
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -450,6 +450,7 @@ class ChatHandler
bool HandleReloadWpScriptsCommand(const char* args);
bool HandleReloadConditions(const char* args);
bool HandleReloadCreatureText(const char* args);
+ bool HandleReloadSmartAI(const char* args);
bool HandleResetAchievementsCommand(const char * args);
bool HandleResetAllCommand(const char * args);
diff --git a/src/server/game/Chat/Commands/Level3.cpp b/src/server/game/Chat/Commands/Level3.cpp
index 3f10d91fc0a..8453e0f67c6 100755
--- a/src/server/game/Chat/Commands/Level3.cpp
+++ b/src/server/game/Chat/Commands/Level3.cpp
@@ -59,6 +59,7 @@
#include "ScriptMgr.h"
#include "LFGMgr.h"
#include "CreatureTextMgr.h"
+#include "SmartAI.h"
//reload commands
bool ChatHandler::HandleReloadAllCommand(const char*)
@@ -1177,6 +1178,14 @@ bool ChatHandler::HandleReloadCreatureText(const char* /*args*/)
return true;
}
+bool ChatHandler::HandleReloadSmartAI(const char* /*args*/)
+{
+ sLog.outString("Re-Loading SmartAI Scripts...");
+ sSmartScriptMgr.LoadSmartAIFromDB();
+ SendGlobalGMSysMessage("SmartAI Scripts reloaded.");
+ return true;
+}
+
bool ChatHandler::HandleAccountSetGmLevelCommand(const char *args)
{
if (!*args)
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index 6d4b4431060..0c26428c8d8 100755
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -41,6 +41,8 @@ void AddSC_quest_spell_scripts();
void AddSC_item_spell_scripts();
void AddSC_example_spell_scripts();
+void AddSC_SmartSCripts();
+
#ifdef SCRIPTS
//world
void AddSC_areatrigger_scripts();
@@ -565,6 +567,7 @@ void AddScripts()
{
AddExampleScripts();
AddSpellScripts();
+ AddSC_SmartSCripts();
#ifdef SCRIPTS
AddWorldScripts();
AddEasternKingdomsScripts();
diff --git a/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp b/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp
index fcebf186f94..41cfdb2317f 100755
--- a/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp
+++ b/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp
@@ -48,6 +48,7 @@
#include "MapManager.h"
#include "InstanceScript.h"
#include "LFGMgr.h"
+#include "GameObjectAI.h"
void WorldSession::HandleRepopRequestOpcode(WorldPacket & recv_data)
{
@@ -144,7 +145,10 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket & recv_data)
unit->AI()->sGossipSelectCode(_player, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId), code.c_str());
}
else
+ {
sScriptMgr.OnGossipSelectCode(_player, go, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId), code.c_str());
+ go->AI()->GossipSelectCode(_player, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId), code.c_str());
+ }
}
else
{
@@ -156,7 +160,10 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket & recv_data)
unit->AI()->sGossipSelect(_player, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId));
}
else
+ {
sScriptMgr.OnGossipSelect(_player, go, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId));
+ go->AI()->GossipSelect(_player, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId));
+ }
}
}
diff --git a/src/server/game/Server/Protocol/Handlers/QuestHandler.cpp b/src/server/game/Server/Protocol/Handlers/QuestHandler.cpp
index 81b5b91ef94..98ecd8ad526 100755
--- a/src/server/game/Server/Protocol/Handlers/QuestHandler.cpp
+++ b/src/server/game/Server/Protocol/Handlers/QuestHandler.cpp
@@ -34,6 +34,7 @@
#include "ConditionMgr.h"
#include "Creature.h"
#include "CreatureAI.h"
+#include "GameObjectAI.h"
void WorldSession::HandleQuestgiverStatusQueryOpcode(WorldPacket & recv_data)
{
@@ -217,6 +218,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPacket & recv_data)
}
case TYPEID_GAMEOBJECT:
sScriptMgr.OnQuestAccept(_player, ((GameObject*)pObject), qInfo);
+ (pObject->ToGameObject())->AI()->QuestAccept(_player, qInfo);
break;
default:
break;
@@ -331,6 +333,7 @@ void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPacket & recv_data)
// Send next quest
if (Quest const* nextquest = _player->GetNextQuest(guid ,pQuest))
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextquest,guid,true);
+ pObject->ToGameObject()->AI()->QuestReward(_player, pQuest, reward);
}
break;
default:
diff --git a/src/server/game/Server/Protocol/Handlers/SpellHandler.cpp b/src/server/game/Server/Protocol/Handlers/SpellHandler.cpp
index 4f0c6a9ed44..23f4ac97968 100755
--- a/src/server/game/Server/Protocol/Handlers/SpellHandler.cpp
+++ b/src/server/game/Server/Protocol/Handlers/SpellHandler.cpp
@@ -30,6 +30,7 @@
#include "SpellAuras.h"
#include "CreatureAI.h"
#include "ScriptMgr.h"
+#include "GameObjectAI.h"
void WorldSession::HandleClientCastFlags(WorldPacket& recvPacket, uint8 castFlags, SpellCastTargets & targets)
{
@@ -287,6 +288,8 @@ void WorldSession::HandleGameObjectUseOpcode(WorldPacket & recv_data)
if (sScriptMgr.OnGossipHello(_player, obj))
return;
+ obj->AI()->GossipHello(_player);
+
obj->Use(_player);
}
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index a36f639c1c9..e9979038618 100755
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -60,6 +60,7 @@
#include "Formulas.h"
#include "Vehicle.h"
#include "ScriptMgr.h"
+#include "GameObjectAI.h"
pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
{
@@ -2649,6 +2650,8 @@ void Spell::SendLoot(uint64 guid, LootType loottype)
if (sScriptMgr.OnGossipHello(player, gameObjTarget))
return;
+ gameObjTarget->AI()->GossipHello(player);
+
switch (gameObjTarget->GetGoType())
{
case GAMEOBJECT_TYPE_DOOR:
diff --git a/src/server/game/Texts/CreatureTextMgr.cpp b/src/server/game/Texts/CreatureTextMgr.cpp
index d3f61d28da1..a23be060a49 100755
--- a/src/server/game/Texts/CreatureTextMgr.cpp
+++ b/src/server/game/Texts/CreatureTextMgr.cpp
@@ -419,14 +419,14 @@ bool CreatureTextMgr::TextExist(uint32 sourceEntry, uint8 textGroup)
CreatureTextMap::const_iterator sList = mTextMap.find(sourceEntry);
if (sList == mTextMap.end())
{
- sLog.outErrorDb("CreatureTextMgr::TextExist: Could not find Text for Creature (entry %u) in 'creature_text' table.", sourceEntry);
+ sLog.outDebug("CreatureTextMgr::TextExist: Could not find Text for Creature (entry %u) in 'creature_text' table.", sourceEntry);
return false;
}
CreatureTextHolder TextHolder = (*sList).second;
CreatureTextHolder::const_iterator itr = TextHolder.find(textGroup);
if (itr == TextHolder.end())
{
- sLog.outErrorDb("CreatureTextMgr::TextExist: Could not find TextGroup %u for Creature (entry %u).",uint32(textGroup), sourceEntry);
+ sLog.outDebug("CreatureTextMgr::TextExist: Could not find TextGroup %u for Creature (entry %u).",uint32(textGroup), sourceEntry);
return false;
}
return true;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index efc2690a6e8..2c7790de35c 100755
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -70,6 +70,7 @@
#include "ScriptMgr.h"
#include "WeatherMgr.h"
#include "CreatureTextMgr.h"
+#include "SmartAI.h"
volatile bool World::m_stopEvent = false;
uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE;
@@ -1571,6 +1572,9 @@ void World::SetInitialWorldSettings()
sLog.outString("Loading Waypoints...");
sWaypointMgr->Load();
+ sLog.outString("Loading SmartAI Waypoints...");
+ sSmartWaypointMgr.LoadFromDB();
+
sLog.outString("Loading Creature Formations...");
formation_mgr.LoadCreatureFormations();
@@ -1641,6 +1645,9 @@ void World::SetInitialWorldSettings()
sLog.outString("Validating spell scripts...");
sObjectMgr.ValidateSpellScripts();
+ sLog.outString("Loading SmartAI scripts...");
+ sSmartScriptMgr.LoadSmartAIFromDB();
+
///- Initialize game time and timers
sLog.outDebug("DEBUG:: Initialize game time and timers");
m_gameTime = time(NULL);
diff --git a/src/server/shared/Database/Implementation/WorldDatabase.cpp b/src/server/shared/Database/Implementation/WorldDatabase.cpp
index b24e674c39c..fba8dfa0428 100755
--- a/src/server/shared/Database/Implementation/WorldDatabase.cpp
+++ b/src/server/shared/Database/Implementation/WorldDatabase.cpp
@@ -36,6 +36,8 @@ bool WorldDatabaseConnection::Open()
PrepareStatement(WORLD_REP_CRELINKED_RESPAWN, "REPLACE INTO creature_linked_respawn (guid,linkedGuid) VALUES (?, ?)");
PrepareStatement(WORLD_DEL_GAMEOBJECT_RESPAWN_TIMES, "DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
PrepareStatement(WORLD_LOAD_CRETEXT, "SELECT entry, groupid, id, text, type, language, probability, emote, duration, sound FROM creature_text");
+ PrepareStatement(WORLD_LOAD_SMART_SCRIPTS, "SELECT entryorguid, source_type, id, link, event_type, event_phase_mask, event_chance, event_flags, event_param1, event_param2, event_param3, event_param4, action_type, action_param1, action_param2, action_param3, action_param4, action_param5, action_param6, target_type, target_param1, target_param2, target_param3, target_x, target_y, target_z, target_o FROM smart_scripts");
+ PrepareStatement(WORLD_LOAD_SMARTAI_WP, "SELECT entry, pointid, position_x, position_y, position_z FROM waypoints ORDER BY entry, pointid");
return true;
}
diff --git a/src/server/shared/Database/Implementation/WorldDatabase.h b/src/server/shared/Database/Implementation/WorldDatabase.h
index a8d4550d20e..1995b8e6301 100755
--- a/src/server/shared/Database/Implementation/WorldDatabase.h
+++ b/src/server/shared/Database/Implementation/WorldDatabase.h
@@ -49,6 +49,8 @@ enum WorldDatabaseStatements
WORLD_REP_CRELINKED_RESPAWN,
WORLD_DEL_GAMEOBJECT_RESPAWN_TIMES,
WORLD_LOAD_CRETEXT,
+ WORLD_LOAD_SMART_SCRIPTS,
+ WORLD_LOAD_SMARTAI_WP,
MAX_WORLDDATABASE_STATEMENTS,
};
diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index 006338e8114..7f8603eede1 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -73,6 +73,7 @@ include_directories(
${CMAKE_SOURCE_DIR}/src/server/game/AI/CoreAI
${CMAKE_SOURCE_DIR}/src/server/game/AI/EventAI
${CMAKE_SOURCE_DIR}/src/server/game/AI/ScriptedAI
+ ${CMAKE_SOURCE_DIR}/src/server/game/AI/SmartScripts
${CMAKE_SOURCE_DIR}/src/server/game/AuctionHouse
${CMAKE_SOURCE_DIR}/src/server/game/AuctionHouse/AuctionHouseBot
${CMAKE_SOURCE_DIR}/src/server/game/Battlegrounds