diff options
Diffstat (limited to 'src')
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 |