2010-10-07 15:35:36 +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-07 15:35:36 +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/>.
2008-10-11 14:16:25 -05:00
*/
2009-10-17 15:51:44 -07:00
2013-03-20 14:18:26 +01:00
#include "Creature.h"
2012-11-20 12:30:30 +01:00
#include "BattlegroundMgr.h"
#include "CellImpl.h"
2017-06-04 01:00:45 +02:00
#include "CombatPackets.h"
2008-10-11 14:16:25 -05:00
#include "Common.h"
#include "CreatureAI.h"
#include "CreatureAISelector.h"
2012-11-20 12:30:30 +01:00
#include "CreatureGroups.h"
#include "DatabaseEnv.h"
2018-11-30 17:57:32 +01:00
#include "DB2Stores.h"
2010-06-08 15:50:23 -06:00
#include "Formulas.h"
2012-11-20 12:30:30 +01:00
#include "GameEventMgr.h"
2017-03-02 02:19:25 +01:00
#include "GameTime.h"
2012-11-20 12:30:30 +01:00
#include "GossipDef.h"
2010-06-08 15:50:23 -06:00
#include "GridNotifiersImpl.h"
2010-11-29 07:50:31 +01:00
#include "Group.h"
2012-11-20 12:30:30 +01:00
#include "GroupMgr.h"
#include "InstanceScript.h"
#include "Log.h"
#include "LootMgr.h"
2017-06-04 01:00:45 +02:00
#include "MiscPackets.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
2012-11-20 12:30:30 +01:00
#include "ObjectMgr.h"
2018-01-28 11:37:20 +01:00
#include "PhasingHandler.h"
2012-11-20 12:30:30 +01:00
#include "Player.h"
#include "PoolMgr.h"
2019-07-27 02:52:13 +02:00
#include "QueryPackets.h"
2012-11-20 12:30:30 +01:00
#include "QuestDef.h"
2017-06-04 01:00:45 +02:00
#include "ScriptedGossip.h"
2012-11-20 12:30:30 +01:00
#include "SpellAuraEffects.h"
#include "SpellMgr.h"
#include "TemporarySummon.h"
2017-06-04 01:00:45 +02:00
#include "Transport.h"
2012-11-20 12:30:30 +01:00
#include "Util.h"
#include "Vehicle.h"
#include "World.h"
#include "WorldPacket.h"
2017-06-04 01:00:45 +02:00
#include <G3D/g3dmath.h>
2020-04-03 17:41:48 +02:00
#include <sstream>
2010-06-25 00:18:01 +02:00
2017-11-26 16:37:51 +01:00
std :: string CreatureMovementData :: ToString () const
{
char const * const GroundStates [] = { "None" , "Run" , "Hover" };
char const * const FlightStates [] = { "None" , "DisableGravity" , "CanFly" };
std :: ostringstream str ;
str << std :: boolalpha
<< "Ground: " << GroundStates [ AsUnderlyingType ( Ground )]
<< ", Swim: " << Swim
<< ", Flight: " << FlightStates [ AsUnderlyingType ( Flight )];
if ( Rooted )
str << ", Rooted" ;
return str . str ();
}
2017-12-10 12:39:37 +01:00
VendorItemCount :: VendorItemCount ( uint32 _item , uint32 _count )
: itemId ( _item ), count ( _count ), lastIncrementTime ( GameTime :: GetGameTime ()) { }
2017-06-04 01:00:45 +02:00
bool VendorItem :: IsGoldRequired ( ItemTemplate const * pProto ) const
{
return pProto -> GetFlags2 () & ITEM_FLAG2_DONT_IGNORE_BUY_PRICE || ! ExtendedCost ;
}
2012-08-14 18:11:06 +01:00
bool VendorItemData :: RemoveItem ( uint32 item_id , uint8 type )
2008-10-11 14:16:25 -05:00
{
2017-06-04 01:00:45 +02:00
auto newEnd = std :: remove_if ( m_items . begin (), m_items . end (), [ = ]( VendorItem const & vendorItem )
2008-10-11 14:16:25 -05:00
{
2017-06-04 01:00:45 +02:00
return vendorItem . item == item_id && vendorItem . Type == type ;
});
2020-09-04 13:38:24 +02:00
2017-06-04 01:00:45 +02:00
bool found = newEnd != m_items . end ();
m_items . erase ( newEnd , m_items . end ());
2010-04-30 22:35:37 +02:00
return found ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2012-08-14 18:11:06 +01:00
VendorItem const * VendorItemData :: FindItemCostPair ( uint32 item_id , uint32 extendedCost , uint8 type ) const
2008-10-11 14:16:25 -05:00
{
2017-06-04 01:00:45 +02:00
for ( VendorItem const & vendorItem : m_items )
if ( vendorItem . item == item_id && vendorItem . ExtendedCost == extendedCost && vendorItem . Type == type )
return & vendorItem ;
2016-04-02 02:50:44 +02:00
return nullptr ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2018-11-07 20:23:30 +01:00
CreatureModel const CreatureModel :: DefaultInvisibleModel ( 11686 , 1.0f , 1.0f );
CreatureModel const CreatureModel :: DefaultVisibleModel ( 17519 , 1.0f , 1.0f );
2009-10-17 15:51:44 -07:00
2018-11-07 20:23:30 +01:00
CreatureModel const * CreatureTemplate :: GetModelByIdx ( uint32 idx ) const
2008-10-27 11:48:45 -05:00
{
2018-11-07 20:23:30 +01:00
return idx < Models . size () ? & Models [ idx ] : nullptr ;
2008-10-27 11:48:45 -05:00
}
2009-10-17 15:51:44 -07:00
2018-11-07 20:23:30 +01:00
CreatureModel const * CreatureTemplate :: GetRandomValidModel () const
2015-12-05 18:47:35 +01:00
{
2018-11-07 20:23:30 +01:00
if ( ! Models . size ())
return nullptr ;
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
// If only one element, ignore the Probability (even if 0)
if ( Models . size () == 1 )
return & Models [ 0 ];
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
auto selectedItr = Trinity :: Containers :: SelectRandomWeightedContainerElement ( Models , []( CreatureModel const & model )
{
return model . Probability ;
});
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
return & ( * selectedItr );
}
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
CreatureModel const * CreatureTemplate :: GetFirstValidModel () const
{
for ( CreatureModel const & model : Models )
if ( model . CreatureDisplayID )
return & model ;
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
return nullptr ;
2015-12-05 18:47:35 +01:00
}
2018-11-07 20:23:30 +01:00
CreatureModel const * CreatureTemplate :: GetModelWithDisplayId ( uint32 displayId ) const
2015-12-05 18:47:35 +01:00
{
2018-11-07 20:23:30 +01:00
for ( CreatureModel const & model : Models )
if ( displayId == model . CreatureDisplayID )
return & model ;
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
return nullptr ;
}
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
CreatureModel const * CreatureTemplate :: GetFirstInvisibleModel () const
{
for ( CreatureModel const & model : Models )
if ( CreatureModelInfo const * modelInfo = sObjectMgr -> GetCreatureModelInfo ( model . CreatureDisplayID ))
if ( modelInfo && modelInfo -> is_trigger )
return & model ;
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
return & CreatureModel :: DefaultInvisibleModel ;
}
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
CreatureModel const * CreatureTemplate :: GetFirstVisibleModel () const
{
for ( CreatureModel const & model : Models )
if ( CreatureModelInfo const * modelInfo = sObjectMgr -> GetCreatureModelInfo ( model . CreatureDisplayID ))
if ( modelInfo && ! modelInfo -> is_trigger )
return & model ;
2015-12-05 18:47:35 +01:00
2018-11-07 20:23:30 +01:00
return & CreatureModel :: DefaultVisibleModel ;
2015-12-05 18:47:35 +01:00
}
2020-05-02 22:26:52 +02:00
std :: pair < int16 , int16 > CreatureTemplate :: GetMinMaxLevel () const
{
return
{
HealthScalingExpansion != EXPANSION_LEVEL_CURRENT ? minlevel : minlevel + MAX_LEVEL ,
HealthScalingExpansion != EXPANSION_LEVEL_CURRENT ? maxlevel : maxlevel + MAX_LEVEL
};
}
int32 CreatureTemplate :: GetHealthScalingExpansion () const
{
return HealthScalingExpansion == EXPANSION_LEVEL_CURRENT ? CURRENT_EXPANSION : HealthScalingExpansion ;
}
2019-07-27 02:52:13 +02:00
void CreatureTemplate :: InitializeQueryData ()
{
for ( uint8 loc = LOCALE_enUS ; loc < TOTAL_LOCALES ; ++ loc )
2018-04-19 17:35:57 +02:00
QueryData [ loc ] = BuildQueryData ( static_cast < LocaleConstant > ( loc ));
2019-07-27 02:52:13 +02:00
}
WorldPacket CreatureTemplate :: BuildQueryData ( LocaleConstant loc ) const
{
WorldPackets :: Query :: QueryCreatureResponse queryTemp ;
queryTemp . CreatureID = Entry ;
queryTemp . Allow = true ;
WorldPackets :: Query :: CreatureStats & stats = queryTemp . Stats ;
stats . Leader = RacialLeader ;
stats . Name [ 0 ] = Name ;
stats . NameAlt [ 0 ] = FemaleName ;
stats . Flags [ 0 ] = type_flags ;
stats . Flags [ 1 ] = type_flags2 ;
stats . CreatureType = type ;
stats . CreatureFamily = family ;
stats . Classification = rank ;
for ( uint32 i = 0 ; i < MAX_KILL_CREDIT ; ++ i )
stats . ProxyCreatureID [ i ] = KillCredit [ i ];
std :: transform ( Models . begin (), Models . end (), std :: back_inserter ( stats . Display . CreatureDisplay ),
[ & stats ]( CreatureModel const & model ) -> WorldPackets :: Query :: CreatureXDisplay
{
stats . Display . TotalProbability += model . Probability ;
return { model . CreatureDisplayID , model . DisplayScale , model . Probability };
});
stats . HpMulti = ModHealth ;
stats . EnergyMulti = ModMana ;
stats . CreatureMovementInfoID = movementId ;
stats . RequiredExpansion = RequiredExpansion ;
stats . HealthScalingExpansion = HealthScalingExpansion ;
stats . VignetteID = VignetteID ;
stats . Class = unit_class ;
2021-08-05 19:37:53 +02:00
stats . CreatureDifficultyID = CreatureDifficultyID ;
2019-07-27 02:52:13 +02:00
stats . WidgetSetID = WidgetSetID ;
stats . WidgetSetUnitConditionID = WidgetSetUnitConditionID ;
stats . Title = SubName ;
stats . TitleAlt = TitleAlt ;
stats . CursorName = IconName ;
if ( std :: vector < uint32 > const * items = sObjectMgr -> GetCreatureQuestItemList ( Entry ))
stats . QuestItems . insert ( stats . QuestItems . begin (), items -> begin (), items -> end ());
if ( loc != LOCALE_enUS )
if ( CreatureLocale const * creatureLocale = sObjectMgr -> GetCreatureLocale ( Entry ))
{
ObjectMgr :: GetLocaleString ( creatureLocale -> Name , loc , stats . Name [ 0 ]);
ObjectMgr :: GetLocaleString ( creatureLocale -> NameAlt , loc , stats . NameAlt [ 0 ]);
ObjectMgr :: GetLocaleString ( creatureLocale -> Title , loc , stats . Title );
ObjectMgr :: GetLocaleString ( creatureLocale -> TitleAlt , loc , stats . TitleAlt );
}
2018-04-19 17:35:57 +02:00
queryTemp . Write ();
2018-08-22 19:44:21 +02:00
queryTemp . ShrinkToFit ();
2018-04-19 17:35:57 +02:00
return queryTemp . Move ();
2019-07-27 02:52:13 +02:00
}
2018-11-30 17:57:32 +01:00
CreatureLevelScaling const * CreatureTemplate :: GetLevelScaling ( Difficulty difficulty ) const
{
auto it = scalingStore . find ( difficulty );
if ( it != scalingStore . end ())
return & it -> second ;
struct DefaultCreatureLevelScaling : public CreatureLevelScaling
{
DefaultCreatureLevelScaling ()
{
DeltaLevelMin = 0 ;
DeltaLevelMax = 0 ;
ContentTuningID = 0 ;
}
};
static const DefaultCreatureLevelScaling defScaling ;
return & defScaling ;
}
2008-11-25 16:36:50 -06:00
bool AssistDelayEvent :: Execute ( uint64 /*e_time*/ , uint32 /*p_time*/ )
{
2014-05-20 21:21:33 +02:00
if ( Unit * victim = ObjectAccessor :: GetUnit ( m_owner , m_victim ))
2008-11-25 16:36:50 -06:00
{
while ( ! m_assistants . empty ())
{
2014-05-20 21:21:33 +02:00
Creature * assistant = ObjectAccessor :: GetCreature ( m_owner , * m_assistants . begin ());
2008-11-25 16:36:50 -06:00
m_assistants . pop_front ();
2009-10-17 15:51:44 -07:00
2008-11-25 16:36:50 -06:00
if ( assistant && assistant -> CanAssistTo ( & m_owner , victim ))
{
2008-12-01 12:59:39 -06:00
assistant -> SetNoCallAssistance ( true );
2017-07-01 20:18:02 +02:00
assistant -> EngageWithTarget ( victim );
2008-11-25 16:36:50 -06:00
}
}
}
return true ;
}
2009-10-17 15:51:44 -07:00
2010-03-03 02:48:31 +02:00
CreatureBaseStats const * CreatureBaseStats :: GetBaseStats ( uint8 level , uint8 unitClass )
2010-01-10 22:02:03 +01:00
{
2010-12-22 21:25:23 +01:00
return sObjectMgr -> GetCreatureBaseStats ( level , unitClass );
2010-01-10 22:02:03 +01:00
}
2010-01-16 19:51:59 +03:00
bool ForcedDespawnDelayEvent :: Execute ( uint64 /*e_time*/ , uint32 /*p_time*/ )
{
2016-08-25 23:04:21 +02:00
m_owner . DespawnOrUnsummon ( 0 , m_respawnTimer ); // since we are here, we are not TempSummon as object type cannot change during runtime
2010-01-16 19:51:59 +03:00
return true ;
}
2017-08-21 20:28:18 +02:00
Creature :: Creature ( bool isWorldObject ) : Unit ( isWorldObject ), MapObject (), m_groupLootTimer ( 0 ), m_PlayerDamageReq ( 0 ), _pickpocketLootRestore ( 0 ),
m_corpseRemoveTime ( 0 ), m_respawnTime ( 0 ), m_respawnDelay ( 300 ), m_corpseDelay ( 60 ), m_respawnradius ( 0.0f ), m_boundaryCheckTime ( 2500 ), m_combatPulseTime ( 0 ), m_combatPulseDelay ( 0 ), m_reactState ( REACT_AGGRESSIVE ),
m_defaultMovementType ( IDLE_MOTION_TYPE ), m_spawnId ( UI64LIT ( 0 )), m_equipmentId ( 0 ), m_originalEquipmentId ( 0 ), m_AlreadyCallAssistance ( false ), m_AlreadySearchedAssistance ( false ), m_cannotReachTarget ( false ), m_cannotReachTimer ( 0 ),
2017-11-06 22:19:23 +01:00
m_AI_locked ( false ), m_meleeDamageSchoolMask ( SPELL_SCHOOL_MASK_NORMAL ), m_originalEntry ( 0 ), m_homePosition (), m_transportHomePosition (), m_creatureInfo ( nullptr ), m_creatureData ( nullptr ), _waypointPathId ( 0 ), _currentWaypointNodeInfo ( 0 , 0 ),
2017-08-21 20:28:18 +02:00
m_formation ( nullptr ), m_triggerJustAppeared ( true ), m_respawnCompatibilityMode ( false ), m_focusSpell ( nullptr ), m_focusDelay ( 0 ), m_shouldReacquireTarget ( false ), m_suppressedOrientation ( 0.0f ), _lastDamagedTime ( 0 ),
_regenerateHealth ( true ), _regenerateHealthLock ( false )
2008-10-11 14:16:25 -05:00
{
2010-01-10 01:23:15 +01:00
m_regenTimer = CREATURE_REGEN_INTERVAL ;
2009-10-17 15:51:44 -07:00
2016-03-24 16:12:53 +01:00
for ( uint8 i = 0 ; i < MAX_CREATURE_SPELLS ; ++ i )
2008-10-11 14:16:25 -05:00
m_spells [ i ] = 0 ;
2009-10-17 15:51:44 -07:00
2009-04-10 18:11:09 +02:00
DisableReputationGain = false ;
2009-10-17 15:51:44 -07:00
2010-12-23 23:25:44 +01:00
m_SightDistance = sWorld -> getFloatConfig ( CONFIG_SIGHT_MONSTER );
2009-08-23 16:27:00 -05:00
m_CombatDistance = 0 ; //MELEE_RANGE;
2009-10-28 16:31:19 -07:00
ResetLootMode (); // restore default loot mode
2011-12-20 17:05:08 -05:00
m_isTempWorldObject = false ;
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 Creature :: AddToWorld ()
{
///- Register the creature for guid lookup
2010-04-07 19:14:10 +02:00
if ( ! IsInWorld ())
2009-03-23 20:13:37 -06:00
{
2015-02-25 00:13:14 +01:00
GetMap () -> GetObjectsStore (). Insert < Creature > ( GetGUID (), this );
if ( m_spawnId )
GetMap () -> GetCreatureBySpawnIdStore (). insert ( std :: make_pair ( m_spawnId , this ));
2009-03-23 20:13:37 -06:00
Unit :: AddToWorld ();
2009-09-19 12:13:08 -07:00
SearchFormation ();
2009-04-06 20:33:52 -05:00
AIM_Initialize ();
2010-04-07 19:14:10 +02:00
if ( IsVehicle ())
2009-08-28 11:26:20 -05:00
GetVehicleKit () -> Install ();
2018-09-09 16:15:45 +02:00
2018-08-29 13:55:49 +02:00
if ( GetZoneScript ())
GetZoneScript () -> OnCreatureCreate ( this );
2009-03-23 20:13:37 -06:00
}
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 Creature :: RemoveFromWorld ()
{
2010-04-07 19:14:10 +02:00
if ( IsInWorld ())
2009-03-23 20:13:37 -06:00
{
2014-01-04 18:22:06 +01:00
if ( GetZoneScript ())
GetZoneScript () -> OnCreatureRemove ( this );
2015-02-25 00:13:14 +01:00
2010-04-07 19:14:10 +02:00
if ( m_formation )
2012-05-30 08:01:02 +02:00
sFormationMgr -> RemoveCreatureFromGroup ( m_formation , this );
2015-02-25 00:13:14 +01:00
2009-03-23 20:13:37 -06:00
Unit :: RemoveFromWorld ();
2015-02-25 00:13:14 +01:00
if ( m_spawnId )
Trinity :: Containers :: MultimapErasePair ( GetMap () -> GetCreatureBySpawnIdStore (), m_spawnId , this );
GetMap () -> GetObjectsStore (). Remove < Creature > ( GetGUID ());
2009-03-23 20:13:37 -06:00
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2009-08-14 22:26:07 -05:00
void Creature :: DisappearAndDie ()
{
2017-02-05 15:39:22 +01:00
ForcedDespawn ( 0 );
2009-08-14 22:26:07 -05:00
}
2009-10-17 15:51:44 -07:00
2017-08-12 01:40:25 +02:00
bool Creature :: IsReturningHome () const
{
2018-06-03 10:06:57 -07:00
if ( GetMotionMaster () -> GetCurrentMovementGeneratorType () == HOME_MOTION_TYPE )
2017-08-12 01:40:25 +02:00
return true ;
return false ;
}
2009-09-19 12:13:08 -07:00
void Creature :: SearchFormation ()
2008-12-15 00:07:21 +02:00
{
2013-06-11 19:54:27 -02:30
if ( IsSummon ())
2009-04-04 15:04:24 -06:00
return ;
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
ObjectGuid :: LowType lowguid = GetSpawnId ();
2010-04-07 19:14:10 +02:00
if ( ! lowguid )
2009-04-06 13:27:42 -05:00
return ;
2009-10-17 15:51:44 -07:00
2018-01-22 01:54:15 -03:00
if ( FormationInfo const * formationInfo = sFormationMgr -> GetFormationInfo ( lowguid ))
sFormationMgr -> AddCreatureToGroup ( formationInfo -> LeaderSpawnId , this );
2008-12-15 00:07:21 +02:00
}
2009-10-17 15:51:44 -07:00
2017-08-12 01:40:25 +02:00
bool Creature :: IsFormationLeader () const
{
if ( ! m_formation )
return false ;
return m_formation -> IsLeader ( this );
}
void Creature :: SignalFormationMovement ( Position const & destination , uint32 id /* = 0*/ , uint32 moveType /* = 0*/ , bool orientation /* = false*/ )
{
if ( ! m_formation )
return ;
if ( ! m_formation -> IsLeader ( this ))
return ;
m_formation -> LeaderMoveTo ( destination , id , moveType , orientation );
}
bool Creature :: IsFormationLeaderMoveAllowed () const
{
if ( ! m_formation )
return false ;
return m_formation -> CanLeaderStartMoving ();
}
2017-02-05 15:39:22 +01:00
void Creature :: RemoveCorpse ( bool setSpawnTime , bool destroyForNearbyPlayers )
2008-10-11 14:16:25 -05:00
{
2011-06-27 20:22:53 +02:00
if ( getDeathState () != CORPSE )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
if ( m_respawnCompatibilityMode )
{
2017-12-10 12:39:37 +01:00
m_corpseRemoveTime = GameTime :: GetGameTime ();
2017-05-07 21:48:41 +01:00
setDeathState ( DEAD );
RemoveAllAuras ();
loot . clear ();
uint32 respawnDelay = m_respawnDelay ;
2018-08-23 14:33:28 +02:00
if ( CreatureAI * ai = AI ())
ai -> CorpseRemoved ( respawnDelay );
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
if ( destroyForNearbyPlayers )
DestroyForNearbyPlayers ();
2017-02-05 15:39:22 +01:00
2017-05-07 21:48:41 +01:00
// Should get removed later, just keep "compatibility" with scripts
if ( setSpawnTime )
2017-12-10 12:39:37 +01:00
m_respawnTime = std :: max < time_t > ( GameTime :: GetGameTime () + respawnDelay , m_respawnTime );
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
// if corpse was removed during falling, the falling will continue and override relocation to respawn position
if ( IsFalling ())
StopMoving ();
2017-01-30 10:57:24 +01:00
2017-05-07 21:48:41 +01:00
float x , y , z , o ;
GetRespawnPosition ( x , y , z , & o );
2017-02-05 15:39:22 +01:00
2017-05-07 21:48:41 +01:00
// We were spawned on transport, calculate real position
if ( IsSpawnedOnTransport ())
{
Position & pos = m_movementInfo . transport . pos ;
pos . m_positionX = x ;
pos . m_positionY = y ;
pos . m_positionZ = z ;
pos . SetOrientation ( o );
if ( TransportBase * transport = GetDirectTransport ())
transport -> CalculatePassengerPosition ( x , y , z , & o );
}
2017-02-05 15:39:22 +01:00
2017-11-23 18:12:44 +01:00
UpdateAllowedPositionZ ( x , y , z );
2017-05-07 21:48:41 +01:00
SetHomePosition ( x , y , z , o );
GetMap () -> CreatureRelocation ( this , x , y , z , o );
2017-02-05 15:39:22 +01:00
}
2017-05-07 21:48:41 +01:00
else
{
// In case this is called directly and normal respawn timer not set
// Since this timer will be longer than the already present time it
// will be ignored if the correct place added a respawn timer
if ( setSpawnTime )
{
uint32 respawnDelay = m_respawnDelay ;
2017-12-10 12:39:37 +01:00
m_respawnTime = std :: max < time_t > ( GameTime :: GetGameTime () + respawnDelay , m_respawnTime );
2017-02-05 15:39:22 +01:00
2017-05-07 21:48:41 +01:00
SaveRespawnTime ( 0 , false );
}
2017-08-21 17:40:47 +02:00
if ( TempSummon * summon = ToTempSummon ())
summon -> UnSummon ();
else
AddObjectToRemoveList ();
2017-05-07 21:48:41 +01:00
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
/**
* change the entry of creature until respawn
*/
2014-04-28 20:05:36 +02:00
bool Creature :: InitEntry ( uint32 entry , CreatureData const * data /*= nullptr*/ )
2008-10-11 14:16:25 -05:00
{
2013-03-20 14:18:26 +01:00
CreatureTemplate const * normalInfo = sObjectMgr -> GetCreatureTemplate ( entry );
2010-04-07 19:14:10 +02:00
if ( ! normalInfo )
2008-10-11 14:16:25 -05:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "sql.sql" , "Creature::InitEntry creature entry %u does not exist." , entry );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2009-12-17 11:06:50 +01:00
// get difficulty 1 mode entry
2015-01-10 01:35:47 +01:00
CreatureTemplate const * cinfo = nullptr ;
2018-09-15 17:55:26 +02:00
DifficultyEntry const * difficultyEntry = sDifficultyStore . LookupEntry ( GetMap () -> GetDifficultyID ());
2015-01-10 01:35:47 +01:00
while ( ! cinfo && difficultyEntry )
2008-10-11 14:16:25 -05:00
{
2015-01-10 01:35:47 +01:00
int32 idx = CreatureTemplate :: DifficultyIDToDifficultyEntryIndex ( difficultyEntry -> ID );
if ( idx == - 1 )
break ;
2011-02-04 15:45:59 +01:00
2015-01-10 01:35:47 +01:00
if ( normalInfo -> DifficultyEntry [ idx ])
{
cinfo = sObjectMgr -> GetCreatureTemplate ( normalInfo -> DifficultyEntry [ idx ]);
break ;
2008-10-11 14:16:25 -05:00
}
2011-02-04 15:45:59 +01:00
2015-01-10 01:35:47 +01:00
if ( ! difficultyEntry -> FallbackDifficultyID )
break ;
difficultyEntry = sDifficultyStore . LookupEntry ( difficultyEntry -> FallbackDifficultyID );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2015-01-10 01:35:47 +01:00
if ( ! cinfo )
cinfo = normalInfo ;
2013-02-13 20:24:23 +01:00
// Initialize loot duplicate count depending on raid difficulty
2013-02-14 11:41:20 +01:00
if ( GetMap () -> Is25ManRaid ())
2013-02-13 20:24:23 +01:00
loot . maxDuplicates = 3 ;
2013-03-20 14:18:26 +01:00
SetEntry ( entry ); // normal entry always
2008-10-11 14:16:25 -05:00
m_creatureInfo = cinfo ; // map mode related always
2009-10-17 15:51:44 -07:00
2009-05-29 21:24:23 -05:00
// equal to player Race field, but creature does not have race
2019-06-03 20:40:34 +02:00
SetRace ( 0 );
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
// known valid are: CLASS_WARRIOR, CLASS_PALADIN, CLASS_ROGUE, CLASS_MAGE
2019-06-03 20:40:34 +02:00
SetClass ( uint8 ( cinfo -> unit_class ));
2009-10-17 15:51:44 -07:00
2008-10-27 11:48:45 -05:00
// Cancel load if no model defined
2018-11-07 20:23:30 +01:00
if ( ! ( cinfo -> GetFirstValidModel ()))
2008-10-11 14:16:25 -05:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "sql.sql" , "Creature (Entry: %u) has no model defined in table `creature_template`, can't load. " , entry );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2018-11-07 20:23:30 +01:00
CreatureModel model = * ObjectMgr :: ChooseDisplayId ( cinfo , data );
CreatureModelInfo const * minfo = sObjectMgr -> GetCreatureModelRandomGender ( & model , cinfo );
2009-06-15 18:02:12 -05:00
if ( ! minfo ) // Cancel load if no model defined
2008-10-11 14:16:25 -05:00
{
2018-11-07 20:23:30 +01:00
TC_LOG_ERROR ( "sql.sql" , "Creature (Entry: %u) has invalid model %u defined in table `creature_template`, can't load." , entry , model . CreatureDisplayID );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2018-11-07 20:23:30 +01:00
SetDisplayId ( model . CreatureDisplayID , model . DisplayScale );
SetNativeDisplayId ( model . CreatureDisplayID , model . DisplayScale );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// Load creature equipment
2016-05-29 18:51:58 +01:00
if ( ! data || data -> equipmentId == 0 )
LoadEquipment (); // use default equipment (if available)
2017-12-08 17:33:10 +01:00
else // override, 0 means no equipment
2013-02-20 01:17:00 +00:00
{
m_originalEquipmentId = data -> equipmentId ;
2008-10-11 14:16:25 -05:00
LoadEquipment ( data -> equipmentId );
2013-02-20 01:17:00 +00:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
SetName ( normalInfo -> Name ); // at normal entry always
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
SetModCastingSpeed ( 1.0f );
SetModSpellHaste ( 1.0f );
SetModHaste ( 1.0f );
SetModRangedHaste ( 1.0f );
SetModHasteRegen ( 1.0f );
SetModTimeRate ( 1.0f );
2009-10-17 15:51:44 -07:00
2016-04-16 01:36:32 +02:00
SetSpeedRate ( MOVE_WALK , cinfo -> speed_walk );
SetSpeedRate ( MOVE_RUN , cinfo -> speed_run );
SetSpeedRate ( MOVE_SWIM , 1.0f ); // using 1.0 rate
SetSpeedRate ( MOVE_FLIGHT , 1.0f ); // using 1.0 rate
2009-10-17 15:51:44 -07:00
2018-11-07 20:23:30 +01:00
// Will set UNIT_FIELD_BOUNDINGRADIUS, UNIT_FIELD_COMBATREACH and UNIT_FIELD_DISPLAYSCALE
2012-05-30 08:01:02 +02:00
SetObjectScale ( cinfo -> scale );
2019-06-03 20:40:34 +02:00
SetHoverHeight ( cinfo -> HoverHeight );
2009-10-17 15:51:44 -07:00
2017-12-07 17:41:11 +01:00
SetCanDualWield ( cinfo -> flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK );
2008-10-11 14:16:25 -05:00
// checked at loading
2017-02-28 16:39:59 -03:00
m_defaultMovementType = MovementGeneratorType ( data ? data -> movementType : cinfo -> MovementType );
2010-04-07 23:25:02 +02:00
if ( ! m_respawnradius && m_defaultMovementType == RANDOM_MOTION_TYPE )
2008-10-11 14:16:25 -05:00
m_defaultMovementType = IDLE_MOTION_TYPE ;
2009-10-17 15:51:44 -07:00
2017-03-08 11:19:00 -03:00
for ( uint8 i = 0 ; i < MAX_CREATURE_SPELLS ; ++ i )
2012-05-30 08:01:02 +02:00
m_spells [ i ] = GetCreatureTemplate () -> spells [ i ];
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2016-08-23 23:46:24 +02:00
bool Creature :: UpdateEntry ( uint32 entry , CreatureData const * data /*= nullptr*/ , bool updateLevel /* = true */ )
2008-10-11 14:16:25 -05:00
{
2014-04-28 20:05:36 +02:00
if ( ! InitEntry ( entry , data ))
2008-10-11 14:16:25 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
CreatureTemplate const * cInfo = GetCreatureTemplate ();
2010-01-10 22:02:03 +01:00
2017-08-21 20:28:18 +02:00
_regenerateHealth = cInfo -> RegenHealth ;
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
// creatures always have melee weapon ready if any unless specified otherwise
if ( ! GetCreatureAddon ())
SetSheath ( SHEATH_STATE_MELEE );
2009-10-17 15:51:44 -07:00
2017-04-28 02:24:04 -03:00
SetFaction ( cInfo -> faction );
2009-10-17 15:51:44 -07:00
2017-05-21 14:15:04 +02:00
uint64 npcFlags ;
uint32 unitFlags , unitFlags2 , unitFlags3 , dynamicFlags ;
ObjectMgr :: ChooseCreatureFlags ( cInfo , npcFlags , unitFlags , unitFlags2 , unitFlags3 , dynamicFlags , data );
2010-10-09 17:52:00 +02:00
2010-04-07 19:14:10 +02:00
if ( cInfo -> flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT )
2019-06-03 20:40:34 +02:00
npcFlags |= sGameEventMgr -> GetNPCFlag ( this );
SetNpcFlags ( NPCFlags ( npcFlags & 0xFFFFFFFF ));
SetNpcFlags2 ( NPCFlags2 ( npcFlags >> 32 ));
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
// if unit is in combat, keep this flag
unitFlags &= ~ UNIT_FLAG_IN_COMBAT ;
if ( IsInCombat ())
unitFlags |= UNIT_FLAG_IN_COMBAT ;
2019-06-03 20:40:34 +02:00
SetUnitFlags ( UnitFlags ( unitFlags ));
SetUnitFlags2 ( UnitFlags2 ( unitFlags2 ));
SetUnitFlags3 ( UnitFlags3 ( unitFlags3 ));
2010-10-09 17:52:00 +02:00
2019-06-03 20:40:34 +02:00
SetDynamicFlags ( dynamicFlags );
2009-10-17 15:51:44 -07:00
2020-03-10 18:43:34 +01:00
SetUpdateFieldValue ( m_values . ModifyValue ( & Unit :: m_unitData ). ModifyValue ( & UF :: UnitData :: StateAnimID ), sDB2Manager . GetEmptyAnimStateID ());
2018-10-04 18:50:21 +02:00
2017-12-07 17:41:11 +01:00
SetCanDualWield ( cInfo -> flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK );
2016-11-25 17:58:48 +01:00
SetBaseAttackTime ( BASE_ATTACK , cInfo -> BaseAttackTime );
SetBaseAttackTime ( OFF_ATTACK , cInfo -> BaseAttackTime );
SetBaseAttackTime ( RANGED_ATTACK , cInfo -> RangeAttackTime );
2014-01-04 18:22:06 +01:00
2016-08-23 23:46:24 +02:00
if ( updateLevel )
SelectLevel ();
2017-07-29 21:40:28 +02:00
else
UpdateLevelDependantStats (); // We still re-initialize level dependant stats on entry update
2016-09-24 03:50:20 +02:00
2010-01-10 22:02:03 +01:00
SetMeleeDamageSchool ( SpellSchools ( cInfo -> dmgschool ));
2017-02-27 14:20:32 -03:00
SetStatFlatModifier ( UNIT_MOD_RESISTANCE_HOLY , BASE_VALUE , float ( cInfo -> resistance [ SPELL_SCHOOL_HOLY ]));
SetStatFlatModifier ( UNIT_MOD_RESISTANCE_FIRE , BASE_VALUE , float ( cInfo -> resistance [ SPELL_SCHOOL_FIRE ]));
SetStatFlatModifier ( UNIT_MOD_RESISTANCE_NATURE , BASE_VALUE , float ( cInfo -> resistance [ SPELL_SCHOOL_NATURE ]));
SetStatFlatModifier ( UNIT_MOD_RESISTANCE_FROST , BASE_VALUE , float ( cInfo -> resistance [ SPELL_SCHOOL_FROST ]));
SetStatFlatModifier ( UNIT_MOD_RESISTANCE_SHADOW , BASE_VALUE , float ( cInfo -> resistance [ SPELL_SCHOOL_SHADOW ]));
SetStatFlatModifier ( UNIT_MOD_RESISTANCE_ARCANE , BASE_VALUE , float ( cInfo -> resistance [ SPELL_SCHOOL_ARCANE ]));
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
SetCanModifyStats ( true );
UpdateAllStats ();
2009-10-17 15:51:44 -07:00
2009-12-19 19:10:14 +01:00
// checked and error show at loading templates
2014-04-27 18:13:55 -04:00
if ( FactionTemplateEntry const * factionTemplate = sFactionTemplateStore . LookupEntry ( cInfo -> faction ))
2017-03-08 11:19:00 -03:00
SetPvP (( factionTemplate -> Flags & FACTION_TEMPLATE_FLAG_PVP ) != 0 );
2009-10-17 15:51:44 -07:00
2012-12-05 23:34:50 +02:00
// updates spell bars for vehicles and set player's faction - should be called here, to overwrite faction that is set from the new template
if ( IsVehicle ())
2012-12-17 05:39:04 +01:00
{
2012-12-05 23:34:50 +02:00
if ( Player * owner = Creature :: GetCharmerOrOwnerPlayerOrPlayerItself ()) // this check comes in case we don't have a player
2012-12-17 05:39:04 +01:00
{
2017-04-28 02:24:04 -03:00
SetFaction ( owner -> GetFaction ()); // vehicles should have same as owner faction
2012-12-17 05:39:04 +01:00
owner -> VehicleSpellInitialize ();
2012-12-05 23:34:50 +02:00
}
}
2010-04-21 13:55:43 +02:00
// trigger creature is always not selectable and can not be attacked
2013-06-11 19:54:27 -02:30
if ( IsTrigger ())
2019-06-03 20:40:34 +02:00
AddUnitFlag ( UNIT_FLAG_NOT_SELECTABLE );
2009-10-17 15:51:44 -07:00
2010-06-27 03:47:18 +02:00
InitializeReactState ();
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( cInfo -> flags_extra & CREATURE_FLAG_EXTRA_NO_TAUNT )
2009-01-27 15:23:26 -06:00
{
ApplySpellImmune ( 0 , IMMUNITY_STATE , SPELL_AURA_MOD_TAUNT , true );
2011-04-29 20:47:02 +02:00
ApplySpellImmune ( 0 , IMMUNITY_EFFECT , SPELL_EFFECT_ATTACK_ME , true );
2009-01-27 15:23:26 -06:00
}
2009-10-17 15:51:44 -07:00
2017-11-26 16:37:51 +01:00
if ( GetMovementTemplate (). IsRooted ())
2016-07-15 16:48:04 +02:00
SetControlled ( true , UNIT_STATE_ROOT );
2013-06-07 16:05:39 +02:00
UpdateMovementFlags ();
2015-11-22 18:30:42 +01:00
LoadCreaturesAddon ();
2017-12-18 10:59:47 -03:00
LoadTemplateImmunities ();
2018-01-03 20:04:19 +01:00
2018-08-27 19:08:17 +02:00
GetThreatManager (). EvaluateSuppressed ();
2008-10-11 14:16:25 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void Creature :: Update ( uint32 diff )
{
2018-08-23 14:33:28 +02:00
if ( IsAIEnabled () && m_triggerJustAppeared && m_deathState != DEAD )
2010-11-03 18:33:47 +01:00
{
2017-08-09 00:21:52 +02:00
if ( m_respawnCompatibilityMode && m_vehicleKit )
2011-04-22 21:59:37 +02:00
m_vehicleKit -> Reset ();
2017-08-21 17:40:47 +02:00
m_triggerJustAppeared = false ;
AI () -> JustAppeared ();
2010-11-03 18:33:47 +01:00
}
2013-06-07 16:05:39 +02:00
UpdateMovementFlags ();
2012-12-29 04:01:46 +01:00
2011-09-29 12:43:05 +02:00
switch ( m_deathState )
2008-10-11 14:16:25 -05:00
{
2012-05-30 08:01:02 +02:00
case JUST_RESPAWNED :
// Must not be called, see Creature::setDeathState JUST_RESPAWNED -> ALIVE promoting.
2014-10-22 17:33:55 +02:00
TC_LOG_ERROR ( "entities.unit" , "Creature (%s) in wrong state: JUST_RESPAWNED (4)" , GetGUID (). ToString (). c_str ());
2008-10-11 14:16:25 -05:00
break ;
case JUST_DIED :
2011-07-29 14:18:28 +02:00
// Must not be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
2017-05-07 21:48:41 +01:00
TC_LOG_ERROR ( "entities.unit" , "Creature (%s) in wrong state: JUST_DIED (1)" , GetGUID (). ToString (). c_str ());
2008-10-11 14:16:25 -05:00
break ;
case DEAD :
{
2018-02-22 20:31:42 +01:00
if ( ! m_respawnCompatibilityMode )
{
TC_LOG_ERROR ( "entities.unit" , "Creature (GUID: " UI64FMTD " Entry: %u) in wrong state: DEAD (3)" , GetGUID (). GetCounter (), GetEntry ());
break ;
}
2017-12-10 12:39:37 +01:00
time_t now = GameTime :: GetGameTime ();
2010-12-24 16:18:23 +01:00
if ( m_respawnTime <= now )
2008-10-11 14:16:25 -05:00
{
2018-02-22 20:31:42 +01:00
// Delay respawn if spawn group is not active
if ( m_creatureData && ! GetMap () -> IsSpawnGroupActive ( m_creatureData -> spawnGroupData -> groupId ))
2017-05-07 21:48:41 +01:00
{
m_respawnTime = now + urand ( 4 , 7 );
break ; // Will be rechecked on next Update call after delay expires
}
2010-12-24 16:18:23 +01:00
2015-02-25 00:13:14 +01:00
ObjectGuid dbtableHighGuid = ObjectGuid :: Create < HighGuid :: Creature > ( GetMapId (), GetEntry (), m_spawnId );
2017-05-07 21:48:41 +01:00
time_t linkedRespawnTime = GetMap () -> GetLinkedRespawnTime ( dbtableHighGuid );
if ( ! linkedRespawnTime ) // Can respawn
2009-04-14 14:33:48 +02:00
Respawn ();
2010-12-24 16:18:23 +01:00
else // the master is dead
2008-10-11 14:16:25 -05:00
{
2014-09-14 16:14:12 +02:00
ObjectGuid targetGuid = sObjectMgr -> GetLinkedRespawnGuid ( dbtableHighGuid );
2017-05-07 21:48:41 +01:00
if ( targetGuid == dbtableHighGuid ) // if linking self, never respawn
SetRespawnTime ( WEEK );
2009-04-14 14:33:48 +02:00
else
2017-04-11 23:23:52 -03:00
{
// else copy time from master and add a little
2017-05-07 21:48:41 +01:00
time_t baseRespawnTime = std :: max ( linkedRespawnTime , now );
2017-04-11 23:23:52 -03:00
time_t const offset = urand ( 5 , MINUTE );
// linked guid can be a boss, uses std::numeric_limits<time_t>::max to never respawn in that instance
// we shall inherit it instead of adding and causing an overflow
if ( baseRespawnTime <= std :: numeric_limits < time_t >:: max () - offset )
m_respawnTime = baseRespawnTime + offset ;
else
m_respawnTime = std :: numeric_limits < time_t >:: max ();
}
2010-12-24 18:55:50 +01:00
SaveRespawnTime (); // also save to DB immediately
2008-10-11 14:16:25 -05:00
}
}
break ;
}
case CORPSE :
{
2012-01-14 15:34:41 +01:00
Unit :: Update ( diff );
2011-07-12 00:19:50 +02:00
// deathstate changed on spells update, prevent problems
if ( m_deathState != CORPSE )
break ;
2014-10-22 00:35:00 +02:00
if ( m_groupLootTimer && ! lootingGroupLowGUID . IsEmpty ())
2010-03-11 22:55:02 +01:00
{
if ( m_groupLootTimer <= diff )
{
2014-10-22 00:35:00 +02:00
if ( Group * group = sGroupMgr -> GetGroupByGUID ( lootingGroupLowGUID ))
2017-02-04 22:37:16 +01:00
group -> EndRoll ( & loot , GetMap ());
2014-10-22 00:35:00 +02:00
2010-03-11 22:55:02 +01:00
m_groupLootTimer = 0 ;
2014-10-22 00:35:00 +02:00
lootingGroupLowGUID . Clear ();
2010-03-11 22:55:02 +01:00
}
2014-10-22 00:35:00 +02:00
else
m_groupLootTimer -= diff ;
2010-03-11 22:55:02 +01:00
}
2017-12-10 12:39:37 +01:00
else if ( m_corpseRemoveTime <= GameTime :: GetGameTime ())
2008-10-11 14:16:25 -05:00
{
2010-09-15 06:22:54 +02:00
RemoveCorpse ( false );
2019-06-03 20:40:34 +02:00
TC_LOG_DEBUG ( "entities.unit" , "Removing corpse... %u " , GetEntry ());
2008-10-11 14:16:25 -05:00
}
break ;
}
case ALIVE :
{
2010-04-07 22:59:46 +02:00
Unit :: Update ( diff );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// creature can be dead after Unit::Update call
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
2013-06-11 19:54:27 -02:30
if ( ! IsAlive ())
2008-10-11 14:16:25 -05:00
break ;
2009-10-17 15:51:44 -07:00
2018-01-03 20:04:19 +01:00
GetThreatManager (). Update ( diff );
2016-08-21 11:07:38 +02:00
if ( m_shouldReacquireTarget && ! IsFocusing ( nullptr , true ))
{
SetTarget ( m_suppressedTarget );
2018-01-10 00:07:37 +01:00
if ( ! HasUnitFlag2 ( UNIT_FLAG2_DISABLE_TURN ))
2016-08-21 11:07:38 +02:00
{
2018-01-10 00:07:37 +01:00
if ( ! m_suppressedTarget . IsEmpty ())
{
if ( WorldObject const * objTarget = ObjectAccessor :: GetWorldObject ( * this , m_suppressedTarget ))
SetFacingToObject ( objTarget , false );
}
else
SetFacingTo ( m_suppressedOrientation , false );
2016-08-21 11:07:38 +02:00
}
m_shouldReacquireTarget = false ;
}
2016-01-13 15:33:17 +01:00
// periodic check to see if the creature has passed an evade boundary
2018-08-23 14:33:28 +02:00
if ( IsAIEnabled () && ! IsInEvadeMode () && IsEngaged ())
2016-01-13 15:33:17 +01:00
{
if ( diff >= m_boundaryCheckTime )
{
AI () -> CheckInRoom ();
m_boundaryCheckTime = 2500 ;
} else
m_boundaryCheckTime -= diff ;
}
2015-09-13 22:02:38 +02:00
// if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now
2017-07-01 20:18:02 +02:00
if ( m_combatPulseDelay > 0 && IsEngaged () && GetMap () -> IsDungeon ())
2015-09-13 22:02:38 +02:00
{
if ( diff > m_combatPulseTime )
m_combatPulseTime = 0 ;
else
m_combatPulseTime -= diff ;
if ( m_combatPulseTime == 0 )
{
2020-09-04 13:38:24 +02:00
Map :: PlayerList const & players = GetMap () -> GetPlayers ();
2015-09-25 15:14:53 +02:00
if ( ! players . isEmpty ())
for ( Map :: PlayerList :: const_iterator it = players . begin (); it != players . end (); ++ it )
{
if ( Player * player = it -> GetSource ())
{
if ( player -> IsGameMaster ())
continue ;
2017-07-01 20:18:02 +02:00
if ( player -> IsAlive () && IsHostileTo ( player ))
EngageWithTarget ( player );
2015-09-25 15:14:53 +02:00
}
}
2015-09-13 22:02:38 +02:00
m_combatPulseTime = m_combatPulseDelay * IN_MILLISECONDS ;
}
}
2018-08-23 14:33:28 +02:00
// do not allow the AI to be changed during update
m_AI_locked = true ;
Unit :: AIUpdateTick ( diff );
m_AI_locked = false ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// creature can be dead after UpdateAI call
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
2013-06-11 19:54:27 -02:30
if ( ! IsAlive ())
2008-10-11 14:16:25 -05:00
break ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( m_regenTimer > 0 )
2009-10-10 16:26:00 -07:00
{
2010-04-07 19:14:10 +02:00
if ( diff >= m_regenTimer )
2009-10-10 16:26:00 -07:00
m_regenTimer = 0 ;
else
m_regenTimer -= diff ;
}
2009-10-17 15:51:44 -07:00
2016-03-24 16:12:53 +01:00
if ( m_regenTimer == 0 )
{
2017-08-21 20:28:18 +02:00
bool bInCombat = IsInCombat () && ( ! GetVictim () || // if IsInCombat() is true and this has no victim
2016-03-24 16:12:53 +01:00
! EnsureVictim () -> GetCharmerOrOwnerPlayerOrPlayerItself () || // or the victim/owner/charmer is not a player
! EnsureVictim () -> GetCharmerOrOwnerPlayerOrPlayerItself () -> IsGameMaster ()); // or the victim/owner/charmer is not a GameMaster
2009-10-17 15:51:44 -07:00
2016-03-24 16:12:53 +01:00
if ( ! IsInEvadeMode () && ( ! bInCombat || IsPolymorphed () || CanNotReachTarget ())) // regenerate health if not in combat or if polymorphed
RegenerateHealth ();
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
if ( GetPowerType () == POWER_ENERGY )
Regenerate ( POWER_ENERGY );
else
Regenerate ( POWER_MANA );
2016-03-24 16:12:53 +01:00
m_regenTimer = CREATURE_REGEN_INTERVAL ;
2009-08-23 22:36:51 -05:00
}
2009-10-17 15:51:44 -07:00
2016-03-24 16:12:53 +01:00
if ( CanNotReachTarget () && ! IsInEvadeMode () && ! GetMap () -> IsRaid ())
{
m_cannotReachTimer += diff ;
if ( m_cannotReachTimer >= CREATURE_NOPATH_EVADE_TIME )
2018-08-23 14:33:28 +02:00
if ( CreatureAI * ai = AI ())
ai -> EnterEvadeMode ( CreatureAI :: EVADE_REASON_NO_PATH );
2009-10-17 16:20:24 -07:00
}
2008-10-11 14:16:25 -05:00
break ;
}
default :
break ;
}
}
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
void Creature :: Regenerate ( Powers power )
2008-10-11 14:16:25 -05:00
{
2017-02-05 15:39:22 +01:00
uint32 curValue = GetPower ( power );
uint32 maxValue = GetMaxPower ( power );
if ( ! HasUnitFlag2 ( UNIT_FLAG2_REGENERATE_POWER ))
return ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
if ( curValue >= maxValue )
return ;
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
float addvalue = 0.0f ;
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
switch ( power )
2008-10-11 14:16:25 -05:00
{
2017-02-05 15:39:22 +01:00
case POWER_FOCUS :
{
// For hunter pets.
addvalue = 24 * sWorld -> getRate ( RATE_POWER_FOCUS );
break ;
}
case POWER_ENERGY :
{
// For deathknight's ghoul.
addvalue = 20 ;
break ;
}
case POWER_MANA :
{
// Combat and any controlled creature
if ( IsInCombat () || GetCharmerOrOwnerGUID (). IsEmpty ())
{
float ManaIncreaseRate = sWorld -> getRate ( RATE_POWER_MANA );
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
addvalue = uint32 (( 27.0f / 5.0f + 17.0f ) * ManaIncreaseRate );
}
else
addvalue = maxValue / 3 ;
break ;
}
default :
return ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2010-01-10 01:23:15 +01:00
// Apply modifiers (if any).
2017-02-05 15:39:22 +01:00
addvalue *= GetTotalAuraMultiplierByMiscValue ( SPELL_AURA_MOD_POWER_REGEN_PERCENT , power );
2010-01-10 01:23:15 +01:00
2017-02-05 15:39:22 +01:00
addvalue += GetTotalAuraModifierByMiscValue ( SPELL_AURA_MOD_POWER_REGEN , power ) * ( IsHunterPet () ? PET_FOCUS_REGEN_INTERVAL : CREATURE_REGEN_INTERVAL ) / ( 5 * IN_MILLISECONDS );
2010-01-10 01:23:15 +01:00
2017-02-05 15:39:22 +01:00
ModifyPower ( power , int32 ( addvalue ));
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 Creature :: RegenerateHealth ()
{
2017-08-21 20:28:18 +02:00
if ( ! CanRegenerateHealth ())
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
uint32 curValue = GetHealth ();
uint32 maxValue = GetMaxHealth ();
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
if ( curValue >= maxValue )
return ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
uint32 addvalue = 0 ;
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
// Not only pet, but any controlled creature (and not polymorphed)
if ( ! GetCharmerOrOwnerGUID (). IsEmpty () && ! IsPolymorphed ())
2008-10-11 14:16:25 -05:00
{
2010-12-23 23:25:44 +01:00
float HealthIncreaseRate = sWorld -> getRate ( RATE_HEALTH );
2009-10-17 15:51:44 -07:00
2016-04-26 18:56:35 +02:00
addvalue = 0.015f * (( float ) GetMaxHealth ()) * HealthIncreaseRate ;
2008-10-11 14:16:25 -05:00
}
else
addvalue = maxValue / 3 ;
2009-10-17 15:51:44 -07:00
2010-01-10 01:23:15 +01:00
// Apply modifiers (if any).
2016-12-13 02:06:07 -03:00
addvalue *= GetTotalAuraMultiplier ( SPELL_AURA_MOD_HEALTH_REGEN_PERCENT );
2010-01-10 01:23:15 +01:00
2010-07-12 00:30:17 +02:00
addvalue += GetTotalAuraModifier ( SPELL_AURA_MOD_REGEN ) * CREATURE_REGEN_INTERVAL / ( 5 * IN_MILLISECONDS );
2010-01-10 01:23:15 +01:00
2008-10-11 14:16:25 -05:00
ModifyHealth ( addvalue );
}
2009-10-17 15:51:44 -07:00
2009-05-21 10:48:00 -05:00
void Creature :: DoFleeToGetAssistance ()
{
2013-06-11 19:54:27 -02:30
if ( ! GetVictim ())
2009-05-21 10:48:00 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( HasAuraType ( SPELL_AURA_PREVENTS_FLEEING ))
2009-05-21 10:48:00 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-12-23 23:25:44 +01:00
float radius = sWorld -> getFloatConfig ( CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS );
2009-05-21 10:48:00 -05:00
if ( radius > 0 )
{
2016-04-02 02:50:44 +02:00
Creature * creature = nullptr ;
2013-06-11 19:54:27 -02:30
Trinity :: NearestAssistCreatureInCreatureRangeCheck u_check ( this , GetVictim (), radius );
2011-10-07 11:08:09 -05:00
Trinity :: CreatureLastSearcher < Trinity :: NearestAssistCreatureInCreatureRangeCheck > searcher ( this , creature , u_check );
2017-05-13 19:37:33 +02:00
Cell :: VisitGridObjects ( this , searcher , radius );
2009-10-17 15:51:44 -07:00
2009-05-21 10:48:00 -05:00
SetNoSearchAssistance ( true );
2016-04-16 01:36:32 +02:00
UpdateSpeed ( MOVE_RUN );
2010-01-16 19:35:30 +03:00
2011-10-07 11:08:09 -05:00
if ( ! creature )
2013-03-08 21:41:30 +01:00
/// @todo use 31365
2012-02-03 19:12:41 +02:00
SetControlled ( true , UNIT_STATE_FLEEING );
2009-05-21 10:48:00 -05:00
else
2011-10-07 11:08:09 -05:00
GetMotionMaster () -> MoveSeekAssistance ( creature -> GetPositionX (), creature -> GetPositionY (), creature -> GetPositionZ ());
2009-05-21 10:48:00 -05:00
}
}
2009-10-17 15:51:44 -07:00
2016-03-11 17:09:26 +01:00
bool Creature :: AIM_Destroy ()
{
if ( m_AI_locked )
{
TC_LOG_DEBUG ( "scripts" , "AIM_Destroy: failed to destroy, locked." );
return false ;
}
2018-08-23 14:33:28 +02:00
SetAI ( nullptr );
2016-03-11 17:09:26 +01:00
return true ;
}
2018-02-05 20:52:19 +01:00
bool Creature :: AIM_Create ( CreatureAI * ai /*= nullptr*/ )
2008-10-11 14:16:25 -05:00
{
// make sure nothing can change the AI during AI update
2010-04-07 19:14:10 +02:00
if ( m_AI_locked )
2008-10-11 14:16:25 -05:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_DEBUG ( "scripts" , "AIM_Initialize: failed to init, locked." );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2009-04-06 20:33:52 -05:00
Motion_Initialize ();
2009-10-17 16:20:24 -07:00
2018-08-24 15:34:34 +02:00
SetAI ( ai ? ai : FactorySelector :: SelectAI ( this ));
2018-02-05 21:08:09 +01:00
return true ;
2018-02-05 20:52:19 +01:00
}
2016-03-11 17:09:26 +01:00
2018-02-05 20:52:19 +01:00
bool Creature :: AIM_Initialize ( CreatureAI * ai )
{
if ( ! AIM_Create ( ai ))
return false ;
2018-08-23 14:33:28 +02:00
AI () -> InitializeAI ();
if ( GetVehicleKit ())
GetVehicleKit () -> Reset ();
2009-04-06 20:33:52 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2009-04-06 20:33:52 -05:00
void Creature :: Motion_Initialize ()
{
2018-01-22 01:54:15 -03:00
if ( m_formation )
2009-10-17 16:20:24 -07:00
{
2018-01-22 01:54:15 -03:00
if ( m_formation -> GetLeader () == this )
m_formation -> FormationReset ( false );
else if ( m_formation -> IsFormed ())
{
2018-06-03 10:06:57 -07:00
GetMotionMaster () -> MoveIdle (); // wait the order of leader
2018-01-22 01:54:15 -03:00
return ;
}
2009-04-07 10:51:45 +02:00
}
2018-01-22 01:54:15 -03:00
GetMotionMaster () -> Initialize ();
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
bool Creature :: Create ( ObjectGuid :: LowType guidlow , Map * map , uint32 entry , Position const & pos , CreatureData const * data /*= nullptr*/ , uint32 vehId /*= 0*/ , bool dynamic )
2008-10-11 14:16:25 -05:00
{
2009-08-01 00:20:32 +02:00
ASSERT ( map );
SetMap ( map );
2014-06-02 17:43:59 -05:00
2018-01-28 11:37:20 +01:00
if ( data )
{
PhasingHandler :: InitDbPhaseShift ( GetPhaseShift (), data -> phaseUseFlags , data -> phaseId , data -> phaseGroup );
PhasingHandler :: InitDbVisibleMapId ( GetPhaseShift (), data -> terrainSwapMap );
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
// Set if this creature can handle dynamic spawns
if ( ! dynamic )
SetRespawnCompatibilityMode ();
2014-04-28 20:05:36 +02:00
CreatureTemplate const * cinfo = sObjectMgr -> GetCreatureTemplate ( entry );
2010-11-16 14:00:47 +01:00
if ( ! cinfo )
{
2014-10-27 21:56:12 +00:00
TC_LOG_ERROR ( "sql.sql" , "Creature::Create(): creature template (guidlow: " UI64FMTD ", entry: %u) does not exist." , guidlow , entry );
2010-11-16 14:00:47 +01:00
return false ;
}
2012-05-30 08:01:02 +02:00
//! Relocate before CreateFromProto, to initialize coords and allow
//! returning correct zone id for selecting OutdoorPvP/Battlefield script
2017-05-07 21:48:41 +01:00
Relocate ( pos );
2009-10-17 15:51:44 -07:00
2016-06-21 22:19:31 +02:00
// Check if the position is valid before calling CreateFromProto(), otherwise we might add Auras to Creatures at
// invalid position, triggering a crash about Auras not removed in the destructor
2010-04-07 19:14:10 +02:00
if ( ! IsPositionValid ())
2009-05-30 22:15:05 -05:00
{
2017-05-07 21:48:41 +01:00
TC_LOG_ERROR ( "entities.unit" , "Creature::Create(): given coordinates for creature (guidlow " UI64FMTD ", entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)" , guidlow , entry , pos . GetPositionX (), pos . GetPositionY (), pos . GetPositionZ (), pos . GetOrientation ());
2009-05-30 22:15:05 -05:00
return false ;
}
2017-07-23 00:01:11 +02:00
UpdatePositionData ();
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
// Allow players to see those units while dead, do it here (mayby altered by addon auras)
if ( cinfo -> type_flags & CREATURE_TYPE_FLAG_GHOST_VISIBLE )
m_serverSideVisibility . SetValue ( SERVERSIDE_VISIBILITY_GHOST , GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST );
2016-06-21 22:19:31 +02:00
if ( ! CreateFromProto ( guidlow , entry , data , vehId ))
return false ;
2018-11-07 20:23:30 +01:00
cinfo = GetCreatureTemplate (); // might be different than initially requested
2017-02-05 15:02:08 +01:00
if ( cinfo -> flags_extra & CREATURE_FLAG_EXTRA_DUNGEON_BOSS && map -> IsDungeon ())
m_respawnDelay = 0 ; // special value, prevents respawn for dungeon bosses unless overridden
2018-11-07 20:23:30 +01:00
switch ( cinfo -> rank )
2008-10-11 14:16:25 -05:00
{
2011-02-18 19:48:47 +01:00
case CREATURE_ELITE_RARE :
m_corpseDelay = sWorld -> getIntConfig ( CONFIG_CORPSE_DECAY_RARE );
break ;
case CREATURE_ELITE_ELITE :
m_corpseDelay = sWorld -> getIntConfig ( CONFIG_CORPSE_DECAY_ELITE );
break ;
case CREATURE_ELITE_RAREELITE :
m_corpseDelay = sWorld -> getIntConfig ( CONFIG_CORPSE_DECAY_RAREELITE );
break ;
case CREATURE_ELITE_WORLDBOSS :
m_corpseDelay = sWorld -> getIntConfig ( CONFIG_CORPSE_DECAY_WORLDBOSS );
break ;
default :
m_corpseDelay = sWorld -> getIntConfig ( CONFIG_CORPSE_DECAY_NORMAL );
break ;
}
2009-10-17 15:51:44 -07:00
2011-02-18 19:48:47 +01:00
LoadCreaturesAddon ();
2012-05-30 08:01:02 +02:00
//! Need to be called after LoadCreaturesAddon - MOVEMENTFLAG_HOVER is set there
2017-11-23 18:12:44 +01:00
m_positionZ += GetHoverOffset ();
2012-05-30 08:01:02 +02:00
2017-03-18 11:31:31 +01:00
LastUsedScriptID = GetScriptId ();
2011-02-18 19:48:47 +01:00
2017-03-27 06:41:53 +02:00
if ( IsSpiritHealer () || IsSpiritGuide () || ( GetCreatureTemplate () -> flags_extra & CREATURE_FLAG_EXTRA_GHOST_VISIBILITY ))
2010-11-13 17:18:09 +01:00
{
m_serverSideVisibility . SetValue ( SERVERSIDE_VISIBILITY_GHOST , GHOST_VISIBILITY_GHOST );
m_serverSideVisibilityDetect . SetValue ( SERVERSIDE_VISIBILITY_GHOST , GHOST_VISIBILITY_GHOST );
}
2018-11-07 20:23:30 +01:00
if ( cinfo -> flags_extra & CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING )
2014-07-11 03:08:20 +02:00
AddUnitState ( UNIT_STATE_IGNORE_PATHFINDING );
2018-11-07 20:23:30 +01:00
if ( cinfo -> flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK )
2016-02-04 15:46:23 +01:00
{
ApplySpellImmune ( 0 , IMMUNITY_EFFECT , SPELL_EFFECT_KNOCK_BACK , true );
ApplySpellImmune ( 0 , IMMUNITY_EFFECT , SPELL_EFFECT_KNOCK_BACK_DEST , true );
}
2018-01-03 20:04:19 +01:00
GetThreatManager (). Initialize ();
2011-02-18 19:48:47 +01:00
return true ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2018-01-27 21:47:37 +01:00
Creature * Creature :: CreateCreature ( uint32 entry , Map * map , Position const & pos , uint32 vehId /*= 0*/ )
{
CreatureTemplate const * cInfo = sObjectMgr -> GetCreatureTemplate ( entry );
if ( ! cInfo )
return nullptr ;
ObjectGuid :: LowType lowGuid ;
if ( vehId || cInfo -> VehicleId )
lowGuid = map -> GenerateLowGuid < HighGuid :: Vehicle > ();
else
lowGuid = map -> GenerateLowGuid < HighGuid :: Creature > ();
Creature * creature = new Creature ();
2017-05-07 21:48:41 +01:00
if ( ! creature -> Create ( lowGuid , map , entry , pos , nullptr , vehId ))
2018-01-27 21:47:37 +01:00
{
delete creature ;
return nullptr ;
}
return creature ;
}
Creature * Creature :: CreateCreatureFromDB ( ObjectGuid :: LowType spawnId , Map * map , bool addToMap /*= true*/ , bool allowDuplicate /*= false*/ )
{
Creature * creature = new Creature ();
2017-05-07 21:48:41 +01:00
if ( ! creature -> LoadFromDB ( spawnId , map , addToMap , allowDuplicate ))
2018-01-27 21:47:37 +01:00
{
delete creature ;
return nullptr ;
}
return creature ;
}
2018-01-03 20:04:19 +01:00
Unit * Creature :: SelectVictim ()
{
Unit * target = nullptr ;
2018-01-29 23:14:46 +01:00
if ( CanHaveThreatList ())
2018-08-27 19:08:17 +02:00
target = GetThreatManager (). GetCurrentVictim ();
2018-01-03 20:04:19 +01:00
else if ( ! HasReactState ( REACT_PASSIVE ))
{
// We're a player pet, probably
target = getAttackerForHelper ();
if ( ! target && IsSummon ())
{
if ( Unit * owner = ToTempSummon () -> GetOwner ())
{
if ( owner -> IsInCombat ())
target = owner -> getAttackerForHelper ();
if ( ! target )
{
for ( ControlList :: const_iterator itr = owner -> m_Controlled . begin (); itr != owner -> m_Controlled . end (); ++ itr )
{
if (( * itr ) -> IsInCombat ())
{
target = ( * itr ) -> getAttackerForHelper ();
if ( target )
break ;
}
}
}
}
}
}
else
return nullptr ;
if ( target && _IsTargetAcceptable ( target ) && CanCreatureAttack ( target ))
{
if ( ! IsFocusing ( nullptr , true ))
SetInFront ( target );
return target ;
}
/// @todo a vehicle may eat some mob, so mob should not evade
if ( GetVehicle ())
return nullptr ;
Unit :: AuraEffectList const & iAuras = GetAuraEffectsByType ( SPELL_AURA_MOD_INVISIBILITY );
if ( ! iAuras . empty ())
{
for ( Unit :: AuraEffectList :: const_iterator itr = iAuras . begin (); itr != iAuras . end (); ++ itr )
{
if (( * itr ) -> GetBase () -> IsPermanent ())
{
AI () -> EnterEvadeMode ( CreatureAI :: EVADE_REASON_OTHER );
break ;
}
}
return nullptr ;
}
// enter in evade mode in other case
AI () -> EnterEvadeMode ( CreatureAI :: EVADE_REASON_NO_HOSTILES );
return nullptr ;
}
2013-03-04 12:41:17 +01:00
void Creature :: InitializeReactState ()
{
2014-08-14 16:09:14 +02:00
if ( IsTotem () || IsTrigger () || IsCritter () || IsSpiritService ())
2013-03-04 12:41:17 +01:00
SetReactState ( REACT_PASSIVE );
2014-04-28 20:05:36 +02:00
/*
else if (IsCivilian())
SetReactState(REACT_DEFENSIVE);
*/
2013-03-04 12:41:17 +01:00
else
SetReactState ( REACT_AGGRESSIVE );
}
2011-10-07 11:08:09 -05:00
bool Creature :: isCanInteractWithBattleMaster ( Player * player , bool msg ) const
2008-10-11 14:16:25 -05:00
{
2013-06-11 19:54:27 -02:30
if ( ! IsBattleMaster ())
2008-10-11 14:16:25 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2010-12-22 21:25:23 +01:00
BattlegroundTypeId bgTypeId = sBattlegroundMgr -> GetBattleMasterBG ( GetEntry ());
2010-04-07 19:14:10 +02:00
if ( ! msg )
2011-10-07 11:08:09 -05:00
return player -> GetBGAccessByLevel ( bgTypeId );
2009-10-17 15:51:44 -07:00
2011-10-07 11:08:09 -05:00
if ( ! player -> GetBGAccessByLevel ( bgTypeId ))
2008-10-11 14:16:25 -05:00
{
2016-08-31 00:59:04 +02:00
ClearGossipMenuFor ( player );
2011-09-29 12:43:05 +02:00
switch ( bgTypeId )
2008-10-11 14:16:25 -05:00
{
2016-08-31 00:59:04 +02:00
case BATTLEGROUND_AV : SendGossipMenuFor ( player , 7616 , this ); break ;
case BATTLEGROUND_WS : SendGossipMenuFor ( player , 7599 , this ); break ;
case BATTLEGROUND_AB : SendGossipMenuFor ( player , 7642 , this ); break ;
2008-10-11 14:16:25 -05:00
case BATTLEGROUND_EY :
case BATTLEGROUND_NA :
case BATTLEGROUND_BE :
case BATTLEGROUND_AA :
2008-12-24 09:58:26 -06:00
case BATTLEGROUND_RL :
case BATTLEGROUND_SA :
case BATTLEGROUND_DS :
2016-08-31 00:59:04 +02:00
case BATTLEGROUND_RV : SendGossipMenuFor ( player , 10024 , this ); break ;
2008-12-24 09:58:26 -06:00
default : break ;
2008-10-11 14:16:25 -05:00
}
return false ;
}
return true ;
}
2009-10-17 15:51:44 -07:00
2017-07-26 00:53:25 +02:00
bool Creature :: CanResetTalents ( Player * player ) const
2008-10-11 14:16:25 -05:00
{
2017-07-24 23:24:32 +02:00
return player -> getLevel () >= 15
2012-05-30 08:01:02 +02:00
&& player -> getClass () == GetCreatureTemplate () -> trainer_class ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-09-15 14:08:17 +02:00
Player * Creature :: GetLootRecipient () const
2008-10-11 14:16:25 -05:00
{
2010-09-15 13:44:40 +02:00
if ( ! m_lootRecipient )
2016-04-02 02:50:44 +02:00
return nullptr ;
2014-10-22 00:35:00 +02:00
2014-10-16 22:47:02 +02:00
return ObjectAccessor :: FindConnectedPlayer ( m_lootRecipient );
2010-09-15 13:44:40 +02:00
}
2011-09-15 14:08:17 +02:00
Group * Creature :: GetLootRecipientGroup () const
2010-09-15 13:44:40 +02:00
{
2014-10-22 00:35:00 +02:00
if ( m_lootRecipientGroup . IsEmpty ())
2016-04-02 02:50:44 +02:00
return nullptr ;
2014-10-22 00:35:00 +02:00
2011-05-05 10:23:00 +02:00
return sGroupMgr -> GetGroupByGUID ( m_lootRecipientGroup );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-02-13 20:42:06 +01:00
void Creature :: SetLootRecipient ( Unit * unit , bool withGroup )
2008-10-11 14:16:25 -05:00
{
// set the player whose group should receive the right
// to loot the creature after it dies
2016-04-02 02:50:44 +02:00
// should be set to nullptr after the loot disappears
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
if ( ! unit )
{
2014-09-14 16:14:12 +02:00
m_lootRecipient . Clear ();
2014-10-22 00:35:00 +02:00
m_lootRecipientGroup . Clear ();
2019-06-03 20:40:34 +02:00
RemoveDynamicFlag ( UNIT_DYNFLAG_LOOTABLE | UNIT_DYNFLAG_TAPPED );
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2010-09-13 22:49:20 +02:00
if ( unit -> GetTypeId () != TYPEID_PLAYER && ! unit -> IsVehicle ())
return ;
2008-10-11 14:16:25 -05:00
Player * player = unit -> GetCharmerOrOwnerPlayerOrPlayerItself ();
2010-04-07 19:14:10 +02:00
if ( ! player ) // normal creature, no player involved
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
m_lootRecipient = player -> GetGUID ();
2017-02-13 20:42:06 +01:00
if ( withGroup )
{
if ( Group * group = player -> GetGroup ())
m_lootRecipientGroup = group -> GetGUID ();
}
else
m_lootRecipientGroup = ObjectGuid :: Empty ;
2010-09-15 13:44:40 +02:00
2019-06-03 20:40:34 +02:00
AddDynamicFlag ( UNIT_DYNFLAG_TAPPED );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2010-03-14 00:22:53 +01:00
// return true if this creature is tapped by the player or by a member of his group.
2011-08-19 14:53:40 +02:00
bool Creature :: isTappedBy ( Player const * player ) const
2010-03-14 00:22:53 +01:00
{
if ( player -> GetGUID () == m_lootRecipient )
return true ;
2011-08-19 14:53:40 +02:00
Group const * playerGroup = player -> GetGroup ();
2010-09-15 13:44:40 +02:00
if ( ! playerGroup || playerGroup != GetLootRecipientGroup ()) // if we dont have a group we arent the recipient
return false ; // if creature doesnt have group bound it means it was solo killed by someone else
2010-03-14 00:22:53 +01:00
return true ;
}
2008-10-11 14:16:25 -05:00
void Creature :: SaveToDB ()
{
// this should only be used when the creature has already been loaded
2008-11-14 16:28:45 -06:00
// preferably after adding to map, because mapid may not be valid otherwise
2015-02-25 00:13:14 +01:00
CreatureData const * data = sObjectMgr -> GetCreatureData ( m_spawnId );
2010-04-07 19:14:10 +02:00
if ( ! data )
2008-10-11 14:16:25 -05:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "entities.unit" , "Creature::SaveToDB failed, cannot get creature data!" );
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2016-05-30 23:49:35 +02:00
uint32 mapId = GetTransport () ? GetTransport () -> GetGOInfo () -> moTransport . SpawnMap : GetMapId ();
2018-09-15 17:55:26 +02:00
SaveToDB ( mapId , data -> spawnDifficulties );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2018-09-15 17:55:26 +02:00
void Creature :: SaveToDB ( uint32 mapid , std :: vector < Difficulty > const & spawnDifficulties )
2008-10-11 14:16:25 -05:00
{
// update in loaded data
2015-02-25 00:13:14 +01:00
if ( ! m_spawnId )
m_spawnId = sObjectMgr -> GenerateCreatureSpawnId ();
2014-10-22 17:33:55 +02:00
2015-02-25 00:13:14 +01:00
CreatureData & data = sObjectMgr -> NewOrExistCreatureData ( m_spawnId );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
uint32 displayId = GetNativeDisplayId ();
2019-06-03 20:40:34 +02:00
uint64 npcflag = ( uint64 ( m_unitData -> NpcFlags [ 1 ]) << 32 ) | m_unitData -> NpcFlags [ 0 ];
uint32 unitFlags = m_unitData -> Flags ;
uint32 unitFlags2 = m_unitData -> Flags2 ;
uint32 unitFlags3 = m_unitData -> Flags3 ;
uint32 dynamicflags = m_objectData -> DynamicFlags ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// check if it's a custom model and if not, use 0 for displayId
2012-05-30 08:01:02 +02:00
CreatureTemplate const * cinfo = GetCreatureTemplate ();
2009-06-15 18:02:12 -05:00
if ( cinfo )
2008-10-11 14:16:25 -05:00
{
2018-11-07 20:23:30 +01:00
for ( CreatureModel model : cinfo -> Models )
if ( displayId && displayId == model . CreatureDisplayID )
displayId = 0 ;
2010-10-09 17:52:00 +02:00
if ( npcflag == cinfo -> npcflag )
npcflag = 0 ;
2017-05-21 14:15:04 +02:00
if ( unitFlags == cinfo -> unit_flags )
unitFlags = 0 ;
if ( unitFlags2 == cinfo -> unit_flags2 )
unitFlags2 = 0 ;
if ( unitFlags3 == cinfo -> unit_flags3 )
unitFlags3 = 0 ;
2010-10-09 17:52:00 +02:00
if ( dynamicflags == cinfo -> dynamicflags )
dynamicflags = 0 ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
if ( ! data . spawnId )
data . spawnId = m_spawnId ;
ASSERT ( data . spawnId == m_spawnId );
2008-10-11 14:16:25 -05:00
data . id = GetEntry ();
data . displayid = displayId ;
2013-02-18 12:24:18 +00:00
data . equipmentId = GetCurrentEquipmentId ();
2013-10-16 18:37:29 +02:00
if ( ! GetTransport ())
2017-05-07 21:48:41 +01:00
data . spawnPoint . WorldRelocate ( this );
2013-10-16 18:37:29 +02:00
else
2017-05-07 21:48:41 +01:00
data . spawnPoint . WorldRelocate ( mapid , GetTransOffsetX (), GetTransOffsetY (), GetTransOffsetZ (), GetTransOffsetO ());
2008-10-11 14:16:25 -05:00
data . spawntimesecs = m_respawnDelay ;
// prevent add data integrity problems
2012-05-30 08:01:02 +02:00
data . spawndist = GetDefaultMovementType () == IDLE_MOTION_TYPE ? 0.0f : m_respawnradius ;
2008-10-11 14:16:25 -05:00
data . currentwaypoint = 0 ;
data . curhealth = GetHealth ();
data . curmana = GetPower ( POWER_MANA );
// prevent add data integrity problems
2010-04-07 23:25:02 +02:00
data . movementType = ! m_respawnradius && GetDefaultMovementType () == RANDOM_MOTION_TYPE
2008-10-11 14:16:25 -05:00
? IDLE_MOTION_TYPE : GetDefaultMovementType ();
2018-09-15 17:55:26 +02:00
data . spawnDifficulties = spawnDifficulties ;
2010-10-09 17:52:00 +02:00
data . npcflag = npcflag ;
2017-05-21 14:15:04 +02:00
data . unit_flags = unitFlags ;
data . unit_flags2 = unitFlags2 ;
data . unit_flags3 = unitFlags3 ;
2010-10-09 17:52:00 +02:00
data . dynamicflags = dynamicflags ;
2017-05-07 21:48:41 +01:00
if ( ! data . spawnGroupData )
data . spawnGroupData = sObjectMgr -> GetDefaultSpawnGroup ();
2015-04-06 00:40:56 +02:00
2018-01-10 22:01:40 +01:00
data . phaseId = GetDBPhase () > 0 ? GetDBPhase () : data . phaseId ;
data . phaseGroup = GetDBPhase () < 0 ? - GetDBPhase () : data . phaseGroup ;
2009-10-17 15:51:44 -07:00
2011-07-29 14:18:28 +02:00
// update in DB
2019-07-27 10:52:33 +02:00
WorldDatabaseTransaction trans = WorldDatabase . BeginTransaction ();
2009-10-17 15:51:44 -07:00
2019-07-27 01:00:37 +02:00
WorldDatabasePreparedStatement * stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_CREATURE );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( 0 , m_spawnId );
2012-05-30 08:01:02 +02:00
trans -> Append ( stmt );
uint8 index = 0 ;
stmt = WorldDatabase . GetPreparedStatement ( WORLD_INS_CREATURE );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( index ++ , m_spawnId );
2012-05-30 08:01:02 +02:00
stmt -> setUInt32 ( index ++ , GetEntry ());
stmt -> setUInt16 ( index ++ , uint16 ( mapid ));
2020-04-03 17:41:48 +02:00
stmt -> setString ( index ++ , [ & data ]() -> std :: string
{
if ( data . spawnDifficulties . empty ())
return "" ;
std :: ostringstream os ;
auto itr = data . spawnDifficulties . begin ();
os << int32 ( * itr ++ );
for (; itr != data . spawnDifficulties . end (); ++ itr )
os << ',' << int32 ( * itr );
return os . str ();
}());
2018-01-06 19:56:11 +01:00
stmt -> setUInt32 ( index ++ , data . phaseId );
2015-03-16 07:28:49 +01:00
stmt -> setUInt32 ( index ++ , data . phaseGroup );
2012-05-30 08:01:02 +02:00
stmt -> setUInt32 ( index ++ , displayId );
2015-01-24 19:49:05 +01:00
stmt -> setUInt8 ( index ++ , GetCurrentEquipmentId ());
2012-05-30 08:01:02 +02:00
stmt -> setFloat ( index ++ , GetPositionX ());
stmt -> setFloat ( index ++ , GetPositionY ());
stmt -> setFloat ( index ++ , GetPositionZ ());
stmt -> setFloat ( index ++ , GetOrientation ());
stmt -> setUInt32 ( index ++ , m_respawnDelay );
stmt -> setFloat ( index ++ , m_respawnradius );
stmt -> setUInt32 ( index ++ , 0 );
stmt -> setUInt32 ( index ++ , GetHealth ());
stmt -> setUInt32 ( index ++ , GetPower ( POWER_MANA ));
stmt -> setUInt8 ( index ++ , uint8 ( GetDefaultMovementType ()));
2015-05-04 12:39:36 +02:00
stmt -> setUInt64 ( index ++ , npcflag );
2017-05-21 14:15:04 +02:00
stmt -> setUInt32 ( index ++ , unitFlags );
stmt -> setUInt32 ( index ++ , unitFlags2 );
stmt -> setUInt32 ( index ++ , unitFlags3 );
2012-05-30 08:01:02 +02:00
stmt -> setUInt32 ( index ++ , dynamicflags );
trans -> Append ( stmt );
2009-10-17 15:51:44 -07:00
2010-08-21 03:19:25 +02:00
WorldDatabase . CommitTransaction ( trans );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2014-01-06 20:30:11 +01:00
void Creature :: SelectLevel ()
2008-10-11 14:16:25 -05:00
{
2014-01-06 20:30:11 +01:00
CreatureTemplate const * cInfo = GetCreatureTemplate ();
2018-04-12 21:57:49 +02:00
// level
2020-05-02 22:26:52 +02:00
std :: pair < int16 , int16 > levels = cInfo -> GetMinMaxLevel ();
uint8 minlevel = std :: min ( levels . first , levels . second );
uint8 maxlevel = std :: max ( levels . first , levels . second );
2018-04-12 21:57:49 +02:00
uint8 level = minlevel == maxlevel ? minlevel : urand ( minlevel , maxlevel );
SetLevel ( level );
2018-11-30 17:57:32 +01:00
CreatureLevelScaling const * scaling = cInfo -> GetLevelScaling ( GetMap () -> GetDifficultyID ());
2018-04-12 21:57:49 +02:00
2021-08-05 19:37:53 +02:00
if ( Optional < ContentTuningLevels > levels = sDB2Manager . GetContentTuningData ( scaling -> ContentTuningID , 0 ))
{
SetUpdateFieldValue ( m_values . ModifyValue ( & Unit :: m_unitData ). ModifyValue ( & UF :: UnitData :: ScalingLevelMin ), levels -> MinLevel );
SetUpdateFieldValue ( m_values . ModifyValue ( & Unit :: m_unitData ). ModifyValue ( & UF :: UnitData :: ScalingLevelMax ), levels -> MaxLevel );
}
2018-04-12 21:57:49 +02:00
2018-11-30 17:57:32 +01:00
int32 mindelta = std :: min ( scaling -> DeltaLevelMax , scaling -> DeltaLevelMin );
int32 maxdelta = std :: max ( scaling -> DeltaLevelMax , scaling -> DeltaLevelMin );
int32 delta = mindelta == maxdelta ? mindelta : irand ( mindelta , maxdelta );
SetUpdateFieldValue ( m_values . ModifyValue ( & Unit :: m_unitData ). ModifyValue ( & UF :: UnitData :: ScalingLevelDelta ), delta );
SetUpdateFieldValue ( m_values . ModifyValue ( & Unit :: m_unitData ). ModifyValue ( & UF :: UnitData :: ContentTuningID ), scaling -> ContentTuningID );
2017-07-29 21:40:28 +02:00
UpdateLevelDependantStats ();
2016-09-24 03:50:20 +02:00
}
2009-10-17 15:51:44 -07:00
2016-09-24 03:50:20 +02:00
void Creature :: UpdateLevelDependantStats ()
{
CreatureTemplate const * cInfo = GetCreatureTemplate ();
uint32 rank = IsPet () ? 0 : cInfo -> rank ;
2018-11-30 17:57:32 +01:00
uint8 level = getLevel ();
CreatureBaseStats const * stats = sObjectMgr -> GetCreatureBaseStats ( level , cInfo -> unit_class );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// health
float healthmod = _GetHealthMod ( rank );
2009-10-17 15:51:44 -07:00
2018-11-30 17:57:32 +01:00
uint32 basehp = GetMaxHealthByLevel ( level );
2009-12-20 20:23:18 -07:00
uint32 health = uint32 ( basehp * healthmod );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
SetCreateHealth ( health );
SetMaxHealth ( health );
SetHealth ( health );
2009-06-11 17:54:59 +02:00
ResetPlayerDamageReq ();
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// mana
2014-01-06 20:30:11 +01:00
uint32 mana = stats -> GenerateMana ( cInfo );
2008-10-11 14:16:25 -05:00
SetCreateMana ( mana );
2009-10-17 15:51:44 -07:00
2012-07-24 15:28:41 +02:00
switch ( getClass ())
{
2018-03-04 12:48:22 +01:00
case CLASS_PALADIN :
case CLASS_MAGE :
SetMaxPower ( POWER_MANA , mana );
SetFullPower ( POWER_MANA );
2012-07-24 15:28:41 +02:00
break ;
2018-03-04 12:48:22 +01:00
default : // We don't set max power here, 0 makes power bar hidden
2012-07-24 15:28:41 +02:00
break ;
}
2009-10-17 15:51:44 -07:00
2017-02-27 14:20:32 -03:00
SetStatFlatModifier ( UNIT_MOD_HEALTH , BASE_VALUE , ( float ) health );
2008-10-11 14:16:25 -05:00
2014-01-04 18:22:06 +01:00
// damage
2018-11-30 17:57:32 +01:00
float basedamage = GetBaseDamageForLevel ( level );
2009-10-17 15:51:44 -07:00
2014-01-04 18:22:06 +01:00
float weaponBaseMinDamage = basedamage ;
2014-08-21 16:56:11 +02:00
float weaponBaseMaxDamage = basedamage * 1.5f ;
2009-10-17 15:51:44 -07:00
2014-01-04 18:22:06 +01:00
SetBaseWeaponDamage ( BASE_ATTACK , MINDAMAGE , weaponBaseMinDamage );
SetBaseWeaponDamage ( BASE_ATTACK , MAXDAMAGE , weaponBaseMaxDamage );
2009-10-17 15:51:44 -07:00
2014-01-04 18:22:06 +01:00
SetBaseWeaponDamage ( OFF_ATTACK , MINDAMAGE , weaponBaseMinDamage );
SetBaseWeaponDamage ( OFF_ATTACK , MAXDAMAGE , weaponBaseMaxDamage );
2009-10-17 15:51:44 -07:00
2014-01-04 18:22:06 +01:00
SetBaseWeaponDamage ( RANGED_ATTACK , MINDAMAGE , weaponBaseMinDamage );
SetBaseWeaponDamage ( RANGED_ATTACK , MAXDAMAGE , weaponBaseMaxDamage );
2009-10-17 15:51:44 -07:00
2017-02-27 14:20:32 -03:00
SetStatFlatModifier ( UNIT_MOD_ATTACK_POWER , BASE_VALUE , stats -> AttackPower );
SetStatFlatModifier ( UNIT_MOD_ATTACK_POWER_RANGED , BASE_VALUE , stats -> RangedAttackPower );
2016-09-24 03:50:20 +02:00
2018-11-30 17:57:32 +01:00
float armor = GetBaseArmorForLevel ( level ); /// @todo Why is this treated as uint32 when it's a float?
2017-02-27 14:20:32 -03:00
SetStatFlatModifier ( UNIT_MOD_ARMOR , BASE_VALUE , armor );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2021-10-03 21:20:45 +02:00
void Creature :: SelectWildBattlePetLevel ()
{
if ( IsWildBattlePet ())
{
uint8 wildBattlePetLevel = WILD_BATTLE_PET_DEFAULT_LEVEL ;
if ( AreaTableEntry const * areaTable = sAreaTableStore . LookupEntry ( GetZoneId ()))
if ( areaTable -> WildBattlePetLevelMin > 0 )
wildBattlePetLevel = urand ( areaTable -> WildBattlePetLevelMin , areaTable -> WildBattlePetLevelMax );
SetWildBattlePetLevel ( wildBattlePetLevel );
}
}
2008-10-11 14:16:25 -05:00
float Creature :: _GetHealthMod ( int32 Rank )
{
switch ( Rank ) // define rates for each elite rank
{
case CREATURE_ELITE_NORMAL :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_NORMAL_HP );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_ELITE :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_ELITE_HP );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_RAREELITE :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_RAREELITE_HP );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_WORLDBOSS :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_WORLDBOSS_HP );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_RARE :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_RARE_HP );
2008-10-11 14:16:25 -05:00
default :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_ELITE_HP );
2008-10-11 14:16:25 -05:00
}
}
2009-10-17 15:51:44 -07:00
2017-06-07 17:23:50 +02:00
void Creature :: LowerPlayerDamageReq ( uint64 unDamage )
2013-03-04 12:41:17 +01:00
{
if ( m_PlayerDamageReq )
m_PlayerDamageReq > unDamage ? m_PlayerDamageReq -= unDamage : m_PlayerDamageReq = 0 ;
}
2008-10-11 14:16:25 -05:00
float Creature :: _GetDamageMod ( int32 Rank )
{
switch ( Rank ) // define rates for each elite rank
{
case CREATURE_ELITE_NORMAL :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_NORMAL_DAMAGE );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_ELITE :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_ELITE_DAMAGE );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_RAREELITE :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_RAREELITE_DAMAGE );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_WORLDBOSS :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_RARE :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_RARE_DAMAGE );
2008-10-11 14:16:25 -05:00
default :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_ELITE_DAMAGE );
2008-10-11 14:16:25 -05:00
}
}
2009-10-17 15:51:44 -07:00
2013-03-20 14:18:26 +01:00
float Creature :: GetSpellDamageMod ( int32 Rank ) const
2008-10-11 14:16:25 -05:00
{
switch ( Rank ) // define rates for each elite rank
{
case CREATURE_ELITE_NORMAL :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_NORMAL_SPELLDAMAGE );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_ELITE :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_RAREELITE :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_WORLDBOSS :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE );
2008-10-11 14:16:25 -05:00
case CREATURE_ELITE_RARE :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_RARE_SPELLDAMAGE );
2008-10-11 14:16:25 -05:00
default :
2010-12-23 23:25:44 +01:00
return sWorld -> getRate ( RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE );
2008-10-11 14:16:25 -05:00
}
}
2009-10-17 15:51:44 -07:00
2014-10-26 02:57:28 +02:00
bool Creature :: CreateFromProto ( ObjectGuid :: LowType guidlow , uint32 entry , CreatureData const * data /*= nullptr*/ , uint32 vehId /*= 0*/ )
2008-10-11 14:16:25 -05:00
{
2009-05-30 22:15:05 -05:00
SetZoneScript ();
2014-01-04 18:22:06 +01:00
if ( GetZoneScript () && data )
2009-05-30 22:15:05 -05:00
{
2014-04-28 20:05:36 +02:00
entry = GetZoneScript () -> GetCreatureEntry ( guidlow , data );
if ( ! entry )
2009-05-30 22:15:05 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2014-04-28 20:05:36 +02:00
CreatureTemplate const * cinfo = sObjectMgr -> GetCreatureTemplate ( entry );
2010-04-07 19:14:10 +02:00
if ( ! cinfo )
2008-10-11 14:16:25 -05:00
{
2014-10-27 21:56:12 +00:00
TC_LOG_ERROR ( "sql.sql" , "Creature::CreateFromProto(): creature template (guidlow: " UI64FMTD ", entry: %u) does not exist." , guidlow , entry );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2014-04-28 20:05:36 +02:00
SetOriginalEntry ( entry );
2009-10-17 15:51:44 -07:00
2014-10-29 21:13:21 +01:00
if ( vehId || cinfo -> VehicleId )
Object :: _Create ( ObjectGuid :: Create < HighGuid :: Vehicle > ( GetMapId (), entry , guidlow ));
else
Object :: _Create ( ObjectGuid :: Create < HighGuid :: Creature > ( GetMapId (), entry , guidlow ));
2009-10-17 15:51:44 -07:00
2014-04-28 20:05:36 +02:00
if ( ! UpdateEntry ( entry , data ))
2008-10-11 14:16:25 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2014-06-15 17:49:17 +02:00
if ( ! vehId )
{
if ( GetCreatureTemplate () -> VehicleId )
{
vehId = GetCreatureTemplate () -> VehicleId ;
entry = GetCreatureTemplate () -> Entry ;
}
else
vehId = cinfo -> VehicleId ;
}
2013-03-13 11:34:58 +01:00
if ( vehId )
2015-04-06 00:40:56 +02:00
CreateVehicleKit ( vehId , entry , true );
2013-02-18 00:39:58 +01:00
2008-10-11 14:16:25 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
bool Creature :: LoadFromDB ( ObjectGuid :: LowType spawnId , Map * map , bool addToMap , bool allowDuplicate )
2008-10-11 14:16:25 -05:00
{
2016-02-03 12:34:38 +00:00
if ( ! allowDuplicate )
{
// If an alive instance of this spawnId is already found, skip creation
// If only dead instance(s) exist, despawn them and spawn a new (maybe also dead) version
const auto creatureBounds = map -> GetCreatureBySpawnIdStore (). equal_range ( spawnId );
std :: vector < Creature *> despawnList ;
if ( creatureBounds . first != creatureBounds . second )
{
for ( auto itr = creatureBounds . first ; itr != creatureBounds . second ; ++ itr )
{
if ( itr -> second -> IsAlive ())
{
2016-04-02 19:22:30 +02:00
TC_LOG_DEBUG ( "maps" , "Would have spawned " UI64FMTD " but %s already exists" , spawnId , creatureBounds . first -> second -> GetGUID (). ToString (). c_str ());
2016-02-03 12:34:38 +00:00
return false ;
}
else
{
despawnList . push_back ( itr -> second );
2016-04-02 19:22:30 +02:00
TC_LOG_DEBUG ( "maps" , "Despawned dead instance of spawn " UI64FMTD " (%s)" , spawnId , itr -> second -> GetGUID (). ToString (). c_str ());
2016-02-03 12:34:38 +00:00
}
}
for ( Creature * despawnCreature : despawnList )
{
despawnCreature -> AddObjectToRemoveList ();
}
}
}
2015-02-25 00:13:14 +01:00
CreatureData const * data = sObjectMgr -> GetCreatureData ( spawnId );
2010-04-07 19:14:10 +02:00
if ( ! data )
2008-10-11 14:16:25 -05:00
{
2018-02-22 20:31:42 +01:00
TC_LOG_ERROR ( "sql.sql" , "Creature (SpawnID " UI64FMTD ") not found in table `creature`, can't load. " , spawnId );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
m_spawnId = spawnId ;
2017-05-07 21:48:41 +01:00
m_respawnCompatibilityMode = (( data -> spawnGroupData -> flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE ) != 0 );
2016-08-08 11:22:11 +02:00
m_creatureData = data ;
2017-02-05 15:02:08 +01:00
m_respawnradius = data -> spawndist ;
m_respawnDelay = data -> spawntimesecs ;
2017-05-07 21:48:41 +01:00
if ( ! Create ( map -> GenerateLowGuid < HighGuid :: Creature > (), map , data -> id , data -> spawnPoint , data , 0U , ! m_respawnCompatibilityMode ))
2008-10-11 14:16:25 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2009-02-09 08:16:34 -05:00
//We should set first home position, because then AI calls home movement
2017-11-23 18:12:44 +01:00
SetHomePosition ( * this );
2009-10-17 15:51:44 -07:00
2011-06-27 20:22:53 +02:00
m_deathState = ALIVE ;
2009-10-17 15:51:44 -07:00
2017-02-05 15:02:08 +01:00
m_respawnTime = GetMap () -> GetCreatureRespawnTime ( m_spawnId );
2016-09-09 16:21:27 +02:00
2018-02-22 20:31:42 +01:00
if ( ! m_respawnTime && ! map -> IsSpawnGroupActive ( data -> spawnGroupData -> groupId ))
{
2018-02-27 22:10:36 +01:00
// @todo pools need fixing! this is just a temporary crashfix, but they violate dynspawn principles
ASSERT ( m_respawnCompatibilityMode || sPoolMgr -> IsPartOfAPool < Creature > ( spawnId ), "Creature (SpawnID " UI64FMTD ") trying to load in inactive spawn group %s." , spawnId , data -> spawnGroupData -> name . c_str ());
2018-02-22 20:31:42 +01:00
m_respawnTime = GameTime :: GetGameTime () + urand ( 4 , 7 );
}
2016-09-09 16:21:27 +02:00
2010-04-07 19:14:10 +02:00
if ( m_respawnTime ) // respawn on Update
2008-12-08 16:34:03 -06:00
{
2018-02-27 22:10:36 +01:00
ASSERT ( m_respawnCompatibilityMode || sPoolMgr -> IsPartOfAPool < Creature > ( spawnId ), "Creature (SpawnID " UI64FMTD ") trying to load despite a respawn timer in progress." , spawnId );
2008-10-11 14:16:25 -05:00
m_deathState = DEAD ;
2012-05-30 08:01:02 +02:00
if ( CanFly ())
2008-12-08 16:34:03 -06:00
{
2017-05-07 21:48:41 +01:00
float tz = map -> GetHeight ( GetPhaseShift (), data -> spawnPoint , true , MAX_FALL_DISTANCE );
if ( data -> spawnPoint . GetPositionZ () - tz > 0.1f && Trinity :: IsValidMapCoord ( tz ))
Relocate ( data -> spawnPoint . GetPositionX (), data -> spawnPoint . GetPositionY (), tz );
2008-12-08 16:34:03 -06:00
}
}
2009-10-17 15:51:44 -07:00
2016-08-08 11:22:11 +02:00
SetSpawnHealth ();
2009-10-17 15:51:44 -07:00
2021-10-03 21:20:45 +02:00
SelectWildBattlePetLevel ();
2008-10-11 14:16:25 -05:00
// checked at creature_template loading
m_defaultMovementType = MovementGeneratorType ( data -> movementType );
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
loot . SetGUID ( ObjectGuid :: Create < HighGuid :: LootObject > ( GetMapId (), data -> id , GetMap () -> GenerateLowGuid < HighGuid :: LootObject > ()));
2014-12-30 02:23:37 +02:00
2011-11-28 14:05:25 -05:00
if ( addToMap && ! GetMap () -> AddToMap ( this ))
return false ;
2008-10-11 14:16:25 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2014-01-04 18:22:06 +01:00
void Creature :: SetCanDualWield ( bool value )
{
Unit :: SetCanDualWield ( value );
UpdateDamagePhysical ( OFF_ATTACK );
}
2013-02-18 12:24:18 +00:00
void Creature :: LoadEquipment ( int8 id , bool force /*= true*/ )
2008-10-11 14:16:25 -05:00
{
2013-02-18 12:24:18 +00:00
if ( id == 0 )
2008-10-11 14:16:25 -05:00
{
if ( force )
{
2013-02-18 12:24:18 +00:00
for ( uint8 i = 0 ; i < MAX_EQUIPMENT_ITEMS ; ++ i )
2015-07-01 02:22:03 +02:00
SetVirtualItem ( i , 0 );
2008-10-11 14:16:25 -05:00
m_equipmentId = 0 ;
}
2016-01-30 11:40:24 +02:00
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2013-02-18 12:24:18 +00:00
EquipmentInfo const * einfo = sObjectMgr -> GetEquipmentInfo ( GetEntry (), id );
2008-10-11 14:16:25 -05:00
if ( ! einfo )
return ;
2009-10-17 15:51:44 -07:00
2013-02-18 12:24:18 +00:00
m_equipmentId = id ;
2015-07-01 02:22:03 +02:00
for ( uint8 i = 0 ; i < MAX_EQUIPMENT_ITEMS ; ++ i )
2017-02-19 10:58:08 +01:00
SetVirtualItem ( i , einfo -> Items [ i ]. ItemId , einfo -> Items [ i ]. AppearanceModId , einfo -> Items [ i ]. ItemVisual );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2016-08-08 11:22:11 +02:00
void Creature :: SetSpawnHealth ()
{
2017-08-21 20:28:18 +02:00
if ( _regenerateHealthLock )
return ;
2016-08-08 11:22:11 +02:00
uint32 curhealth ;
2017-08-21 20:28:18 +02:00
if ( m_creatureData && ! _regenerateHealth )
2016-08-08 11:22:11 +02:00
{
2016-10-11 01:43:14 -03:00
curhealth = m_creatureData -> curhealth ;
if ( curhealth )
2016-08-08 11:22:11 +02:00
{
2016-10-11 01:43:14 -03:00
curhealth = uint32 ( curhealth * _GetHealthMod ( GetCreatureTemplate () -> rank ));
if ( curhealth < 1 )
curhealth = 1 ;
2016-08-08 11:22:11 +02:00
}
2016-10-11 01:43:14 -03:00
SetPower ( POWER_MANA , m_creatureData -> curmana );
2016-08-08 11:22:11 +02:00
}
else
{
curhealth = GetMaxHealth ();
2018-01-03 16:39:00 +01:00
SetFullPower ( POWER_MANA );
2016-08-08 11:22:11 +02:00
}
SetHealth (( m_deathState == ALIVE || m_deathState == JUST_RESPAWNED ) ? curhealth : 0 );
}
2008-10-11 14:16:25 -05:00
bool Creature :: hasQuest ( uint32 quest_id ) const
{
2010-12-22 21:25:23 +01:00
QuestRelationBounds qr = sObjectMgr -> GetCreatureQuestRelationBounds ( GetEntry ());
2010-09-13 13:18:27 +02:00
for ( QuestRelations :: const_iterator itr = qr . first ; itr != qr . second ; ++ itr )
2008-10-11 14:16:25 -05:00
{
2010-04-07 23:25:02 +02:00
if ( itr -> second == quest_id )
2008-10-11 14:16:25 -05:00
return true ;
}
return false ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
bool Creature :: hasInvolvedQuest ( uint32 quest_id ) const
{
2010-12-22 21:25:23 +01:00
QuestRelationBounds qir = sObjectMgr -> GetCreatureQuestInvolvedRelationBounds ( GetEntry ());
2010-09-13 13:18:27 +02:00
for ( QuestRelations :: const_iterator itr = qir . first ; itr != qir . second ; ++ itr )
2008-10-11 14:16:25 -05:00
{
2010-04-07 23:25:02 +02:00
if ( itr -> second == quest_id )
2008-10-11 14:16:25 -05:00
return true ;
}
return false ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void Creature :: DeleteFromDB ()
{
2015-02-25 00:13:14 +01:00
if ( ! m_spawnId )
2008-10-11 14:16:25 -05:00
{
2014-10-22 17:33:55 +02:00
TC_LOG_ERROR ( "entities.unit" , "Trying to delete not saved %s!" , GetGUID (). ToString (). c_str ());
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2017-08-02 22:05:40 +02:00
// remove any scheduled respawns
2017-05-07 21:48:41 +01:00
GetMap () -> RemoveRespawnTime ( SPAWN_TYPE_CREATURE , m_spawnId );
2017-08-02 22:05:40 +02:00
// delete data from memory
2015-02-25 00:13:14 +01:00
sObjectMgr -> DeleteCreatureData ( m_spawnId );
2009-10-17 15:51:44 -07:00
2017-08-02 22:05:40 +02:00
// delete data and all its associations from DB
2019-07-27 10:52:33 +02:00
WorldDatabaseTransaction trans = WorldDatabase . BeginTransaction ();
2012-05-30 08:01:02 +02:00
2019-07-27 01:00:37 +02:00
WorldDatabasePreparedStatement * stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_CREATURE );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( 0 , m_spawnId );
2012-05-30 08:01:02 +02:00
trans -> Append ( stmt );
2017-05-07 21:48:41 +01:00
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_SPAWNGROUP_MEMBER );
stmt -> setUInt8 ( 0 , uint8 ( SPAWN_TYPE_CREATURE ));
stmt -> setUInt64 ( 1 , m_spawnId );
trans -> Append ( stmt );
2012-05-30 08:01:02 +02:00
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_CREATURE_ADDON );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( 0 , m_spawnId );
2012-05-30 08:01:02 +02:00
trans -> Append ( stmt );
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_GAME_EVENT_CREATURE );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( 0 , m_spawnId );
2012-05-30 08:01:02 +02:00
trans -> Append ( stmt );
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_GAME_EVENT_MODEL_EQUIP );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( 0 , m_spawnId );
2012-05-30 08:01:02 +02:00
trans -> Append ( stmt );
2018-01-08 00:32:22 +01:00
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_LINKED_RESPAWN );
stmt -> setUInt64 ( 0 , m_spawnId );
stmt -> setUInt32 ( 1 , LINKED_RESPAWN_CREATURE_TO_CREATURE );
trans -> Append ( stmt );
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_LINKED_RESPAWN );
stmt -> setUInt64 ( 0 , m_spawnId );
stmt -> setUInt32 ( 1 , LINKED_RESPAWN_CREATURE_TO_GO );
trans -> Append ( stmt );
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_LINKED_RESPAWN_MASTER );
stmt -> setUInt64 ( 0 , m_spawnId );
stmt -> setUInt32 ( 1 , LINKED_RESPAWN_CREATURE_TO_CREATURE );
trans -> Append ( stmt );
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_LINKED_RESPAWN_MASTER );
stmt -> setUInt64 ( 0 , m_spawnId );
stmt -> setUInt32 ( 1 , LINKED_RESPAWN_GO_TO_CREATURE );
trans -> Append ( stmt );
2010-08-21 03:19:25 +02:00
WorldDatabase . CommitTransaction ( trans );
2017-08-02 22:05:40 +02:00
// then delete any active instances of the creature
2017-08-09 00:21:52 +02:00
auto const & spawnMap = GetMap () -> GetCreatureBySpawnIdStore ();
for ( auto it = spawnMap . find ( m_spawnId ); it != spawnMap . end (); it = spawnMap . find ( m_spawnId ))
2017-08-02 22:05:40 +02:00
it -> second -> AddObjectToRemoveList ();
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-10-13 13:26:27 -04:00
bool Creature :: IsInvisibleDueToDespawn () const
2008-11-06 10:27:58 -06:00
{
2011-10-13 13:26:27 -04:00
if ( Unit :: IsInvisibleDueToDespawn ())
return true ;
2009-10-17 15:51:44 -07:00
2017-12-10 12:39:37 +01:00
if ( IsAlive () || isDying () || m_corpseRemoveTime > GameTime :: GetGameTime ())
2011-10-13 13:26:27 -04:00
return false ;
2009-10-17 15:51:44 -07:00
2011-10-13 13:26:27 -04:00
return true ;
2008-11-06 10:27:58 -06:00
}
2009-10-17 15:51:44 -07:00
2011-10-13 17:01:55 -04:00
bool Creature :: CanAlwaysSee ( WorldObject const * obj ) const
2011-02-10 22:36:35 -03:00
{
2018-08-23 14:33:28 +02:00
if ( IsAIEnabled () && AI () -> CanSeeAlways ( obj ))
2011-02-10 22:36:35 -03:00
return true ;
return false ;
}
2013-06-11 21:25:12 -02:30
bool Creature :: CanStartAttack ( Unit const * who , bool force ) const
2008-11-21 10:07:11 -06:00
{
2013-06-11 19:54:27 -02:30
if ( IsCivilian ())
2009-06-15 12:02:55 -05:00
return false ;
2009-10-17 16:20:24 -07:00
2015-06-26 16:52:40 +02:00
// This set of checks is should be done only for creatures
2021-09-26 16:07:09 +02:00
if (( IsImmuneToNPC () && ! who -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
|| ( IsImmuneToPC () && who -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED )))
2015-06-26 16:52:40 +02:00
return false ;
2015-07-05 04:03:36 +02:00
2011-10-01 16:57:10 +01:00
// Do not attack non-combat pets
if ( who -> GetTypeId () == TYPEID_UNIT && who -> GetCreatureType () == CREATURE_TYPE_NON_COMBAT_PET )
return false ;
2011-03-06 12:22:04 +01:00
2012-05-30 08:01:02 +02:00
if ( ! CanFly () && ( GetDistanceZ ( who ) > CREATURE_Z_ATTACK_RANGE + m_CombatDistance ))
2009-06-15 12:02:55 -05:00
//|| who->IsControlledByPlayer() && who->IsFlying()))
// we cannot check flying for other creatures, too much map/vmap calculation
2013-03-08 21:41:30 +01:00
/// @todo should switch to range attack
2008-11-21 10:07:11 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( ! force )
2009-08-27 13:54:57 -05:00
{
2010-04-07 19:14:10 +02:00
if ( ! _IsTargetAcceptable ( who ))
2009-09-29 01:16:08 -07:00
return false ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( ! force && ( IsNeutralToAll () || ! IsWithinDistInMap ( who , GetAttackDistance ( who ) + m_CombatDistance )))
2009-08-27 13:54:57 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2013-06-11 21:25:12 -02:30
if ( ! CanCreatureAttack ( who , force ))
2008-11-21 10:07:11 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2015-01-26 19:15:06 +00:00
// No aggro from gray creatures
2017-07-29 21:40:28 +02:00
if ( CheckNoGrayAggroConfig ( who -> GetLevelForTarget ( this ), GetLevelForTarget ( who )))
2015-01-26 19:15:06 +00:00
return false ;
2008-11-21 10:07:11 -06:00
return IsWithinLOSInMap ( who );
}
2009-10-17 15:51:44 -07:00
2015-01-26 19:15:06 +00:00
bool Creature :: CheckNoGrayAggroConfig ( uint32 playerLevel , uint32 creatureLevel ) const
{
if ( Trinity :: XP :: GetColorCode ( playerLevel , creatureLevel ) != XP_GRAY )
return false ;
uint32 notAbove = sWorld -> getIntConfig ( CONFIG_NO_GRAY_AGGRO_ABOVE );
uint32 notBelow = sWorld -> getIntConfig ( CONFIG_NO_GRAY_AGGRO_BELOW );
if ( notAbove == 0 && notBelow == 0 )
return false ;
if ( playerLevel <= notBelow || ( playerLevel >= notAbove && notAbove > 0 ))
return true ;
return false ;
}
2011-11-07 11:18:00 -06:00
float Creature :: GetAttackDistance ( Unit const * player ) const
2008-10-11 14:16:25 -05:00
{
2017-11-15 21:56:15 +01:00
// WoW Wiki: the minimum radius seems to be 5 yards, while the maximum range is 45 yards
float maxRadius = ( 45.0f * sWorld -> getRate ( RATE_CREATURE_AGGRO ));
float minRadius = ( 5.0f * sWorld -> getRate ( RATE_CREATURE_AGGRO ));
2010-12-23 23:25:44 +01:00
float aggroRate = sWorld -> getRate ( RATE_CREATURE_AGGRO );
2017-11-15 21:56:15 +01:00
uint8 expansionMaxLevel = uint8 ( GetMaxLevelForExpansion ( GetCreatureTemplate () -> RequiredExpansion ));
2017-11-19 16:30:54 +01:00
uint32 playerLevel = player -> GetLevelForTarget ( this );
uint32 creatureLevel = GetLevelForTarget ( player );
2009-10-17 15:51:44 -07:00
2017-11-15 21:56:15 +01:00
if ( aggroRate == 0.0f )
2017-11-19 16:30:54 +01:00
return 0.0f ;
2009-10-17 15:51:44 -07:00
2017-11-15 21:56:15 +01:00
// The aggro radius for creatures with equal level as the player is 20 yards.
// The combatreach should not get taken into account for the distance so we drop it from the range (see Supremus as expample)
2017-11-19 16:30:54 +01:00
float baseAggroDistance = 20.0f - GetCombatReach ();
float aggroRadius = baseAggroDistance ;
2009-10-17 15:51:44 -07:00
2017-11-15 21:56:15 +01:00
// detect range auras
2017-11-19 16:30:54 +01:00
if (( creatureLevel + 5 ) <= sWorld -> getIntConfig ( CONFIG_MAX_PLAYER_LEVEL ))
2008-10-11 14:16:25 -05:00
{
2017-11-15 21:56:15 +01:00
aggroRadius += GetTotalAuraModifier ( SPELL_AURA_MOD_DETECT_RANGE );
2009-10-17 15:51:44 -07:00
2017-11-19 16:30:54 +01:00
aggroRadius += player -> GetTotalAuraModifier ( SPELL_AURA_MOD_DETECTED_RANGE );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-11-15 21:56:15 +01:00
// The aggro range of creatures with higher levels than the total player level for the expansion should get the maxlevel treatment
// This makes sure that creatures such as bosses wont have a bigger aggro range than the rest of the npc's
2017-11-19 16:30:54 +01:00
// The following code is used for blizzlike behavior such as skipable bosses (e.g. Commander Springvale at level 85)
if ( creatureLevel > expansionMaxLevel )
aggroRadius += float ( expansionMaxLevel ) - float ( playerLevel );
// + - 1 yard for each level difference between player and creature
else
aggroRadius += float ( creatureLevel ) - float ( playerLevel );
2017-11-15 21:56:15 +01:00
// Make sure that we wont go over the total range limits
if ( aggroRadius > maxRadius )
aggroRadius = maxRadius ;
else if ( aggroRadius < minRadius )
aggroRadius = minRadius ;
2009-10-17 15:51:44 -07:00
2017-11-15 21:56:15 +01:00
return ( aggroRadius * aggroRate );
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 Creature :: setDeathState ( DeathState s )
{
2011-06-27 20:22:53 +02:00
Unit :: setDeathState ( s );
if ( s == JUST_DIED )
2008-10-11 14:16:25 -05:00
{
2017-12-10 12:39:37 +01:00
m_corpseRemoveTime = GameTime :: GetGameTime () + m_corpseDelay ;
2017-05-07 21:48:41 +01:00
uint32 respawnDelay = m_respawnDelay ;
if ( uint32 scalingMode = sWorld -> getIntConfig ( CONFIG_RESPAWN_DYNAMICMODE ))
GetMap () -> ApplyDynamicModeRespawnScaling ( this , m_spawnId , respawnDelay , scalingMode );
// @todo remove the boss respawn time hack in a dynspawn follow-up once we have creature groups in instances
if ( m_respawnCompatibilityMode )
{
if ( IsDungeonBoss () && ! m_respawnDelay )
m_respawnTime = std :: numeric_limits < time_t >:: max (); // never respawn in this instance
else
2017-12-10 12:39:37 +01:00
m_respawnTime = GameTime :: GetGameTime () + respawnDelay + m_corpseDelay ;
2017-05-07 21:48:41 +01:00
}
2017-02-05 15:02:08 +01:00
else
2017-05-07 21:48:41 +01:00
{
if ( IsDungeonBoss () && ! m_respawnDelay )
m_respawnTime = std :: numeric_limits < time_t >:: max (); // never respawn in this instance
else
2017-12-10 12:39:37 +01:00
m_respawnTime = GameTime :: GetGameTime () + respawnDelay ;
2017-05-07 21:48:41 +01:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// always save boss respawn time at death to prevent crash cheating
2010-12-23 23:25:44 +01:00
if ( sWorld -> getBoolConfig ( CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY ) || isWorldBoss ())
2008-10-11 14:16:25 -05:00
SaveRespawnTime ();
2017-05-07 21:48:41 +01:00
else if ( ! m_respawnCompatibilityMode )
SaveRespawnTime ( 0 , false );
2009-10-17 15:51:44 -07:00
2016-04-02 02:50:44 +02:00
ReleaseFocus ( nullptr , false ); // remove spellcast focus
2016-10-16 12:35:32 +02:00
DoNotReacquireTarget (); // cancel delayed re-target
2016-01-13 18:38:54 +01:00
SetTarget ( ObjectGuid :: Empty ); // drop target - dead mobs shouldn't ever target things
2019-06-03 20:40:34 +02:00
SetNpcFlags ( UNIT_NPC_FLAG_NONE );
SetNpcFlags2 ( UNIT_NPC_FLAG_2_NONE );
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
SetMountDisplayId ( 0 ); // if creature is mounted on a virtual mount, remove it at death
2015-04-19 14:56:12 +02:00
2009-06-03 19:29:55 -05:00
setActive ( false );
2009-10-17 15:51:44 -07:00
2010-01-16 19:35:30 +03:00
if ( HasSearchedAssistance ())
{
SetNoSearchAssistance ( false );
2016-04-16 01:36:32 +02:00
UpdateSpeed ( MOVE_RUN );
2010-01-16 19:35:30 +03:00
}
2009-10-17 16:20:24 -07:00
2009-04-07 10:51:45 +02:00
//Dismiss group if is leader
2018-01-22 01:54:15 -03:00
if ( m_formation && m_formation -> GetLeader () == this )
2009-04-07 10:51:45 +02:00
m_formation -> FormationReset ( true );
2009-10-17 15:51:44 -07:00
2017-11-26 16:52:42 +01:00
bool needsFalling = IsFlying () || IsHovering ();
SetHover ( false );
if ( needsFalling )
2013-12-12 09:50:52 +01:00
GetMotionMaster () -> MoveFall ();
2009-10-17 15:51:44 -07:00
2009-08-26 19:28:21 -05:00
Unit :: setDeathState ( CORPSE );
2009-04-07 10:51:45 +02:00
}
2012-05-30 08:01:02 +02:00
else if ( s == JUST_RESPAWNED )
2008-10-11 14:16:25 -05:00
{
2016-08-08 11:22:11 +02:00
if ( IsPet ())
SetFullHealth ();
else
SetSpawnHealth ();
2016-04-02 02:50:44 +02:00
SetLootRecipient ( nullptr );
2009-06-11 17:54:59 +02:00
ResetPlayerDamageReq ();
2012-12-27 19:31:22 +01:00
2016-11-03 13:55:30 +01:00
SetCannotReachTarget ( false );
2013-06-07 16:05:39 +02:00
UpdateMovementFlags ();
2012-12-27 19:31:22 +01:00
2017-03-25 12:16:13 +01:00
ClearUnitState ( UNIT_STATE_ALL_ERASABLE );
2016-01-24 01:11:19 +00:00
if ( ! IsPet ())
{
CreatureData const * creatureData = GetCreatureData ();
2017-05-21 14:15:04 +02:00
CreatureTemplate const * cInfo = GetCreatureTemplate ();
uint64 npcFlags ;
uint32 unitFlags , unitFlags2 , unitFlags3 , dynamicFlags ;
ObjectMgr :: ChooseCreatureFlags ( cInfo , npcFlags , unitFlags , unitFlags2 , unitFlags3 , dynamicFlags , creatureData );
2016-01-24 01:11:19 +00:00
2017-05-21 14:15:04 +02:00
if ( cInfo -> flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT )
2019-06-03 20:40:34 +02:00
npcFlags |= sGameEventMgr -> GetNPCFlag ( this );
SetNpcFlags ( NPCFlags ( npcFlags & 0xFFFFFFFF ));
SetNpcFlags2 ( NPCFlags2 ( npcFlags >> 32 ));
2016-01-24 01:11:19 +00:00
2019-06-03 20:40:34 +02:00
SetUnitFlags ( UnitFlags ( unitFlags ));
SetUnitFlags2 ( UnitFlags2 ( unitFlags2 ));
SetUnitFlags3 ( UnitFlags3 ( unitFlags3 ));
SetDynamicFlags ( dynamicFlags );
2016-01-24 01:11:19 +00:00
2019-06-03 20:40:34 +02:00
RemoveUnitFlag ( UNIT_FLAG_IN_COMBAT );
2016-01-24 01:11:19 +00:00
2017-05-21 14:15:04 +02:00
SetMeleeDamageSchool ( SpellSchools ( cInfo -> dmgschool ));
2016-01-24 01:11:19 +00:00
}
2009-04-06 20:33:52 -05:00
Motion_Initialize ();
2009-08-25 00:56:23 -05:00
Unit :: setDeathState ( ALIVE );
2015-11-10 19:58:13 +01:00
LoadCreaturesAddon ();
2009-04-07 10:51:45 +02:00
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2009-06-17 01:01:39 -05:00
void Creature :: Respawn ( bool force )
2008-10-11 14:16:25 -05:00
{
2010-04-07 19:14:10 +02:00
if ( force )
2009-06-17 01:01:39 -05:00
{
2013-06-11 19:54:27 -02:30
if ( IsAlive ())
2009-06-17 01:01:39 -05:00
setDeathState ( JUST_DIED );
2010-04-07 19:14:10 +02:00
else if ( getDeathState () != CORPSE )
2009-06-17 01:01:39 -05:00
setDeathState ( CORPSE );
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
if ( m_respawnCompatibilityMode )
2008-10-11 14:16:25 -05:00
{
2017-05-07 21:48:41 +01:00
DestroyForNearbyPlayers ();
RemoveCorpse ( false , false );
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
if ( getDeathState () == DEAD )
{
if ( m_spawnId )
GetMap () -> RemoveRespawnTime ( SPAWN_TYPE_CREATURE , m_spawnId );
TC_LOG_DEBUG ( "entities.unit" , "Respawning creature %s (%s)" , GetName (). c_str (), GetGUID (). ToString (). c_str ());
m_respawnTime = 0 ;
ResetPickPocketRefillTimer ();
loot . clear ();
if ( m_originalEntry != GetEntry ())
UpdateEntry ( m_originalEntry );
2017-02-13 20:42:06 +01:00
2017-07-29 21:40:28 +02:00
SelectLevel ();
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
setDeathState ( JUST_RESPAWNED );
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
CreatureModel display ( GetNativeDisplayId (), GetNativeDisplayScale (), 1.0f );
if ( sObjectMgr -> GetCreatureModelRandomGender ( & display , GetCreatureTemplate ()))
{
SetDisplayId ( display . CreatureDisplayID , display . DisplayScale );
SetNativeDisplayId ( display . CreatureDisplayID , display . DisplayScale );
}
2009-10-17 15:51:44 -07:00
2018-06-03 10:06:57 -07:00
GetMotionMaster () -> InitializeDefault ();
// Re-initialize reactstate that could be altered by movementgenerators
2017-05-07 21:48:41 +01:00
InitializeReactState ();
2010-09-15 06:23:25 +02:00
2018-08-23 14:33:28 +02:00
if ( UnitAI * ai = AI ()) // reset the AI to be sure no dirty or uninitialized values will be used till next tick
ai -> Reset ();
2017-08-11 23:06:37 -03:00
2017-08-06 16:07:15 +02:00
m_triggerJustAppeared = true ;
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
uint32 poolid = GetSpawnId () ? sPoolMgr -> IsPartOfAPool < Creature > ( GetSpawnId ()) : 0 ;
if ( poolid )
sPoolMgr -> UpdatePool < Creature > ( poolid , GetSpawnId ());
}
UpdateObjectVisibility ();
}
else
{
if ( m_spawnId )
GetMap () -> RemoveRespawnTime ( SPAWN_TYPE_CREATURE , m_spawnId , true );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
TC_LOG_DEBUG ( "entities.unit" , "Respawning creature %s (%s)" ,
GetName (). c_str (), GetGUID (). ToString (). c_str ());
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2018-07-18 17:48:15 +02:00
void Creature :: ForcedDespawn ( uint32 timeMSToDespawn , Seconds forceRespawnTimer )
2009-06-18 17:05:01 -05:00
{
2010-01-16 19:51:59 +03:00
if ( timeMSToDespawn )
{
2017-05-27 02:21:06 -03:00
m_Events . AddEvent ( new ForcedDespawnDelayEvent ( * this , forceRespawnTimer ), m_Events . CalculateTime ( timeMSToDespawn ));
2010-01-16 19:51:59 +03:00
return ;
}
2017-05-07 21:48:41 +01:00
if ( m_respawnCompatibilityMode )
{
uint32 corpseDelay = GetCorpseDelay ();
uint32 respawnDelay = GetRespawnDelay ();
// do it before killing creature
DestroyForNearbyPlayers ();
bool overrideRespawnTime = false ;
if ( IsAlive ())
{
if ( forceRespawnTimer > Seconds :: zero ())
{
SetCorpseDelay ( 0 );
SetRespawnDelay ( forceRespawnTimer . count ());
overrideRespawnTime = true ;
}
setDeathState ( JUST_DIED );
}
2017-05-27 02:21:06 -03:00
2017-05-07 21:48:41 +01:00
// Skip corpse decay time
RemoveCorpse ( ! overrideRespawnTime , false );
2017-02-05 15:39:22 +01:00
2017-05-07 21:48:41 +01:00
SetCorpseDelay ( corpseDelay );
SetRespawnDelay ( respawnDelay );
}
else
2016-08-25 23:04:21 +02:00
{
2017-05-27 02:21:06 -03:00
if ( forceRespawnTimer > Seconds :: zero ())
2017-05-07 21:48:41 +01:00
SaveRespawnTime ( forceRespawnTimer . count ());
else
2017-05-27 02:21:06 -03:00
{
2017-05-07 21:48:41 +01:00
uint32 respawnDelay = m_respawnDelay ;
if ( uint32 scalingMode = sWorld -> getIntConfig ( CONFIG_RESPAWN_DYNAMICMODE ))
GetMap () -> ApplyDynamicModeRespawnScaling ( this , m_spawnId , respawnDelay , scalingMode );
2017-12-10 12:39:37 +01:00
m_respawnTime = GameTime :: GetGameTime () + respawnDelay ;
2017-05-07 21:48:41 +01:00
SaveRespawnTime ();
2017-05-27 02:21:06 -03:00
}
2017-05-07 21:48:41 +01:00
AddObjectToRemoveList ();
2016-08-25 23:04:21 +02:00
}
2009-06-18 17:05:01 -05:00
}
2009-10-17 15:51:44 -07:00
2018-07-18 17:48:15 +02:00
void Creature :: DespawnOrUnsummon ( uint32 msTimeToDespawn /*= 0*/ , Seconds forceRespawnTimer /*= 0*/ )
2011-01-02 21:33:37 +01:00
{
2020-09-04 13:38:24 +02:00
if ( TempSummon * summon = ToTempSummon ())
2011-03-07 21:25:34 +01:00
summon -> UnSummon ( msTimeToDespawn );
2011-01-02 21:33:37 +01:00
else
2016-08-25 23:04:21 +02:00
ForcedDespawn ( msTimeToDespawn , forceRespawnTimer );
2011-01-02 21:33:37 +01:00
}
2017-12-18 10:59:47 -03:00
void Creature :: LoadTemplateImmunities ()
2017-02-04 23:50:32 +01:00
{
2017-03-08 11:24:21 -03:00
// uint32 max used for "spell id", the immunity system will not perform SpellInfo checks against invalid spells
// used so we know which immunities were loaded from template
static uint32 const placeholderSpellId = std :: numeric_limits < uint32 >:: max ();
// unapply template immunities (in case we're updating entry)
for ( uint32 i = MECHANIC_NONE + 1 ; i < MAX_MECHANIC ; ++ i )
ApplySpellImmune ( placeholderSpellId , IMMUNITY_MECHANIC , i , false );
2017-12-18 10:59:47 -03:00
for ( uint32 i = SPELL_SCHOOL_NORMAL ; i < MAX_SPELL_SCHOOL ; ++ i )
ApplySpellImmune ( placeholderSpellId , IMMUNITY_SCHOOL , 1 << i , false );
2017-03-08 11:24:21 -03:00
// don't inherit immunities for hunter pets
if ( GetOwnerGUID (). IsPlayer () && IsHunterPet ())
return ;
if ( uint32 mask = GetCreatureTemplate () -> MechanicImmuneMask )
{
for ( uint32 i = MECHANIC_NONE + 1 ; i < MAX_MECHANIC ; ++ i )
{
if ( mask & ( 1 << ( i - 1 )))
ApplySpellImmune ( placeholderSpellId , IMMUNITY_MECHANIC , i , true );
}
}
2017-12-17 16:29:22 +01:00
2017-12-18 10:59:47 -03:00
if ( uint32 mask = GetCreatureTemplate () -> SpellSchoolImmuneMask )
{
for ( uint8 i = SPELL_SCHOOL_NORMAL ; i < MAX_SPELL_SCHOOL ; ++ i )
{
2017-12-17 16:29:22 +01:00
if ( mask & ( 1 << i ))
ApplySpellImmune ( placeholderSpellId , IMMUNITY_SCHOOL , 1 << i , true );
2017-12-18 10:59:47 -03:00
}
}
2017-12-17 16:29:22 +01:00
}
2018-02-10 16:43:01 -03:00
bool Creature :: IsImmunedToSpell ( SpellInfo const * spellInfo , WorldObject const * caster ) const
2008-10-11 14:16:25 -05:00
{
if ( ! spellInfo )
return false ;
2009-10-17 15:51:44 -07:00
2011-12-14 16:13:10 +01:00
bool immunedToAllEffects = true ;
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo -> GetEffects ())
2012-06-17 02:21:40 +02:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . IsEffect () && ! IsImmunedToSpellEffect ( spellInfo , spellEffectInfo , caster ))
2011-12-14 16:13:10 +01:00
{
immunedToAllEffects = false ;
break ;
}
2012-06-17 02:21:40 +02:00
}
2017-02-04 23:50:32 +01:00
2011-12-14 16:13:10 +01:00
if ( immunedToAllEffects )
2008-10-11 14:16:25 -05:00
return true ;
2009-10-17 15:51:44 -07:00
2016-12-11 15:01:13 -03:00
return Unit :: IsImmunedToSpell ( spellInfo , caster );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2021-09-04 15:13:15 +02:00
bool Creature :: IsImmunedToSpellEffect ( SpellInfo const * spellInfo , SpellEffectInfo const & spellEffectInfo , WorldObject const * caster ) const
2008-10-11 14:16:25 -05:00
{
2021-09-04 15:13:15 +02:00
if ( GetCreatureTemplate () -> type == CREATURE_TYPE_MECHANICAL && spellEffectInfo . IsEffect ( SPELL_EFFECT_HEAL ))
2010-07-28 01:26:18 +02:00
return true ;
2021-09-04 15:13:15 +02:00
return Unit :: IsImmunedToSpellEffect ( spellInfo , spellEffectInfo , caster );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2013-03-04 12:41:17 +01:00
bool Creature :: isElite () const
{
2013-06-11 18:39:51 -02:30
if ( IsPet ())
2013-03-04 12:41:17 +01:00
return false ;
uint32 rank = GetCreatureTemplate () -> rank ;
return rank != CREATURE_ELITE_NORMAL && rank != CREATURE_ELITE_RARE ;
}
bool Creature :: isWorldBoss () const
{
2013-06-11 18:39:51 -02:30
if ( IsPet ())
2013-03-04 12:41:17 +01:00
return false ;
2016-05-26 12:56:50 +04:00
return ( GetCreatureTemplate () -> type_flags & CREATURE_TYPE_FLAG_BOSS_MOB ) != 0 ;
2013-03-04 12:41:17 +01:00
}
2010-04-25 12:59:27 +02:00
// select nearest hostile unit within the given distance (regardless of threat list).
2013-09-30 10:11:02 +02:00
Unit * Creature :: SelectNearestTarget ( float dist , bool playerOnly /* = false */ ) const
2008-11-20 20:28:17 -06:00
{
2017-05-13 19:37:33 +02:00
if ( dist == 0.0f )
dist = MAX_VISIBILITY_DISTANCE ;
2009-10-17 15:51:44 -07:00
2016-04-02 02:50:44 +02:00
Unit * target = nullptr ;
2017-05-13 19:37:33 +02:00
Trinity :: NearestHostileUnitCheck u_check ( this , dist , playerOnly );
Trinity :: UnitLastSearcher < Trinity :: NearestHostileUnitCheck > searcher ( this , target , u_check );
Cell :: VisitAllObjects ( this , searcher , dist );
2010-04-25 12:59:27 +02:00
return target ;
}
// select nearest hostile unit within the given attack distance (i.e. distance is ignored if > than ATTACK_DISTANCE), regardless of threat list.
Unit * Creature :: SelectNearestTargetInAttackDistance ( float dist ) const
{
2011-06-12 17:13:28 +02:00
if ( dist > MAX_VISIBILITY_DISTANCE )
{
2014-10-22 17:33:55 +02:00
TC_LOG_ERROR ( "entities.unit" , "Creature (%s) SelectNearestTargetInAttackDistance called with dist > MAX_VISIBILITY_DISTANCE. Distance set to ATTACK_DISTANCE." , GetGUID (). ToString (). c_str ());
2011-06-12 17:13:28 +02:00
dist = ATTACK_DISTANCE ;
}
2010-04-25 12:59:27 +02:00
2017-05-13 19:37:33 +02:00
Unit * target = nullptr ;
Trinity :: NearestHostileUnitInAttackDistanceCheck u_check ( this , dist );
Trinity :: UnitLastSearcher < Trinity :: NearestHostileUnitInAttackDistanceCheck > searcher ( this , target , u_check );
Cell :: VisitAllObjects ( this , searcher , std :: max ( dist , ATTACK_DISTANCE ));
2008-11-21 10:07:11 -06:00
return target ;
2008-11-20 20:28:17 -06:00
}
2009-10-17 15:51:44 -07:00
2009-12-19 17:43:47 +01:00
void Creature :: SendAIReaction ( AiReaction reactionType )
{
2014-11-19 00:30:31 +02:00
WorldPackets :: Combat :: AIReaction packet ;
2009-12-19 17:43:47 +01:00
2014-11-19 00:30:31 +02:00
packet . UnitGUID = GetGUID ();
packet . Reaction = reactionType ;
2009-12-19 17:43:47 +01:00
2014-11-19 21:09:30 +01:00
SendMessageToSet ( packet . Write (), true );
2009-12-19 17:43:47 +01:00
2013-11-08 10:50:51 +01:00
TC_LOG_DEBUG ( "network" , "WORLD: Sent SMSG_AI_REACTION, type %u." , reactionType );
2009-12-19 17:43:47 +01:00
}
2009-05-21 10:48:00 -05:00
void Creature :: CallAssistance ()
2008-10-11 14:16:25 -05:00
{
2013-06-11 19:54:27 -02:30
if ( ! m_AlreadyCallAssistance && GetVictim () && ! IsPet () && ! IsCharmed ())
2008-10-11 14:16:25 -05:00
{
2008-11-25 16:36:50 -06:00
SetNoCallAssistance ( true );
2009-10-17 15:51:44 -07:00
2010-12-23 23:25:44 +01:00
float radius = sWorld -> getFloatConfig ( CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS );
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( radius > 0 )
2008-10-11 14:16:25 -05:00
{
std :: list < Creature *> assistList ;
2017-05-13 19:37:33 +02:00
Trinity :: AnyAssistCreatureInRangeCheck u_check ( this , GetVictim (), radius );
Trinity :: CreatureListSearcher < Trinity :: AnyAssistCreatureInRangeCheck > searcher ( this , assistList , u_check );
Cell :: VisitGridObjects ( this , searcher , radius );
2009-10-17 15:51:44 -07:00
2008-11-25 16:36:50 -06:00
if ( ! assistList . empty ())
2008-10-11 14:16:25 -05:00
{
2014-04-19 21:48:22 +02:00
AssistDelayEvent * e = new AssistDelayEvent ( EnsureVictim () -> GetGUID (), * this );
2008-11-25 16:36:50 -06:00
while ( ! assistList . empty ())
{
// Pushing guids because in delay can happen some creature gets despawned => invalid pointer
e -> AddAssistant (( * assistList . begin ()) -> GetGUID ());
assistList . pop_front ();
}
2010-12-23 23:25:44 +01:00
m_Events . AddEvent ( e , m_Events . CalculateTime ( sWorld -> getIntConfig ( CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY )));
2008-10-11 14:16:25 -05:00
}
}
}
}
2009-10-17 15:51:44 -07:00
2011-10-19 17:40:44 +01:00
void Creature :: CallForHelp ( float radius )
2009-06-04 16:10:26 -05:00
{
2018-08-22 22:21:47 +02:00
if ( radius <= 0.0f || ! IsEngaged () || IsPet () || IsCharmed ())
2009-06-04 16:10:26 -05:00
return ;
2009-10-17 15:51:44 -07:00
2018-08-22 22:21:47 +02:00
Unit * target = GetThreatManager (). GetCurrentVictim ();
if ( ! target )
target = GetThreatManager (). GetAnyTarget ();
2018-08-23 12:37:44 +02:00
if ( ! target )
target = GetCombatManager (). GetAnyTarget ();
2018-08-22 22:21:47 +02:00
ASSERT ( target , "Creature %u (%s) is engaged without threat list" , GetEntry (), GetName (). c_str ());
Trinity :: CallOfHelpCreatureInRangeDo u_do ( this , target , radius );
2009-12-20 15:20:04 +01:00
Trinity :: CreatureWorker < Trinity :: CallOfHelpCreatureInRangeDo > worker ( this , u_do );
2017-05-13 19:37:33 +02:00
Cell :: VisitGridObjects ( this , worker , radius );
2009-06-04 16:10:26 -05:00
}
2009-10-17 15:51:44 -07:00
2020-09-04 13:38:24 +02:00
bool Creature :: CanAssistTo ( Unit const * u , Unit const * enemy , bool checkfaction /*= true*/ ) const
2008-11-25 16:36:50 -06:00
{
2008-12-07 09:21:12 -06:00
// is it true?
2010-04-07 19:14:10 +02:00
if ( ! HasReactState ( REACT_AGGRESSIVE ))
2008-11-25 16:36:50 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2008-11-25 16:36:50 -06:00
// we don't need help from zombies :)
2013-06-11 19:54:27 -02:30
if ( ! IsAlive ())
2008-11-25 16:36:50 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
// we cannot assist in evade mode
if ( IsInEvadeMode ())
return false ;
// or if enemy is in evade mode
if ( enemy -> GetTypeId () == TYPEID_UNIT && enemy -> ToCreature () -> IsInEvadeMode ())
return false ;
2009-07-24 09:15:52 +08:00
// we don't need help from non-combatant ;)
2013-06-11 19:54:27 -02:30
if ( IsCivilian ())
2009-07-24 09:15:52 +08:00
return false ;
2009-10-17 15:51:44 -07:00
2017-07-09 02:07:29 +02:00
if ( HasUnitFlag ( UnitFlags ( UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE )) || IsImmuneToNPC ())
2011-03-06 12:22:04 +01:00
return false ;
2008-11-25 16:36:50 -06:00
// skip fighting creature
2017-07-01 20:18:02 +02:00
if ( IsEngaged ())
2008-11-25 16:36:50 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2008-11-25 16:36:50 -06:00
// only free creature
2014-10-21 20:27:00 +02:00
if ( ! GetCharmerOrOwnerGUID (). IsEmpty ())
2008-11-25 16:36:50 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2009-05-21 10:15:09 -05:00
// only from same creature faction
if ( checkfaction )
{
2017-04-28 02:24:04 -03:00
if ( GetFaction () != u -> GetFaction ())
2009-05-21 10:15:09 -05:00
return false ;
}
else
{
if ( ! IsFriendlyTo ( u ))
return false ;
}
2009-10-17 15:51:44 -07:00
2008-11-25 16:36:50 -06:00
// skip non hostile to caster enemy creatures
2009-05-21 10:15:09 -05:00
if ( ! IsHostileTo ( enemy ))
2008-11-25 16:36:50 -06:00
return false ;
2009-10-17 15:51:44 -07:00
2008-11-25 16:36:50 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2009-09-29 15:13:46 -07:00
// use this function to avoid having hostile creatures attack
// friendlies and other mobs they shouldn't attack
2017-03-04 04:18:55 -03:00
bool Creature :: _IsTargetAcceptable ( Unit const * target ) const
2009-09-29 15:13:46 -07:00
{
2010-07-30 20:04:28 +02:00
ASSERT ( target );
2009-10-22 02:19:30 -07:00
2009-10-23 23:26:04 -07:00
// if the target cannot be attacked, the target is not acceptable
2009-10-24 14:53:03 -07:00
if ( IsFriendlyTo ( target )
2011-08-19 14:53:40 +02:00
|| ! target -> isTargetableForAttack ( false )
2009-10-24 14:53:03 -07:00
|| ( m_vehicle && ( IsOnVehicle ( target ) || m_vehicle -> GetBase () -> IsOnVehicle ( target ))))
2009-10-23 20:01:15 -07:00
return false ;
2009-10-17 15:51:44 -07:00
2012-02-03 19:12:41 +02:00
if ( target -> HasUnitState ( UNIT_STATE_DIED ))
2011-02-10 22:36:35 -03:00
{
// guards can detect fake death
2019-06-03 20:40:34 +02:00
if ( IsGuard () && target -> HasUnitFlag2 ( UNIT_FLAG2_FEIGN_DEATH ))
2011-02-10 22:36:35 -03:00
return true ;
else
return false ;
}
2009-10-23 23:26:04 -07:00
// if I'm already fighting target, or I'm hostile towards the target, the target is acceptable
2017-07-01 20:18:02 +02:00
if ( IsEngagedBy ( target ) || IsHostileTo ( target ))
2009-10-23 23:26:04 -07:00
return true ;
2009-10-17 15:51:44 -07:00
2009-10-10 16:26:00 -07:00
// if the target's victim is not friendly, or the target is friendly, the target is not acceptable
return false ;
2009-09-29 15:13:46 -07:00
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
void Creature :: SaveRespawnTime ( uint32 forceDelay , bool savetodb )
2008-10-11 14:16:25 -05:00
{
2015-02-25 00:13:14 +01:00
if ( IsSummon () || ! m_spawnId || ( m_creatureData && ! m_creatureData -> dbData ))
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
if ( m_respawnCompatibilityMode )
{
GetMap () -> SaveRespawnTimeDB ( SPAWN_TYPE_CREATURE , m_spawnId , m_respawnTime );
return ;
}
2017-12-10 12:39:37 +01:00
time_t thisRespawnTime = forceDelay ? GameTime :: GetGameTime () + forceDelay : m_respawnTime ;
2017-10-25 23:13:04 +01:00
GetMap () -> SaveRespawnTime ( SPAWN_TYPE_CREATURE , m_spawnId , GetEntry (), thisRespawnTime , GetMap () -> GetZoneId ( GetPhaseShift (), GetHomePosition ()), Trinity :: ComputeGridCoord ( GetHomePosition (). GetPositionX (), GetHomePosition (). GetPositionY ()). GetId (), savetodb && m_creatureData && m_creatureData -> dbData );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2009-10-17 16:20:24 -07:00
// this should not be called by petAI or
2013-06-11 21:25:12 -02:30
bool Creature :: CanCreatureAttack ( Unit const * victim , bool /*force*/ ) const
2008-10-11 14:16:25 -05:00
{
2012-05-30 08:01:02 +02:00
if ( ! victim -> IsInMap ( this ))
2009-08-15 14:30:14 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
if ( ! IsValidAttackTarget ( victim ))
2009-08-15 14:30:14 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
if ( ! victim -> isInAccessiblePlaceFor ( this ))
2009-08-15 14:30:14 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2018-08-23 14:33:28 +02:00
if ( IsAIEnabled () && ! AI () -> CanAIAttack ( victim ))
2009-08-26 16:54:59 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
// we cannot attack in evade mode
if ( IsInEvadeMode ())
return false ;
// or if enemy is in evade mode
if ( victim -> GetTypeId () == TYPEID_UNIT && victim -> ToCreature () -> IsInEvadeMode ())
return false ;
2009-10-17 15:51:44 -07:00
2017-02-05 15:39:22 +01:00
if ( ! GetCharmerOrOwnerGUID (). IsPlayer ())
{
if ( GetMap () -> IsDungeon ())
return true ;
// don't check distance to home position if recently damaged, this should include taunt auras
2017-03-02 02:19:25 +01:00
if ( ! isWorldBoss () && ( GetLastDamagedTime () > GameTime :: GetGameTime () || HasAuraType ( SPELL_AURA_MOD_TAUNT )))
2016-02-09 22:53:38 +01:00
return true ;
2017-02-05 15:39:22 +01:00
}
2016-02-09 22:53:38 +01:00
2017-02-05 15:39:22 +01:00
// Map visibility range, but no more than 2*cell size
float dist = std :: min < float > ( GetMap () -> GetVisibilityRange (), SIZE_OF_GRID_CELL * 2 );
2009-10-17 15:51:44 -07:00
2011-09-15 14:08:17 +02:00
if ( Unit * unit = GetCharmerOrOwner ())
2012-05-30 08:01:02 +02:00
return victim -> IsWithinDist ( unit , dist );
2009-08-15 14:30:14 -05:00
else
2017-02-05 15:39:22 +01:00
{
// include sizes for huge npcs
2017-03-23 00:43:04 +01:00
dist += GetCombatReach () + victim -> GetCombatReach ();
2017-02-05 15:39:22 +01:00
// to prevent creatures in air ignore attacks because distance is already too high...
2017-11-26 16:37:51 +01:00
if ( GetMovementTemplate (). IsFlightAllowed ())
2017-02-05 15:39:22 +01:00
return victim -> IsInDist2d ( & m_homePosition , dist );
else
return victim -> IsInDist ( & m_homePosition , dist );
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-04-28 22:46:40 +02:00
CreatureAddon const * Creature :: GetCreatureAddon () const
2008-10-11 14:16:25 -05:00
{
2015-02-25 00:13:14 +01:00
if ( m_spawnId )
2008-10-11 14:16:25 -05:00
{
2015-02-25 00:13:14 +01:00
if ( CreatureAddon const * addon = sObjectMgr -> GetCreatureAddon ( m_spawnId ))
2008-10-11 14:16:25 -05:00
return addon ;
}
2009-10-17 15:51:44 -07:00
2009-12-17 11:06:50 +01:00
// dependent from difficulty mode entry
2012-05-30 08:01:02 +02:00
return sObjectMgr -> GetCreatureTemplateAddon ( GetCreatureTemplate () -> Entry );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
//creature_addon table
2015-11-10 19:58:13 +01:00
bool Creature :: LoadCreaturesAddon ()
2008-10-11 14:16:25 -05:00
{
2011-09-15 14:08:17 +02:00
CreatureAddon const * cainfo = GetCreatureAddon ();
2010-04-07 19:14:10 +02:00
if ( ! cainfo )
2008-10-11 14:16:25 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
if ( cainfo -> mount != 0 )
Mount ( cainfo -> mount );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
if ( cainfo -> bytes1 != 0 )
2009-05-31 15:58:58 -05:00
{
// 0 StandState
// 1 FreeTalentPoints Pet only, so always 0 for default creature
// 2 StandFlags
// 3 StandMiscFlags
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
SetStandState ( UnitStandStateType ( cainfo -> bytes1 & 0xFF ));
SetVisFlags ( UnitVisFlags (( cainfo -> bytes1 >> 16 ) & 0xFF ));
SetAnimTier ( UnitBytes1_Flags (( cainfo -> bytes1 >> 24 ) & 0xFF ), false );
2012-05-30 08:01:02 +02:00
//! Suspected correlation between UNIT_FIELD_BYTES_1, offset 3, value 0x2:
2013-02-22 01:21:49 +01:00
//! If no inhabittype_fly (if no MovementFlag_DisableGravity or MovementFlag_CanFly flag found in sniffs)
//! Check using InhabitType as movement flags are assigned dynamically
//! basing on whether the creature is in air or not
2012-05-30 08:01:02 +02:00
//! Set MovementFlag_Hover. Otherwise do nothing.
2017-11-26 16:37:51 +01:00
if ( CanHover ())
2012-05-30 08:01:02 +02:00
AddUnitMovementFlag ( MOVEMENTFLAG_HOVER );
2009-05-31 15:58:58 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
if ( cainfo -> bytes2 != 0 )
2009-05-31 15:58:58 -05:00
{
// 0 SheathState
2016-12-13 20:58:22 +01:00
// 1 PvpFlags
// 2 PetFlags Pet only, so always 0 for default creature
2009-05-31 15:58:58 -05:00
// 3 ShapeshiftForm Must be determined/set by shapeshift spell/aura
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
SetSheath ( SheathState ( cainfo -> bytes2 & 0xFF ));
SetPvpFlags ( UNIT_BYTE2_FLAG_NONE );
SetPetFlags ( UNIT_PET_FLAG_NONE );
SetShapeshiftForm ( FORM_NONE );
2009-05-31 15:58:58 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
if ( cainfo -> emote != 0 )
2019-06-03 20:40:34 +02:00
SetEmoteState ( Emote ( cainfo -> emote ));
2009-10-17 15:51:44 -07:00
2016-08-20 19:23:07 -02:30
SetAIAnimKitId ( cainfo -> aiAnimKit );
SetMovementAnimKitId ( cainfo -> movementAnimKit );
SetMeleeAnimKitId ( cainfo -> meleeAnimKit );
2019-01-16 20:48:01 +01:00
// Check if visibility distance different
if ( cainfo -> visibilityDistanceType != VisibilityDistanceType :: Normal )
SetVisibilityDistanceOverride ( cainfo -> visibilityDistanceType );
2017-11-06 22:19:23 +01:00
// Load Path
2009-09-19 12:13:08 -07:00
if ( cainfo -> path_id != 0 )
2017-11-06 22:19:23 +01:00
_waypointPathId = cainfo -> path_id ;
2009-10-17 15:51:44 -07:00
2011-04-28 22:46:40 +02:00
if ( ! cainfo -> auras . empty ())
2008-10-11 14:16:25 -05:00
{
2011-04-28 22:46:40 +02:00
for ( std :: vector < uint32 >:: const_iterator itr = cainfo -> auras . begin (); itr != cainfo -> auras . end (); ++ itr )
2008-10-11 14:16:25 -05:00
{
2020-06-12 01:29:18 +02:00
SpellInfo const * AdditionalSpellInfo = sSpellMgr -> GetSpellInfo ( * itr , GetMap () -> GetDifficultyID ());
2008-10-11 14:16:25 -05:00
if ( ! AdditionalSpellInfo )
{
2014-10-22 17:33:55 +02:00
TC_LOG_ERROR ( "sql.sql" , "Creature (%s) has wrong spell %u defined in `auras` field." , GetGUID (). ToString (). c_str (), * itr );
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
// skip already applied aura
2011-04-28 22:46:40 +02:00
if ( HasAura ( * itr ))
2008-10-11 14:16:25 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2011-04-28 22:46:40 +02:00
AddAura ( * itr , this );
2014-10-22 17:33:55 +02:00
TC_LOG_DEBUG ( "entities.unit" , "Spell: %u added to creature (%s)" , * itr , GetGUID (). ToString (). c_str ());
2008-10-11 14:16:25 -05:00
}
}
2012-05-30 08:01:02 +02:00
2008-10-11 14:16:25 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2008-11-14 16:28:45 -06:00
/// Send a message to LocalDefense channel for players opposition team in the zone
2008-10-11 14:16:25 -05:00
void Creature :: SendZoneUnderAttackMessage ( Player * attacker )
{
uint32 enemy_team = attacker -> GetTeam ();
2015-04-04 19:52:53 +02:00
WorldPackets :: Misc :: ZoneUnderAttack packet ;
2015-04-04 18:03:56 +02:00
packet . AreaID = GetAreaId ();
2016-04-02 02:50:44 +02:00
sWorld -> SendGlobalMessage ( packet . Write (), nullptr , ( enemy_team == ALLIANCE ? HORDE : ALLIANCE ));
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
bool Creature :: HasSpell ( uint32 spellID ) const
{
2018-03-19 17:29:13 -03:00
return std :: find ( std :: begin ( m_spells ), std :: end ( m_spells ), spellID ) != std :: end ( m_spells );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
time_t Creature :: GetRespawnTimeEx () const
{
2017-12-10 12:39:37 +01:00
time_t now = GameTime :: GetGameTime ();
2010-09-15 06:22:54 +02:00
if ( m_respawnTime > now )
2008-10-11 14:16:25 -05:00
return m_respawnTime ;
else
return now ;
}
2009-10-17 15:51:44 -07:00
2017-12-10 12:39:37 +01:00
void Creature :: SetRespawnTime ( uint32 respawn )
{
m_respawnTime = respawn ? GameTime :: GetGameTime () + respawn : 0 ;
}
2011-10-18 10:53:34 -04:00
void Creature :: GetRespawnPosition ( float & x , float & y , float & z , float * ori , float * dist ) const
2008-10-11 14:16:25 -05:00
{
2017-05-07 21:48:41 +01:00
if ( m_creatureData )
2008-10-11 14:16:25 -05:00
{
2017-05-07 21:48:41 +01:00
if ( ori )
m_creatureData -> spawnPoint . GetPosition ( x , y , z , * ori );
else
m_creatureData -> spawnPoint . GetPosition ( x , y , z );
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
if ( dist )
* dist = m_creatureData -> spawndist ;
}
else
{
Position const & homePos = GetHomePosition ();
if ( ori )
homePos . GetPosition ( x , y , z , * ori );
else
homePos . GetPosition ( x , y , z );
if ( dist )
* dist = 0 ;
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-11-26 16:37:51 +01:00
CreatureMovementData const & Creature :: GetMovementTemplate () const
{
if ( CreatureMovementData const * movementOverride = sObjectMgr -> GetCreatureMovementOverride ( m_spawnId ))
return * movementOverride ;
return GetCreatureTemplate () -> Movement ;
}
2008-10-11 14:16:25 -05:00
void Creature :: AllLootRemovedFromCorpse ()
{
2014-06-26 22:01:55 +02:00
if ( loot . loot_type != LOOT_SKINNING && ! IsPet () && GetCreatureTemplate () -> SkinLootId && hasLootRecipient ())
if ( LootTemplates_Skinning . HaveLootFor ( GetCreatureTemplate () -> SkinLootId ))
2019-06-03 20:40:34 +02:00
AddUnitFlag ( UNIT_FLAG_SKINNABLE );
2009-10-17 15:51:44 -07:00
2017-12-10 12:39:37 +01:00
time_t now = GameTime :: GetGameTime ();
2014-07-12 17:27:26 +02:00
// Do not reset corpse remove time if corpse is already removed
2014-06-26 22:01:55 +02:00
if ( m_corpseRemoveTime <= now )
return ;
2009-10-17 15:51:44 -07:00
2014-06-26 22:01:55 +02:00
float decayRate = sWorld -> getRate ( RATE_CORPSE_DECAY_LOOTED );
2014-06-24 15:21:34 -05:00
2014-06-26 22:01:55 +02:00
// corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
if ( loot . loot_type == LOOT_SKINNING )
2014-07-12 17:27:26 +02:00
m_corpseRemoveTime = now ;
2014-06-26 22:01:55 +02:00
else
2014-08-19 00:00:54 +02:00
m_corpseRemoveTime = now + uint32 ( m_corpseDelay * decayRate );
2014-06-26 22:01:55 +02:00
2017-02-05 15:02:08 +01:00
m_respawnTime = std :: max < time_t > ( m_corpseRemoveTime + m_respawnDelay , m_respawnTime );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-07-29 21:40:28 +02:00
bool Creature :: HasScalableLevels () const
2008-10-11 14:16:25 -05:00
{
2017-07-29 21:40:28 +02:00
CreatureTemplate const * cinfo = GetCreatureTemplate ();
2018-11-30 17:57:32 +01:00
CreatureLevelScaling const * scaling = cinfo -> GetLevelScaling ( GetMap () -> GetDifficultyID ());
2021-08-05 19:37:53 +02:00
return scaling -> ContentTuningID != 0 ;
2017-07-29 21:40:28 +02:00
}
uint64 Creature :: GetMaxHealthByLevel ( uint8 level ) const
{
CreatureTemplate const * cInfo = GetCreatureTemplate ();
2018-11-30 17:57:32 +01:00
CreatureLevelScaling const * scaling = cInfo -> GetLevelScaling ( GetMap () -> GetDifficultyID ());
2020-05-02 22:26:52 +02:00
float baseHealth = sDB2Manager . EvaluateExpectedStat ( ExpectedStatType :: CreatureHealth , level , cInfo -> GetHealthScalingExpansion (), scaling -> ContentTuningID , Classes ( cInfo -> unit_class ));
2018-11-30 17:57:32 +01:00
return baseHealth * cInfo -> ModHealth * cInfo -> ModHealthExtra ;
2017-07-29 21:40:28 +02:00
}
float Creature :: GetHealthMultiplierForTarget ( WorldObject const * target ) const
{
if ( ! HasScalableLevels ())
return 1.0f ;
uint8 levelForTarget = GetLevelForTarget ( target );
if ( getLevel () < levelForTarget )
return 1.0f ;
return double ( GetMaxHealthByLevel ( levelForTarget )) / double ( GetCreateHealth ());
}
float Creature :: GetBaseDamageForLevel ( uint8 level ) const
{
CreatureTemplate const * cInfo = GetCreatureTemplate ();
2018-11-30 17:57:32 +01:00
CreatureLevelScaling const * scaling = cInfo -> GetLevelScaling ( GetMap () -> GetDifficultyID ());
2020-05-02 22:26:52 +02:00
return sDB2Manager . EvaluateExpectedStat ( ExpectedStatType :: CreatureAutoAttackDps , level , cInfo -> GetHealthScalingExpansion (), scaling -> ContentTuningID , Classes ( cInfo -> unit_class ));
2017-07-29 21:40:28 +02:00
}
float Creature :: GetDamageMultiplierForTarget ( WorldObject const * target ) const
{
if ( ! HasScalableLevels ())
return 1.0f ;
uint8 levelForTarget = GetLevelForTarget ( target );
return GetBaseDamageForLevel ( levelForTarget ) / GetBaseDamageForLevel ( getLevel ());
}
float Creature :: GetBaseArmorForLevel ( uint8 level ) const
{
CreatureTemplate const * cInfo = GetCreatureTemplate ();
2018-11-30 17:57:32 +01:00
CreatureLevelScaling const * scaling = cInfo -> GetLevelScaling ( GetMap () -> GetDifficultyID ());
2020-05-02 22:26:52 +02:00
float baseArmor = sDB2Manager . EvaluateExpectedStat ( ExpectedStatType :: CreatureArmor , level , cInfo -> GetHealthScalingExpansion (), scaling -> ContentTuningID , Classes ( cInfo -> unit_class ));
2018-11-30 17:57:32 +01:00
return baseArmor * cInfo -> ModArmor ;
2017-07-29 21:40:28 +02:00
}
float Creature :: GetArmorMultiplierForTarget ( WorldObject const * target ) const
{
if ( ! HasScalableLevels ())
return 1.0f ;
uint8 levelForTarget = GetLevelForTarget ( target );
return GetBaseArmorForLevel ( levelForTarget ) / GetBaseArmorForLevel ( getLevel ());
}
uint8 Creature :: GetLevelForTarget ( WorldObject const * target ) const
{
if ( Unit const * unitTarget = target -> ToUnit ())
{
if ( isWorldBoss ())
{
uint8 level = unitTarget -> getLevel () + sWorld -> getIntConfig ( CONFIG_WORLD_BOSS_LEVEL_DIFF );
return RoundToInterval < uint8 > ( level , 1u , 255u );
}
// If this creature should scale level, adapt level depending of target level
// between UNIT_FIELD_SCALING_LEVEL_MIN and UNIT_FIELD_SCALING_LEVEL_MAX
if ( HasScalableLevels ())
{
2020-05-02 22:26:52 +02:00
int32 scalingLevelMin = m_unitData -> ScalingLevelMin ;
int32 scalingLevelMax = m_unitData -> ScalingLevelMax ;
int32 scalingLevelDelta = m_unitData -> ScalingLevelDelta ;
int32 scalingFactionGroup = m_unitData -> ScalingFactionGroup ;
int32 targetLevel = unitTarget -> m_unitData -> EffectiveLevel ;
if ( ! targetLevel )
targetLevel = unitTarget -> getLevel ();
int32 targetLevelDelta = 0 ;
2017-07-29 21:40:28 +02:00
2020-05-02 22:26:52 +02:00
if ( Player const * playerTarget = target -> ToPlayer ())
{
if ( scalingFactionGroup && sFactionTemplateStore . AssertEntry ( sChrRacesStore . AssertEntry ( playerTarget -> getRace ()) -> FactionID ) -> FactionGroup != scalingFactionGroup )
scalingLevelMin = scalingLevelMax ;
int32 maxCreatureScalingLevel = playerTarget -> m_activePlayerData -> MaxCreatureScalingLevel ;
targetLevelDelta = std :: min ( maxCreatureScalingLevel > 0 ? maxCreatureScalingLevel - targetLevel : 0 , * playerTarget -> m_activePlayerData -> ScalingPlayerLevelDelta );
}
2017-07-29 21:40:28 +02:00
2020-05-02 22:26:52 +02:00
int32 levelWithDelta = targetLevel + targetLevelDelta ;
int32 level = RoundToInterval ( levelWithDelta , scalingLevelMin , scalingLevelMax ) + scalingLevelDelta ;
return RoundToInterval ( level , 1 , MAX_LEVEL + 3 );
2017-07-29 21:40:28 +02:00
}
}
2009-10-17 15:51:44 -07:00
2017-07-29 21:40:28 +02:00
return Unit :: GetLevelForTarget ( target );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-06-01 02:29:20 -03:00
std :: string const & Creature :: GetAIName () const
2009-04-14 18:31:31 -05:00
{
2011-04-28 22:57:08 +02:00
return sObjectMgr -> GetCreatureTemplate ( GetEntry ()) -> AIName ;
2009-04-14 18:31:31 -05:00
}
2009-10-17 15:51:44 -07:00
2009-04-14 18:31:31 -05:00
std :: string Creature :: GetScriptName () const
2008-10-11 14:16:25 -05:00
{
2010-12-22 21:25:23 +01:00
return sObjectMgr -> GetScriptName ( GetScriptId ());
2008-11-20 16:16:57 -06:00
}
2009-10-17 15:51:44 -07:00
2009-04-14 18:31:31 -05:00
uint32 Creature :: GetScriptId () const
2008-11-20 16:16:57 -06:00
{
2017-03-18 11:31:31 +01:00
if ( CreatureData const * creatureData = GetCreatureData ())
2017-05-07 21:48:41 +01:00
if ( uint32 scriptId = creatureData -> scriptId )
2017-03-31 01:58:15 -03:00
return scriptId ;
2017-03-18 11:31:31 +01:00
2011-04-28 22:57:08 +02:00
return sObjectMgr -> GetCreatureTemplate ( GetEntry ()) -> ScriptID ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
VendorItemData const * Creature :: GetVendorItems () const
{
2010-12-22 21:25:23 +01:00
return sObjectMgr -> GetNpcVendorItemList ( GetEntry ());
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
uint32 Creature :: GetVendorItemCurrentCount ( VendorItem const * vItem )
{
2010-04-07 19:14:10 +02:00
if ( ! vItem -> maxcount )
2008-10-11 14:16:25 -05:00
return vItem -> maxcount ;
2009-10-17 15:51:44 -07:00
2009-12-24 10:20:15 +01:00
VendorItemCounts :: iterator itr = m_vendorItemCounts . begin ();
for (; itr != m_vendorItemCounts . end (); ++ itr )
2010-04-07 23:25:02 +02:00
if ( itr -> itemId == vItem -> item )
2009-12-24 10:20:15 +01:00
break ;
2010-04-07 19:14:10 +02:00
if ( itr == m_vendorItemCounts . end ())
2008-10-11 14:16:25 -05:00
return vItem -> maxcount ;
2009-10-17 15:51:44 -07:00
2009-12-24 10:20:15 +01:00
VendorItemCount * vCount = &* itr ;
2009-10-17 15:51:44 -07:00
2017-12-10 12:39:37 +01:00
time_t ptime = GameTime :: GetGameTime ();
2009-10-17 15:51:44 -07:00
2010-08-23 15:11:06 +02:00
if ( time_t ( vCount -> lastIncrementTime + vItem -> incrtime ) <= ptime )
2013-05-30 13:14:06 +02:00
if ( ItemTemplate const * pProto = sObjectMgr -> GetItemTemplate ( vItem -> item ))
2008-10-11 14:16:25 -05:00
{
2013-05-30 13:14:06 +02:00
uint32 diff = uint32 (( ptime - vCount -> lastIncrementTime ) / vItem -> incrtime );
2014-12-21 13:35:43 +01:00
if (( vCount -> count + diff * pProto -> GetBuyCount ()) >= vItem -> maxcount )
2013-05-30 13:14:06 +02:00
{
m_vendorItemCounts . erase ( itr );
return vItem -> maxcount ;
}
2009-10-17 15:51:44 -07:00
2014-12-21 13:35:43 +01:00
vCount -> count += diff * pProto -> GetBuyCount ();
2013-05-30 13:14:06 +02:00
vCount -> lastIncrementTime = ptime ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
return vCount -> count ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
uint32 Creature :: UpdateVendorItemCurrentCount ( VendorItem const * vItem , uint32 used_count )
{
2010-04-07 19:14:10 +02:00
if ( ! vItem -> maxcount )
2008-10-11 14:16:25 -05:00
return 0 ;
2009-10-17 15:51:44 -07:00
2009-12-24 10:20:15 +01:00
VendorItemCounts :: iterator itr = m_vendorItemCounts . begin ();
for (; itr != m_vendorItemCounts . end (); ++ itr )
2010-04-07 23:25:02 +02:00
if ( itr -> itemId == vItem -> item )
2009-12-24 10:20:15 +01:00
break ;
2010-04-07 19:14:10 +02:00
if ( itr == m_vendorItemCounts . end ())
2008-10-11 14:16:25 -05:00
{
2010-08-23 15:11:06 +02:00
uint32 new_count = vItem -> maxcount > used_count ? vItem -> maxcount - used_count : 0 ;
2011-04-29 20:47:02 +02:00
m_vendorItemCounts . push_back ( VendorItemCount ( vItem -> item , new_count ));
2008-10-11 14:16:25 -05:00
return new_count ;
}
2009-10-17 15:51:44 -07:00
2009-12-24 10:20:15 +01:00
VendorItemCount * vCount = &* itr ;
2009-10-17 15:51:44 -07:00
2017-12-10 12:39:37 +01:00
time_t ptime = GameTime :: GetGameTime ();
2009-10-17 15:51:44 -07:00
2010-08-30 15:25:15 +02:00
if ( time_t ( vCount -> lastIncrementTime + vItem -> incrtime ) <= ptime )
2013-05-30 13:14:06 +02:00
if ( ItemTemplate const * pProto = sObjectMgr -> GetItemTemplate ( vItem -> item ))
{
uint32 diff = uint32 (( ptime - vCount -> lastIncrementTime ) / vItem -> incrtime );
2014-12-21 13:35:43 +01:00
if (( vCount -> count + diff * pProto -> GetBuyCount ()) < vItem -> maxcount )
vCount -> count += diff * pProto -> GetBuyCount ();
2013-05-30 13:14:06 +02:00
else
vCount -> count = vItem -> maxcount ;
}
2009-10-17 15:51:44 -07:00
2009-12-24 10:20:15 +01:00
vCount -> count = vCount -> count > used_count ? vCount -> count - used_count : 0 ;
2008-10-11 14:16:25 -05:00
vCount -> lastIncrementTime = ptime ;
return vCount -> count ;
}
2009-10-17 15:51:44 -07:00
2008-11-14 17:03:03 -06:00
// overwrite WorldObject function for proper name localization
2020-04-18 10:59:35 +02:00
std :: string Creature :: GetNameForLocaleIdx ( LocaleConstant locale ) const
2008-11-14 17:03:03 -06:00
{
2020-04-18 10:59:35 +02:00
if ( locale != DEFAULT_LOCALE )
if ( CreatureLocale const * cl = sObjectMgr -> GetCreatureLocale ( GetEntry ()))
if ( cl -> Name . size () > locale && ! cl -> Name [ locale ]. empty ())
return cl -> Name [ locale ];
2009-10-17 15:51:44 -07:00
2008-11-14 17:03:03 -06:00
return GetName ();
}
2009-10-17 15:51:44 -07:00
2013-03-04 12:41:17 +01:00
uint32 Creature :: GetPetAutoSpellOnPos ( uint8 pos ) const
{
if ( pos >= MAX_SPELL_CHARM || m_charmInfo -> GetCharmSpell ( pos ) -> GetType () != ACT_ENABLED )
return 0 ;
else
return m_charmInfo -> GetCharmSpell ( pos ) -> GetAction ();
}
2016-11-28 01:22:46 +01:00
float Creature :: GetPetChaseDistance () const
{
2017-11-22 20:54:19 +01:00
float range = 0.f ;
2016-11-28 01:22:46 +01:00
for ( uint8 i = 0 ; i < GetPetAutoSpellSize (); ++ i )
{
uint32 spellID = GetPetAutoSpellOnPos ( i );
if ( ! spellID )
continue ;
2020-06-12 01:29:18 +02:00
if ( SpellInfo const * spellInfo = sSpellMgr -> GetSpellInfo ( spellID , GetMap () -> GetDifficultyID ()))
2016-11-28 01:22:46 +01:00
{
2017-11-22 20:54:19 +01:00
if ( spellInfo -> GetRecoveryTime () == 0 && spellInfo -> RangeEntry -> ID != 1 /*Self*/ && spellInfo -> RangeEntry -> ID != 2 /*Combat Range*/ && spellInfo -> GetMaxRange () > range )
range = spellInfo -> GetMaxRange ();
2016-11-28 01:22:46 +01:00
}
}
return range ;
}
2012-12-06 09:58:32 -05:00
float Creature :: GetAggroRange ( Unit const * target ) const
{
// Determines the aggro range for creatures (usually pets), used mainly for aggressive pet target selection.
// Based on data from wowwiki due to lack of 3.3.5a data
2020-09-04 13:38:24 +02:00
if ( target && IsPet ())
2012-12-06 09:58:32 -05:00
{
uint32 targetLevel = 0 ;
2012-12-14 01:23:53 +00:00
2012-12-06 09:58:32 -05:00
if ( target -> GetTypeId () == TYPEID_PLAYER )
2017-07-29 21:40:28 +02:00
targetLevel = target -> GetLevelForTarget ( this );
2012-12-06 09:58:32 -05:00
else if ( target -> GetTypeId () == TYPEID_UNIT )
2017-07-29 21:40:28 +02:00
targetLevel = target -> ToCreature () -> GetLevelForTarget ( this );
2012-12-06 09:58:32 -05:00
2017-07-29 21:40:28 +02:00
uint32 myLevel = GetLevelForTarget ( target );
2012-12-06 09:58:32 -05:00
int32 levelDiff = int32 ( targetLevel ) - int32 ( myLevel );
2012-12-14 01:23:53 +00:00
2012-12-06 09:58:32 -05:00
// The maximum Aggro Radius is capped at 45 yards (25 level difference)
if ( levelDiff < - 25 )
levelDiff = - 25 ;
// The base aggro radius for mob of same level
float aggroRadius = 20 ;
// Aggro Radius varies with level difference at a rate of roughly 1 yard/level
aggroRadius -= ( float ) levelDiff ;
// detect range auras
aggroRadius += GetTotalAuraModifier ( SPELL_AURA_MOD_DETECT_RANGE );
// detected range auras
aggroRadius += target -> GetTotalAuraModifier ( SPELL_AURA_MOD_DETECTED_RANGE );
// Just in case, we don't want pets running all over the map
if ( aggroRadius > MAX_AGGRO_RADIUS )
aggroRadius = MAX_AGGRO_RADIUS ;
// Minimum Aggro Radius for a mob seems to be combat range (5 yards)
// hunter pets seem to ignore minimum aggro radius so we'll default it a little higher
if ( aggroRadius < 10 )
aggroRadius = 10 ;
return ( aggroRadius );
}
// Default
return 0.0f ;
}
Unit * Creature :: SelectNearestHostileUnitInAggroRange ( bool useLOS ) const
{
// Selects nearest hostile target within creature's aggro range. Used primarily by
// pets set to aggressive. Will not return neutral or friendly targets.
2020-08-14 17:06:03 +02:00
Unit * target = nullptr ;
2012-12-06 09:58:32 -05:00
2017-05-13 19:37:33 +02:00
Trinity :: NearestHostileUnitInAggroRangeCheck u_check ( this , useLOS );
Trinity :: UnitSearcher < Trinity :: NearestHostileUnitInAggroRangeCheck > searcher ( this , target , u_check );
2012-12-06 09:58:32 -05:00
2017-05-13 19:37:33 +02:00
Cell :: VisitGridObjects ( this , searcher , MAX_AGGRO_RADIUS );
2012-12-06 09:58:32 -05:00
return target ;
}
2013-06-07 16:05:39 +02:00
void Creature :: UpdateMovementFlags ()
{
2013-06-22 13:42:13 +02:00
// Do not update movement flags if creature is controlled by a player (charm/vehicle)
2016-08-20 12:43:04 +02:00
if ( m_playerMovingMe )
2013-06-22 13:42:13 +02:00
return ;
2017-01-27 20:47:54 -02:00
// Creatures with CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE should control MovementFlags in your own scripts
if ( GetCreatureTemplate () -> flags_extra & CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE )
return ;
2013-06-07 16:05:39 +02:00
// Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc)
2017-06-08 00:25:06 +02:00
float ground = GetFloorZ ();
2013-06-07 16:05:39 +02:00
2017-11-26 16:37:51 +01:00
bool canHover = CanHover ();
bool isInAir = ( G3D :: fuzzyGt ( GetPositionZ (), ground + ( canHover ? * m_unitData -> HoverHeight : 0.0f ) + GROUND_HEIGHT_TOLERANCE ) || G3D :: fuzzyLt ( GetPositionZ (), ground - GROUND_HEIGHT_TOLERANCE )); // Can be underground too, prevent the falling
2013-06-07 16:05:39 +02:00
2017-11-26 16:37:51 +01:00
if ( GetMovementTemplate (). IsFlightAllowed () && isInAir && ! IsFalling ())
2013-06-07 16:05:39 +02:00
{
2017-11-26 16:37:51 +01:00
if ( GetMovementTemplate (). Flight == CreatureFlightMovementType :: CanFly )
2013-06-07 16:05:39 +02:00
SetCanFly ( true );
else
SetDisableGravity ( true );
2017-11-26 16:37:51 +01:00
if ( ! HasAuraType ( SPELL_AURA_HOVER ))
SetHover ( false );
2013-06-07 16:05:39 +02:00
}
else
{
SetCanFly ( false );
SetDisableGravity ( false );
2017-11-26 16:52:42 +01:00
if ( IsAlive () && ( CanHover () || HasAuraType ( SPELL_AURA_HOVER )))
2017-11-26 16:37:51 +01:00
SetHover ( true );
2013-06-07 16:05:39 +02:00
}
2013-06-22 13:42:13 +02:00
if ( ! isInAir )
2013-08-10 13:44:08 +02:00
SetFall ( false );
2013-06-22 13:42:13 +02:00
2017-11-26 16:37:51 +01:00
SetSwim ( GetMovementTemplate (). IsSwimAllowed () && IsInWater ());
2013-06-07 16:05:39 +02:00
}
2013-06-11 17:28:16 +02:00
void Creature :: SetObjectScale ( float scale )
{
Unit :: SetObjectScale ( scale );
if ( CreatureModelInfo const * minfo = sObjectMgr -> GetCreatureModelInfo ( GetDisplayId ()))
{
2019-06-03 20:40:34 +02:00
SetBoundingRadius (( IsPet () ? 1.0f : minfo -> bounding_radius ) * scale );
2017-03-23 00:43:04 +01:00
SetCombatReach (( IsPet () ? DEFAULT_PLAYER_COMBAT_REACH : minfo -> combat_reach ) * scale );
2013-06-11 17:28:16 +02:00
}
}
2018-11-07 20:23:30 +01:00
void Creature :: SetDisplayId ( uint32 modelId , float displayScale /*= 1.f*/ )
2013-06-11 17:28:16 +02:00
{
2018-11-07 20:23:30 +01:00
Unit :: SetDisplayId ( modelId , displayScale );
2013-06-11 17:28:16 +02:00
if ( CreatureModelInfo const * minfo = sObjectMgr -> GetCreatureModelInfo ( modelId ))
{
2019-06-03 20:40:34 +02:00
SetBoundingRadius (( IsPet () ? 1.0f : minfo -> bounding_radius ) * GetObjectScale ());
2017-03-23 00:43:04 +01:00
SetCombatReach (( IsPet () ? DEFAULT_PLAYER_COMBAT_REACH : minfo -> combat_reach ) * GetObjectScale ());
2013-06-11 17:28:16 +02:00
}
}
2013-10-03 17:07:25 +02:00
2018-11-07 20:23:30 +01:00
void Creature :: SetDisplayFromModel ( uint32 modelIdx )
{
if ( CreatureModel const * model = GetCreatureTemplate () -> GetModelByIdx ( modelIdx ))
SetDisplayId ( model -> CreatureDisplayID , model -> DisplayScale );
}
2014-11-23 04:25:47 +01:00
void Creature :: SetTarget ( ObjectGuid const & guid )
2013-10-03 17:07:25 +02:00
{
2016-08-21 11:07:38 +02:00
if ( IsFocusing ( nullptr , true ))
m_suppressedTarget = guid ;
else
2019-06-03 20:40:34 +02:00
SetUpdateFieldValue ( m_values . ModifyValue ( & Unit :: m_unitData ). ModifyValue ( & UF :: UnitData :: Target ), guid );
2013-10-03 17:07:25 +02:00
}
2016-08-21 11:07:38 +02:00
void Creature :: FocusTarget ( Spell const * focusSpell , WorldObject const * target )
2013-10-03 17:07:25 +02:00
{
// already focused
2016-04-02 02:50:44 +02:00
if ( m_focusSpell )
2016-08-21 11:07:38 +02:00
return ;
2018-02-15 17:48:25 -03:00
// some spells shouldn't track targets
if ( focusSpell -> IsFocusDisabled ())
return ;
2017-01-22 02:24:48 -03:00
SpellInfo const * spellInfo = focusSpell -> GetSpellInfo ();
2016-08-21 11:07:38 +02:00
// don't use spell focus for vehicle spells
2020-06-12 01:29:18 +02:00
if ( spellInfo -> HasAura ( SPELL_AURA_CONTROL_VEHICLE ))
2016-08-21 11:07:38 +02:00
return ;
2016-01-13 18:38:54 +01:00
if (( ! target || target == this ) && ! focusSpell -> GetCastTime ()) // instant cast, untargeted (or self-targeted) spell doesn't need any facing updates
2016-08-21 11:07:38 +02:00
return ;
2013-10-03 17:07:25 +02:00
2016-08-21 11:07:38 +02:00
// store pre-cast values for target and orientation (used to later restore)
if ( ! IsFocusing ( nullptr , true ))
{ // only overwrite these fields if we aren't transitioning from one spell focus to another
2019-06-03 20:40:34 +02:00
m_suppressedTarget = GetTarget ();
2016-08-21 11:07:38 +02:00
m_suppressedOrientation = GetOrientation ();
}
2013-10-03 17:07:25 +02:00
2016-08-21 11:07:38 +02:00
m_focusSpell = focusSpell ;
2016-01-13 18:38:54 +01:00
// set target, then force send update packet to players if it changed to provide appropriate facing
ObjectGuid newTarget = target ? target -> GetGUID () : ObjectGuid :: Empty ;
2019-06-03 20:40:34 +02:00
if ( GetTarget () != newTarget )
2016-01-13 18:38:54 +01:00
{
2019-06-03 20:40:34 +02:00
SetUpdateFieldValue ( m_values . ModifyValue ( & Unit :: m_unitData ). ModifyValue ( & UF :: UnitData :: Target ), newTarget );
2016-01-24 01:11:19 +00:00
2016-01-13 18:38:54 +01:00
if ( // here we determine if the (relatively expensive) forced update is worth it, or whether we can afford to wait until the scheduled update tick
( // only require instant update for spells that actually have a visual
2017-01-22 02:24:48 -03:00
spellInfo -> GetSpellVisual ()
2016-01-24 01:11:19 +00:00
) && (
2016-01-13 18:38:54 +01:00
! focusSpell -> GetCastTime () || // if the spell is instant cast
2017-01-22 02:24:48 -03:00
spellInfo -> HasAttribute ( SPELL_ATTR5_DONT_TURN_DURING_CAST ) // client gets confused if we attempt to turn at the regularly scheduled update packet
2016-01-13 18:38:54 +01:00
)
)
{
2017-01-22 02:24:48 -03:00
std :: vector < Player *> playersNearby ;
2016-03-22 23:23:03 +00:00
GetPlayerListInGrid ( playersNearby , GetVisibilityRange ());
for ( Player * player : playersNearby )
{
// only update players that are known to the client (have already been created)
if ( player -> HaveAtClient ( this ))
SendUpdateToPlayer ( player );
}
2016-01-13 18:38:54 +01:00
}
}
2018-01-10 00:07:37 +01:00
bool const noTurnDuringCast = spellInfo -> HasAttribute ( SPELL_ATTR5_DONT_TURN_DURING_CAST );
if ( ! HasUnitFlag2 ( UNIT_FLAG2_DISABLE_TURN ))
{
// Face the target - we need to do this before the unit state is modified for no-turn spells
if ( target )
SetFacingToObject ( target , false );
else if ( noTurnDuringCast )
if ( Unit * victim = GetVictim ())
SetFacingToObject ( victim , false ); // ensure orientation is correct at beginning of cast
}
2016-01-13 18:38:54 +01:00
2018-01-10 00:07:37 +01:00
if ( noTurnDuringCast )
2018-02-20 17:27:02 -03:00
AddUnitState ( UNIT_STATE_FOCUSING );
2016-01-13 18:38:54 +01:00
}
bool Creature :: IsFocusing ( Spell const * focusSpell , bool withDelay )
{
if ( ! IsAlive ()) // dead creatures cannot focus
{
ReleaseFocus ( nullptr , false );
return false ;
}
2016-04-02 02:50:44 +02:00
if ( focusSpell && ( focusSpell != m_focusSpell ))
2016-01-13 18:38:54 +01:00
return false ;
2016-04-02 02:50:44 +02:00
if ( ! m_focusSpell )
2016-01-13 18:38:54 +01:00
{
2016-04-02 02:50:44 +02:00
if ( ! withDelay || ! m_focusDelay )
2016-01-13 18:38:54 +01:00
return false ;
2016-04-02 02:50:44 +02:00
if ( GetMSTimeDiffToNow ( m_focusDelay ) > 1000 ) // @todo figure out if we can get rid of this magic number somehow
2016-01-13 18:38:54 +01:00
{
2016-04-02 02:50:44 +02:00
m_focusDelay = 0 ; // save checks in the future
2016-01-13 18:38:54 +01:00
return false ;
}
}
return true ;
2013-10-03 17:07:25 +02:00
}
2016-01-13 18:38:54 +01:00
void Creature :: ReleaseFocus ( Spell const * focusSpell , bool withDelay )
2013-10-03 17:07:25 +02:00
{
2016-04-02 02:50:44 +02:00
if ( ! m_focusSpell )
2016-01-13 18:38:54 +01:00
return ;
2013-10-03 17:07:25 +02:00
// focused to something else
2016-04-02 02:50:44 +02:00
if ( focusSpell && focusSpell != m_focusSpell )
2013-10-03 17:07:25 +02:00
return ;
2018-01-10 00:07:37 +01:00
if ( IsPet () && ! HasUnitFlag2 ( UNIT_FLAG2_DISABLE_TURN )) // player pets do not use delay system
2016-08-21 11:07:38 +02:00
{
2019-06-03 20:40:34 +02:00
SetUpdateFieldValue ( m_values . ModifyValue ( & Unit :: m_unitData ). ModifyValue ( & UF :: UnitData :: Target ), m_suppressedTarget );
2016-08-21 11:07:38 +02:00
if ( ! m_suppressedTarget . IsEmpty ())
{
if ( WorldObject const * objTarget = ObjectAccessor :: GetWorldObject ( * this , m_suppressedTarget ))
2017-04-27 14:55:06 +02:00
SetFacingToObject ( objTarget , false );
2016-08-21 11:07:38 +02:00
}
else
2017-04-27 14:55:06 +02:00
SetFacingTo ( m_suppressedOrientation , false );
2016-08-21 11:07:38 +02:00
}
2016-03-23 16:22:05 +01:00
else
2016-08-21 11:07:38 +02:00
// tell the creature that it should reacquire its actual target after the delay expires (this is handled in ::Update)
2016-04-02 02:50:44 +02:00
// player pets don't need to do this, as they automatically reacquire their target on focus release
MustReacquireTarget ();
2016-01-13 18:38:54 +01:00
2016-04-02 02:50:44 +02:00
if ( m_focusSpell -> GetSpellInfo () -> HasAttribute ( SPELL_ATTR5_DONT_TURN_DURING_CAST ))
2018-02-20 17:27:02 -03:00
ClearUnitState ( UNIT_STATE_FOCUSING );
2013-10-03 17:07:25 +02:00
2016-04-02 02:50:44 +02:00
m_focusSpell = nullptr ;
2017-03-02 02:19:25 +01:00
m_focusDelay = ( ! IsPet () && withDelay ) ? GameTime :: GetGameTimeMS () : 0 ; // don't allow re-target right away to prevent visual bugs
2013-10-03 17:07:25 +02:00
}
2017-04-27 19:40:23 +02:00
bool Creature :: IsMovementPreventedByCasting () const
{
// first check if currently a movement allowed channel is active and we're not casting
if ( Spell * spell = m_currentSpells [ CURRENT_CHANNELED_SPELL ])
{
if ( spell -> getState () != SPELL_STATE_FINISHED && spell -> IsChannelActive ())
if ( spell -> GetSpellInfo () -> IsMoveAllowedChannel ())
2017-05-02 11:43:50 +02:00
return false ;
2017-04-27 19:40:23 +02:00
}
if ( const_cast < Creature *> ( this ) -> IsFocusing ( nullptr , true ))
return true ;
if ( HasUnitState ( UNIT_STATE_CASTING ))
return true ;
return false ;
}
2014-06-24 22:13:59 -05:00
void Creature :: StartPickPocketRefillTimer ()
{
2017-12-10 12:39:37 +01:00
_pickpocketLootRestore = GameTime :: GetGameTime () + sWorld -> getIntConfig ( CONFIG_CREATURE_PICKPOCKET_REFILL );
}
bool Creature :: CanGeneratePickPocketLoot () const
{
return _pickpocketLootRestore <= GameTime :: GetGameTime ();
2014-06-24 22:13:59 -05:00
}
2015-06-13 12:27:19 +02:00
void Creature :: SetTextRepeatId ( uint8 textGroup , uint8 id )
{
CreatureTextRepeatIds & repeats = m_textRepeat [ textGroup ];
if ( std :: find ( repeats . begin (), repeats . end (), id ) == repeats . end ())
repeats . push_back ( id );
else
2015-07-03 02:23:58 +01:00
TC_LOG_ERROR ( "sql.sql" , "CreatureTextMgr: TextGroup %u for Creature (%s) % s , id % u already added ", uint32(textGroup), GetName().c_str(), GetGUID().ToString().c_str(), uint32(id)) ;
2015-06-13 12:27:19 +02:00
}
CreatureTextRepeatIds Creature :: GetTextRepeatGroup ( uint8 textGroup )
{
CreatureTextRepeatIds ids ;
CreatureTextRepeatGroup :: const_iterator groupItr = m_textRepeat . find ( textGroup );
if ( groupItr != m_textRepeat . end ())
ids = groupItr -> second ;
return ids ;
}
void Creature :: ClearTextRepeatGroup ( uint8 textGroup )
{
CreatureTextRepeatGroup :: iterator groupItr = m_textRepeat . find ( textGroup );
if ( groupItr != m_textRepeat . end ())
groupItr -> second . clear ();
}
2017-05-05 14:44:47 +02:00
bool Creature :: CanGiveExperience () const
{
return ! IsCritter ()
&& ! IsPet ()
&& ! IsTotem ()
&& ! ( GetCreatureTemplate () -> flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL );
}
2017-05-07 21:48:41 +01:00
2018-01-03 20:04:19 +01:00
void Creature :: AtEnterCombat ()
{
Unit :: AtEnterCombat ();
if ( ! ( GetCreatureTemplate () -> type_flags & CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED ))
Dismount ();
if ( IsPet () || IsGuardian ()) // update pets' speed for catchup OOC speed
{
UpdateSpeed ( MOVE_RUN );
UpdateSpeed ( MOVE_SWIM );
UpdateSpeed ( MOVE_FLIGHT );
}
}
void Creature :: AtExitCombat ()
{
Unit :: AtExitCombat ();
ClearUnitState ( UNIT_STATE_ATTACK_PLAYER );
if ( HasDynamicFlag ( UNIT_DYNFLAG_TAPPED ))
SetDynamicFlags ( GetCreatureTemplate () -> dynamicflags );
if ( IsPet () || IsGuardian ()) // update pets' speed for catchup OOC speed
{
UpdateSpeed ( MOVE_RUN );
UpdateSpeed ( MOVE_SWIM );
UpdateSpeed ( MOVE_FLIGHT );
}
}
2017-05-07 21:48:41 +01:00
bool Creature :: IsEscortNPC ( bool onlyIfActive )
{
2018-08-23 14:33:28 +02:00
if ( CreatureAI * ai = AI ())
return ai -> IsEscortNPC ( onlyIfActive );
return false ;
2017-05-07 21:48:41 +01:00
}