2013-11-10 03:31:20 -03:30
/*
2020-01-02 06:44:10 +01:00
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
2008-10-14 11:57:03 -05:00
*
2013-11-10 03:31:20 -03:30
* 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.
2008-10-14 11:57:03 -05:00
*
2013-11-10 03:31:20 -03:30
* 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/>.
*/
2009-10-17 15:51:44 -07:00
2012-07-29 21:40:10 +02:00
#include "ScriptedCreature.h"
2017-05-28 16:34:44 +02:00
#include "AreaBoundary.h"
2017-06-04 01:00:45 +02:00
#include "DB2Stores.h"
2012-07-29 21:40:10 +02:00
#include "Cell.h"
#include "CellImpl.h"
2020-06-12 01:29:18 +02:00
#include "CreatureAIImpl.h"
2017-05-28 16:34:44 +02:00
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "InstanceScript.h"
2017-05-18 23:52:58 +02:00
#include "Log.h"
2017-06-04 01:00:45 +02:00
#include "MotionMaster.h"
#include "ObjectAccessor.h"
2021-12-19 18:12:12 +01:00
#include "PhasingHandler.h"
2017-05-28 16:34:44 +02:00
#include "Spell.h"
2017-06-04 01:00:45 +02:00
#include "SpellMgr.h"
#include "TemporarySummon.h"
2009-10-17 15:51:44 -07:00
2017-05-28 16:34:44 +02:00
void SummonList :: Summon ( Creature const * summon )
{
2019-05-15 01:33:55 +02:00
_storage . push_back ( summon -> GetGUID ());
2017-05-28 16:34:44 +02:00
}
void SummonList :: Despawn ( Creature const * summon )
{
2019-05-15 01:33:55 +02:00
_storage . remove ( summon -> GetGUID ());
2017-05-28 16:34:44 +02:00
}
2018-08-22 21:51:16 +02:00
void SummonList :: DoZoneInCombat ( uint32 entry )
2009-05-13 10:54:11 -05:00
{
2019-05-15 01:33:55 +02:00
for ( StorageType :: iterator i = _storage . begin (); i != _storage . end ();)
2009-05-13 10:54:11 -05:00
{
2019-05-15 01:33:55 +02:00
Creature * summon = ObjectAccessor :: GetCreature ( * _me , * i );
2009-05-13 10:54:11 -05:00
++ i ;
2018-08-23 14:33:28 +02:00
if ( summon && summon -> IsAIEnabled ()
2013-03-21 11:06:05 +01:00
&& ( ! entry || summon -> GetEntry () == entry ))
{
2018-08-22 21:51:16 +02:00
summon -> AI () -> DoZoneInCombat ( nullptr );
2013-03-21 11:06:05 +01:00
}
2009-05-13 10:54:11 -05:00
}
}
2009-10-17 15:51:44 -07:00
2008-11-03 10:16:06 -06:00
void SummonList :: DespawnEntry ( uint32 entry )
{
2019-05-15 01:33:55 +02:00
for ( StorageType :: iterator i = _storage . begin (); i != _storage . end ();)
2008-11-03 10:16:06 -06:00
{
2019-05-15 01:33:55 +02:00
Creature * summon = ObjectAccessor :: GetCreature ( * _me , * i );
2010-04-07 19:14:10 +02:00
if ( ! summon )
2019-05-15 01:33:55 +02:00
i = _storage . erase ( i );
2010-04-07 19:14:10 +02:00
else if ( summon -> GetEntry () == entry )
2008-11-03 10:16:06 -06:00
{
2019-05-15 01:33:55 +02:00
i = _storage . erase ( i );
2011-10-05 20:48:34 +02:00
summon -> DespawnOrUnsummon ();
2008-11-03 10:16:06 -06:00
}
else
2009-04-22 23:03:49 -05:00
++ i ;
2008-11-03 10:16:06 -06:00
}
}
2009-10-17 15:51:44 -07:00
2008-11-03 10:16:06 -06:00
void SummonList :: DespawnAll ()
{
2019-05-15 01:33:55 +02:00
while ( ! _storage . empty ())
2008-11-03 10:16:06 -06:00
{
2019-05-15 01:33:55 +02:00
Creature * summon = ObjectAccessor :: GetCreature ( * _me , _storage . front ());
_storage . pop_front ();
2013-03-21 11:06:05 +01:00
if ( summon )
2011-10-05 20:48:34 +02:00
summon -> DespawnOrUnsummon ();
2008-11-03 10:16:06 -06:00
}
}
2009-10-17 15:51:44 -07:00
2011-03-04 23:53:11 +01:00
void SummonList :: RemoveNotExisting ()
{
2019-05-15 01:33:55 +02:00
for ( StorageType :: iterator i = _storage . begin (); i != _storage . end ();)
2011-03-04 23:53:11 +01:00
{
2019-05-15 01:33:55 +02:00
if ( ObjectAccessor :: GetCreature ( * _me , * i ))
2011-03-04 23:53:11 +01:00
++ i ;
else
2019-05-15 01:33:55 +02:00
i = _storage . erase ( i );
2011-03-04 23:53:11 +01:00
}
}
2012-11-20 09:29:52 +01:00
bool SummonList :: HasEntry ( uint32 entry ) const
2011-03-04 23:53:11 +01:00
{
2020-02-29 13:22:51 +01:00
for ( ObjectGuid const & guid : _storage )
2011-03-04 23:53:11 +01:00
{
2020-02-29 13:22:51 +01:00
Creature * summon = ObjectAccessor :: GetCreature ( * _me , guid );
2012-11-20 09:29:52 +01:00
if ( summon && summon -> GetEntry () == entry )
2011-03-04 23:53:11 +01:00
return true ;
}
return false ;
}
2017-05-20 00:09:37 +02:00
void SummonList :: DoActionImpl ( int32 action , StorageType const & summons )
{
2020-02-29 13:22:51 +01:00
for ( ObjectGuid const & guid : summons )
2017-05-20 00:09:37 +02:00
{
2019-05-15 01:33:55 +02:00
Creature * summon = ObjectAccessor :: GetCreature ( * _me , guid );
2018-08-23 14:33:28 +02:00
if ( summon && summon -> IsAIEnabled ())
2017-05-20 00:09:37 +02:00
summon -> AI () -> DoAction ( action );
}
}
2021-10-22 12:51:45 +02:00
ScriptedAI :: ScriptedAI ( Creature * creature ) : ScriptedAI ( creature , creature -> GetScriptId ()) { }
2019-05-15 19:23:28 +02:00
ScriptedAI :: ScriptedAI ( Creature * creature , uint32 scriptId ) : CreatureAI ( creature , scriptId ), IsFleeing ( false ), _isCombatMovementAllowed ( true )
2009-04-29 00:15:18 -05:00
{
2011-03-19 10:54:16 +01:00
_isHeroic = me -> GetMap () -> IsHeroic ();
2018-09-15 17:55:26 +02:00
_difficulty = me -> GetMap () -> GetDifficultyID ();
2009-04-29 00:15:18 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
void ScriptedAI :: AttackStartNoMove ( Unit * who )
2008-10-25 11:40:10 -05:00
{
2011-08-18 14:55:27 -03:00
if ( ! who )
2008-10-25 11:40:10 -05:00
return ;
2009-10-17 15:51:44 -07:00
2013-02-04 19:18:48 -03:00
if ( me -> Attack ( who , true ))
2011-08-18 14:55:27 -03:00
DoStartNoMovement ( who );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2013-02-03 14:46:17 -03:00
void ScriptedAI :: AttackStart ( Unit * who )
{
if ( IsCombatMovementAllowed ())
CreatureAI :: AttackStart ( who );
else
AttackStartNoMove ( who );
}
2013-02-19 17:35:27 +01:00
void ScriptedAI :: UpdateAI ( uint32 /*diff*/ )
2008-10-11 14:16:25 -05:00
{
2019-05-15 19:23:28 +02:00
// Check if we have a current target
2009-08-14 19:45:06 +02:00
if ( ! UpdateVictim ())
return ;
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
DoMeleeAttackIfReady ();
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
void ScriptedAI :: DoStartMovement ( Unit * victim , float distance , float angle )
2008-10-11 14:16:25 -05:00
{
2011-08-18 14:55:27 -03:00
if ( victim )
me -> GetMotionMaster () -> MoveChase ( victim , distance , angle );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
void ScriptedAI :: DoStartNoMovement ( Unit * victim )
2008-10-11 14:16:25 -05:00
{
2011-08-18 14:55:27 -03:00
if ( ! victim )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-14 23:07:41 +02:00
me -> GetMotionMaster () -> MoveIdle ();
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void ScriptedAI :: DoStopAttack ()
{
2013-06-11 19:54:27 -02:30
if ( me -> GetVictim ())
2010-04-14 23:07:41 +02:00
me -> AttackStop ();
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
void ScriptedAI :: DoCastSpell ( Unit * target , SpellInfo const * spellInfo , bool triggered )
2008-10-11 14:16:25 -05:00
{
2013-12-25 14:16:55 -03:30
if ( ! target || me -> IsNonMeleeSpellCast ( false ))
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-14 23:07:41 +02:00
me -> StopMoving ();
2017-12-30 20:28:41 +01:00
me -> CastSpell ( target , spellInfo -> Id , triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
void ScriptedAI :: DoPlaySoundToSet ( WorldObject * source , uint32 soundId )
2008-10-11 14:16:25 -05:00
{
2011-08-18 14:55:27 -03:00
if ( ! source )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2016-01-24 15:56:10 +01:00
if ( ! sSoundKitStore . LookupEntry ( soundId ))
2008-10-11 14:16:25 -05:00
{
2019-05-15 19:23:28 +02:00
TC_LOG_ERROR ( "scripts.ai" , "ScriptedAI::DoPlaySoundToSet: Invalid soundId %u used in DoPlaySoundToSet (Source: %s)" , soundId , source -> GetGUID (). ToString (). c_str ());
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
source -> PlayDirectSound ( soundId );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-07-01 20:18:02 +02:00
void ScriptedAI :: AddThreat ( Unit * victim , float amount , Unit * who )
{
if ( ! victim )
return ;
if ( ! who )
who = me ;
who -> GetThreatManager (). AddThreat ( victim , amount , nullptr , true , true );
}
void ScriptedAI :: ModifyThreatByPercent ( Unit * victim , int32 pct , Unit * who )
{
if ( ! victim )
return ;
if ( ! who )
who = me ;
who -> GetThreatManager (). ModifyThreatByPercent ( victim , pct );
}
void ScriptedAI :: ResetThreat ( Unit * victim , Unit * who )
{
if ( ! victim )
return ;
if ( ! who )
who = me ;
who -> GetThreatManager (). ResetThreat ( victim );
}
void ScriptedAI :: ResetThreatList ( Unit * who )
{
if ( ! who )
who = me ;
who -> GetThreatManager (). ResetAllThreat ();
}
float ScriptedAI :: GetThreat ( Unit const * victim , Unit const * who )
{
if ( ! victim )
return 0.0f ;
if ( ! who )
who = me ;
return who -> GetThreatManager (). GetThreat ( victim );
}
2019-10-22 13:36:23 +02:00
void ScriptedAI :: ForceCombatStop ( Creature * who , bool reset /*= true*/ )
{
if ( ! who || ! who -> IsInCombat ())
return ;
who -> CombatStop ( true );
who -> DoNotReacquireSpellFocusTarget ();
who -> GetMotionMaster () -> Clear ( MOTION_PRIORITY_NORMAL );
2021-12-19 18:12:12 +01:00
if ( reset )
{
2019-10-22 13:36:23 +02:00
who -> LoadCreaturesAddon ();
who -> SetLootRecipient ( nullptr );
who -> ResetPlayerDamageReq ();
who -> SetLastDamagedTime ( 0 );
who -> SetCannotReachTarget ( false );
}
}
2021-12-19 18:12:12 +01:00
void ScriptedAI :: ForceCombatStopForCreatureEntry ( uint32 entry , float maxSearchRange /*= 250.0f*/ , bool samePhase /*= true*/ , bool reset /*= true*/ )
2019-10-22 13:36:23 +02:00
{
TC_LOG_DEBUG ( "scripts.ai" , "ScriptedAI::ForceCombatStopForCreatureEntry: called on '%s'. Debug info: %s" , me -> GetGUID (). ToString (). c_str (), me -> GetDebugInfo (). c_str ());
std :: list < Creature *> creatures ;
Trinity :: AllCreaturesOfEntryInRange check ( me , entry , maxSearchRange );
Trinity :: CreatureListSearcher < Trinity :: AllCreaturesOfEntryInRange > searcher ( me , creatures , check );
2021-12-19 18:12:12 +01:00
if ( ! samePhase )
PhasingHandler :: SetAlwaysVisible ( me , true , false );
2019-10-22 13:36:23 +02:00
Cell :: VisitGridObjects ( me , searcher , maxSearchRange );
2021-12-19 18:12:12 +01:00
if ( ! samePhase )
PhasingHandler :: SetAlwaysVisible ( me , false , false );
2019-10-22 13:36:23 +02:00
for ( Creature * creature : creatures )
ForceCombatStop ( creature , reset );
}
2019-11-25 20:43:23 +01:00
void ScriptedAI :: ForceCombatStopForCreatureEntry ( std :: vector < uint32 > creatureEntries , float maxSearchRange /*= 250.0f*/ , bool samePhase /*= true*/ , bool reset /*= true*/ )
{
for ( uint32 const entry : creatureEntries )
ForceCombatStopForCreatureEntry ( entry , maxSearchRange , samePhase , reset );
}
2011-03-19 10:54:16 +01:00
Creature * ScriptedAI :: DoSpawnCreature ( uint32 entry , float offsetX , float offsetY , float offsetZ , float angle , uint32 type , uint32 despawntime )
2008-10-11 14:16:25 -05:00
{
2011-03-19 10:54:16 +01:00
return me -> SummonCreature ( entry , me -> GetPositionX () + offsetX , me -> GetPositionY () + offsetY , me -> GetPositionZ () + offsetZ , angle , TempSummonType ( type ), despawntime );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-05-28 16:34:44 +02:00
bool ScriptedAI :: HealthBelowPct ( uint32 pct ) const
{
return me -> HealthBelowPct ( pct );
}
bool ScriptedAI :: HealthAbovePct ( uint32 pct ) const
{
return me -> HealthAbovePct ( pct );
}
2015-03-08 13:31:57 +01:00
SpellInfo const * ScriptedAI :: SelectSpell ( Unit * target , uint32 school , uint32 mechanic , SelectTargetType targets , float rangeMin , float rangeMax , SelectEffect effect )
2008-10-11 14:16:25 -05:00
{
2019-05-15 19:23:28 +02:00
// No target so we can't cast
2011-03-19 10:54:16 +01:00
if ( ! target )
2016-05-11 23:24:26 +02:00
return nullptr ;
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// Silenced so we can't cast
2019-06-03 20:40:34 +02:00
if ( me -> HasUnitFlag ( UNIT_FLAG_SILENCED ))
2016-05-11 23:24:26 +02:00
return nullptr ;
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// Using the extended script system we first create a list of viable spells
2016-03-24 16:12:53 +01:00
SpellInfo const * apSpell [ MAX_CREATURE_SPELLS ];
memset ( apSpell , 0 , MAX_CREATURE_SPELLS * sizeof ( SpellInfo * ));
2009-10-17 15:51:44 -07:00
2011-03-19 10:54:16 +01:00
uint32 spellCount = 0 ;
2009-10-17 15:51:44 -07:00
2016-05-11 23:24:26 +02:00
SpellInfo const * tempSpell = nullptr ;
2020-06-12 01:29:18 +02:00
AISpellInfoType const * aiSpell = nullptr ;
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// Check if each spell is viable(set it to null if not)
2020-02-29 13:22:51 +01:00
for ( uint32 spell : me -> m_spells )
2008-10-11 14:16:25 -05:00
{
2020-02-29 13:22:51 +01:00
tempSpell = sSpellMgr -> GetSpellInfo ( spell , me -> GetMap () -> GetDifficultyID ());
aiSpell = GetAISpellInfo ( spell , me -> GetMap () -> GetDifficultyID ());
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// This spell doesn't exist
2020-06-12 01:29:18 +02:00
if ( ! tempSpell || ! aiSpell )
2008-10-11 14:16:25 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// Targets and Effects checked first as most used restrictions
2019-05-15 19:23:28 +02:00
// Check the spell targets if specified
2020-06-12 01:29:18 +02:00
if ( targets && ! ( aiSpell -> Targets & ( 1 << ( targets - 1 ))))
2008-10-11 14:16:25 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// Check the type of spell if we are looking for a specific spell type
2020-06-12 01:29:18 +02:00
if ( effect && ! ( aiSpell -> Effects & ( 1 << ( effect - 1 ))))
2008-10-11 14:16:25 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// Check for school if specified
2011-03-19 10:54:16 +01:00
if ( school && ( tempSpell -> SchoolMask & school ) == 0 )
2008-10-11 14:16:25 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// Check for spell mechanic if specified
2011-03-19 10:54:16 +01:00
if ( mechanic && tempSpell -> Mechanic != mechanic )
2008-10-11 14:16:25 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// Continue if we don't have the mana to actually cast this spell
bool hasPower = true ;
for ( SpellPowerCost const & cost : tempSpell -> CalcPowerCost ( me , tempSpell -> GetSchoolMask ()))
{
if ( cost . Amount > me -> GetPower ( cost . Power ))
{
hasPower = false ;
break ;
}
}
if ( ! hasPower )
continue ;
// Check if the spell meets our range requirements
2011-07-26 23:09:28 +02:00
if ( rangeMin && me -> GetSpellMinRangeForTarget ( target , tempSpell ) < rangeMin )
2008-10-11 14:16:25 -05:00
continue ;
2011-07-26 23:09:28 +02:00
if ( rangeMax && me -> GetSpellMaxRangeForTarget ( target , tempSpell ) > rangeMax )
2008-10-11 14:16:25 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// Check if our target is in range
2011-07-26 23:09:28 +02:00
if ( me -> IsWithinDistInMap ( target , float ( me -> GetSpellMinRangeForTarget ( target , tempSpell ))) || ! me -> IsWithinDistInMap ( target , float ( me -> GetSpellMaxRangeForTarget ( target , tempSpell ))))
2008-10-11 14:16:25 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// All good so lets add it to the spell list
2011-03-19 10:54:16 +01:00
apSpell [ spellCount ] = tempSpell ;
++ spellCount ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2019-05-15 19:23:28 +02:00
// We got our usable spells so now lets randomly pick one
2011-03-19 10:54:16 +01:00
if ( ! spellCount )
2016-05-11 23:24:26 +02:00
return nullptr ;
2009-10-17 15:51:44 -07:00
2011-03-19 10:54:16 +01:00
return apSpell [ urand ( 0 , spellCount - 1 )];
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
void ScriptedAI :: DoTeleportTo ( float x , float y , float z , uint32 time )
2009-02-23 13:14:04 -06:00
{
2011-08-18 14:55:27 -03:00
me -> Relocate ( x , y , z );
2012-01-14 15:34:41 +01:00
float speed = me -> GetDistance ( x , y , z ) / (( float ) time * 0.001f );
me -> MonsterMoveWithSpeed ( x , y , z , speed );
2009-02-23 13:14:04 -06:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
void ScriptedAI :: DoTeleportTo ( const float position [ 4 ])
2009-05-13 10:54:11 -05:00
{
2011-08-18 14:55:27 -03:00
me -> NearTeleportTo ( position [ 0 ], position [ 1 ], position [ 2 ], position [ 3 ]);
2009-05-13 10:54:11 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
void ScriptedAI :: DoTeleportPlayer ( Unit * unit , float x , float y , float z , float o )
2008-10-11 14:16:25 -05:00
{
2012-05-30 08:01:02 +02:00
if ( ! unit )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
if ( Player * player = unit -> ToPlayer ())
player -> TeleportTo ( unit -> GetMapId (), x , y , z , o , TELE_TO_NOT_LEAVE_COMBAT );
else
2019-05-15 19:23:28 +02:00
TC_LOG_ERROR ( "scripts.ai" , "ScriptedAI::DoTeleportPlayer: Creature %s Tried to teleport non-player unit (%s) to x : % f y : % f z : % f o : % f . Aborted . ",
2014-09-16 19:47:10 +02:00
me -> GetGUID (). ToString (). c_str (), unit -> GetGUID (). ToString (). c_str (), x , y , z , o );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
void ScriptedAI :: DoTeleportAll ( float x , float y , float z , float o )
2008-11-18 19:40:06 -06:00
{
2011-08-18 14:55:27 -03:00
Map * map = me -> GetMap ();
2008-11-18 19:40:06 -06:00
if ( ! map -> IsDungeon ())
return ;
2009-10-17 15:51:44 -07:00
2020-02-29 13:22:51 +01:00
for ( MapReference const & mapref : map -> GetPlayers ())
if ( Player * player = mapref . GetSource ())
2013-06-11 19:54:27 -02:30
if ( player -> IsAlive ())
2011-08-18 14:55:27 -03:00
player -> TeleportTo ( me -> GetMapId (), x , y , z , o , TELE_TO_NOT_LEAVE_COMBAT );
2008-11-18 19:40:06 -06:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
Unit * ScriptedAI :: DoSelectLowestHpFriendly ( float range , uint32 minHPDiff )
2008-10-11 14:16:25 -05:00
{
2016-05-11 23:24:26 +02:00
Unit * unit = nullptr ;
2011-08-18 14:55:27 -03:00
Trinity :: MostHPMissingInRange u_check ( me , range , minHPDiff );
Trinity :: UnitLastSearcher < Trinity :: MostHPMissingInRange > searcher ( me , unit , u_check );
2017-05-13 19:37:33 +02:00
Cell :: VisitAllObjects ( me , searcher , range );
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
return unit ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2016-11-27 20:28:06 -02:00
Unit * ScriptedAI :: DoSelectBelowHpPctFriendlyWithEntry ( uint32 entry , float range , uint8 minHPDiff , bool excludeSelf )
{
Unit * unit = nullptr ;
Trinity :: FriendlyBelowHpPctEntryInRange u_check ( me , entry , range , minHPDiff , excludeSelf );
Trinity :: UnitLastSearcher < Trinity :: FriendlyBelowHpPctEntryInRange > searcher ( me , unit , u_check );
Cell :: VisitAllObjects ( me , searcher , range );
return unit ;
}
2011-08-18 14:55:27 -03:00
std :: list < Creature *> ScriptedAI :: DoFindFriendlyCC ( float range )
2008-10-11 14:16:25 -05:00
{
2011-08-18 14:55:27 -03:00
std :: list < Creature *> list ;
Trinity :: FriendlyCCedInRange u_check ( me , range );
Trinity :: CreatureListSearcher < Trinity :: FriendlyCCedInRange > searcher ( me , list , u_check );
2017-05-13 19:37:33 +02:00
Cell :: VisitAllObjects ( me , searcher , range );
2012-10-26 14:30:52 +02:00
2011-08-18 14:55:27 -03:00
return list ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
std :: list < Creature *> ScriptedAI :: DoFindFriendlyMissingBuff ( float range , uint32 uiSpellid )
2008-10-11 14:16:25 -05:00
{
2011-08-18 14:55:27 -03:00
std :: list < Creature *> list ;
Trinity :: FriendlyMissingBuffInRange u_check ( me , range , uiSpellid );
Trinity :: CreatureListSearcher < Trinity :: FriendlyMissingBuffInRange > searcher ( me , list , u_check );
2017-05-13 19:37:33 +02:00
Cell :: VisitAllObjects ( me , searcher , range );
2012-10-26 14:30:52 +02:00
2011-08-18 14:55:27 -03:00
return list ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
Player * ScriptedAI :: GetPlayerAtMinimumRange ( float minimumRange )
2009-07-24 05:27:49 +02:00
{
2016-05-11 23:24:26 +02:00
Player * player = nullptr ;
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
Trinity :: PlayerAtMinimumRangeAway check ( me , minimumRange );
Trinity :: PlayerSearcher < Trinity :: PlayerAtMinimumRangeAway > searcher ( me , player , check );
2017-05-13 19:37:33 +02:00
Cell :: VisitWorldObjects ( me , searcher , minimumRange );
2009-10-17 15:51:44 -07:00
2011-08-18 14:55:27 -03:00
return player ;
2009-07-24 05:27:49 +02:00
}
2009-10-17 15:51:44 -07:00
2011-03-19 10:54:16 +01:00
void ScriptedAI :: SetEquipmentSlots ( bool loadDefault , int32 mainHand /*= EQUIP_NO_CHANGE*/ , int32 offHand /*= EQUIP_NO_CHANGE*/ , int32 ranged /*= EQUIP_NO_CHANGE*/ )
2009-04-19 21:16:11 +02:00
{
2011-03-19 10:54:16 +01:00
if ( loadDefault )
2009-04-19 21:16:11 +02:00
{
2013-02-18 12:24:18 +00:00
me -> LoadEquipment ( me -> GetOriginalEquipmentId (), true );
2009-04-19 21:16:11 +02:00
return ;
}
2009-10-17 15:51:44 -07:00
2011-03-19 10:54:16 +01:00
if ( mainHand >= 0 )
2015-07-01 02:22:03 +02:00
me -> SetVirtualItem ( 0 , uint32 ( mainHand ));
2009-10-17 15:51:44 -07:00
2011-03-19 10:54:16 +01:00
if ( offHand >= 0 )
2015-07-01 02:22:03 +02:00
me -> SetVirtualItem ( 1 , uint32 ( offHand ));
2009-10-17 15:51:44 -07:00
2011-03-19 10:54:16 +01:00
if ( ranged >= 0 )
2015-07-01 02:22:03 +02:00
me -> SetVirtualItem ( 2 , uint32 ( ranged ));
2009-04-19 21:16:11 +02:00
}
2009-10-17 15:51:44 -07:00
2011-03-19 10:54:16 +01:00
void ScriptedAI :: SetCombatMovement ( bool allowMovement )
2009-05-19 21:03:43 +02:00
{
2011-03-19 10:54:16 +01:00
_isCombatMovementAllowed = allowMovement ;
2009-05-19 21:03:43 +02:00
}
2009-10-17 15:51:44 -07:00
2011-05-28 00:28:04 +02:00
// BossAI - for instanced bosses
2019-05-15 19:23:28 +02:00
BossAI :: BossAI ( Creature * creature , uint32 bossId ) : ScriptedAI ( creature ), instance ( creature -> GetInstanceScript ()), summons ( creature ), _bossId ( bossId )
2015-08-23 14:43:11 +02:00
{
2016-01-13 15:33:17 +01:00
if ( instance )
SetBoundary ( instance -> GetBossBoundary ( bossId ));
2015-08-23 14:43:11 +02:00
scheduler . SetValidator ([ this ]
{
return ! me -> HasUnitState ( UNIT_STATE_CASTING );
});
}
2009-10-17 15:51:44 -07:00
2009-05-10 22:48:22 -05:00
void BossAI :: _Reset ()
{
2013-06-11 19:54:27 -02:30
if ( ! me -> IsAlive ())
2009-06-13 11:23:50 -05:00
return ;
2009-10-17 15:51:44 -07:00
2015-09-13 22:02:38 +02:00
me -> SetCombatPulseDelay ( 0 );
2011-03-13 22:28:37 +01:00
me -> ResetLootMode ();
2009-05-10 22:48:22 -05:00
events . Reset ();
summons . DespawnAll ();
2015-08-23 14:43:11 +02:00
scheduler . CancelAll ();
2017-05-07 21:48:41 +01:00
if ( instance && instance -> GetBossState ( _bossId ) != DONE )
2011-03-19 10:54:16 +01:00
instance -> SetBossState ( _bossId , NOT_STARTED );
2009-05-10 22:48:22 -05:00
}
2009-10-17 15:51:44 -07:00
2009-05-10 22:48:22 -05:00
void BossAI :: _JustDied ()
{
events . Reset ();
summons . DespawnAll ();
2015-08-23 14:43:11 +02:00
scheduler . CancelAll ();
2010-04-07 19:14:10 +02:00
if ( instance )
2011-03-19 10:54:16 +01:00
instance -> SetBossState ( _bossId , DONE );
2009-05-10 22:48:22 -05:00
}
2009-10-17 15:51:44 -07:00
2017-05-28 16:34:44 +02:00
void BossAI :: _JustReachedHome ()
{
me -> setActive ( false );
}
2020-01-14 23:53:08 +01:00
void BossAI :: _JustEngagedWith ( Unit * who )
2009-05-10 22:48:22 -05:00
{
2010-04-07 19:14:10 +02:00
if ( instance )
2011-01-01 23:37:35 +01:00
{
// bosses do not respawn, check only on enter combat
2020-01-14 23:53:08 +01:00
if ( ! instance -> CheckRequiredBosses ( _bossId , who -> ToPlayer ()))
2011-01-01 23:37:35 +01:00
{
2016-01-13 15:33:17 +01:00
EnterEvadeMode ( EVADE_REASON_SEQUENCE_BREAK );
2011-01-01 23:37:35 +01:00
return ;
}
2011-03-19 10:54:16 +01:00
instance -> SetBossState ( _bossId , IN_PROGRESS );
2011-01-01 23:37:35 +01:00
}
2015-08-23 14:43:11 +02:00
me -> SetCombatPulseDelay ( 5 );
me -> setActive ( true );
DoZoneInCombat ();
ScheduleTasks ();
2009-05-10 22:48:22 -05:00
}
2009-10-17 15:51:44 -07:00
2009-05-26 19:55:41 -05:00
void BossAI :: TeleportCheaters ()
{
float x , y , z ;
me -> GetPosition ( x , y , z );
2012-10-26 14:30:52 +02:00
2018-01-03 20:04:19 +01:00
for ( auto const & pair : me -> GetCombatManager (). GetPvECombatRefs ())
{
Unit * target = pair . second -> GetOther ( me );
2018-03-08 18:41:46 +01:00
if ( target -> IsControlledByPlayer () && ! IsInBoundary ( target ))
2018-01-03 20:04:19 +01:00
target -> NearTeleportTo ( x , y , z , 0 );
}
2009-05-26 19:55:41 -05:00
}
2009-10-17 15:51:44 -07:00
2011-03-19 10:54:16 +01:00
void BossAI :: JustSummoned ( Creature * summon )
2009-05-10 22:48:22 -05:00
{
summons . Summon ( summon );
2017-07-01 20:18:02 +02:00
if ( me -> IsEngaged ())
2009-05-13 10:54:11 -05:00
DoZoneInCombat ( summon );
2009-05-10 22:48:22 -05:00
}
2009-10-17 15:51:44 -07:00
2011-03-19 10:54:16 +01:00
void BossAI :: SummonedCreatureDespawn ( Creature * summon )
2009-05-10 22:48:22 -05:00
{
summons . Despawn ( summon );
}
2009-10-17 15:51:44 -07:00
2013-02-19 17:35:27 +01:00
void BossAI :: UpdateAI ( uint32 diff )
2011-05-28 00:28:04 +02:00
{
if ( ! UpdateVictim ())
return ;
events . Update ( diff );
2012-02-03 19:12:41 +02:00
if ( me -> HasUnitState ( UNIT_STATE_CASTING ))
2011-05-28 00:28:04 +02:00
return ;
while ( uint32 eventId = events . ExecuteEvent ())
2016-09-03 17:21:22 +02:00
{
2011-05-28 00:28:04 +02:00
ExecuteEvent ( eventId );
2016-09-03 17:21:22 +02:00
if ( me -> HasUnitState ( UNIT_STATE_CASTING ))
return ;
}
2011-05-28 00:28:04 +02:00
DoMeleeAttackIfReady ();
}
2017-05-28 16:34:44 +02:00
bool BossAI :: CanAIAttack ( Unit const * target ) const
{
2018-03-08 18:41:46 +01:00
return IsInBoundary ( target );
2017-05-28 16:34:44 +02:00
}
2020-09-04 13:38:24 +02:00
void BossAI :: _DespawnAtEvade ( Seconds delayToRespawn /*= 30s*/ , Creature * who /*= nullptr*/ )
2012-12-31 21:14:54 +01:00
{
2017-05-07 21:48:41 +01:00
if ( delayToRespawn < Seconds ( 2 ))
2016-02-14 19:53:17 +01:00
{
2019-05-15 19:23:28 +02:00
TC_LOG_ERROR ( "scripts.ai" , "BossAI::_DespawnAtEvade: called with delay of " SI64FMTD " seconds, defaulting to 2 (me: %s)" , delayToRespawn . count (), me -> GetGUID (). ToString (). c_str ());
2017-05-07 21:48:41 +01:00
delayToRespawn = Seconds ( 2 );
2016-02-14 19:53:17 +01:00
}
2016-05-13 22:23:25 +02:00
if ( ! who )
who = me ;
2012-12-31 21:14:54 +01:00
2016-05-13 22:23:25 +02:00
if ( TempSummon * whoSummon = who -> ToTempSummon ())
{
2019-05-15 19:23:28 +02:00
TC_LOG_WARN ( "scripts.ai" , "BossAI::_DespawnAtEvade: called on a temporary summon (who: %s)" , who -> GetGUID (). ToString (). c_str ());
2016-05-13 22:23:25 +02:00
whoSummon -> UnSummon ();
return ;
}
2012-12-31 21:14:54 +01:00
2020-09-04 13:38:24 +02:00
who -> DespawnOrUnsummon ( 0 , delayToRespawn );
2016-05-13 22:23:25 +02:00
if ( instance && who == me )
2016-02-14 19:53:17 +01:00
instance -> SetBossState ( _bossId , FAIL );
2012-12-31 21:14:54 +01:00
}
2011-05-28 00:28:04 +02:00
// WorldBossAI - for non-instanced bosses
2019-05-15 19:23:28 +02:00
WorldBossAI :: WorldBossAI ( Creature * creature ) : ScriptedAI ( creature ), summons ( creature ) { }
2011-05-28 00:28:04 +02:00
void WorldBossAI :: _Reset ()
{
2013-06-11 19:54:27 -02:30
if ( ! me -> IsAlive ())
2011-05-28 00:28:04 +02:00
return ;
events . Reset ();
summons . DespawnAll ();
}
void WorldBossAI :: _JustDied ()
{
events . Reset ();
summons . DespawnAll ();
}
2017-12-31 03:23:42 +01:00
void WorldBossAI :: _JustEngagedWith ()
2011-05-28 00:28:04 +02:00
{
2020-06-16 23:08:56 +02:00
Unit * target = SelectTarget ( SelectTargetMethod :: Random , 0 , 0.0f , true );
2011-05-28 02:14:47 +02:00
if ( target )
2011-05-28 01:24:27 +02:00
AttackStart ( target );
2011-05-28 00:28:04 +02:00
}
void WorldBossAI :: JustSummoned ( Creature * summon )
{
summons . Summon ( summon );
2020-06-16 23:08:56 +02:00
Unit * target = SelectTarget ( SelectTargetMethod :: Random , 0 , 0.0f , true );
2011-06-12 02:30:32 +02:00
if ( target )
2011-05-28 01:24:27 +02:00
summon -> AI () -> AttackStart ( target );
2011-05-28 00:28:04 +02:00
}
void WorldBossAI :: SummonedCreatureDespawn ( Creature * summon )
{
summons . Despawn ( summon );
}
2013-02-19 17:35:27 +01:00
void WorldBossAI :: UpdateAI ( uint32 diff )
2011-05-25 18:50:44 +02:00
{
if ( ! UpdateVictim ())
return ;
events . Update ( diff );
2012-02-03 19:12:41 +02:00
if ( me -> HasUnitState ( UNIT_STATE_CASTING ))
2011-05-25 18:50:44 +02:00
return ;
while ( uint32 eventId = events . ExecuteEvent ())
2016-09-03 17:21:22 +02:00
{
2011-05-25 18:50:44 +02:00
ExecuteEvent ( eventId );
2016-09-03 17:21:22 +02:00
if ( me -> HasUnitState ( UNIT_STATE_CASTING ))
return ;
}
2011-05-25 18:50:44 +02:00
DoMeleeAttackIfReady ();
}