2010-10-27 21:01:47 +02:00
/*
2020-01-02 06:44:10 +01:00
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
2010-10-27 21:01:47 +02:00
*
* 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 "SmartAI.h"
2021-02-05 18:12:55 +01:00
#include "AreaTrigger.h"
2017-05-18 23:52:58 +02:00
#include "Creature.h"
2017-08-12 01:40:25 +02:00
#include "CreatureGroups.h"
#include "DB2Structure.h"
2017-05-18 23:52:58 +02:00
#include "GameObject.h"
#include "Group.h"
#include "Log.h"
2017-06-04 01:00:45 +02:00
#include "MotionMaster.h"
2017-05-18 23:52:58 +02:00
#include "ObjectAccessor.h"
#include "PetDefines.h"
#include "Player.h"
2012-07-29 21:40:10 +02:00
#include "ScriptMgr.h"
2017-02-17 21:33:18 +01:00
#include "Vehicle.h"
2010-10-27 21:01:47 +02:00
2019-05-15 01:33:55 +02:00
SmartAI :: SmartAI ( Creature * creature , uint32 scriptId ) : CreatureAI ( creature , scriptId ), _charmed ( false ), _followCreditType ( 0 ), _followArrivedTimer ( 0 ), _followCredit ( 0 ), _followArrivedEntry ( 0 ), _followDistance ( 0.f ), _followAngle ( 0.f ),
2017-08-12 01:40:25 +02:00
_escortState ( SMART_ESCORT_NONE ), _escortNPCFlags ( 0 ), _escortInvokerCheckTimer ( 1000 ), _currentWaypointNode ( 0 ), _waypointReached ( false ), _waypointPauseTimer ( 0 ), _waypointPauseForced ( false ), _repeatWaypointPath ( false ),
2019-05-15 01:33:55 +02:00
_OOCReached ( false ), _waypointPathEnded ( false ), _run ( true ), _evadeDisabled ( false ), _canAutoAttack ( true ), _canCombatMove ( true ), _invincibilityHPLevel ( 0 ), _despawnTime ( 0 ), _despawnState ( 0 ), _vehicleConditionsTimer ( 0 ),
_gossipReturn ( false ), _escortQuestId ( 0 )
2010-10-27 21:01:47 +02:00
{
2019-05-15 01:33:55 +02:00
_vehicleConditions = sConditionMgr -> HasConditionsForNotGroupedEntry ( CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE , creature -> GetEntry ());
2010-10-27 21:01:47 +02:00
}
2016-09-02 23:14:50 +02:00
bool SmartAI :: IsAIControlled () const
{
2019-05-15 01:33:55 +02:00
return ! _charmed ;
2016-09-02 23:14:50 +02:00
}
2017-08-12 01:40:25 +02:00
void SmartAI :: StartPath ( bool run /* = false*/ , uint32 pathId /* = 0*/ , bool repeat /* = false*/ , Unit * invoker /* = nullptr*/ , uint32 nodeId /* = 1*/ )
2016-11-25 00:31:10 +01:00
{
2010-10-27 21:01:47 +02:00
if ( HasEscortState ( SMART_ESCORT_ESCORTING ))
StopPath ();
2017-02-17 21:33:18 +01:00
2017-08-12 01:40:25 +02:00
SetRun ( run );
if ( pathId )
2017-02-17 21:33:18 +01:00
{
2017-08-12 01:40:25 +02:00
if ( ! LoadPath ( pathId ))
2010-10-27 21:01:47 +02:00
return ;
2017-02-17 21:33:18 +01:00
}
2017-08-12 01:40:25 +02:00
if ( _path . nodes . empty ())
2010-10-27 21:01:47 +02:00
return ;
2017-08-12 01:40:25 +02:00
_currentWaypointNode = nodeId ;
_waypointPathEnded = false ;
2017-02-17 21:33:18 +01:00
2017-08-12 01:40:25 +02:00
_repeatWaypointPath = repeat ;
2017-02-17 21:33:18 +01:00
2017-08-12 01:40:25 +02:00
// Do not use AddEscortState, removing everything from previous
_escortState = SMART_ESCORT_ESCORTING ;
2017-02-17 21:33:18 +01:00
2017-08-12 01:40:25 +02:00
if ( invoker && invoker -> GetTypeId () == TYPEID_PLAYER )
{
_escortNPCFlags = me -> m_unitData -> NpcFlags [ 0 ];
me -> SetNpcFlags (( NPCFlags ) 0 );
2010-10-27 21:01:47 +02:00
}
2017-08-12 01:40:25 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_START , nullptr , _currentWaypointNode , GetScript () -> GetPathId ());
me -> GetMotionMaster () -> MovePath ( _path , _repeatWaypointPath );
2010-10-27 21:01:47 +02:00
}
bool SmartAI :: LoadPath ( uint32 entry )
{
if ( HasEscortState ( SMART_ESCORT_ESCORTING ))
return false ;
2017-08-12 01:40:25 +02:00
WaypointPath const * path = sSmartWaypointMgr -> GetPath ( entry );
if ( ! path || path -> nodes . empty ())
2010-10-27 21:01:47 +02:00
{
GetScript () -> SetPathId ( 0 );
return false ;
}
2017-08-12 01:40:25 +02:00
_path . id = path -> id ;
_path . nodes = path -> nodes ;
for ( WaypointNode & waypoint : _path . nodes )
{
Trinity :: NormalizeMapCoord ( waypoint . x );
Trinity :: NormalizeMapCoord ( waypoint . y );
2019-05-15 01:33:55 +02:00
waypoint . moveType = _run ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK ;
2017-08-12 01:40:25 +02:00
}
2010-10-27 21:01:47 +02:00
GetScript () -> SetPathId ( entry );
return true ;
}
void SmartAI :: PausePath ( uint32 delay , bool forced )
{
2017-11-06 22:19:23 +01:00
if ( ! HasEscortState ( SMART_ESCORT_ESCORTING ))
{
2018-06-03 10:06:57 -07:00
me -> PauseMovement ( delay , MOTION_SLOT_DEFAULT , forced );
2017-11-06 22:19:23 +01:00
if ( me -> GetMotionMaster () -> GetCurrentMovementGeneratorType () == WAYPOINT_MOTION_TYPE )
{
std :: pair < uint32 , uint32 > waypointInfo = me -> GetCurrentWaypointInfo ();
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_PAUSED , nullptr , waypointInfo . first , waypointInfo . second );
}
return ;
}
2010-10-27 21:01:47 +02:00
if ( HasEscortState ( SMART_ESCORT_PAUSED ))
{
2019-05-15 19:23:28 +02:00
TC_LOG_ERROR ( "scripts.ai.sai" , "SmartAI::PausePath: Creature wanted to pause waypoint (current waypoint: %u) movement while already paused, ignoring. (%s)" , _currentWaypointNode , me -> GetGUID (). ToString (). c_str ());
2010-10-27 21:01:47 +02:00
return ;
}
2017-08-12 01:40:25 +02:00
_waypointPauseTimer = delay ;
2016-11-25 00:31:10 +01:00
if ( forced )
2010-10-27 21:01:47 +02:00
{
2017-08-12 01:40:25 +02:00
_waypointPauseForced = forced ;
2019-05-15 01:33:55 +02:00
SetRun ( _run );
2017-08-12 01:40:25 +02:00
me -> PauseMovement ();
2017-08-12 17:35:46 +02:00
me -> SetHomePosition ( me -> GetPosition ());
2010-10-27 21:01:47 +02:00
}
2017-08-12 01:40:25 +02:00
else
_waypointReached = false ;
AddEscortState ( SMART_ESCORT_PAUSED );
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_PAUSED , nullptr , _currentWaypointNode , GetScript () -> GetPathId ());
2010-10-27 21:01:47 +02:00
}
void SmartAI :: StopPath ( uint32 DespawnTime , uint32 quest , bool fail )
{
2017-11-06 22:19:23 +01:00
if ( ! HasEscortState ( SMART_ESCORT_ESCORTING ))
{
std :: pair < uint32 , uint32 > waypointInfo = { 0 , 0 };
if ( me -> GetMotionMaster () -> GetCurrentMovementGeneratorType () == WAYPOINT_MOTION_TYPE )
waypointInfo = me -> GetCurrentWaypointInfo ();
2019-05-15 01:33:55 +02:00
if ( _despawnState != 2 )
2017-11-06 22:19:23 +01:00
SetDespawnTime ( DespawnTime );
me -> GetMotionMaster () -> MoveIdle ();
if ( waypointInfo . first )
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_STOPPED , nullptr , waypointInfo . first , waypointInfo . second );
if ( ! fail )
{
if ( waypointInfo . first )
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_ENDED , nullptr , waypointInfo . first , waypointInfo . second );
2019-05-15 01:33:55 +02:00
if ( _despawnState == 1 )
2017-11-06 22:19:23 +01:00
StartDespawn ();
}
return ;
}
2010-10-27 21:01:47 +02:00
if ( quest )
2019-05-15 01:33:55 +02:00
_escortQuestId = quest ;
2010-10-27 21:01:47 +02:00
2019-05-15 01:33:55 +02:00
if ( _despawnState != 2 )
2017-08-12 01:40:25 +02:00
SetDespawnTime ( DespawnTime );
2010-10-27 21:01:47 +02:00
me -> GetMotionMaster () -> MoveIdle ();
2017-08-12 01:40:25 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_STOPPED , nullptr , _currentWaypointNode , GetScript () -> GetPathId ());
2010-10-27 21:01:47 +02:00
EndPath ( fail );
}
void SmartAI :: EndPath ( bool fail )
{
RemoveEscortState ( SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING );
2017-08-12 01:40:25 +02:00
_path . nodes . clear ();
_waypointPauseTimer = 0 ;
2010-12-13 22:37:56 +01:00
2017-08-12 01:40:25 +02:00
if ( _escortNPCFlags )
2017-02-17 21:33:18 +01:00
{
2017-08-12 01:40:25 +02:00
me -> SetNpcFlags (( NPCFlags ) _escortNPCFlags );
_escortNPCFlags = 0 ;
2017-02-17 21:33:18 +01:00
}
2017-06-04 16:43:36 -03:00
ObjectVector const * targets = GetScript () -> GetStoredTargetVector ( SMART_ESCORT_TARGETS , * me );
2019-05-15 01:33:55 +02:00
if ( targets && _escortQuestId )
2010-10-27 21:01:47 +02:00
{
if ( targets -> size () == 1 && GetScript () -> IsPlayer (( * targets -> begin ())))
{
2017-06-03 10:21:06 +02:00
Player * player = targets -> front () -> ToPlayer ();
2015-09-28 17:20:27 +02:00
if ( ! fail && player -> IsAtGroupRewardDistance ( me ) && ! player -> HasCorpse ())
2019-05-15 01:33:55 +02:00
player -> GroupEventHappens ( _escortQuestId , me );
2010-10-27 21:01:47 +02:00
2016-10-15 19:08:21 +02:00
if ( fail )
2019-05-15 01:33:55 +02:00
player -> FailQuest ( _escortQuestId );
2010-10-27 21:01:47 +02:00
2011-08-20 20:31:31 -03:00
if ( Group * group = player -> GetGroup ())
2010-10-27 21:01:47 +02:00
{
2016-08-05 12:41:56 +02:00
for ( GroupReference * groupRef = group -> GetFirstMember (); groupRef != nullptr ; groupRef = groupRef -> next ())
2010-10-27 21:01:47 +02:00
{
2013-06-11 21:25:12 -02:30
Player * groupGuy = groupRef -> GetSource ();
2017-02-04 22:37:16 +01:00
if ( ! groupGuy -> IsInMap ( player ))
continue ;
2010-10-27 21:01:47 +02:00
2015-09-28 17:20:27 +02:00
if ( ! fail && groupGuy -> IsAtGroupRewardDistance ( me ) && ! groupGuy -> HasCorpse ())
2019-05-15 01:33:55 +02:00
groupGuy -> AreaExploredOrEventHappens ( _escortQuestId );
2016-10-15 19:08:21 +02:00
else if ( fail )
2019-05-15 01:33:55 +02:00
groupGuy -> FailQuest ( _escortQuestId );
2010-10-27 21:01:47 +02:00
}
}
2016-08-05 12:41:56 +02:00
}
else
2010-10-27 21:01:47 +02:00
{
2017-06-03 10:21:06 +02:00
for ( WorldObject * target : * targets )
2010-10-27 21:01:47 +02:00
{
2017-06-03 10:21:06 +02:00
if ( GetScript () -> IsPlayer ( target ))
2010-10-27 21:01:47 +02:00
{
2017-06-03 10:21:06 +02:00
Player * player = target -> ToPlayer ();
2015-09-28 17:20:27 +02:00
if ( ! fail && player -> IsAtGroupRewardDistance ( me ) && ! player -> HasCorpse ())
2019-05-15 01:33:55 +02:00
player -> AreaExploredOrEventHappens ( _escortQuestId );
2016-10-15 19:08:21 +02:00
else if ( fail )
2019-05-15 01:33:55 +02:00
player -> FailQuest ( _escortQuestId );
2010-10-27 21:01:47 +02:00
}
}
}
}
2017-06-03 10:21:06 +02:00
2017-08-12 01:40:25 +02:00
// End Path events should be only processed if it was SUCCESSFUL stop or stop called by SMART_ACTION_WAYPOINT_STOP
if ( fail )
return ;
2021-04-26 22:39:22 +03:00
uint32 pathid = GetScript () -> GetPathId ();
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_ENDED , nullptr , _currentWaypointNode , pathid );
2017-08-12 01:40:25 +02:00
if ( _repeatWaypointPath )
{
if ( IsAIControlled ())
2019-05-15 01:33:55 +02:00
StartPath ( _run , GetScript () -> GetPathId (), _repeatWaypointPath );
2017-08-12 01:40:25 +02:00
}
2021-04-26 22:39:22 +03:00
else if ( pathid == GetScript () -> GetPathId ()) // if it ' s not the same pathid , our script wants to start another path ; don ' t override it
2017-08-12 01:40:25 +02:00
GetScript () -> SetPathId ( 0 );
2019-05-15 01:33:55 +02:00
if ( _despawnState == 1 )
2010-10-27 21:01:47 +02:00
StartDespawn ();
}
void SmartAI :: ResumePath ()
{
2017-08-12 01:40:25 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_RESUMED , nullptr , _currentWaypointNode , GetScript () -> GetPathId ());
RemoveEscortState ( SMART_ESCORT_PAUSED );
_waypointPauseForced = false ;
_waypointReached = false ;
_waypointPauseTimer = 0 ;
2019-05-15 01:33:55 +02:00
SetRun ( _run );
2017-08-12 01:40:25 +02:00
me -> ResumeMovement ();
2010-10-27 21:01:47 +02:00
}
void SmartAI :: ReturnToLastOOCPos ()
{
2016-09-02 23:14:50 +02:00
if ( ! IsAIControlled ())
return ;
2017-08-12 01:40:25 +02:00
me -> SetWalk ( false );
me -> GetMotionMaster () -> MovePoint ( SMART_ESCORT_LAST_OOC_POINT , me -> GetHomePosition ());
2010-10-27 21:01:47 +02:00
}
2013-02-19 17:35:27 +01:00
void SmartAI :: UpdateAI ( uint32 diff )
2010-10-27 21:01:47 +02:00
{
2019-08-02 21:26:32 +02:00
if ( ! me -> IsAlive ())
{
if ( IsEngaged ())
EngagementOver ();
return ;
}
2017-02-17 21:33:18 +01:00
CheckConditions ( diff );
2017-11-07 19:22:39 +01:00
2019-12-30 19:03:14 +01:00
bool hasVictim = UpdateVictim ();
2010-10-27 21:01:47 +02:00
GetScript () -> OnUpdate ( diff );
2017-11-07 19:22:39 +01:00
2010-10-27 21:01:47 +02:00
UpdatePath ( diff );
2017-11-07 19:22:39 +01:00
UpdateFollow ( diff );
2010-10-27 21:01:47 +02:00
UpdateDespawn ( diff );
2016-09-02 23:14:50 +02:00
if ( ! IsAIControlled ())
return ;
2019-12-30 19:03:14 +01:00
if ( ! hasVictim )
2010-10-27 21:01:47 +02:00
return ;
2019-05-15 01:33:55 +02:00
if ( _canAutoAttack )
2010-10-27 21:01:47 +02:00
DoMeleeAttackIfReady ();
}
bool SmartAI :: IsEscortInvokerInRange ()
{
2017-06-04 16:43:36 -03:00
if ( ObjectVector const * targets = GetScript () -> GetStoredTargetVector ( SMART_ESCORT_TARGETS , * me ))
2010-10-27 21:01:47 +02:00
{
2017-02-17 21:33:18 +01:00
float checkDist = me -> GetInstanceScript () ? SMART_ESCORT_MAX_PLAYER_DIST * 2 : SMART_ESCORT_MAX_PLAYER_DIST ;
2010-10-27 21:01:47 +02:00
if ( targets -> size () == 1 && GetScript () -> IsPlayer (( * targets -> begin ())))
{
2011-08-20 20:31:31 -03:00
Player * player = ( * targets -> begin ()) -> ToPlayer ();
2017-02-17 21:33:18 +01:00
if ( me -> GetDistance ( player ) <= checkDist )
return true ;
2010-10-27 21:01:47 +02:00
2011-08-20 20:31:31 -03:00
if ( Group * group = player -> GetGroup ())
2010-10-27 21:01:47 +02:00
{
2016-08-05 12:41:56 +02:00
for ( GroupReference * groupRef = group -> GetFirstMember (); groupRef != nullptr ; groupRef = groupRef -> next ())
2010-10-27 21:01:47 +02:00
{
2013-06-11 21:25:12 -02:30
Player * groupGuy = groupRef -> GetSource ();
2017-02-17 21:33:18 +01:00
if ( groupGuy -> IsInMap ( player ) && me -> GetDistance ( groupGuy ) <= checkDist )
2010-10-27 21:01:47 +02:00
return true ;
}
}
2016-08-05 12:41:56 +02:00
}
else
2010-10-27 21:01:47 +02:00
{
2017-06-03 10:21:06 +02:00
for ( WorldObject * target : * targets )
2010-10-27 21:01:47 +02:00
{
2017-06-03 10:21:06 +02:00
if ( GetScript () -> IsPlayer ( target ))
2010-10-27 21:01:47 +02:00
{
2017-06-03 10:21:06 +02:00
if ( me -> GetDistance ( target -> ToPlayer ()) <= checkDist )
2010-10-27 21:01:47 +02:00
return true ;
}
}
}
2017-02-17 21:33:18 +01:00
// no valid target found
return false ;
2010-10-27 21:01:47 +02:00
}
2017-02-17 21:33:18 +01:00
// no player invoker was stored, just ignore range check
return true ;
2010-10-27 21:01:47 +02:00
}
2017-11-06 22:19:23 +01:00
///@todo move escort related logic
2018-01-01 17:22:29 +01:00
void SmartAI :: WaypointPathStarted ( uint32 pathId )
2017-11-06 22:19:23 +01:00
{
if ( ! HasEscortState ( SMART_ESCORT_ESCORTING ))
{
2018-01-01 17:22:29 +01:00
// @todo remove the constant 1 at some point, it's never anything different
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_START , nullptr , 1 , pathId );
2017-11-06 22:19:23 +01:00
return ;
}
}
2017-08-12 01:40:25 +02:00
///@todo Implement new smart event SMART_EVENT_WAYPOINT_STARTED
void SmartAI :: WaypointStarted ( uint32 /*nodeId*/ , uint32 /*pathId*/ )
2010-10-27 21:01:47 +02:00
{
2017-08-12 01:40:25 +02:00
}
void SmartAI :: WaypointReached ( uint32 nodeId , uint32 pathId )
{
2017-11-06 22:19:23 +01:00
if ( ! HasEscortState ( SMART_ESCORT_ESCORTING ))
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_REACHED , nullptr , nodeId , pathId );
return ;
}
2017-08-12 01:40:25 +02:00
_currentWaypointNode = nodeId ;
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_REACHED , nullptr , _currentWaypointNode , pathId );
2010-10-27 21:01:47 +02:00
2017-08-12 01:40:25 +02:00
if ( _waypointPauseTimer && ! _waypointPauseForced )
{
_waypointReached = true ;
me -> PauseMovement ();
2017-08-12 17:35:46 +02:00
me -> SetHomePosition ( me -> GetPosition ());
2017-08-12 01:40:25 +02:00
}
else if ( HasEscortState ( SMART_ESCORT_ESCORTING ) && me -> GetMotionMaster () -> GetCurrentMovementGeneratorType () == WAYPOINT_MOTION_TYPE )
{
if ( _currentWaypointNode == _path . nodes . size ())
_waypointPathEnded = true ;
else
2019-05-15 01:33:55 +02:00
SetRun ( _run );
2017-08-12 01:40:25 +02:00
}
2010-10-27 21:01:47 +02:00
}
2017-11-06 22:19:23 +01:00
///@todo move escort related logic
void SmartAI :: WaypointPathEnded ( uint32 nodeId , uint32 pathId )
{
if ( ! HasEscortState ( SMART_ESCORT_ESCORTING ))
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_WAYPOINT_ENDED , nullptr , nodeId , pathId );
return ;
}
}
2017-08-12 01:40:25 +02:00
void SmartAI :: MovementInform ( uint32 type , uint32 id )
2010-10-27 21:01:47 +02:00
{
2017-08-12 01:40:25 +02:00
if ( type == POINT_MOTION_TYPE && id == SMART_ESCORT_LAST_OOC_POINT )
2013-02-19 16:35:26 +01:00
me -> ClearUnitState ( UNIT_STATE_EVADE );
2017-08-12 01:40:25 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_MOVEMENTINFORM , nullptr , type , id );
if ( ! HasEscortState ( SMART_ESCORT_ESCORTING ))
2010-10-27 21:01:47 +02:00
return ;
2017-08-12 01:40:25 +02:00
if ( type == POINT_MOTION_TYPE && id == SMART_ESCORT_LAST_OOC_POINT )
_OOCReached = true ;
2010-10-27 21:01:47 +02:00
}
2016-01-13 15:33:17 +01:00
void SmartAI :: EnterEvadeMode ( EvadeReason /*why*/ )
2010-10-27 21:01:47 +02:00
{
2019-05-15 01:33:55 +02:00
if ( _evadeDisabled )
2016-08-19 14:53:53 +02:00
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_EVADE );
return ;
}
2017-02-17 21:33:18 +01:00
if ( ! IsAIControlled ())
{
me -> AttackStop ();
return ;
}
2016-09-02 23:14:50 +02:00
if ( ! _EnterEvadeMode ())
return ;
2017-02-17 21:33:18 +01:00
2013-02-19 16:35:26 +01:00
me -> AddUnitState ( UNIT_STATE_EVADE );
2014-01-10 17:36:04 +01:00
2017-11-06 22:19:23 +01:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_EVADE ); // must be after _EnterEvadeMode (spells, auras, ...)
2010-10-27 21:01:47 +02:00
2019-05-15 01:33:55 +02:00
SetRun ( _run );
2017-11-06 22:19:23 +01:00
if ( Unit * owner = me -> GetCharmerOrOwner ())
{
me -> GetMotionMaster () -> MoveFollow ( owner , PET_FOLLOW_DIST , PET_FOLLOW_ANGLE );
me -> ClearUnitState ( UNIT_STATE_EVADE );
}
else if ( HasEscortState ( SMART_ESCORT_ESCORTING ))
2010-10-27 21:01:47 +02:00
{
AddEscortState ( SMART_ESCORT_RETURNING );
ReturnToLastOOCPos ();
2011-10-24 23:21:34 +01:00
}
2019-05-15 01:33:55 +02:00
else if ( Unit * target = ! _followGUID . IsEmpty () ? ObjectAccessor :: GetUnit ( * me , _followGUID ) : nullptr )
2011-10-24 23:21:34 +01:00
{
2019-05-15 01:33:55 +02:00
me -> GetMotionMaster () -> MoveFollow ( target , _followDistance , _followAngle );
2014-05-16 19:46:02 +01:00
// evade is not cleared in MoveFollow, so we can't keep it
me -> ClearUnitState ( UNIT_STATE_EVADE );
2010-10-27 21:01:47 +02:00
}
2011-10-24 23:21:34 +01:00
else
me -> GetMotionMaster () -> MoveTargetedHome ();
2010-10-27 21:01:47 +02:00
2017-08-12 01:40:25 +02:00
if ( ! me -> HasUnitState ( UNIT_STATE_EVADE ))
GetScript () -> OnReset ();
2010-10-27 21:01:47 +02:00
}
void SmartAI :: MoveInLineOfSight ( Unit * who )
{
2011-10-01 16:57:10 +01:00
if ( ! who )
return ;
2012-02-18 16:52:08 +01:00
2010-10-27 21:01:47 +02:00
GetScript () -> OnMoveInLineOfSight ( who );
2012-02-18 16:52:08 +01:00
2016-09-02 23:14:50 +02:00
if ( ! IsAIControlled ())
return ;
2017-08-12 17:57:13 +02:00
if ( HasEscortState ( SMART_ESCORT_ESCORTING ) && AssistPlayerInCombatAgainst ( who ))
2010-10-27 21:01:47 +02:00
return ;
2015-09-13 03:29:28 +02:00
CreatureAI :: MoveInLineOfSight ( who );
2010-10-27 21:01:47 +02:00
}
2016-08-05 12:41:56 +02:00
bool SmartAI :: AssistPlayerInCombatAgainst ( Unit * who )
2010-10-27 21:01:47 +02:00
{
2016-09-02 23:14:50 +02:00
if ( me -> HasReactState ( REACT_PASSIVE ) || ! IsAIControlled ())
2016-02-20 03:20:43 +01:00
return false ;
2016-02-20 03:13:11 +01:00
2013-06-11 19:54:27 -02:30
if ( ! who || ! who -> GetVictim ())
2010-10-27 21:01:47 +02:00
return false ;
2017-08-12 01:40:25 +02:00
// experimental (unknown) flag not present
2016-05-26 12:56:50 +04:00
if ( ! ( me -> GetCreatureTemplate () -> type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST ))
2010-10-27 21:01:47 +02:00
return false ;
2017-08-12 01:40:25 +02:00
// not a player
2014-04-19 21:48:22 +02:00
if ( ! who -> EnsureVictim () -> GetCharmerOrOwnerPlayerOrPlayerItself ())
2010-10-27 21:01:47 +02:00
return false ;
2017-08-12 18:19:18 +02:00
if ( ! who -> isInAccessiblePlaceFor ( me ))
return false ;
if ( ! CanAIAttack ( who ))
return false ;
// we cannot attack in evade mode
if ( me -> IsInEvadeMode ())
return false ;
// or if enemy is in evade mode
if ( who -> GetTypeId () == TYPEID_UNIT && who -> ToCreature () -> IsInEvadeMode ())
return false ;
2017-02-17 21:33:18 +01:00
if ( ! me -> IsValidAssistTarget ( who -> GetVictim ()))
2010-10-27 21:01:47 +02:00
return false ;
2017-08-12 18:19:18 +02:00
// too far away and no free sight
2011-08-20 20:31:31 -03:00
if ( me -> IsWithinDistInMap ( who , SMART_MAX_AID_DIST ) && me -> IsWithinLOSInMap ( who ))
2010-10-27 21:01:47 +02:00
{
2017-07-01 20:18:02 +02:00
me -> EngageWithTarget ( who );
return true ;
2010-10-27 21:01:47 +02:00
}
return false ;
}
2018-02-13 18:29:38 +01:00
void SmartAI :: InitializeAI ()
2010-10-27 21:01:47 +02:00
{
2018-02-13 18:42:28 +01:00
GetScript () -> OnInitialize ( me );
2019-05-15 01:33:55 +02:00
_despawnTime = 0 ;
_despawnState = 0 ;
2017-08-12 01:40:25 +02:00
_escortState = SMART_ESCORT_NONE ;
2017-11-06 22:19:23 +01:00
2010-11-16 01:13:04 +01:00
me -> SetVisible ( true );
2017-11-06 22:19:23 +01:00
2019-05-15 01:33:55 +02:00
_followGUID . Clear (); // do not reset follower on Reset(), we need it after combat evade
_followDistance = 0 ;
_followAngle = 0 ;
_followCredit = 0 ;
_followArrivedTimer = 1000 ;
_followArrivedEntry = 0 ;
_followCreditType = 0 ;
2010-10-27 21:01:47 +02:00
}
2019-06-23 17:37:35 +02:00
void SmartAI :: JustAppeared ()
{
2019-06-23 23:32:21 +02:00
CreatureAI :: JustAppeared ();
2019-06-23 17:37:35 +02:00
if ( me -> isDead ())
return ;
GetScript () -> ProcessEventsFor ( SMART_EVENT_RESPAWN );
GetScript () -> OnReset ();
}
2010-10-27 21:01:47 +02:00
void SmartAI :: JustReachedHome ()
{
2013-12-12 21:23:14 -03:30
GetScript () -> OnReset ();
2017-11-06 22:19:23 +01:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_REACHED_HOME );
2013-12-12 21:23:14 -03:30
2017-11-06 22:19:23 +01:00
CreatureGroup * formation = me -> GetFormation ();
2018-01-22 01:54:15 -03:00
if ( ! formation || formation -> GetLeader () == me || ! formation -> IsFormed ())
2013-12-12 21:23:14 -03:30
{
2018-06-03 10:06:57 -07:00
if ( me -> GetMotionMaster () -> GetCurrentMovementGeneratorType ( MOTION_SLOT_DEFAULT ) != WAYPOINT_MOTION_TYPE )
{
if ( me -> GetWaypointPath ())
me -> GetMotionMaster () -> MovePath ( me -> GetWaypointPath (), true );
}
2019-07-19 15:52:35 +02:00
me -> ResumeMovement ();
2013-12-12 21:23:14 -03:30
}
2018-01-22 01:54:15 -03:00
else if ( formation -> IsFormed ())
2017-11-06 22:19:23 +01:00
me -> GetMotionMaster () -> MoveIdle (); // wait the order of leader
2010-10-27 21:01:47 +02:00
}
2017-12-31 03:23:42 +01:00
void SmartAI :: JustEngagedWith ( Unit * enemy )
2010-10-27 21:01:47 +02:00
{
2016-09-02 23:14:50 +02:00
if ( IsAIControlled ())
me -> InterruptNonMeleeSpells ( false ); // must be before ProcessEvents
2010-10-27 21:01:47 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_AGGRO , enemy );
}
void SmartAI :: JustDied ( Unit * killer )
{
if ( HasEscortState ( SMART_ESCORT_ESCORTING ))
EndPath ( true );
2017-08-12 01:40:25 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_DEATH , killer );
2010-10-27 21:01:47 +02:00
}
void SmartAI :: KilledUnit ( Unit * victim )
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_KILL , victim );
}
2011-06-15 17:32:31 +02:00
void SmartAI :: JustSummoned ( Creature * creature )
2010-10-27 21:01:47 +02:00
{
2011-06-15 17:32:31 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_SUMMONED_UNIT , creature );
2010-10-27 21:01:47 +02:00
}
void SmartAI :: AttackStart ( Unit * who )
{
2017-02-17 21:33:18 +01:00
// dont allow charmed npcs to act on their own
if ( ! IsAIControlled ())
{
2017-08-12 01:40:25 +02:00
if ( who )
2019-05-15 01:33:55 +02:00
me -> Attack ( who , _canAutoAttack );
2017-02-17 21:33:18 +01:00
return ;
}
2019-05-15 01:33:55 +02:00
if ( who && me -> Attack ( who , _canAutoAttack ))
2010-10-27 21:01:47 +02:00
{
2018-06-03 10:06:57 -07:00
me -> GetMotionMaster () -> Clear ( MOTION_PRIORITY_NORMAL );
me -> PauseMovement ();
2017-08-12 01:40:25 +02:00
2019-05-15 01:33:55 +02:00
if ( _canCombatMove )
2017-08-12 01:40:25 +02:00
{
2019-05-15 01:33:55 +02:00
SetRun ( _run );
2010-10-27 21:01:47 +02:00
me -> GetMotionMaster () -> MoveChase ( who );
2017-08-12 01:40:25 +02:00
}
2010-10-27 21:01:47 +02:00
}
}
2020-09-04 13:38:24 +02:00
void SmartAI :: SpellHit ( Unit * unit , SpellInfo const * spellInfo )
2010-10-27 21:01:47 +02:00
{
2011-08-20 20:31:31 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_SPELLHIT , unit , 0 , 0 , false , spellInfo );
2010-10-27 21:01:47 +02:00
}
2019-06-26 18:08:19 +02:00
void SmartAI :: SpellHitByGameObject ( GameObject * object , SpellInfo const * spellInfo )
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_SPELLHIT , nullptr , 0 , 0 , false , spellInfo , object );
}
2020-09-04 13:38:24 +02:00
void SmartAI :: SpellHitTarget ( Unit * target , SpellInfo const * spellInfo )
2010-10-27 21:01:47 +02:00
{
2011-08-20 20:31:31 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_SPELLHIT_TARGET , target , 0 , 0 , false , spellInfo );
2010-10-27 21:01:47 +02:00
}
2019-06-26 18:08:19 +02:00
void SmartAI :: SpellHitTargetGameObject ( GameObject * target , SpellInfo const * spellInfo )
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_SPELLHIT_TARGET , nullptr , 0 , 0 , false , spellInfo , target );
}
2011-08-20 20:31:31 -03:00
void SmartAI :: DamageTaken ( Unit * doneBy , uint32 & damage )
2010-10-27 21:01:47 +02:00
{
2011-08-20 20:31:31 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_DAMAGED , doneBy , damage );
2016-09-02 23:14:50 +02:00
if ( ! IsAIControlled ()) // don't allow players to use unkillable units
return ;
2019-05-15 01:33:55 +02:00
if ( _invincibilityHPLevel && ( damage >= me -> GetHealth () - _invincibilityHPLevel ))
damage = me -> GetHealth () - _invincibilityHPLevel ; // damage should not be nullified, because of player damage req.
2010-10-27 21:01:47 +02:00
}
2011-08-20 20:31:31 -03:00
void SmartAI :: HealReceived ( Unit * doneBy , uint32 & addhealth )
2010-10-27 21:01:47 +02:00
{
2011-08-20 20:31:31 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_RECEIVE_HEAL , doneBy , addhealth );
2010-10-27 21:01:47 +02:00
}
2011-08-20 20:31:31 -03:00
void SmartAI :: ReceiveEmote ( Player * player , uint32 textEmote )
2010-10-27 21:01:47 +02:00
{
2011-08-20 20:31:31 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_RECEIVE_EMOTE , player , textEmote );
2010-10-27 21:01:47 +02:00
}
2019-05-23 21:08:29 +02:00
void SmartAI :: IsSummonedBy ( WorldObject * summoner )
2010-10-27 21:01:47 +02:00
{
2019-05-23 21:08:29 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_JUST_SUMMONED , summoner -> ToUnit (), 0 , 0 , false , nullptr , summoner -> ToGameObject ());
2010-10-27 21:01:47 +02:00
}
2011-08-20 20:31:31 -03:00
void SmartAI :: DamageDealt ( Unit * doneTo , uint32 & damage , DamageEffectType /*damagetype*/ )
2010-10-27 21:01:47 +02:00
{
2011-08-20 20:31:31 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_DAMAGED_TARGET , doneTo , damage );
2010-10-27 21:01:47 +02:00
}
void SmartAI :: SummonedCreatureDespawn ( Creature * unit )
{
2019-05-23 20:54:09 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_SUMMON_DESPAWNED , unit , unit -> GetEntry ());
2010-10-27 21:01:47 +02:00
}
void SmartAI :: CorpseRemoved ( uint32 & respawnDelay )
{
2016-08-05 12:41:56 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_CORPSE_REMOVED , nullptr , respawnDelay );
2010-10-27 21:01:47 +02:00
}
void SmartAI :: PassengerBoarded ( Unit * who , int8 seatId , bool apply )
{
2012-12-02 17:45:15 +01:00
GetScript () -> ProcessEventsFor ( apply ? SMART_EVENT_PASSENGER_BOARDED : SMART_EVENT_PASSENGER_REMOVED , who , uint32 ( seatId ), 0 , apply );
2010-10-27 21:01:47 +02:00
}
2018-08-23 14:33:28 +02:00
void SmartAI :: OnCharmed ( bool /*isNew*/ )
2010-10-27 21:01:47 +02:00
{
2018-08-23 14:33:28 +02:00
bool const charmed = me -> IsCharmed ();
if ( charmed ) // do this before we change charmed state, as charmed state might prevent these things from processing
2016-09-02 23:14:50 +02:00
{
if ( HasEscortState ( SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING ))
EndPath ( true );
}
2017-08-12 01:40:25 +02:00
2019-05-15 01:33:55 +02:00
_charmed = charmed ;
2013-09-09 17:44:07 +02:00
2020-03-01 13:31:14 +01:00
if ( charmed )
me -> GetMotionMaster () -> MoveFollow ( me -> GetCharmer (), PET_FOLLOW_DIST , me -> GetFollowAngle ());
2018-08-23 14:33:28 +02:00
if ( ! charmed && ! me -> IsInEvadeMode ())
2016-09-02 23:14:50 +02:00
{
2017-08-12 01:40:25 +02:00
if ( _repeatWaypointPath )
2019-05-15 01:33:55 +02:00
StartPath ( _run , GetScript () -> GetPathId (), true );
2016-09-02 23:14:50 +02:00
else
2019-05-15 01:33:55 +02:00
me -> SetWalk ( ! _run );
2016-09-02 23:14:50 +02:00
2018-08-28 12:52:50 +02:00
if ( ! me -> LastCharmerGUID . IsEmpty ())
{
if ( ! me -> HasReactState ( REACT_PASSIVE ))
if ( Unit * lastCharmer = ObjectAccessor :: GetUnit ( * me , me -> LastCharmerGUID ))
me -> EngageWithTarget ( lastCharmer );
me -> LastCharmerGUID . Clear ();
}
2016-09-02 23:14:50 +02:00
}
2018-08-23 14:33:28 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_CHARMED , nullptr , 0 , 0 , charmed );
2010-10-27 21:01:47 +02:00
}
2013-02-19 17:35:27 +01:00
void SmartAI :: DoAction ( int32 param )
2010-10-27 21:01:47 +02:00
{
2016-08-05 12:41:56 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_ACTION_DONE , nullptr , param );
2010-10-27 21:01:47 +02:00
}
2012-11-20 09:29:52 +01:00
uint32 SmartAI :: GetData ( uint32 /*id*/ ) const
2010-10-27 21:01:47 +02:00
{
return 0 ;
}
2018-02-15 11:44:37 +01:00
void SmartAI :: SetData ( uint32 id , uint32 value , Unit * invoker )
2010-10-27 21:01:47 +02:00
{
2018-02-15 11:44:37 +01:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_DATA_SET , invoker , id , value );
2010-10-27 21:01:47 +02:00
}
2017-12-27 18:17:13 -03:00
void SmartAI :: SetGUID ( ObjectGuid const & /*guid*/ , int32 /*id*/ ) { }
2010-10-27 21:01:47 +02:00
2014-09-15 00:23:47 +02:00
ObjectGuid SmartAI :: GetGUID ( int32 /*id*/ ) const
2010-10-27 21:01:47 +02:00
{
2014-09-15 22:07:11 +02:00
return ObjectGuid :: Empty ;
2010-10-27 21:01:47 +02:00
}
void SmartAI :: SetRun ( bool run )
{
2013-06-18 16:48:46 +02:00
me -> SetWalk ( ! run );
2019-05-15 01:33:55 +02:00
_run = run ;
2018-08-19 19:59:32 +02:00
for ( auto & node : _path . nodes )
node . moveType = run ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK ;
2010-10-27 21:01:47 +02:00
}
2017-02-17 21:33:18 +01:00
void SmartAI :: SetDisableGravity ( bool fly )
2010-10-27 21:01:47 +02:00
{
2012-03-12 00:45:58 +01:00
me -> SetDisableGravity ( fly );
2010-10-27 21:01:47 +02:00
}
2017-02-17 21:33:18 +01:00
void SmartAI :: SetCanFly ( bool fly )
{
me -> SetCanFly ( fly );
}
2011-08-21 00:07:38 -03:00
void SmartAI :: SetSwim ( bool swim )
2010-10-27 21:01:47 +02:00
{
2013-06-18 16:48:46 +02:00
me -> SetSwim ( swim );
2010-10-27 21:01:47 +02:00
}
2016-08-19 14:53:53 +02:00
void SmartAI :: SetEvadeDisabled ( bool disable )
{
2019-05-15 01:33:55 +02:00
_evadeDisabled = disable ;
2016-08-19 14:53:53 +02:00
}
2017-05-01 18:19:36 -03:00
bool SmartAI :: GossipHello ( Player * player )
2010-10-27 21:01:47 +02:00
{
2017-05-27 03:56:09 -03:00
_gossipReturn = false ;
2010-10-27 21:01:47 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_GOSSIP_HELLO , player );
2017-05-27 03:56:09 -03:00
return _gossipReturn ;
2010-10-27 21:01:47 +02:00
}
2017-05-01 18:19:36 -03:00
bool SmartAI :: GossipSelect ( Player * player , uint32 menuId , uint32 gossipListId )
2010-10-27 21:01:47 +02:00
{
2017-05-27 03:56:09 -03:00
_gossipReturn = false ;
2015-10-04 16:16:56 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_GOSSIP_SELECT , player , menuId , gossipListId );
2017-05-27 03:56:09 -03:00
return _gossipReturn ;
2010-10-27 21:01:47 +02:00
}
2020-09-04 13:38:24 +02:00
bool SmartAI :: GossipSelectCode ( Player * /*player*/ , uint32 /*menuId*/ , uint32 /*gossipListId*/ , char const * /*code*/ )
2017-05-01 18:19:36 -03:00
{
return false ;
}
2010-10-27 21:01:47 +02:00
2017-05-01 18:19:36 -03:00
void SmartAI :: QuestAccept ( Player * player , Quest const * quest )
2010-10-27 21:01:47 +02:00
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_ACCEPTED_QUEST , player , quest -> GetQuestId ());
}
2021-05-16 19:14:23 +02:00
void SmartAI :: QuestReward ( Player * player , Quest const * quest , LootItemType /*type*/ , uint32 opt )
2010-10-27 21:01:47 +02:00
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_REWARD_QUEST , player , quest -> GetQuestId (), opt );
}
2010-11-17 23:24:21 +01:00
2010-10-27 21:01:47 +02:00
void SmartAI :: SetCombatMove ( bool on )
{
2019-05-15 01:33:55 +02:00
if ( _canCombatMove == on )
2010-10-27 21:01:47 +02:00
return ;
2017-08-12 01:40:25 +02:00
2019-05-15 01:33:55 +02:00
_canCombatMove = on ;
2017-08-12 01:40:25 +02:00
2016-09-02 23:14:50 +02:00
if ( ! IsAIControlled ())
return ;
2017-08-12 01:40:25 +02:00
if ( me -> IsEngaged ())
2010-10-27 21:01:47 +02:00
{
2018-06-03 10:06:57 -07:00
if ( on )
2010-10-27 21:01:47 +02:00
{
2018-06-03 10:06:57 -07:00
if ( ! me -> HasReactState ( REACT_PASSIVE ) && me -> GetVictim () && ! me -> GetMotionMaster () -> HasMovementGenerator ([]( MovementGenerator const * movement ) -> bool
{
2019-07-19 16:07:00 +02:00
return movement -> GetMovementGeneratorType () == CHASE_MOTION_TYPE && movement -> Mode == MOTION_MODE_DEFAULT && movement -> Priority == MOTION_PRIORITY_NORMAL ;
2018-06-03 10:06:57 -07:00
}))
{
2019-05-15 01:33:55 +02:00
SetRun ( _run );
2018-06-03 10:06:57 -07:00
me -> GetMotionMaster () -> MoveChase ( me -> GetVictim ());
}
2010-10-27 21:01:47 +02:00
}
2018-06-03 10:06:57 -07:00
else if ( MovementGenerator * movement = me -> GetMotionMaster () -> GetMovementGenerator ([]( MovementGenerator const * a ) -> bool
{
return a -> GetMovementGeneratorType () == CHASE_MOTION_TYPE && a -> Mode == MOTION_MODE_DEFAULT && a -> Priority == MOTION_PRIORITY_NORMAL ;
}))
me -> GetMotionMaster () -> Remove ( movement );
2010-10-27 21:01:47 +02:00
}
}
void SmartAI :: SetFollow ( Unit * target , float dist , float angle , uint32 credit , uint32 end , uint32 creditType )
{
if ( ! target )
2013-12-17 10:12:23 +01:00
{
2017-02-17 21:33:18 +01:00
StopFollow ( false );
2010-10-27 21:01:47 +02:00
return ;
2013-12-17 10:12:23 +01:00
}
2019-05-15 01:33:55 +02:00
_followGUID = target -> GetGUID ();
_followDistance = dist ;
_followAngle = angle ;
_followArrivedTimer = 1000 ;
_followCredit = credit ;
_followArrivedEntry = end ;
_followCreditType = creditType ;
SetRun ( _run );
me -> GetMotionMaster () -> MoveFollow ( target , _followDistance , _followAngle );
2010-10-27 21:01:47 +02:00
}
2010-12-07 18:31:34 +01:00
2017-02-17 21:33:18 +01:00
void SmartAI :: StopFollow ( bool complete )
2013-12-17 10:12:23 +01:00
{
2019-05-15 01:33:55 +02:00
_followGUID . Clear ();
_followDistance = 0 ;
_followAngle = 0 ;
_followCredit = 0 ;
_followArrivedTimer = 1000 ;
_followArrivedEntry = 0 ;
_followCreditType = 0 ;
2013-12-17 10:12:23 +01:00
me -> GetMotionMaster () -> MoveIdle ();
2017-02-17 21:33:18 +01:00
if ( ! complete )
return ;
2019-05-15 01:33:55 +02:00
Player * player = ObjectAccessor :: GetPlayer ( * me , _followGUID );
2018-02-13 16:38:28 +01:00
if ( player )
2017-02-17 21:33:18 +01:00
{
2019-05-15 01:33:55 +02:00
if ( ! _followCreditType )
player -> RewardPlayerAndGroupAtEvent ( _followCredit , me );
2017-02-17 21:33:18 +01:00
else
2019-05-15 01:33:55 +02:00
player -> GroupEventHappens ( _followCredit , me );
2017-02-17 21:33:18 +01:00
}
SetDespawnTime ( 5000 );
2013-12-17 10:12:23 +01:00
StartDespawn ();
2018-02-13 16:38:28 +01:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_FOLLOW_COMPLETED , player );
2013-12-17 10:12:23 +01:00
}
2017-02-17 21:33:18 +01:00
2018-02-13 18:42:28 +01:00
void SmartAI :: SetTimedActionList ( SmartScriptHolder & e , uint32 entry , Unit * invoker )
2010-12-07 18:31:34 +01:00
{
2018-02-13 18:42:28 +01:00
GetScript () -> SetTimedActionList ( e , entry , invoker );
2010-12-07 18:31:34 +01:00
}
2012-02-18 16:52:08 +01:00
2017-05-01 18:19:36 -03:00
void SmartAI :: OnGameEvent ( bool start , uint16 eventId )
2011-12-11 16:22:18 +01:00
{
2016-08-05 12:41:56 +02:00
GetScript () -> ProcessEventsFor ( start ? SMART_EVENT_GAME_EVENT_START : SMART_EVENT_GAME_EVENT_END , nullptr , eventId );
2011-12-11 16:22:18 +01:00
}
2018-02-24 15:52:09 -03:00
void SmartAI :: OnSpellClick ( Unit * clicker , bool spellClickHandled )
2012-06-19 00:07:20 +02:00
{
2018-02-24 15:52:09 -03:00
if ( ! spellClickHandled )
2013-06-01 18:30:36 +02:00
return ;
2012-06-19 00:07:20 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_ON_SPELLCLICK , clicker );
}
2017-02-17 21:33:18 +01:00
void SmartAI :: CheckConditions ( uint32 diff )
{
2019-05-15 01:33:55 +02:00
if ( ! _vehicleConditions )
2017-02-17 21:33:18 +01:00
return ;
2019-05-15 01:33:55 +02:00
if ( _vehicleConditionsTimer <= diff )
2017-02-17 21:33:18 +01:00
{
2020-02-29 13:22:51 +01:00
if ( Vehicle * vehicleKit = me -> GetVehicleKit ())
2017-02-17 21:33:18 +01:00
{
2020-02-29 13:22:51 +01:00
for ( std :: pair < int8 const , VehicleSeat >& seat : vehicleKit -> Seats )
if ( Unit * passenger = ObjectAccessor :: GetUnit ( * me , seat . second . Passenger . Guid ))
2017-02-17 21:33:18 +01:00
{
2020-02-29 13:22:51 +01:00
if ( Player * player = passenger -> ToPlayer ())
2017-02-17 21:33:18 +01:00
{
if ( ! sConditionMgr -> IsObjectMeetingNotGroupedConditions ( CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE , me -> GetEntry (), player , me ))
{
player -> ExitVehicle ();
return ; // check other pessanger in next tick
}
}
}
}
2019-05-15 01:33:55 +02:00
_vehicleConditionsTimer = 1000 ;
2017-02-17 21:33:18 +01:00
}
else
2019-05-15 01:33:55 +02:00
_vehicleConditionsTimer -= diff ;
2017-02-17 21:33:18 +01:00
}
2017-11-07 19:22:39 +01:00
void SmartAI :: UpdatePath ( uint32 diff )
{
if ( ! HasEscortState ( SMART_ESCORT_ESCORTING ))
return ;
if ( _escortInvokerCheckTimer < diff )
{
if ( ! IsEscortInvokerInRange ())
{
2019-05-15 01:33:55 +02:00
StopPath ( 0 , _escortQuestId , true );
2017-11-07 19:22:39 +01:00
// allow to properly hook out of range despawn action, which in most cases should perform the same operation as dying
GetScript () -> ProcessEventsFor ( SMART_EVENT_DEATH , me );
me -> DespawnOrUnsummon ();
return ;
}
_escortInvokerCheckTimer = 1000 ;
}
else
_escortInvokerCheckTimer -= diff ;
// handle pause
if ( HasEscortState ( SMART_ESCORT_PAUSED ) && ( _waypointReached || _waypointPauseForced ))
{
if ( _waypointPauseTimer <= diff )
{
if ( ! me -> IsInCombat () && ! HasEscortState ( SMART_ESCORT_RETURNING ))
ResumePath ();
}
else
_waypointPauseTimer -= diff ;
}
else if ( _waypointPathEnded ) // end path
{
_waypointPathEnded = false ;
StopPath ();
return ;
}
if ( HasEscortState ( SMART_ESCORT_RETURNING ))
{
if ( _OOCReached ) // reached OOC WP
{
_OOCReached = false ;
RemoveEscortState ( SMART_ESCORT_RETURNING );
if ( ! HasEscortState ( SMART_ESCORT_PAUSED ))
ResumePath ();
}
}
}
void SmartAI :: UpdateFollow ( uint32 diff )
{
2019-05-15 01:33:55 +02:00
if ( ! _followGUID . IsEmpty ())
2017-11-07 19:22:39 +01:00
{
2019-05-15 01:33:55 +02:00
if ( _followArrivedTimer < diff )
2017-11-07 19:22:39 +01:00
{
2019-05-15 01:33:55 +02:00
if ( me -> FindNearestCreature ( _followArrivedEntry , INTERACTION_DISTANCE , true ))
2017-11-07 19:22:39 +01:00
{
StopFollow ( true );
return ;
}
2019-05-15 01:33:55 +02:00
_followArrivedTimer = 1000 ;
2017-11-07 19:22:39 +01:00
}
else
2019-05-15 01:33:55 +02:00
_followArrivedTimer -= diff ;
2017-11-07 19:22:39 +01:00
}
}
void SmartAI :: UpdateDespawn ( uint32 diff )
{
2019-05-15 01:33:55 +02:00
if ( _despawnState <= 1 || _despawnState > 3 )
2017-11-07 19:22:39 +01:00
return ;
2019-05-15 01:33:55 +02:00
if ( _despawnTime < diff )
2017-11-07 19:22:39 +01:00
{
2019-05-15 01:33:55 +02:00
if ( _despawnState == 2 )
2017-11-07 19:22:39 +01:00
{
me -> SetVisible ( false );
2019-05-15 01:33:55 +02:00
_despawnTime = 5000 ;
_despawnState ++ ;
2017-11-07 19:22:39 +01:00
}
else
2018-02-14 02:23:55 +01:00
me -> DespawnOrUnsummon ();
2017-11-07 19:22:39 +01:00
}
else
2019-05-15 01:33:55 +02:00
_despawnTime -= diff ;
2017-11-07 19:22:39 +01:00
}
2012-04-14 18:26:16 +02:00
void SmartGameObjectAI :: UpdateAI ( uint32 diff )
2010-10-27 21:01:47 +02:00
{
GetScript () -> OnUpdate ( diff );
}
void SmartGameObjectAI :: InitializeAI ()
{
2017-05-01 18:19:36 -03:00
GetScript () -> OnInitialize ( me );
2019-03-13 23:24:51 +01:00
// do not call respawn event if go is not spawned
if ( me -> isSpawned ())
GetScript () -> ProcessEventsFor ( SMART_EVENT_RESPAWN );
2010-10-27 21:01:47 +02:00
//Reset();
}
void SmartGameObjectAI :: Reset ()
{
GetScript () -> OnReset ();
}
// Called when a player opens a gossip dialog with the gameobject.
2017-06-06 20:36:01 +02:00
bool SmartGameObjectAI :: GossipHello ( Player * player )
2010-10-27 21:01:47 +02:00
{
2017-05-27 03:56:09 -03:00
_gossipReturn = false ;
2017-06-06 20:36:01 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_GOSSIP_HELLO , player , 0 , 0 , false , nullptr , me );
return _gossipReturn ;
}
bool SmartGameObjectAI :: OnReportUse ( Player * player )
{
_gossipReturn = false ;
GetScript () -> ProcessEventsFor ( SMART_EVENT_GOSSIP_HELLO , player , 1 , 0 , false , nullptr , me );
2017-05-27 03:56:09 -03:00
return _gossipReturn ;
2010-10-27 21:01:47 +02:00
}
// Called when a player selects a gossip item in the gameobject's gossip menu.
bool SmartGameObjectAI :: GossipSelect ( Player * player , uint32 sender , uint32 action )
{
2017-05-27 03:56:09 -03:00
_gossipReturn = false ;
2017-05-01 18:19:36 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_GOSSIP_SELECT , player , sender , action , false , nullptr , me );
2017-05-27 03:56:09 -03:00
return _gossipReturn ;
2010-10-27 21:01:47 +02:00
}
// Called when a player selects a gossip with a code in the gameobject's gossip menu.
2020-09-04 13:38:24 +02:00
bool SmartGameObjectAI :: GossipSelectCode ( Player * /*player*/ , uint32 /*menuId*/ , uint32 /*gossipListId*/ , char const * /*code*/ )
2010-10-27 21:01:47 +02:00
{
return false ;
}
// Called when a player accepts a quest from the gameobject.
2017-05-01 18:19:36 -03:00
void SmartGameObjectAI :: QuestAccept ( Player * player , Quest const * quest )
2010-10-27 21:01:47 +02:00
{
2017-05-01 18:19:36 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_ACCEPTED_QUEST , player , quest -> GetQuestId (), 0 , false , nullptr , me );
2010-10-27 21:01:47 +02:00
}
// Called when a player selects a quest reward.
2021-05-16 19:14:23 +02:00
void SmartGameObjectAI :: QuestReward ( Player * player , Quest const * quest , LootItemType /*type*/ , uint32 opt )
2010-10-27 21:01:47 +02:00
{
2017-05-01 18:19:36 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_REWARD_QUEST , player , quest -> GetQuestId (), opt , false , nullptr , me );
2010-10-27 21:01:47 +02:00
}
// Called when the gameobject is destroyed (destructible buildings only).
2018-02-10 16:43:01 -03:00
void SmartGameObjectAI :: Destroyed ( WorldObject * attacker , uint32 eventId )
2010-10-27 21:01:47 +02:00
{
2018-02-10 16:43:01 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_DEATH , attacker ? attacker -> ToUnit () : nullptr , eventId , 0 , false , nullptr , me );
2010-10-27 21:01:47 +02:00
}
2018-02-15 11:44:37 +01:00
void SmartGameObjectAI :: SetData ( uint32 id , uint32 value , Unit * invoker )
2010-10-27 21:01:47 +02:00
{
2018-02-15 11:44:37 +01:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_DATA_SET , invoker , id , value );
2010-10-27 21:01:47 +02:00
}
2018-02-13 18:42:28 +01:00
void SmartGameObjectAI :: SetTimedActionList ( SmartScriptHolder & e , uint32 entry , Unit * invoker )
2010-12-07 20:25:45 +01:00
{
2018-02-13 18:42:28 +01:00
GetScript () -> SetTimedActionList ( e , entry , invoker );
2010-12-07 20:25:45 +01:00
}
2011-12-11 16:22:18 +01:00
void SmartGameObjectAI :: OnGameEvent ( bool start , uint16 eventId )
{
2016-08-05 12:41:56 +02:00
GetScript () -> ProcessEventsFor ( start ? SMART_EVENT_GAME_EVENT_START : SMART_EVENT_GAME_EVENT_END , nullptr , eventId );
2011-12-11 16:22:18 +01:00
}
2017-05-01 18:19:36 -03:00
void SmartGameObjectAI :: OnLootStateChanged ( uint32 state , Unit * unit )
2012-01-06 11:09:01 -05:00
{
2017-05-01 18:19:36 -03:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_GO_LOOT_STATE_CHANGED , unit , state );
2012-01-06 11:09:01 -05:00
}
2012-06-11 11:10:46 +02:00
void SmartGameObjectAI :: EventInform ( uint32 eventId )
{
2016-08-05 12:41:56 +02:00
GetScript () -> ProcessEventsFor ( SMART_EVENT_GO_EVENT_INFORM , nullptr , eventId );
2012-06-11 11:10:46 +02:00
}
2020-09-04 13:38:24 +02:00
void SmartGameObjectAI :: SpellHit ( Unit * unit , SpellInfo const * spellInfo )
2017-02-17 21:33:18 +01:00
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_SPELLHIT , unit , 0 , 0 , false , spellInfo );
}
2019-05-24 20:52:54 +02:00
void SmartGameObjectAI :: JustSummoned ( Creature * creature )
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_SUMMONED_UNIT , creature );
}
void SmartGameObjectAI :: SummonedCreatureDespawn ( Creature * unit )
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_SUMMON_DESPAWNED , unit , unit -> GetEntry ());
}
2010-10-27 21:01:47 +02:00
class SmartTrigger : public AreaTriggerScript
{
public :
2013-10-27 22:27:46 +01:00
SmartTrigger () : AreaTriggerScript ( "SmartTrigger" ) { }
2010-10-27 21:01:47 +02:00
2021-11-10 00:01:50 +01:00
bool OnTrigger ( Player * player , AreaTriggerEntry const * trigger ) override
2010-10-27 21:01:47 +02:00
{
2013-06-11 19:54:27 -02:30
if ( ! player -> IsAlive ())
2011-10-31 02:01:17 +01:00
return false ;
2014-11-06 01:13:06 +02:00
TC_LOG_DEBUG ( "scripts.ai" , "AreaTrigger %u is using SmartTrigger script" , trigger -> ID );
2010-10-27 21:01:47 +02:00
SmartScript script ;
2016-08-05 12:41:56 +02:00
script . OnInitialize ( nullptr , trigger );
2014-11-06 01:13:06 +02:00
script . ProcessEventsFor ( SMART_EVENT_AREATRIGGER_ONTRIGGER , player , trigger -> ID );
2010-10-27 21:01:47 +02:00
return true ;
}
};
2021-02-05 18:12:55 +01:00
void SmartAreaTriggerAI :: OnInitialize ()
{
GetScript () -> OnInitialize ( at );
}
void SmartAreaTriggerAI :: OnUpdate ( uint32 diff )
{
GetScript () -> OnUpdate ( diff );
}
void SmartAreaTriggerAI :: OnUnitEnter ( Unit * unit )
{
GetScript () -> ProcessEventsFor ( SMART_EVENT_AREATRIGGER_ONTRIGGER , unit );
}
2018-02-13 18:42:28 +01:00
void SmartAreaTriggerAI :: SetTimedActionList ( SmartScriptHolder & e , uint32 entry , Unit * invoker )
2021-02-05 18:12:55 +01:00
{
2018-02-13 18:42:28 +01:00
GetScript () -> SetTimedActionList ( e , entry , invoker );
2021-02-05 18:12:55 +01:00
}
class SmartAreaTriggerEntityScript : public AreaTriggerEntityScript
{
public :
SmartAreaTriggerEntityScript () : AreaTriggerEntityScript ( "SmartAreaTriggerAI" )
{
}
AreaTriggerAI * GetAI ( AreaTrigger * areaTrigger ) const override
{
return new SmartAreaTriggerAI ( areaTrigger );
}
};
2017-03-17 21:21:35 +01:00
class SmartScene : public SceneScript
{
public :
SmartScene () : SceneScript ( "SmartScene" ) { }
void OnSceneStart ( Player * player , uint32 /*sceneInstanceID*/ , SceneTemplate const * sceneTemplate ) override
{
SmartScript smartScript ;
smartScript . OnInitialize ( nullptr , nullptr , sceneTemplate );
smartScript . ProcessEventsFor ( SMART_EVENT_SCENE_START , player );
}
void OnSceneTriggerEvent ( Player * player , uint32 /*sceneInstanceID*/ , SceneTemplate const * sceneTemplate , std :: string const & triggerName ) override
{
SmartScript smartScript ;
smartScript . OnInitialize ( nullptr , nullptr , sceneTemplate );
smartScript . ProcessEventsFor ( SMART_EVENT_SCENE_TRIGGER , player , 0 , 0 , false , nullptr , nullptr , triggerName );
}
void OnSceneCancel ( Player * player , uint32 /*sceneInstanceID*/ , SceneTemplate const * sceneTemplate ) override
{
SmartScript smartScript ;
smartScript . OnInitialize ( nullptr , nullptr , sceneTemplate );
smartScript . ProcessEventsFor ( SMART_EVENT_SCENE_CANCEL , player );
}
void OnSceneComplete ( Player * player , uint32 /*sceneInstanceID*/ , SceneTemplate const * sceneTemplate ) override
{
SmartScript smartScript ;
smartScript . OnInitialize ( nullptr , nullptr , sceneTemplate );
smartScript . ProcessEventsFor ( SMART_EVENT_SCENE_COMPLETE , player );
}
};
2021-03-06 15:20:39 +02:00
class SmartQuest : public QuestScript
{
public :
SmartQuest () : QuestScript ( "SmartQuest" ) { }
// Called when a quest status change
2021-05-09 15:45:29 +02:00
void OnQuestStatusChange ( Player * player , Quest const * quest , QuestStatus /*oldStatus*/ , QuestStatus newStatus ) override
2021-03-06 15:20:39 +02:00
{
SmartScript smartScript ;
smartScript . OnInitialize ( nullptr , nullptr , nullptr , quest );
switch ( newStatus )
{
case QUEST_STATUS_INCOMPLETE :
smartScript . ProcessEventsFor ( SMART_EVENT_QUEST_ACCEPTED , player );
break ;
case QUEST_STATUS_COMPLETE :
smartScript . ProcessEventsFor ( SMART_EVENT_QUEST_COMPLETION , player );
break ;
case QUEST_STATUS_FAILED :
smartScript . ProcessEventsFor ( SMART_EVENT_QUEST_FAIL , player );
break ;
case QUEST_STATUS_REWARDED :
smartScript . ProcessEventsFor ( SMART_EVENT_QUEST_REWARDED , player );
break ;
case QUEST_STATUS_NONE :
default :
break ;
}
}
// Called when a quest objective data change
2021-05-09 15:45:29 +02:00
void OnQuestObjectiveChange ( Player * player , Quest const * quest , QuestObjective const & objective , int32 /*oldAmount*/ , int32 /*newAmount*/ ) override
2021-03-06 15:20:39 +02:00
{
2021-05-09 20:21:32 +02:00
uint16 slot = player -> FindQuestSlot ( quest -> GetQuestId ());
if ( slot < MAX_QUEST_LOG_SIZE && player -> IsQuestObjectiveComplete ( slot , quest , objective ))
2021-03-06 15:20:39 +02:00
{
SmartScript smartScript ;
smartScript . OnInitialize ( nullptr , nullptr , nullptr , quest );
smartScript . ProcessEventsFor ( SMART_EVENT_QUEST_OBJ_COPLETETION , player , objective . ID );
}
}
};
2014-05-02 03:44:21 +02:00
void AddSC_SmartScripts ()
2010-10-27 21:01:47 +02:00
{
new SmartTrigger ();
2021-02-05 18:12:55 +01:00
new SmartAreaTriggerEntityScript ();
2017-03-17 21:21:35 +01:00
new SmartScene ();
2021-03-06 15:20:39 +02:00
new SmartQuest ();
2010-11-16 14:08:12 +01:00
}