Core/AI: implemented SmartScripts System (still beta) not 100% complete

WARNING: Use scripts at own risk. You were warned.
NOTE0: creature, gameobject, areatrigger type scripts should be fully functional
NOTE1: has no effect on any core related stuff if not using any SmartScript
NOTE2: all event/action/etc descriptions can be found in SmartScriptMgr.h

SmartScripts is a reloadable DB-Sript system, with full control for special scripting,
like escorting, following, complex combat handling, pre-stored AI templates(caster, turret, etc) and much more
with a total of 66 events, 78 actions, 22 target types, and can be easily extended

--HG--
branch : trunk
This commit is contained in:
Rat
2010-10-27 21:01:47 +02:00
parent c9fba03da9
commit 5cb119e617
26 changed files with 5496 additions and 560 deletions

View File

@@ -0,0 +1,32 @@
DROP TABLE IF EXISTS smart_scripts;
CREATE TABLE `smart_scripts` (
`entryorguid` mediumint(11) NOT NULL,
`source_type` mediumint(5) unsigned NOT NULL DEFAULT '0',
`id` mediumint(11) unsigned NOT NULL DEFAULT '0',
`link` mediumint(11) unsigned NOT NULL DEFAULT '0',
`event_type` mediumint(5) unsigned NOT NULL DEFAULT '0',
`event_phase_mask` mediumint(11) unsigned NOT NULL DEFAULT '0',
`event_chance` mediumint(5) unsigned NOT NULL DEFAULT '100',
`event_flags` mediumint(11) unsigned NOT NULL DEFAULT '0',
`event_param1` mediumint(11) unsigned NOT NULL DEFAULT '0',
`event_param2` mediumint(11) unsigned NOT NULL DEFAULT '0',
`event_param3` mediumint(11) unsigned NOT NULL DEFAULT '0',
`event_param4` mediumint(11) unsigned NOT NULL DEFAULT '0',
`action_type` mediumint(5) unsigned NOT NULL DEFAULT '0',
`action_param1` mediumint(11) unsigned NOT NULL DEFAULT '0',
`action_param2` mediumint(11) unsigned NOT NULL DEFAULT '0',
`action_param3` mediumint(11) unsigned NOT NULL DEFAULT '0',
`action_param4` mediumint(11) unsigned NOT NULL DEFAULT '0',
`action_param5` mediumint(11) unsigned NOT NULL DEFAULT '0',
`action_param6` mediumint(11) unsigned NOT NULL DEFAULT '0',
`target_type` mediumint(11) unsigned NOT NULL DEFAULT '0',
`target_param1` mediumint(11) unsigned NOT NULL DEFAULT '0',
`target_param2` mediumint(11) unsigned NOT NULL DEFAULT '0',
`target_param3` mediumint(11) unsigned NOT NULL DEFAULT '0',
`target_x` float NOT NULL DEFAULT '0',
`target_y` float NOT NULL DEFAULT '0',
`target_z` float NOT NULL DEFAULT '0',
`target_o` float NOT NULL DEFAULT '0',
`comment` varchar(255) NOT NULL DEFAULT '' COMMENT 'Event Comment',
PRIMARY KEY (`entryorguid`,`source_type`,`id`,`link`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;

View File

@@ -0,0 +1,10 @@
DROP TABLE IF EXISTS waypoints;
CREATE TABLE `waypoints` (
`entry` mediumint(8) unsigned NOT NULL DEFAULT '0',
`pointid` mediumint(8) unsigned NOT NULL DEFAULT '0',
`position_x` float NOT NULL DEFAULT '0',
`position_y` float NOT NULL DEFAULT '0',
`position_z` float NOT NULL DEFAULT '0',
`point_comment` text,
PRIMARY KEY (`entry`,`pointid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Creature waypoints';

View File

@@ -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

View File

@@ -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();

View File

@@ -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*/)
{
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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 },

View File

@@ -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);

View File

@@ -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)

View File

@@ -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();

View File

@@ -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));
}
}
}

View File

@@ -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:

View File

@@ -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);
}

View File

@@ -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:

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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,
};

View File

@@ -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