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-25 10:36:30 +01:00
#include "Object.h"
2018-08-05 18:57:35 +02:00
#include "AreaTriggerPackets.h"
2017-06-04 01:00:45 +02:00
#include "AreaTriggerTemplate.h"
#include "BattlefieldMgr.h"
#include "CellImpl.h"
#include "CinematicMgr.h"
2018-03-10 19:51:36 -03:00
#include "CombatLogPackets.h"
2008-10-11 14:16:25 -05:00
#include "Common.h"
#include "Creature.h"
2021-10-02 19:59:56 +02:00
#include "DB2Stores.h"
2017-03-02 02:19:25 +01:00
#include "GameTime.h"
2008-10-11 14:16:25 -05:00
#include "GridNotifiersImpl.h"
2020-03-04 20:59:04 +01:00
#include "G3DPosition.hpp"
2017-06-04 01:00:45 +02:00
#include "InstanceScenario.h"
#include "Item.h"
#include "Log.h"
#include "MiscPackets.h"
#include "MovementPackets.h"
#include "MovementTypedefs.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "OutdoorPvPMgr.h"
2020-03-04 20:59:04 +01:00
#include "PathGenerator.h"
2018-01-28 11:37:20 +01:00
#include "PhasingHandler.h"
2017-06-04 01:00:45 +02:00
#include "Player.h"
2018-02-10 16:43:01 -03:00
#include "ReputationMgr.h"
2022-01-29 19:44:55 +01:00
#include "SmoothPhasing.h"
2010-01-10 01:23:15 +01:00
#include "SpellAuraEffects.h"
2018-02-10 16:43:01 -03:00
#include "SpellMgr.h"
2021-12-12 19:34:00 +01:00
#include "SpellPackets.h"
2020-08-28 00:11:16 +02:00
#include "StringConvert.h"
2008-10-11 14:16:25 -05:00
#include "TemporarySummon.h"
2009-03-20 14:01:46 -06:00
#include "Totem.h"
2017-06-04 01:00:45 +02:00
#include "Transport.h"
2012-07-14 23:39:12 +02:00
#include "Unit.h"
2017-06-04 01:00:45 +02:00
#include "UpdateData.h"
#include "Util.h"
#include "VMapFactory.h"
#include "Vehicle.h"
2020-08-29 13:02:42 +02:00
#include "VMapManager2.h"
2017-06-04 01:00:45 +02:00
#include "World.h"
#include <G3D/Vector3.h>
2018-12-14 22:01:16 +01:00
#include <sstream>
2009-10-17 15:51:44 -07:00
2019-01-16 20:48:01 +01:00
constexpr float VisibilityDistances [ AsUnderlyingType ( VisibilityDistanceType :: Max )] =
{
DEFAULT_VISIBILITY_DISTANCE ,
VISIBILITY_DISTANCE_TINY ,
VISIBILITY_DISTANCE_SMALL ,
VISIBILITY_DISTANCE_LARGE ,
VISIBILITY_DISTANCE_GIGANTIC ,
MAX_VISIBILITY_DISTANCE
};
2024-03-13 18:51:29 +01:00
Object :: Object () : m_values ( this ), m_scriptRef ( this , NoopObjectDeleter ())
2008-10-11 14:16:25 -05:00
{
m_objectTypeId = TYPEID_OBJECT ;
m_objectType = TYPEMASK_OBJECT ;
2018-10-04 18:50:21 +02:00
m_updateFlag . Clear ();
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
m_inWorld = false ;
2017-12-15 22:11:02 -02:00
m_isNewObject = false ;
2022-01-29 16:01:57 +01:00
m_isDestroyedObject = false ;
2008-10-11 14:16:25 -05:00
m_objectUpdated = false ;
}
2009-10-17 15:51:44 -07:00
2010-04-07 22:59:46 +02:00
Object ::~ Object ()
2008-10-11 14:16:25 -05:00
{
2010-04-07 19:14:10 +02:00
if ( IsInWorld ())
2008-10-11 14:16:25 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_FATAL ( "misc" , "Object::~Object {} deleted but still in world!!" , GetGUID (). ToString ());
2024-03-14 11:37:32 +01:00
if ( Item * item = ToItem ())
TC_LOG_FATAL ( "misc" , "Item slot {}" , item -> GetSlot ());
2015-09-21 15:17:05 +02:00
ABORT ();
2009-04-09 17:15:40 -05:00
}
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( m_objectUpdated )
2009-06-16 17:58:44 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_FATAL ( "misc" , "Object::~Object {} deleted but still in update list!!" , GetGUID (). ToString ());
2015-09-21 15:17:05 +02:00
ABORT ();
2009-06-16 17:58:44 -05:00
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2014-10-29 21:13:21 +01:00
void Object :: _Create ( ObjectGuid const & guid )
2008-10-11 14:16:25 -05:00
{
2019-06-03 20:40:34 +02:00
m_objectUpdated = false ;
m_guid = guid ;
2011-04-11 14:39:00 +06:00
}
2011-08-30 13:46:36 +02:00
void Object :: AddToWorld ()
{
if ( m_inWorld )
return ;
m_inWorld = true ;
// synchronize values mirror with values array (changes will send in updatecreate opcode any way
2015-02-25 00:13:14 +01:00
ASSERT ( ! m_objectUpdated );
ClearUpdateMask ( false );
2024-03-13 18:51:29 +01:00
// Set new ref when adding to world (except if we already have one - also set in constructor to allow scripts to work in initialization phase)
// Changing the ref when adding/removing from world prevents accessing players on different maps (possibly from another thread)
if ( ! m_scriptRef )
m_scriptRef . reset ( this , NoopObjectDeleter ());
2011-08-30 13:46:36 +02:00
}
void Object :: RemoveFromWorld ()
{
if ( ! m_inWorld )
return ;
m_inWorld = false ;
// if we remove from world then sending changes not required
ClearUpdateMask ( true );
2024-03-13 18:51:29 +01:00
m_scriptRef = nullptr ;
2011-08-30 13:46:36 +02:00
}
2011-09-15 14:08:17 +02:00
void Object :: BuildCreateUpdateBlockForPlayer ( UpdateData * data , Player * target ) const
2008-10-11 14:16:25 -05:00
{
2010-04-07 19:14:10 +02:00
if ( ! target )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2018-11-20 17:35:41 +01:00
uint8 updateType = m_isNewObject ? UPDATETYPE_CREATE_OBJECT2 : UPDATETYPE_CREATE_OBJECT ;
2018-10-04 18:50:21 +02:00
uint8 objectType = m_objectTypeId ;
CreateObjectBits flags = m_updateFlag ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( target == this ) // building packet for yourself
2018-10-04 18:50:21 +02:00
{
flags . ThisIsYou = true ;
flags . ActivePlayer = true ;
objectType = TYPEID_ACTIVE_PLAYER ;
}
2009-10-17 15:51:44 -07:00
2015-05-25 01:50:03 +02:00
if ( WorldObject const * worldObject = dynamic_cast < WorldObject const *> ( this ))
{
2018-10-04 18:50:21 +02:00
if ( ! flags . MovementUpdate && ! worldObject -> m_movementInfo . transport . guid . IsEmpty ())
flags . MovementTransport = true ;
2014-11-02 18:53:15 +01:00
2015-05-25 01:50:03 +02:00
if ( worldObject -> GetAIAnimKitId () || worldObject -> GetMovementAnimKitId () || worldObject -> GetMeleeAnimKitId ())
2018-10-04 18:50:21 +02:00
flags . AnimKit = true ;
2022-01-29 19:44:55 +01:00
2022-01-29 19:44:55 +01:00
if ( worldObject -> GetSmoothPhasing () && worldObject -> GetSmoothPhasing () -> GetInfoForSeer ( target -> GetGUID ()))
2022-01-29 19:44:55 +01:00
flags . SmoothPhasing = true ;
2015-05-25 01:50:03 +02:00
}
2013-03-25 14:34:22 +01:00
if ( Unit const * unit = ToUnit ())
2021-12-04 12:45:46 +01:00
{
flags . PlayHoverAnim = unit -> IsPlayingHoverAnim ();
2013-06-13 19:35:01 +02:00
if ( unit -> GetVictim ())
2018-10-04 18:50:21 +02:00
flags . CombatVictim = true ;
2021-12-04 12:45:46 +01:00
}
2013-03-25 14:34:22 +01:00
2022-10-11 23:18:54 +03:00
ByteBuffer & buf = data -> GetBuffer ();
2012-08-07 13:27:33 +02:00
buf << uint8 ( updateType );
2017-05-18 23:52:58 +02:00
buf << GetGUID ();
2018-10-04 18:50:21 +02:00
buf << uint8 ( objectType );
2009-10-17 15:51:44 -07:00
2022-01-29 19:44:55 +01:00
BuildMovementUpdate ( & buf , flags , target );
2019-06-03 20:40:34 +02:00
BuildValuesCreate ( & buf , target );
2022-10-11 23:18:54 +03:00
data -> AddUpdateBlock ();
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2009-12-19 19:46:07 +01:00
void Object :: SendUpdateToPlayer ( Player * player )
2008-10-11 14:16:25 -05:00
{
// send create update to player
2011-11-27 16:06:29 +01:00
UpdateData upd ( player -> GetMapId ());
2008-10-11 14:16:25 -05:00
WorldPacket packet ;
2009-10-17 15:51:44 -07:00
2016-03-22 23:23:03 +00:00
if ( player -> HaveAtClient ( this ))
BuildValuesUpdateBlockForPlayer ( & upd , player );
else
BuildCreateUpdateBlockForPlayer ( & upd , player );
2008-10-11 14:16:25 -05:00
upd . BuildPacket ( & packet );
2017-06-04 01:00:45 +02:00
player -> SendDirectMessage ( & packet );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
void Object :: BuildValuesUpdateBlockForPlayer ( UpdateData * data , Player const * target ) const
{
2022-10-11 23:18:54 +03:00
ByteBuffer & buf = PrepareValuesUpdateBuffer ( data );
2019-06-03 20:40:34 +02:00
BuildValuesUpdate ( & buf , target );
2022-10-11 23:18:54 +03:00
data -> AddUpdateBlock ();
2019-06-03 20:40:34 +02:00
}
void Object :: BuildValuesUpdateBlockForPlayerWithFlag ( UpdateData * data , UF :: UpdateFieldFlag flags , Player const * target ) const
2008-10-11 14:16:25 -05:00
{
2022-10-11 23:18:54 +03:00
ByteBuffer & buf = PrepareValuesUpdateBuffer ( data );
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
BuildValuesUpdateWithFlag ( & buf , flags , target );
2009-10-17 15:51:44 -07:00
2022-10-11 23:18:54 +03:00
data -> AddUpdateBlock ();
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2020-02-09 17:47:25 +01:00
void Object :: BuildDestroyUpdateBlock ( UpdateData * data ) const
{
data -> AddDestroyObject ( GetGUID ());
}
2011-09-15 14:08:17 +02:00
void Object :: BuildOutOfRangeUpdateBlock ( UpdateData * data ) const
2008-10-11 14:16:25 -05:00
{
data -> AddOutOfRangeGUID ( GetGUID ());
}
2009-10-17 15:51:44 -07:00
2022-10-11 23:18:54 +03:00
ByteBuffer & Object :: PrepareValuesUpdateBuffer ( UpdateData * data ) const
2020-04-23 20:22:53 +02:00
{
2022-10-11 23:18:54 +03:00
ByteBuffer & buffer = data -> GetBuffer ();
2020-04-23 20:22:53 +02:00
buffer << uint8 ( UPDATETYPE_VALUES );
buffer << GetGUID ();
return buffer ;
}
2015-01-01 20:38:19 +01:00
void Object :: DestroyForPlayer ( Player * target ) const
2008-10-11 14:16:25 -05:00
{
ASSERT ( target );
2009-10-17 15:51:44 -07:00
2015-01-01 17:53:08 +01:00
UpdateData updateData ( target -> GetMapId ());
2020-02-09 17:47:25 +01:00
BuildDestroyUpdateBlock ( & updateData );
2015-01-01 17:53:08 +01:00
WorldPacket packet ;
updateData . BuildPacket ( & packet );
target -> SendDirectMessage ( & packet );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2022-01-29 16:01:57 +01:00
void Object :: SendOutOfRangeForPlayer ( Player * target ) const
{
ASSERT ( target );
UpdateData updateData ( target -> GetMapId ());
BuildOutOfRangeUpdateBlock ( & updateData );
WorldPacket packet ;
updateData . BuildPacket ( & packet );
target -> SendDirectMessage ( & packet );
}
2022-01-29 19:44:55 +01:00
void Object :: BuildMovementUpdate ( ByteBuffer * data , CreateObjectBits flags , Player * target ) const
2014-11-02 14:56:16 +01:00
{
2017-06-16 20:08:29 +02:00
std :: vector < uint32 > const * PauseTimes = nullptr ;
2014-09-29 20:10:08 +02:00
if ( GameObject const * go = ToGameObject ())
2022-05-25 22:14:32 +02:00
PauseTimes = go -> GetPauseTimes ();
2014-11-02 14:56:16 +01:00
2018-10-04 18:50:21 +02:00
data -> WriteBit ( flags . NoBirthAnim );
data -> WriteBit ( flags . EnablePortals );
data -> WriteBit ( flags . PlayHoverAnim );
data -> WriteBit ( flags . MovementUpdate );
data -> WriteBit ( flags . MovementTransport );
data -> WriteBit ( flags . Stationary );
data -> WriteBit ( flags . CombatVictim );
data -> WriteBit ( flags . ServerTime );
data -> WriteBit ( flags . Vehicle );
data -> WriteBit ( flags . AnimKit );
data -> WriteBit ( flags . Rotation );
data -> WriteBit ( flags . AreaTrigger );
data -> WriteBit ( flags . GameObject );
data -> WriteBit ( flags . SmoothPhasing );
data -> WriteBit ( flags . ThisIsYou );
data -> WriteBit ( flags . SceneObject );
data -> WriteBit ( flags . ActivePlayer );
data -> WriteBit ( flags . Conversation );
2016-06-01 20:14:16 +02:00
data -> FlushBits ();
2014-11-02 14:56:16 +01:00
2018-10-04 18:50:21 +02:00
if ( flags . MovementUpdate )
2008-10-11 14:16:25 -05:00
{
2014-11-02 14:56:16 +01:00
Unit const * unit = ToUnit ();
bool HasFallDirection = unit -> HasUnitMovementFlag ( MOVEMENTFLAG_FALLING );
bool HasFall = HasFallDirection || unit -> m_movementInfo . jump . fallTime != 0 ;
bool HasSpline = unit -> IsSplineEnabled ();
2022-02-24 23:16:55 +01:00
bool HasInertia = unit -> m_movementInfo . inertia . has_value ();
2022-12-04 15:13:20 +01:00
bool HasAdvFlying = unit -> m_movementInfo . advFlying . has_value ();
2023-05-04 17:00:52 +02:00
bool HasStandingOnGameObjectGUID = unit -> m_movementInfo . standingOnGameObjectGUID . has_value ();
2012-07-12 21:20:34 +02:00
2017-06-23 22:53:49 +02:00
* data << GetGUID (); // MoverGUID
2012-07-12 21:20:34 +02:00
2022-02-24 23:16:55 +01:00
* data << uint32 ( unit -> GetUnitMovementFlags ());
* data << uint32 ( unit -> GetExtraUnitMovementFlags ());
* data << uint32 ( unit -> GetExtraUnitMovementFlags2 ());
2016-06-01 20:14:16 +02:00
* data << uint32 ( unit -> m_movementInfo . time ); // MoveTime
2014-11-02 14:56:16 +01:00
* data << float ( unit -> GetPositionX ());
* data << float ( unit -> GetPositionY ());
* data << float ( unit -> GetPositionZ ());
* data << float ( unit -> GetOrientation ());
2012-07-12 21:20:34 +02:00
2014-11-02 14:56:16 +01:00
* data << float ( unit -> m_movementInfo . pitch ); // Pitch
2022-11-12 21:42:37 +01:00
* data << float ( unit -> m_movementInfo . stepUpStartElevation ); // StepUpStartElevation
2012-07-12 21:20:34 +02:00
2016-06-01 20:14:16 +02:00
* data << uint32 ( 0 ); // RemoveForcesIDs.size()
* data << uint32 ( 0 ); // MoveIndex
2012-07-12 21:20:34 +02:00
2016-06-01 20:14:16 +02:00
//for (std::size_t i = 0; i < RemoveForcesIDs.size(); ++i)
2014-11-02 14:56:16 +01:00
// *data << ObjectGuid(RemoveForcesIDs);
2012-07-12 21:20:34 +02:00
2023-05-04 17:00:52 +02:00
data -> WriteBit ( HasStandingOnGameObjectGUID ); // HasStandingOnGameObjectGUID
2014-11-02 14:56:16 +01:00
data -> WriteBit ( ! unit -> m_movementInfo . transport . guid . IsEmpty ()); // HasTransport
data -> WriteBit ( HasFall ); // HasFall
data -> WriteBit ( HasSpline ); // HasSpline - marks that the unit uses spline movement
2020-08-14 17:06:03 +02:00
data -> WriteBit ( false ); // HeightChangeFailed
data -> WriteBit ( false ); // RemoteTimeValid
2022-02-24 23:16:55 +01:00
data -> WriteBit ( HasInertia ); // HasInertia
2022-12-04 15:13:20 +01:00
data -> WriteBit ( HasAdvFlying ); // HasAdvFlying
2009-10-17 15:51:44 -07:00
2014-11-02 14:56:16 +01:00
if ( ! unit -> m_movementInfo . transport . guid . IsEmpty ())
2014-12-29 01:00:16 +01:00
* data << unit -> m_movementInfo . transport ;
2012-07-12 21:20:34 +02:00
2023-05-04 17:00:52 +02:00
if ( HasStandingOnGameObjectGUID )
* data << * unit -> m_movementInfo . standingOnGameObjectGUID ;
2022-02-24 23:16:55 +01:00
if ( HasInertia )
{
2022-12-04 15:13:20 +01:00
* data << unit -> m_movementInfo . inertia -> id ;
2022-02-24 23:16:55 +01:00
* data << unit -> m_movementInfo . inertia -> force . PositionXYZStream ();
* data << uint32 ( unit -> m_movementInfo . inertia -> lifetime );
}
2022-12-04 15:13:20 +01:00
if ( HasAdvFlying )
{
* data << float ( unit -> m_movementInfo . advFlying -> forwardVelocity );
* data << float ( unit -> m_movementInfo . advFlying -> upVelocity );
}
2014-11-02 14:56:16 +01:00
if ( HasFall )
2009-06-11 00:45:59 -05:00
{
2019-05-18 22:21:17 +02:00
* data << uint32 ( unit -> m_movementInfo . jump . fallTime ); // Time
* data << float ( unit -> m_movementInfo . jump . zspeed ); // JumpVelocity
2012-07-12 21:20:34 +02:00
2014-11-02 14:56:16 +01:00
if ( data -> WriteBit ( HasFallDirection ))
{
2019-05-18 22:21:17 +02:00
* data << float ( unit -> m_movementInfo . jump . sinAngle ); // Direction
2014-11-02 14:56:16 +01:00
* data << float ( unit -> m_movementInfo . jump . cosAngle );
2019-05-18 22:21:17 +02:00
* data << float ( unit -> m_movementInfo . jump . xyspeed ); // Speed
2014-11-02 14:56:16 +01:00
}
}
2009-10-17 15:51:44 -07:00
2014-11-02 14:56:16 +01:00
* data << float ( unit -> GetSpeed ( MOVE_WALK ));
* data << float ( unit -> GetSpeed ( MOVE_RUN ));
* data << float ( unit -> GetSpeed ( MOVE_RUN_BACK ));
* data << float ( unit -> GetSpeed ( MOVE_SWIM ));
* data << float ( unit -> GetSpeed ( MOVE_SWIM_BACK ));
* data << float ( unit -> GetSpeed ( MOVE_FLIGHT ));
* data << float ( unit -> GetSpeed ( MOVE_FLIGHT_BACK ));
* data << float ( unit -> GetSpeed ( MOVE_TURN_RATE ));
* data << float ( unit -> GetSpeed ( MOVE_PITCH_RATE ));
2019-09-07 16:58:58 +02:00
if ( MovementForces const * movementForces = unit -> GetMovementForces ())
{
* data << uint32 ( movementForces -> GetForces () -> size ());
* data << float ( movementForces -> GetModMagnitude ()); // MovementForcesModMagnitude
}
else
{
* data << uint32 ( 0 );
* data << float ( 1.0f ); // MovementForcesModMagnitude
}
2014-11-02 14:56:16 +01:00
2022-12-04 15:13:20 +01:00
* data << float ( 2.0f ); // advFlyingAirFriction
* data << float ( 65.0f ); // advFlyingMaxVel
* data << float ( 1.0f ); // advFlyingLiftCoefficient
* data << float ( 3.0f ); // advFlyingDoubleJumpVelMod
* data << float ( 10.0f ); // advFlyingGlideStartMinHeight
* data << float ( 100.0f ); // advFlyingAddImpulseMaxSpeed
* data << float ( 90.0f ); // advFlyingMinBankingRate
* data << float ( 140.0f ); // advFlyingMaxBankingRate
* data << float ( 180.0f ); // advFlyingMinPitchingRateDown
* data << float ( 360.0f ); // advFlyingMaxPitchingRateDown
* data << float ( 90.0f ); // advFlyingMinPitchingRateUp
* data << float ( 270.0f ); // advFlyingMaxPitchingRateUp
* data << float ( 30.0f ); // advFlyingMinTurnVelocityThreshold
* data << float ( 80.0f ); // advFlyingMaxTurnVelocityThreshold
* data << float ( 2.75f ); // advFlyingSurfaceFriction
* data << float ( 7.0f ); // advFlyingOverMaxDeceleration
* data << float ( 0.4f ); // advFlyingLaunchSpeedCoefficient
2016-06-01 20:14:16 +02:00
data -> WriteBit ( HasSpline );
data -> FlushBits ();
2019-09-07 16:58:58 +02:00
if ( MovementForces const * movementForces = unit -> GetMovementForces ())
for ( MovementForce const & force : * movementForces -> GetForces ())
2019-09-08 12:19:16 +02:00
WorldPackets :: Movement :: CommonMovement :: WriteMovementForceWithDirection ( force , * data , unit );
2014-11-02 14:56:16 +01:00
2016-06-01 20:14:16 +02:00
if ( HasSpline )
2014-12-29 01:00:16 +01:00
WorldPackets :: Movement :: CommonMovement :: WriteCreateObjectSplineDataBlock ( * unit -> movespline , * data );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2022-05-25 22:14:32 +02:00
* data << uint32 ( PauseTimes ? PauseTimes -> size () : 0 );
2012-07-24 15:28:41 +02:00
2018-10-04 18:50:21 +02:00
if ( flags . Stationary )
2008-12-24 09:58:26 -06:00
{
2012-07-12 21:20:34 +02:00
WorldObject const * self = static_cast < WorldObject const *> ( this );
2013-10-26 17:06:50 +02:00
* data << float ( self -> GetStationaryX ());
* data << float ( self -> GetStationaryY ());
* data << float ( self -> GetStationaryZ ());
2014-11-02 14:56:16 +01:00
* data << float ( self -> GetStationaryO ());
2008-12-24 09:58:26 -06:00
}
2009-10-17 15:51:44 -07:00
2018-10-04 18:50:21 +02:00
if ( flags . CombatVictim )
2014-11-02 14:56:16 +01:00
* data << ToUnit () -> GetVictim () -> GetGUID (); // CombatVictim
2011-11-27 16:06:29 +01:00
2018-10-04 18:50:21 +02:00
if ( flags . ServerTime )
2022-05-25 22:14:32 +02:00
* data << uint32 ( GameTime :: GetGameTimeMS ());
2014-11-02 14:56:16 +01:00
2018-10-04 18:50:21 +02:00
if ( flags . Vehicle )
2014-11-02 14:56:16 +01:00
{
Unit const * unit = ToUnit ();
2021-09-16 22:22:14 +02:00
* data << uint32 ( unit -> GetVehicleKit () -> GetVehicleInfo () -> ID ); // RecID
2014-11-02 14:56:16 +01:00
* data << float ( unit -> GetOrientation ()); // InitialRawFacing
}
2018-10-04 18:50:21 +02:00
if ( flags . AnimKit )
2014-11-02 14:56:16 +01:00
{
2015-05-25 01:50:03 +02:00
WorldObject const * self = static_cast < WorldObject const *> ( this );
* data << uint16 ( self -> GetAIAnimKitId ()); // AiID
* data << uint16 ( self -> GetMovementAnimKitId ()); // MovementID
* data << uint16 ( self -> GetMeleeAnimKitId ()); // MeleeID
2014-11-02 14:56:16 +01:00
}
2018-10-04 18:50:21 +02:00
if ( flags . Rotation )
2019-04-24 20:39:01 +02:00
* data << uint64 ( ToGameObject () -> GetPackedLocalRotation ()); // Rotation
2014-11-02 14:56:16 +01:00
2022-05-25 22:14:32 +02:00
if ( PauseTimes && ! PauseTimes -> empty ())
2017-06-16 20:08:29 +02:00
data -> append ( PauseTimes -> data (), PauseTimes -> size ());
2016-06-01 20:14:16 +02:00
2018-10-04 18:50:21 +02:00
if ( flags . MovementTransport )
2016-06-01 20:14:16 +02:00
{
WorldObject const * self = static_cast < WorldObject const *> ( this );
* data << self -> m_movementInfo . transport ;
}
2018-10-04 18:50:21 +02:00
if ( flags . AreaTrigger )
2017-01-29 00:00:43 +01:00
{
AreaTrigger const * areaTrigger = ToAreaTrigger ();
2021-09-16 22:22:14 +02:00
AreaTriggerCreateProperties const * createProperties = areaTrigger -> GetCreateProperties ();
AreaTriggerShapeInfo const & shape = areaTrigger -> GetShape ();
2017-01-29 00:00:43 +01:00
* data << uint32 ( areaTrigger -> GetTimeSinceCreated ());
2017-05-16 22:16:21 +02:00
* data << areaTrigger -> GetRollPitchYaw (). PositionXYZStream ();
2017-01-29 00:00:43 +01:00
2023-12-30 17:14:09 +01:00
bool hasAbsoluteOrientation = createProperties && createProperties -> Flags . HasFlag ( AreaTriggerCreatePropertiesFlag :: HasAbsoluteOrientation );
bool hasDynamicShape = createProperties && createProperties -> Flags . HasFlag ( AreaTriggerCreatePropertiesFlag :: HasDynamicShape );
bool hasAttached = createProperties && createProperties -> Flags . HasFlag ( AreaTriggerCreatePropertiesFlag :: HasAttached );
bool hasFaceMovementDir = createProperties && createProperties -> Flags . HasFlag ( AreaTriggerCreatePropertiesFlag :: HasFaceMovementDir );
bool hasFollowsTerrain = createProperties && createProperties -> Flags . HasFlag ( AreaTriggerCreatePropertiesFlag :: HasFollowsTerrain );
bool hasUnk1 = createProperties && createProperties -> Flags . HasFlag ( AreaTriggerCreatePropertiesFlag :: Unk1 );
2024-01-24 20:10:36 +01:00
bool hasUnknown1025 = false ;
2023-12-30 17:14:09 +01:00
bool hasTargetRollPitchYaw = createProperties && createProperties -> Flags . HasFlag ( AreaTriggerCreatePropertiesFlag :: HasTargetRollPitchYaw );
2021-09-16 22:22:14 +02:00
bool hasScaleCurveID = createProperties && createProperties -> ScaleCurveId != 0 ;
bool hasMorphCurveID = createProperties && createProperties -> MorphCurveId != 0 ;
bool hasFacingCurveID = createProperties && createProperties -> FacingCurveId != 0 ;
bool hasMoveCurveID = createProperties && createProperties -> MoveCurveId != 0 ;
bool hasAreaTriggerSphere = shape . IsSphere ();
bool hasAreaTriggerBox = shape . IsBox ();
2024-03-21 21:07:23 +01:00
bool hasAreaTriggerPolygon = shape . IsPolygon ();
2021-09-16 22:22:14 +02:00
bool hasAreaTriggerCylinder = shape . IsCylinder ();
2022-02-24 23:16:55 +01:00
bool hasDisk = shape . IsDisk ();
2023-12-30 17:14:09 +01:00
bool hasBoundedPlane = shape . IsBoundedPlane ();
2017-01-29 00:00:43 +01:00
bool hasAreaTriggerSpline = areaTrigger -> HasSplines ();
2020-09-19 12:44:42 +02:00
bool hasOrbit = areaTrigger -> HasOrbit ();
2020-11-04 21:39:21 +01:00
bool hasMovementScript = false ;
2024-03-21 21:07:23 +01:00
bool hasPositionalSoundKitID = false ;
2017-01-29 00:00:43 +01:00
data -> WriteBit ( hasAbsoluteOrientation );
data -> WriteBit ( hasDynamicShape );
data -> WriteBit ( hasAttached );
data -> WriteBit ( hasFaceMovementDir );
data -> WriteBit ( hasFollowsTerrain );
data -> WriteBit ( hasUnk1 );
2024-01-24 20:10:36 +01:00
data -> WriteBit ( hasUnknown1025 );
2017-01-29 00:00:43 +01:00
data -> WriteBit ( hasTargetRollPitchYaw );
data -> WriteBit ( hasScaleCurveID );
data -> WriteBit ( hasMorphCurveID );
data -> WriteBit ( hasFacingCurveID );
data -> WriteBit ( hasMoveCurveID );
2024-03-21 21:07:23 +01:00
data -> WriteBit ( hasPositionalSoundKitID );
2017-01-29 00:00:43 +01:00
data -> WriteBit ( hasAreaTriggerSphere );
data -> WriteBit ( hasAreaTriggerBox );
data -> WriteBit ( hasAreaTriggerPolygon );
data -> WriteBit ( hasAreaTriggerCylinder );
2022-02-24 23:16:55 +01:00
data -> WriteBit ( hasDisk );
2022-12-04 15:13:20 +01:00
data -> WriteBit ( hasBoundedPlane );
2017-01-29 00:00:43 +01:00
data -> WriteBit ( hasAreaTriggerSpline );
2020-09-19 12:44:42 +02:00
data -> WriteBit ( hasOrbit );
2020-11-04 21:39:21 +01:00
data -> WriteBit ( hasMovementScript );
2017-01-29 00:00:43 +01:00
data -> FlushBits ();
if ( hasAreaTriggerSpline )
{
* data << uint32 ( areaTrigger -> GetTimeToTarget ());
* data << uint32 ( areaTrigger -> GetElapsedTimeForMovement ());
2017-06-04 01:00:45 +02:00
WorldPackets :: Movement :: CommonMovement :: WriteCreateObjectAreaTriggerSpline ( areaTrigger -> GetSpline (), * data );
2017-01-29 00:00:43 +01:00
}
if ( hasTargetRollPitchYaw )
2017-05-16 22:16:21 +02:00
* data << areaTrigger -> GetTargetRollPitchYaw (). PositionXYZStream ();
2017-01-29 00:00:43 +01:00
if ( hasScaleCurveID )
2021-09-16 22:22:14 +02:00
* data << uint32 ( createProperties -> ScaleCurveId );
2017-01-29 00:00:43 +01:00
if ( hasMorphCurveID )
2021-09-16 22:22:14 +02:00
* data << uint32 ( createProperties -> MorphCurveId );
2017-01-29 00:00:43 +01:00
if ( hasFacingCurveID )
2021-09-16 22:22:14 +02:00
* data << uint32 ( createProperties -> FacingCurveId );
2017-01-29 00:00:43 +01:00
if ( hasMoveCurveID )
2021-09-16 22:22:14 +02:00
* data << uint32 ( createProperties -> MoveCurveId );
2017-01-29 00:00:43 +01:00
2024-03-21 21:07:23 +01:00
if ( hasPositionalSoundKitID )
* data << uint32 ( 0 );
2017-01-29 00:00:43 +01:00
if ( hasAreaTriggerSphere )
{
2021-09-16 22:22:14 +02:00
* data << float ( shape . SphereDatas . Radius );
* data << float ( shape . SphereDatas . RadiusTarget );
2017-01-29 00:00:43 +01:00
}
if ( hasAreaTriggerBox )
{
2021-09-16 22:22:14 +02:00
* data << float ( shape . BoxDatas . Extents [ 0 ]);
* data << float ( shape . BoxDatas . Extents [ 1 ]);
* data << float ( shape . BoxDatas . Extents [ 2 ]);
* data << float ( shape . BoxDatas . ExtentsTarget [ 0 ]);
* data << float ( shape . BoxDatas . ExtentsTarget [ 1 ]);
* data << float ( shape . BoxDatas . ExtentsTarget [ 2 ]);
2017-01-29 00:00:43 +01:00
}
if ( hasAreaTriggerPolygon )
{
2023-12-30 17:14:09 +01:00
* data << int32 ( shape . PolygonVertices . size ());
* data << int32 ( shape . PolygonVerticesTarget . size ());
2021-09-16 22:22:14 +02:00
* data << float ( shape . PolygonDatas . Height );
* data << float ( shape . PolygonDatas . HeightTarget );
2017-01-29 00:00:43 +01:00
2023-12-30 17:14:09 +01:00
for ( TaggedPosition < Position :: XY > const & vertice : shape . PolygonVertices )
2017-06-04 01:00:45 +02:00
* data << vertice ;
2017-01-29 00:00:43 +01:00
2023-12-30 17:14:09 +01:00
for ( TaggedPosition < Position :: XY > const & vertice : shape . PolygonVerticesTarget )
2017-06-04 01:00:45 +02:00
* data << vertice ;
2017-01-29 00:00:43 +01:00
}
if ( hasAreaTriggerCylinder )
{
2021-09-16 22:22:14 +02:00
* data << float ( shape . CylinderDatas . Radius );
* data << float ( shape . CylinderDatas . RadiusTarget );
* data << float ( shape . CylinderDatas . Height );
* data << float ( shape . CylinderDatas . HeightTarget );
* data << float ( shape . CylinderDatas . LocationZOffset );
* data << float ( shape . CylinderDatas . LocationZOffsetTarget );
2017-01-29 00:00:43 +01:00
}
2022-02-24 23:16:55 +01:00
if ( hasDisk )
{
* data << float ( shape . DiskDatas . InnerRadius );
* data << float ( shape . DiskDatas . InnerRadiusTarget );
* data << float ( shape . DiskDatas . OuterRadius );
* data << float ( shape . DiskDatas . OuterRadiusTarget );
* data << float ( shape . DiskDatas . Height );
* data << float ( shape . DiskDatas . HeightTarget );
* data << float ( shape . DiskDatas . LocationZOffset );
* data << float ( shape . DiskDatas . LocationZOffsetTarget );
}
2022-12-04 15:13:20 +01:00
if ( hasBoundedPlane )
{
* data << float ( shape . BoundedPlaneDatas . Extents [ 0 ]);
* data << float ( shape . BoundedPlaneDatas . Extents [ 1 ]);
* data << float ( shape . BoundedPlaneDatas . ExtentsTarget [ 0 ]);
* data << float ( shape . BoundedPlaneDatas . ExtentsTarget [ 1 ]);
}
2020-11-04 21:39:21 +01:00
//if (hasMovementScript)
// *data << *areaTrigger->GetMovementScript(); // AreaTriggerMovementScriptInfo
2020-09-19 12:44:42 +02:00
if ( hasOrbit )
2023-10-01 18:56:35 +02:00
* data << * areaTrigger -> GetOrbit ();
2017-01-29 00:00:43 +01:00
}
2014-11-02 14:56:16 +01:00
2018-10-04 18:50:21 +02:00
if ( flags . GameObject )
2017-11-05 20:24:45 +01:00
{
bool bit8 = false ;
uint32 Int1 = 0 ;
GameObject const * gameObject = ToGameObject ();
* data << uint32 ( gameObject -> GetWorldEffectID ());
data -> WriteBit ( bit8 );
data -> FlushBits ();
if ( bit8 )
* data << uint32 ( Int1 );
}
2014-11-02 14:56:16 +01:00
2022-01-29 19:44:55 +01:00
if ( flags . SmoothPhasing )
{
2022-01-29 19:44:55 +01:00
SmoothPhasingInfo const * smoothPhasingInfo = static_cast < WorldObject const *> ( this ) -> GetSmoothPhasing () -> GetInfoForSeer ( target -> GetGUID ());
ASSERT ( smoothPhasingInfo );
2022-01-29 19:44:55 +01:00
2022-01-29 19:44:55 +01:00
data -> WriteBit ( smoothPhasingInfo -> ReplaceActive );
data -> WriteBit ( smoothPhasingInfo -> StopAnimKits );
data -> WriteBit ( smoothPhasingInfo -> ReplaceObject . has_value ());
2022-01-29 19:44:55 +01:00
data -> FlushBits ();
2022-01-29 19:44:55 +01:00
if ( smoothPhasingInfo -> ReplaceObject )
* data << * smoothPhasingInfo -> ReplaceObject ;
2022-01-29 19:44:55 +01:00
}
2014-11-02 14:56:16 +01:00
2021-10-11 12:59:19 +02:00
if ( flags . SceneObject )
{
data -> WriteBit ( false ); // HasLocalScriptData
data -> WriteBit ( false ); // HasPetBattleFullUpdate
data -> FlushBits ();
2014-11-02 14:56:16 +01:00
2016-06-01 20:14:16 +02:00
// if (HasLocalScriptData)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// data->WriteBits(Data.length(), 7);
2017-07-04 20:30:16 +02:00
// data->FlushBits();
2016-06-01 20:14:16 +02:00
// data->WriteString(Data);
2014-11-02 14:56:16 +01:00
// }
2016-06-01 20:14:16 +02:00
// if (HasPetBattleFullUpdate)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// for (std::size_t i = 0; i < 2; ++i)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// *data << ObjectGuid(Players[i].CharacterID);
// *data << int32(Players[i].TrapAbilityID);
// *data << int32(Players[i].TrapStatus);
// *data << uint16(Players[i].RoundTimeSecs);
// *data << int8(Players[i].FrontPet);
// *data << uint8(Players[i].InputFlags);
// data->WriteBits(Players[i].Pets.size(), 2);
2017-07-04 20:30:16 +02:00
// data->FlushBits();
2016-06-01 20:14:16 +02:00
// for (std::size_t j = 0; j < Players[i].Pets.size(); ++j)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// *data << ObjectGuid(Players[i].Pets[j].BattlePetGUID);
// *data << int32(Players[i].Pets[j].SpeciesID);
2021-10-16 12:15:03 +02:00
// *data << int32(Players[i].Pets[j].CreatureID);
2016-06-01 20:14:16 +02:00
// *data << int32(Players[i].Pets[j].DisplayID);
// *data << int16(Players[i].Pets[j].Level);
// *data << int16(Players[i].Pets[j].Xp);
// *data << int32(Players[i].Pets[j].CurHealth);
// *data << int32(Players[i].Pets[j].MaxHealth);
// *data << int32(Players[i].Pets[j].Power);
// *data << int32(Players[i].Pets[j].Speed);
// *data << int32(Players[i].Pets[j].NpcTeamMemberID);
// *data << uint16(Players[i].Pets[j].BreedQuality);
// *data << uint16(Players[i].Pets[j].StatusFlags);
// *data << int8(Players[i].Pets[j].Slot);
// *data << uint32(Players[i].Pets[j].Abilities.size());
// *data << uint32(Players[i].Pets[j].Auras.size());
// *data << uint32(Players[i].Pets[j].States.size());
// for (std::size_t k = 0; k < Players[i].Pets[j].Abilities.size(); ++k)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// *data << int32(Players[i].Pets[j].Abilities[k].AbilityID);
// *data << int16(Players[i].Pets[j].Abilities[k].CooldownRemaining);
// *data << int16(Players[i].Pets[j].Abilities[k].LockdownRemaining);
// *data << int8(Players[i].Pets[j].Abilities[k].AbilityIndex);
// *data << uint8(Players[i].Pets[j].Abilities[k].Pboid);
2014-11-02 14:56:16 +01:00
// }
2016-06-01 20:14:16 +02:00
// for (std::size_t k = 0; k < Players[i].Pets[j].Auras.size(); ++k)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// *data << int32(Players[i].Pets[j].Auras[k].AbilityID);
// *data << uint32(Players[i].Pets[j].Auras[k].InstanceID);
// *data << int32(Players[i].Pets[j].Auras[k].RoundsRemaining);
// *data << int32(Players[i].Pets[j].Auras[k].CurrentRound);
// *data << uint8(Players[i].Pets[j].Auras[k].CasterPBOID);
2014-11-02 14:56:16 +01:00
// }
2016-06-01 20:14:16 +02:00
// for (std::size_t k = 0; k < Players[i].Pets[j].States.size(); ++k)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// *data << uint32(Players[i].Pets[j].States[k].StateID);
// *data << int32(Players[i].Pets[j].States[k].StateValue);
2014-11-02 14:56:16 +01:00
// }
2016-06-01 20:14:16 +02:00
// data->WriteBits(Players[i].Pets[j].CustomName.length(), 7);
2017-07-04 20:30:16 +02:00
// data->FlushBits();
2016-06-01 20:14:16 +02:00
// data->WriteString(Players[i].Pets[j].CustomName);
2014-11-02 14:56:16 +01:00
// }
// }
2016-06-01 20:14:16 +02:00
// for (std::size_t i = 0; i < 3; ++i)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// *data << uint32(Enviros[j].Auras.size());
// *data << uint32(Enviros[j].States.size());
// for (std::size_t j = 0; j < Enviros[j].Auras.size(); ++j)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// *data << int32(Enviros[j].Auras[j].AbilityID);
// *data << uint32(Enviros[j].Auras[j].InstanceID);
// *data << int32(Enviros[j].Auras[j].RoundsRemaining);
// *data << int32(Enviros[j].Auras[j].CurrentRound);
// *data << uint8(Enviros[j].Auras[j].CasterPBOID);
2014-11-02 14:56:16 +01:00
// }
2016-06-01 20:14:16 +02:00
// for (std::size_t j = 0; j < Enviros[j].States.size(); ++j)
2014-11-02 14:56:16 +01:00
// {
2016-06-01 20:14:16 +02:00
// *data << uint32(Enviros[i].States[j].StateID);
// *data << int32(Enviros[i].States[j].StateValue);
2014-11-02 14:56:16 +01:00
// }
// }
2016-06-01 20:14:16 +02:00
// *data << uint16(WaitingForFrontPetsMaxSecs);
// *data << uint16(PvpMaxRoundTime);
// *data << int32(CurRound);
// *data << uint32(NpcCreatureID);
// *data << uint32(NpcDisplayID);
// *data << int8(CurPetBattleState);
// *data << uint8(ForfeitPenalty);
// *data << ObjectGuid(InitialWildPetGUID);
// data->WriteBit(IsPVP);
// data->WriteBit(CanAwardXP);
2017-07-04 20:30:16 +02:00
// data->FlushBits();
2014-11-02 14:56:16 +01:00
// }
2021-10-11 12:59:19 +02:00
}
2014-11-02 14:56:16 +01:00
2018-10-04 18:50:21 +02:00
if ( flags . ActivePlayer )
2017-11-12 02:42:06 +01:00
{
2021-10-05 15:10:33 +02:00
Player const * player = ToPlayer ();
bool HasSceneInstanceIDs = ! player -> GetSceneMgr (). GetSceneTemplateByInstanceMap (). empty ();
2017-11-12 02:42:06 +01:00
bool HasRuneState = ToUnit () -> GetPowerIndex ( POWER_RUNES ) != MAX_POWERS ;
data -> WriteBit ( HasSceneInstanceIDs );
data -> WriteBit ( HasRuneState );
data -> FlushBits ();
2021-10-05 15:10:33 +02:00
if ( HasSceneInstanceIDs )
{
* data << uint32 ( player -> GetSceneMgr (). GetSceneTemplateByInstanceMap (). size ());
for ( auto const & itr : player -> GetSceneMgr (). GetSceneTemplateByInstanceMap ())
* data << uint32 ( itr . first );
}
2017-11-12 02:42:06 +01:00
if ( HasRuneState )
{
float baseCd = float ( player -> GetRuneBaseCooldown ());
uint32 maxRunes = uint32 ( player -> GetMaxPower ( POWER_RUNES ));
* data << uint8 (( 1 << maxRunes ) - 1 );
* data << uint8 ( player -> GetRunesState ());
* data << uint32 ( maxRunes );
for ( uint32 i = 0 ; i < maxRunes ; ++ i )
* data << uint8 (( baseCd - float ( player -> GetRuneCooldown ( i ))) / baseCd * 255 );
}
}
2018-10-04 18:50:21 +02:00
if ( flags . Conversation )
{
Conversation const * self = ToConversation ();
if ( data -> WriteBit ( self -> GetTextureKitId () != 0 ))
* data << uint32 ( self -> GetTextureKitId ());
data -> FlushBits ();
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
UF :: UpdateFieldFlag Object :: GetUpdateFieldFlagsFor ( Player const * /*target*/ ) const
2008-10-11 14:16:25 -05:00
{
2019-06-03 20:40:34 +02:00
return UF :: UpdateFieldFlag :: None ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
void Object :: BuildValuesUpdateWithFlag ( ByteBuffer * data , UF :: UpdateFieldFlag /*flags*/ , Player const * /*target*/ ) const
2014-10-26 22:06:48 +01:00
{
2019-06-03 20:40:34 +02:00
std :: size_t sizePos = data -> wpos ();
* data << uint32 ( 0 );
* data << uint32 ( 0 );
2014-10-26 22:06:48 +01:00
2019-06-03 20:40:34 +02:00
data -> put < uint32 > ( sizePos , data -> wpos () - sizePos - 4 );
2014-10-26 22:06:48 +01:00
}
2015-02-25 00:13:14 +01:00
void Object :: AddToObjectUpdateIfNeeded ()
{
if ( m_inWorld && ! m_objectUpdated )
2020-03-19 20:42:44 +01:00
m_objectUpdated = AddToObjectUpdate ();
2015-02-25 00:13:14 +01:00
}
2008-10-11 14:16:25 -05:00
void Object :: ClearUpdateMask ( bool remove )
{
2019-06-03 20:40:34 +02:00
m_values . ClearChangesMask ( & Object :: m_objectData );
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( m_objectUpdated )
2008-10-11 14:16:25 -05:00
{
2010-04-07 19:14:10 +02:00
if ( remove )
2015-02-25 00:13:14 +01:00
RemoveFromObjectUpdate ();
2008-10-11 14:16:25 -05:00
m_objectUpdated = false ;
}
}
2009-10-17 15:51:44 -07:00
2011-11-07 11:18:00 -06:00
void Object :: BuildFieldsUpdate ( Player * player , UpdateDataMapType & data_map ) const
2009-12-19 19:46:07 +01:00
{
2011-11-07 11:18:00 -06:00
UpdateDataMapType :: iterator iter = data_map . find ( player );
2009-12-19 19:46:07 +01:00
2010-04-07 22:59:46 +02:00
if ( iter == data_map . end ())
2010-02-03 16:53:40 +02:00
{
2014-07-27 20:59:22 +02:00
std :: pair < UpdateDataMapType :: iterator , bool > p = data_map . emplace ( player , UpdateData ( player -> GetMapId ()));
2010-07-30 20:04:28 +02:00
ASSERT ( p . second );
2010-02-03 16:53:40 +02:00
iter = p . first ;
}
2009-12-19 19:46:07 +01:00
2010-02-03 16:53:40 +02:00
BuildValuesUpdateBlockForPlayer ( & iter -> second , iter -> first );
2009-12-19 19:46:07 +01:00
}
2018-12-14 22:01:16 +01:00
std :: string Object :: GetDebugInfo () const
{
std :: stringstream sstr ;
sstr << GetGUID (). ToString () + " Entry " << GetEntry ();
return sstr . str ();
}
2010-08-08 20:32:14 +02:00
void MovementInfo :: OutDebug ()
{
2015-03-08 06:16:46 +01:00
TC_LOG_DEBUG ( "misc" , "MOVEMENT INFO" );
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "misc" , "{}" , guid . ToString ());
2024-04-23 11:09:59 +02:00
TC_LOG_DEBUG ( "misc" , "flags {} ({})" , Movement :: MovementFlags_ToString ( MovementFlags ( flags )), flags );
TC_LOG_DEBUG ( "misc" , "flags2 {} ({})" , Movement :: MovementFlags_ToString ( MovementFlags2 ( flags2 )), flags2 );
TC_LOG_DEBUG ( "misc" , "flags3 {} ({})" , Movement :: MovementFlags_ToString ( MovementFlags3 ( flags3 )), flags2 );
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "misc" , "time {} current time {}" , time , getMSTime ());
TC_LOG_DEBUG ( "misc" , "position: `{}`" , pos . ToString ());
2014-10-21 20:27:00 +02:00
if ( ! transport . guid . IsEmpty ())
2010-08-08 20:32:14 +02:00
{
2015-03-08 06:16:46 +01:00
TC_LOG_DEBUG ( "misc" , "TRANSPORT:" );
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "misc" , "{}" , transport . guid . ToString ());
TC_LOG_DEBUG ( "misc" , "position: `{}`" , transport . pos . ToString ());
TC_LOG_DEBUG ( "misc" , "seat: {}" , transport . seat );
TC_LOG_DEBUG ( "misc" , "time: {}" , transport . time );
2017-03-03 20:26:45 +01:00
if ( transport . prevTime )
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "misc" , "prevTime: {}" , transport . prevTime );
2014-11-02 14:56:16 +01:00
if ( transport . vehicleId )
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "misc" , "vehicleId: {}" , transport . vehicleId );
2010-08-08 20:32:14 +02:00
}
2011-08-28 17:36:02 +02:00
2010-08-08 20:32:14 +02:00
if (( flags & ( MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING )) || ( flags2 & MOVEMENTFLAG2_ALWAYS_ALLOW_PITCHING ))
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "misc" , "pitch: {}" , pitch );
2011-08-28 17:36:02 +02:00
2013-08-23 18:27:42 +02:00
if ( flags & MOVEMENTFLAG_FALLING || jump . fallTime )
2013-04-01 14:45:47 +02:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "misc" , "fallTime: {} j_zspeed: {}" , jump . fallTime , jump . zspeed );
2013-04-01 14:45:47 +02:00
if ( flags & MOVEMENTFLAG_FALLING )
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "misc" , "j_sinAngle: {} j_cosAngle: {} j_xyspeed: {}" , jump . sinAngle , jump . cosAngle , jump . xyspeed );
2013-04-01 14:45:47 +02:00
}
2011-08-28 17:36:02 +02:00
2010-08-08 20:32:14 +02:00
if ( flags & MOVEMENTFLAG_SPLINE_ELEVATION )
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "misc" , "stepUpStartElevation: {}" , stepUpStartElevation );
2023-05-04 17:00:52 +02:00
if ( inertia )
{
TC_LOG_DEBUG ( "misc" , "inertia->id: {}" , inertia -> id );
TC_LOG_DEBUG ( "misc" , "inertia->force: {}" , inertia -> force . ToString ());
TC_LOG_DEBUG ( "misc" , "inertia->lifetime: {}" , inertia -> lifetime );
}
if ( advFlying )
{
TC_LOG_DEBUG ( "misc" , "advFlying->forwardVelocity: {}" , advFlying -> forwardVelocity );
TC_LOG_DEBUG ( "misc" , "advFlying->upVelocity: {}" , advFlying -> upVelocity );
}
if ( standingOnGameObjectGUID )
TC_LOG_DEBUG ( "misc" , "standingOnGameObjectGUID: {}" , standingOnGameObjectGUID -> ToString ());
2010-08-08 20:32:14 +02:00
}
2018-02-10 16:43:01 -03:00
WorldObject :: WorldObject ( bool isWorldObject ) : Object (), WorldLocation (), LastUsedScriptID ( 0 ),
2024-03-14 11:01:58 +01:00
m_movementInfo (), m_name (), m_isActive ( false ), m_isFarVisible ( false ), m_isStoredInWorldObjectGridContainer ( isWorldObject ), m_zoneScript ( nullptr ),
2020-04-07 23:28:44 +02:00
m_transport ( nullptr ), m_zoneId ( 0 ), m_areaId ( 0 ), m_staticFloorZ ( VMAP_INVALID_HEIGHT ), m_outdoors ( false ), m_liquidStatus ( LIQUID_MAP_NO_WATER ),
m_currMap ( nullptr ), m_InstanceId ( 0 ), _dbPhase ( 0 ), m_notifyflags ( 0 )
2010-11-13 17:18:09 +01:00
{
m_serverSideVisibility . SetValue ( SERVERSIDE_VISIBILITY_GHOST , GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST );
m_serverSideVisibilityDetect . SetValue ( SERVERSIDE_VISIBILITY_GHOST , GHOST_VISIBILITY_ALIVE );
}
2009-10-17 15:51:44 -07:00
2023-01-19 21:23:06 +01:00
WorldObject ::~ WorldObject ()
{
// this may happen because there are many !create/delete
2024-03-14 11:01:58 +01:00
if ( IsStoredInWorldObjectGridContainer () && m_currMap )
2023-01-19 21:23:06 +01:00
{
if ( GetTypeId () == TYPEID_CORPSE )
{
TC_LOG_FATAL ( "misc" , "WorldObject::~WorldObject Corpse Type: {} ({}) deleted but still in map!!" ,
ToCorpse () -> GetType (), GetGUID (). ToString ());
ABORT ();
}
ResetMap ();
}
}
2023-02-05 16:24:13 +01:00
void WorldObject :: Update ( uint32 diff )
{
m_Events . Update ( diff );
}
2024-03-14 11:01:58 +01:00
void WorldObject :: SetIsStoredInWorldObjectGridContainer ( bool on )
2008-10-27 08:41:55 -05:00
{
2010-04-07 19:14:10 +02:00
if ( ! IsInWorld ())
2009-02-26 16:29:55 -06:00
return ;
2009-10-17 15:51:44 -07:00
2009-04-06 21:14:51 +02:00
GetMap () -> AddObjectToSwitchList ( this , on );
2009-02-26 16:29:55 -06:00
}
2009-10-17 15:51:44 -07:00
2024-03-14 11:01:58 +01:00
bool WorldObject :: IsStoredInWorldObjectGridContainer () const
2011-12-20 17:05:08 -05:00
{
2024-03-14 11:01:58 +01:00
if ( m_isStoredInWorldObjectGridContainer )
2011-12-20 17:05:08 -05:00
return true ;
if ( ToCreature () && ToCreature () -> m_isTempWorldObject )
return true ;
return false ;
}
2010-04-07 22:59:46 +02:00
void WorldObject :: setActive ( bool on )
2008-10-27 08:41:55 -05:00
{
2010-04-07 19:14:10 +02:00
if ( m_isActive == on )
2008-10-27 08:41:55 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( GetTypeId () == TYPEID_PLAYER )
2008-10-27 08:41:55 -05:00
return ;
2009-10-17 15:51:44 -07:00
2009-03-25 21:32:34 -06:00
m_isActive = on ;
2009-10-17 15:51:44 -07:00
2019-04-27 13:57:46 +02:00
if ( on && ! IsInWorld ())
2009-03-25 21:32:34 -06:00
return ;
2009-10-17 15:51:44 -07:00
2011-09-15 14:08:17 +02:00
Map * map = FindMap ();
2010-04-07 19:14:10 +02:00
if ( ! map )
2009-03-25 21:32:34 -06:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( on )
2022-05-25 22:14:32 +02:00
map -> AddToActive ( this );
2009-03-25 21:32:34 -06:00
else
2022-05-25 22:14:32 +02:00
map -> RemoveFromActive ( this );
2008-10-27 08:41:55 -05:00
}
2009-10-17 15:51:44 -07:00
2019-01-16 20:48:01 +01:00
void WorldObject :: SetVisibilityDistanceOverride ( VisibilityDistanceType type )
{
ASSERT ( type < VisibilityDistanceType :: Max );
if ( GetTypeId () == TYPEID_PLAYER )
return ;
2022-10-01 00:11:52 +02:00
if ( Creature * creature = ToCreature ())
{
creature -> RemoveUnitFlag2 ( UNIT_FLAG2_LARGE_AOI | UNIT_FLAG2_GIGANTIC_AOI | UNIT_FLAG2_INFINITE_AOI );
switch ( type )
{
case VisibilityDistanceType :: Large :
creature -> SetUnitFlag2 ( UNIT_FLAG2_LARGE_AOI );
break ;
case VisibilityDistanceType :: Gigantic :
creature -> SetUnitFlag2 ( UNIT_FLAG2_GIGANTIC_AOI );
break ;
case VisibilityDistanceType :: Infinite :
creature -> SetUnitFlag2 ( UNIT_FLAG2_INFINITE_AOI );
break ;
default :
break ;
}
}
2019-01-16 20:48:01 +01:00
m_visibilityDistanceOverride = VisibilityDistances [ AsUnderlyingType ( type )];
}
2018-02-17 15:09:54 +01:00
void WorldObject :: SetFarVisible ( bool on )
{
if ( GetTypeId () == TYPEID_PLAYER )
return ;
m_isFarVisible = on ;
}
2010-04-19 09:26:37 +02:00
void WorldObject :: CleanupsBeforeDelete ( bool /*finalCleanup*/ )
2009-07-01 18:23:36 -05:00
{
2011-11-11 14:11:58 -05:00
if ( IsInWorld ())
RemoveFromWorld ();
2014-06-13 11:16:17 +02:00
2022-05-25 22:14:32 +02:00
if ( TransportBase * transport = GetTransport ())
2014-06-13 11:16:17 +02:00
transport -> RemovePassenger ( this );
2023-02-05 16:24:13 +01:00
m_Events . KillAllEvents ( false ); // non-delatable (currently cast spells) will not deleted now but it will deleted at call in Map::RemoveAllObjectsInRemoveList
2009-07-01 18:23:36 -05:00
}
2009-10-17 15:51:44 -07:00
2017-06-08 00:25:06 +02:00
void WorldObject :: UpdatePositionData ()
2013-03-04 11:45:57 +01:00
{
2017-06-08 00:25:06 +02:00
PositionFullTerrainStatus data ;
2024-03-07 00:23:52 +01:00
GetMap () -> GetFullTerrainStatusForPosition ( _phaseShift , GetPositionX (), GetPositionY (), GetPositionZ (), data , {}, GetCollisionHeight ());
2017-06-08 00:25:06 +02:00
ProcessPositionDataChanged ( data );
2013-03-04 11:45:57 +01:00
}
2017-06-08 00:25:06 +02:00
void WorldObject :: ProcessPositionDataChanged ( PositionFullTerrainStatus const & data )
2008-10-11 14:16:25 -05:00
{
2017-06-08 00:25:06 +02:00
m_zoneId = m_areaId = data . areaId ;
if ( AreaTableEntry const * area = sAreaTableStore . LookupEntry ( m_areaId ))
2023-10-06 20:22:14 +02:00
if ( area -> ParentAreaID && area -> GetFlags (). HasFlag ( AreaFlags :: IsSubzone ))
2020-12-07 22:32:45 +01:00
m_zoneId = area -> ParentAreaID ;
2018-02-26 17:40:40 +01:00
m_outdoors = data . outdoors ;
2017-06-08 00:25:06 +02:00
m_staticFloorZ = data . floorZ ;
2020-04-07 23:28:44 +02:00
m_liquidStatus = data . liquidStatus ;
2024-03-07 13:31:12 +01:00
m_currentWmo = data . wmoLocation ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-06-08 00:25:06 +02:00
void WorldObject :: AddToWorld ()
2008-10-11 14:16:25 -05:00
{
2017-06-08 00:25:06 +02:00
Object :: AddToWorld ();
GetMap () -> GetZoneAndAreaId ( _phaseShift , m_zoneId , m_areaId , GetPositionX (), GetPositionY (), GetPositionZ ());
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-06-08 00:25:06 +02:00
void WorldObject :: RemoveFromWorld ()
2009-03-11 16:17:37 -06:00
{
2017-06-08 00:25:06 +02:00
if ( ! IsInWorld ())
return ;
2022-01-29 15:18:27 +01:00
UpdateObjectVisibilityOnDestroy ();
2017-06-08 00:25:06 +02:00
Object :: RemoveFromWorld ();
2009-03-11 16:17:37 -06:00
}
2009-10-17 15:51:44 -07:00
2017-02-13 20:42:06 +01:00
bool WorldObject :: IsInWorldPvpZone () const
{
switch ( GetZoneId ())
{
2017-06-08 00:25:06 +02:00
case 4197 : // Wintergrasp
case 5095 : // Tol Barad
case 6941 : // Ashran
return true ;
break ;
default :
return false ;
break ;
2017-02-13 20:42:06 +01:00
}
}
2017-09-15 03:53:02 +02:00
InstanceScript * WorldObject :: GetInstanceScript () const
2008-10-11 14:16:25 -05:00
{
2011-09-15 14:08:17 +02:00
Map * map = GetMap ();
2020-08-14 17:06:03 +02:00
return map -> IsDungeon () ? (( InstanceMap * ) map ) -> GetInstanceScript () : nullptr ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2020-09-04 13:38:24 +02:00
float WorldObject :: GetDistanceZ ( WorldObject const * obj ) const
2008-10-11 14:16:25 -05:00
{
2014-08-21 16:56:11 +02:00
float dz = std :: fabs ( GetPositionZ () - obj -> GetPositionZ ());
2017-03-23 00:43:04 +01:00
float sizefactor = GetCombatReach () + obj -> GetCombatReach ();
2008-10-11 14:16:25 -05:00
float dist = dz - sizefactor ;
2010-04-07 22:59:46 +02:00
return ( dist > 0 ? dist : 0 );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-03-23 00:43:04 +01:00
bool WorldObject :: _IsWithinDist ( WorldObject const * obj , float dist2compare , bool is3D , bool incOwnRadius , bool incTargetRadius ) const
2009-05-14 16:50:47 -05:00
{
2017-03-23 00:43:04 +01:00
float sizefactor = 0 ;
sizefactor += incOwnRadius ? GetCombatReach () : 0.0f ;
sizefactor += incTargetRadius ? obj -> GetCombatReach () : 0.0f ;
2010-07-30 21:54:46 -07:00
float maxdist = dist2compare + sizefactor ;
2017-03-23 00:43:04 +01:00
Position const * thisOrTransport = this ;
Position const * objOrObjTransport = obj ;
2022-05-25 22:14:32 +02:00
if ( GetTransport () && obj -> GetTransport () && obj -> GetTransport () -> GetTransportGUID () == GetTransport () -> GetTransportGUID ())
2010-07-30 21:54:46 -07:00
{
2017-03-23 00:43:04 +01:00
thisOrTransport = & m_movementInfo . transport . pos ;
objOrObjTransport = & obj -> m_movementInfo . transport . pos ;
2010-07-30 21:54:46 -07:00
}
2010-04-07 19:14:10 +02:00
if ( is3D )
2017-03-23 00:43:04 +01:00
return thisOrTransport -> IsInDist ( objOrObjTransport , maxdist );
else
return thisOrTransport -> IsInDist2d ( objOrObjTransport , maxdist );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2020-09-04 13:38:24 +02:00
float WorldObject :: GetDistance ( WorldObject const * obj ) const
2013-03-04 11:45:57 +01:00
{
2017-03-23 00:43:04 +01:00
float d = GetExactDist ( obj ) - GetCombatReach () - obj -> GetCombatReach ();
2013-03-04 11:45:57 +01:00
return d > 0.0f ? d : 0.0f ;
}
2020-09-04 13:38:24 +02:00
float WorldObject :: GetDistance ( Position const & pos ) const
2013-03-04 11:45:57 +01:00
{
2017-03-23 00:43:04 +01:00
float d = GetExactDist ( & pos ) - GetCombatReach ();
2013-03-04 11:45:57 +01:00
return d > 0.0f ? d : 0.0f ;
}
float WorldObject :: GetDistance ( float x , float y , float z ) const
{
2017-03-23 00:43:04 +01:00
float d = GetExactDist ( x , y , z ) - GetCombatReach ();
2013-03-04 11:45:57 +01:00
return d > 0.0f ? d : 0.0f ;
}
2020-09-04 13:38:24 +02:00
float WorldObject :: GetDistance2d ( WorldObject const * obj ) const
2013-03-04 11:45:57 +01:00
{
2017-03-23 00:43:04 +01:00
float d = GetExactDist2d ( obj ) - GetCombatReach () - obj -> GetCombatReach ();
2013-03-04 11:45:57 +01:00
return d > 0.0f ? d : 0.0f ;
}
float WorldObject :: GetDistance2d ( float x , float y ) const
{
2017-03-23 00:43:04 +01:00
float d = GetExactDist2d ( x , y ) - GetCombatReach ();
2013-03-04 11:45:57 +01:00
return d > 0.0f ? d : 0.0f ;
}
2020-09-04 13:38:24 +02:00
bool WorldObject :: IsSelfOrInSameMap ( WorldObject const * obj ) const
2013-03-04 11:45:57 +01:00
{
if ( this == obj )
return true ;
return IsInMap ( obj );
}
2020-09-04 13:38:24 +02:00
bool WorldObject :: IsInMap ( WorldObject const * obj ) const
2013-03-04 11:45:57 +01:00
{
if ( obj )
return IsInWorld () && obj -> IsInWorld () && ( GetMap () == obj -> GetMap ());
return false ;
}
bool WorldObject :: IsWithinDist3d ( float x , float y , float z , float dist ) const
{
2017-03-23 00:43:04 +01:00
return IsInDist ( x , y , z , dist + GetCombatReach ());
2013-03-04 11:45:57 +01:00
}
2020-09-04 13:38:24 +02:00
bool WorldObject :: IsWithinDist3d ( Position const * pos , float dist ) const
2013-03-04 19:14:58 +01:00
{
2017-03-23 00:43:04 +01:00
return IsInDist ( pos , dist + GetCombatReach ());
2013-03-04 11:45:57 +01:00
}
bool WorldObject :: IsWithinDist2d ( float x , float y , float dist ) const
{
2017-03-23 00:43:04 +01:00
return IsInDist2d ( x , y , dist + GetCombatReach ());
2013-03-04 11:45:57 +01:00
}
2020-09-04 13:38:24 +02:00
bool WorldObject :: IsWithinDist2d ( Position const * pos , float dist ) const
2013-03-04 19:14:58 +01:00
{
2017-03-23 00:43:04 +01:00
return IsInDist2d ( pos , dist + GetCombatReach ());
2013-03-04 11:45:57 +01:00
}
2022-12-30 21:50:16 +01:00
bool WorldObject :: IsWithinDist ( WorldObject const * obj , float dist2compare , bool is3D /*= true*/ , bool incOwnRadius /*= true*/ , bool incTargetRadius /*= true*/ ) const
2013-03-04 11:45:57 +01:00
{
2022-12-30 21:50:16 +01:00
return obj && _IsWithinDist ( obj , dist2compare , is3D , incOwnRadius , incTargetRadius );
2013-03-04 11:45:57 +01:00
}
2017-03-23 00:43:04 +01:00
bool WorldObject :: IsWithinDistInMap ( WorldObject const * obj , float dist2compare , bool is3D /*= true*/ , bool incOwnRadius /*= true*/ , bool incTargetRadius /*= true*/ ) const
2013-03-04 11:45:57 +01:00
{
2022-12-25 00:44:12 +01:00
return obj && IsInMap ( obj ) && InSamePhase ( obj ) && _IsWithinDist ( obj , dist2compare , is3D , incOwnRadius , incTargetRadius );
2013-03-04 11:45:57 +01:00
}
2017-06-10 15:08:35 +02:00
Position WorldObject :: GetHitSpherePointFor ( Position const & dest ) const
{
2017-12-29 22:32:07 +01:00
G3D :: Vector3 vThis ( GetPositionX (), GetPositionY (), GetPositionZ () + GetCollisionHeight ());
2017-06-10 15:08:35 +02:00
G3D :: Vector3 vObj ( dest . GetPositionX (), dest . GetPositionY (), dest . GetPositionZ ());
G3D :: Vector3 contactPoint = vThis + ( vObj - vThis ). directionOrZero () * std :: min ( dest . GetExactDist ( GetPosition ()), GetCombatReach ());
2018-04-01 13:52:36 +02:00
return Position ( contactPoint . x , contactPoint . y , contactPoint . z , GetAbsoluteAngle ( contactPoint . x , contactPoint . y ));
2017-06-10 15:08:35 +02:00
}
bool WorldObject :: IsWithinLOS ( float ox , float oy , float oz , LineOfSightChecks checks , VMAP :: ModelIgnoreFlags ignoreFlags ) const
2008-10-11 14:16:25 -05:00
{
2012-02-03 16:03:52 -05:00
if ( IsInWorld ())
2015-11-03 11:20:30 +01:00
{
2017-12-29 22:32:07 +01:00
oz += GetCollisionHeight ();
2015-11-03 11:20:30 +01:00
float x , y , z ;
if ( GetTypeId () == TYPEID_PLAYER )
2017-12-14 16:56:30 +01:00
{
2015-11-03 11:20:30 +01:00
GetPosition ( x , y , z );
2017-12-29 22:32:07 +01:00
z += GetCollisionHeight ();
2017-12-14 16:56:30 +01:00
}
2015-11-03 11:20:30 +01:00
else
GetHitSpherePointFor ({ ox , oy , oz }, x , y , z );
2017-12-14 16:56:30 +01:00
return GetMap () -> isInLineOfSight ( GetPhaseShift (), x , y , z , ox , oy , oz , checks , ignoreFlags );
2015-11-03 11:20:30 +01:00
}
2012-02-03 16:03:52 -05:00
return true ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2020-09-04 13:38:24 +02:00
bool WorldObject :: IsWithinLOSInMap ( WorldObject const * obj , LineOfSightChecks checks , VMAP :: ModelIgnoreFlags ignoreFlags ) const
2015-11-03 11:20:30 +01:00
{
2017-06-10 15:08:35 +02:00
if ( ! IsInMap ( obj ))
return false ;
2015-11-03 11:20:30 +01:00
2017-12-29 22:32:07 +01:00
float ox , oy , oz ;
2017-06-10 15:08:35 +02:00
if ( obj -> GetTypeId () == TYPEID_PLAYER )
2017-12-14 16:56:30 +01:00
{
2017-12-29 22:32:07 +01:00
obj -> GetPosition ( ox , oy , oz );
oz += GetCollisionHeight ();
}
else
obj -> GetHitSpherePointFor ({ GetPositionX (), GetPositionY (), GetPositionZ () + GetCollisionHeight () }, ox , oy , oz );
float x , y , z ;
if ( GetTypeId () == TYPEID_PLAYER )
{
GetPosition ( x , y , z );
z += GetCollisionHeight ();
2017-12-14 16:56:30 +01:00
}
2017-06-10 15:08:35 +02:00
else
2017-12-29 22:32:07 +01:00
GetHitSpherePointFor ({ obj -> GetPositionX (), obj -> GetPositionY (), obj -> GetPositionZ () + obj -> GetCollisionHeight () }, x , y , z );
2017-06-10 15:08:35 +02:00
2017-12-29 22:32:07 +01:00
return GetMap () -> isInLineOfSight ( GetPhaseShift (), x , y , z , ox , oy , oz , checks , ignoreFlags );
2015-11-03 11:20:30 +01:00
}
void WorldObject :: GetHitSpherePointFor ( Position const & dest , float & x , float & y , float & z ) const
{
Position pos = GetHitSpherePointFor ( dest );
x = pos . GetPositionX ();
y = pos . GetPositionY ();
z = pos . GetPositionZ ();
}
2009-05-14 16:50:47 -05:00
bool WorldObject :: GetDistanceOrder ( WorldObject const * obj1 , WorldObject const * obj2 , bool is3D /* = true */ ) const
{
float dx1 = GetPositionX () - obj1 -> GetPositionX ();
float dy1 = GetPositionY () - obj1 -> GetPositionY ();
float distsq1 = dx1 * dx1 + dy1 * dy1 ;
2010-04-07 19:14:10 +02:00
if ( is3D )
2009-05-14 16:50:47 -05:00
{
float dz1 = GetPositionZ () - obj1 -> GetPositionZ ();
distsq1 += dz1 * dz1 ;
}
2009-10-17 15:51:44 -07:00
2009-05-14 16:50:47 -05:00
float dx2 = GetPositionX () - obj2 -> GetPositionX ();
float dy2 = GetPositionY () - obj2 -> GetPositionY ();
float distsq2 = dx2 * dx2 + dy2 * dy2 ;
2010-04-07 19:14:10 +02:00
if ( is3D )
2009-05-14 16:50:47 -05:00
{
float dz2 = GetPositionZ () - obj2 -> GetPositionZ ();
distsq2 += dz2 * dz2 ;
}
2009-10-17 15:51:44 -07:00
2009-05-14 16:50:47 -05:00
return distsq1 < distsq2 ;
}
2009-10-17 15:51:44 -07:00
2009-05-14 16:50:47 -05:00
bool WorldObject :: IsInRange ( WorldObject const * obj , float minRange , float maxRange , bool is3D /* = true */ ) const
{
float dx = GetPositionX () - obj -> GetPositionX ();
float dy = GetPositionY () - obj -> GetPositionY ();
float distsq = dx * dx + dy * dy ;
2010-04-07 19:14:10 +02:00
if ( is3D )
2009-05-14 16:50:47 -05:00
{
float dz = GetPositionZ () - obj -> GetPositionZ ();
distsq += dz * dz ;
}
2009-10-17 15:51:44 -07:00
2017-03-23 00:43:04 +01:00
float sizefactor = GetCombatReach () + obj -> GetCombatReach ();
2009-10-17 15:51:44 -07:00
2009-05-17 10:09:06 -05:00
// check only for real range
2010-04-07 19:14:10 +02:00
if ( minRange > 0.0f )
2009-05-17 10:09:06 -05:00
{
float mindist = minRange + sizefactor ;
2010-04-07 19:14:10 +02:00
if ( distsq < mindist * mindist )
2009-05-17 10:09:06 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2009-05-14 16:50:47 -05:00
float maxdist = maxRange + sizefactor ;
return distsq < maxdist * maxdist ;
}
2009-10-17 15:51:44 -07:00
2009-05-14 16:50:47 -05:00
bool WorldObject :: IsInRange2d ( float x , float y , float minRange , float maxRange ) const
{
float dx = GetPositionX () - x ;
float dy = GetPositionY () - y ;
float distsq = dx * dx + dy * dy ;
2009-10-17 15:51:44 -07:00
2017-03-23 00:43:04 +01:00
float sizefactor = GetCombatReach ();
2009-10-17 15:51:44 -07:00
2009-05-17 10:09:06 -05:00
// check only for real range
2010-04-07 19:14:10 +02:00
if ( minRange > 0.0f )
2009-05-17 10:09:06 -05:00
{
float mindist = minRange + sizefactor ;
2010-04-07 19:14:10 +02:00
if ( distsq < mindist * mindist )
2009-05-17 10:09:06 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2009-05-14 16:50:47 -05:00
float maxdist = maxRange + sizefactor ;
return distsq < maxdist * maxdist ;
}
2009-10-17 15:51:44 -07:00
2009-05-14 16:50:47 -05:00
bool WorldObject :: IsInRange3d ( float x , float y , float z , float minRange , float maxRange ) const
{
float dx = GetPositionX () - x ;
float dy = GetPositionY () - y ;
float dz = GetPositionZ () - z ;
float distsq = dx * dx + dy * dy + dz * dz ;
2009-10-17 15:51:44 -07:00
2017-03-23 00:43:04 +01:00
float sizefactor = GetCombatReach ();
2009-10-17 15:51:44 -07:00
2009-05-17 10:09:06 -05:00
// check only for real range
2010-04-07 19:14:10 +02:00
if ( minRange > 0.0f )
2009-05-17 10:09:06 -05:00
{
float mindist = minRange + sizefactor ;
2010-04-07 19:14:10 +02:00
if ( distsq < mindist * mindist )
2009-05-17 10:09:06 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2009-05-14 16:50:47 -05:00
float maxdist = maxRange + sizefactor ;
return distsq < maxdist * maxdist ;
}
2009-10-17 15:51:44 -07:00
2016-09-10 14:14:34 +02:00
bool WorldObject :: IsInBetween ( Position const & pos1 , Position const & pos2 , float size ) const
2009-05-09 10:50:41 -05:00
{
2016-09-10 14:14:34 +02:00
float dist = GetExactDist2d ( pos1 );
2011-09-20 17:48:13 -03:00
// not using sqrt() for performance
2016-09-10 14:14:34 +02:00
if (( dist * dist ) >= pos1 . GetExactDist2dSq ( pos2 ))
2009-05-09 10:50:41 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( ! size )
2017-03-23 00:43:04 +01:00
size = GetCombatReach () / 2 ;
2009-10-17 15:51:44 -07:00
2018-04-01 13:52:36 +02:00
float angle = pos1 . GetAbsoluteAngle ( pos2 );
2011-09-20 17:48:13 -03:00
// not using sqrt() for performance
2016-09-10 14:14:34 +02:00
return ( size * size ) >= GetExactDist2dSq ( pos1 . GetPositionX () + std :: cos ( angle ) * dist , pos1 . GetPositionY () + std :: sin ( angle ) * dist );
2009-05-09 10:50:41 -05:00
}
2009-10-17 15:51:44 -07:00
2019-06-26 08:56:20 +02:00
bool WorldObject :: isInFront ( WorldObject const * target , float arc ) const
2009-12-17 19:09:40 +01:00
{
2012-02-21 20:03:27 +01:00
return HasInArc ( arc , target );
2009-12-17 19:09:40 +01:00
}
2012-02-21 20:03:27 +01:00
bool WorldObject :: isInBack ( WorldObject const * target , float arc ) const
2009-12-17 19:09:40 +01:00
{
2014-08-21 16:56:11 +02:00
return ! HasInArc ( 2 * float ( M_PI ) - arc , target );
2009-12-17 19:09:40 +01:00
}
2020-09-04 13:38:24 +02:00
void WorldObject :: GetRandomPoint ( Position const & pos , float distance , float & rand_x , float & rand_y , float & rand_z ) const
2008-10-11 14:16:25 -05:00
{
2010-04-07 19:14:10 +02:00
if ( ! distance )
2008-10-11 14:16:25 -05:00
{
2009-08-29 23:20:16 -05:00
pos . GetPosition ( rand_x , rand_y , rand_z );
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
// angle to face `obj` to `this`
2023-10-31 20:20:00 +01:00
float angle = rand_norm () * static_cast < float > ( 2 * M_PI );
float new_dist = rand_norm () + rand_norm ();
2016-07-13 16:41:33 +02:00
new_dist = distance * ( new_dist > 1 ? new_dist - 2 : new_dist );
2009-10-17 15:51:44 -07:00
2012-10-02 15:06:19 +02:00
rand_x = pos . m_positionX + new_dist * std :: cos ( angle );
rand_y = pos . m_positionY + new_dist * std :: sin ( angle );
2009-08-29 23:20:16 -05:00
rand_z = pos . m_positionZ ;
2009-10-17 15:51:44 -07:00
2008-10-14 11:57:03 -05:00
Trinity :: NormalizeMapCoord ( rand_x );
Trinity :: NormalizeMapCoord ( rand_y );
2011-04-29 20:47:02 +02:00
UpdateGroundPositionZ ( rand_x , rand_y , rand_z ); // update to LOS height if available
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2020-09-04 13:38:24 +02:00
Position WorldObject :: GetRandomPoint ( Position const & srcPos , float distance ) const
2013-03-04 15:56:00 +01:00
{
float x , y , z ;
GetRandomPoint ( srcPos , distance , x , y , z );
2014-04-25 21:19:30 +02:00
return Position ( x , y , z , GetOrientation ());
2013-03-04 15:56:00 +01:00
}
2008-10-11 14:16:25 -05:00
void WorldObject :: UpdateGroundPositionZ ( float x , float y , float & z ) const
{
2017-11-23 18:12:44 +01:00
float new_z = GetMapHeight ( x , y , z );
if ( new_z > INVALID_HEIGHT )
2024-03-14 11:37:32 +01:00
{
z = new_z ;
if ( Unit const * unit = ToUnit ())
z += unit -> GetHoverOffset ();
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2019-04-07 20:15:40 +02:00
void WorldObject :: UpdateAllowedPositionZ ( float x , float y , float & z , float * groundZ ) const
2012-01-14 15:34:41 +01:00
{
2013-12-31 14:12:02 +01:00
// TODO: Allow transports to be part of dynamic vmap tree
if ( GetTransport ())
2019-04-07 20:15:40 +02:00
{
if ( groundZ )
* groundZ = z ;
2013-12-31 14:12:02 +01:00
return ;
2019-04-07 20:15:40 +02:00
}
2013-12-31 14:12:02 +01:00
2017-11-23 18:12:44 +01:00
if ( Unit const * unit = ToUnit ())
2012-01-14 15:34:41 +01:00
{
2017-11-23 18:12:44 +01:00
if ( ! unit -> CanFly ())
2012-01-14 15:34:41 +01:00
{
2017-11-23 18:12:44 +01:00
bool canSwim = unit -> CanSwim ();
float ground_z = z ;
float max_z ;
if ( canSwim )
max_z = GetMapWaterOrGroundLevel ( x , y , z , & ground_z );
2012-01-14 15:34:41 +01:00
else
2017-11-23 18:12:44 +01:00
max_z = ground_z = GetMapHeight ( x , y , z );
if ( max_z > INVALID_HEIGHT )
2012-01-14 15:34:41 +01:00
{
2017-11-23 18:12:44 +01:00
// hovering units cannot go below their hover height
float hoverOffset = unit -> GetHoverOffset ();
max_z += hoverOffset ;
ground_z += hoverOffset ;
if ( z > max_z )
z = max_z ;
else if ( z < ground_z )
2012-01-14 15:34:41 +01:00
z = ground_z ;
}
2019-04-07 20:15:40 +02:00
if ( groundZ )
* groundZ = ground_z ;
2012-01-14 15:34:41 +01:00
}
2017-11-23 18:12:44 +01:00
else
2012-01-14 15:34:41 +01:00
{
2017-11-23 18:12:44 +01:00
float ground_z = GetMapHeight ( x , y , z ) + unit -> GetHoverOffset ();
if ( z < ground_z )
2012-01-14 15:34:41 +01:00
z = ground_z ;
2019-04-07 20:15:40 +02:00
if ( groundZ )
* groundZ = ground_z ;
2012-01-14 15:34:41 +01:00
}
}
2017-11-23 18:12:44 +01:00
else
{
float ground_z = GetMapHeight ( x , y , z );
if ( ground_z > INVALID_HEIGHT )
z = ground_z ;
2019-04-07 20:15:40 +02:00
if ( groundZ )
* groundZ = ground_z ;
2017-11-23 18:12:44 +01:00
}
2012-01-14 15:34:41 +01:00
}
2010-11-13 17:18:09 +01:00
float WorldObject :: GetGridActivationRange () const
{
2017-02-08 20:23:37 +01:00
if ( isActiveObject ())
2016-04-27 16:53:18 +02:00
{
2017-02-08 20:39:44 +01:00
if ( GetTypeId () == TYPEID_PLAYER && ToPlayer () -> GetCinematicMgr () -> IsOnCinematic ())
2017-02-08 20:23:37 +01:00
return std :: max ( DEFAULT_VISIBILITY_INSTANCE , GetMap () -> GetVisibilityRange ());
2010-11-13 17:18:09 +01:00
return GetMap () -> GetVisibilityRange ();
2016-04-27 16:53:18 +02:00
}
2017-02-08 20:23:37 +01:00
if ( Creature const * thisCreature = ToCreature ())
return thisCreature -> m_SightDistance ;
return 0.0f ;
2010-11-13 17:18:09 +01:00
}
float WorldObject :: GetVisibilityRange () const
{
2019-01-16 20:48:01 +01:00
if ( IsVisibilityOverridden () && ! ToPlayer ())
return * m_visibilityDistanceOverride ;
2018-02-17 15:09:54 +01:00
else if ( IsFarVisible () && ! ToPlayer ())
2010-11-13 17:18:09 +01:00
return MAX_VISIBILITY_DISTANCE ;
else
return GetMap () -> GetVisibilityRange ();
}
2020-09-04 13:38:24 +02:00
float WorldObject :: GetSightRange ( WorldObject const * target ) const
2010-11-13 17:18:09 +01:00
{
if ( ToUnit ())
{
if ( ToPlayer ())
{
2019-01-16 20:48:01 +01:00
if ( target && target -> IsVisibilityOverridden () && ! target -> ToPlayer ())
return * target -> m_visibilityDistanceOverride ;
2018-02-17 15:09:54 +01:00
else if ( target && target -> IsFarVisible () && ! target -> ToPlayer ())
2010-11-13 17:18:09 +01:00
return MAX_VISIBILITY_DISTANCE ;
2016-04-27 16:53:18 +02:00
else if ( ToPlayer () -> GetCinematicMgr () -> IsOnCinematic ())
return DEFAULT_VISIBILITY_INSTANCE ;
2010-11-13 17:18:09 +01:00
else
return GetMap () -> GetVisibilityRange ();
}
else if ( ToCreature ())
return ToCreature () -> m_SightDistance ;
else
return SIGHT_RANGE_UNIT ;
}
2016-04-27 16:53:18 +02:00
if ( ToDynObject () && isActiveObject ())
{
return GetMap () -> GetVisibilityRange ();
}
2010-11-13 17:18:09 +01:00
return 0.0f ;
}
2021-03-28 16:38:04 +02:00
bool WorldObject :: CheckPrivateObjectOwnerVisibility ( WorldObject const * seer ) const
{
if ( ! IsPrivateObject ())
return true ;
// Owner of this private object
if ( _privateObjectOwner == seer -> GetGUID ())
return true ;
// Another private object of the same owner
if ( _privateObjectOwner == seer -> GetPrivateObjectOwner ())
return true ;
2021-03-28 18:20:57 +02:00
if ( Player const * playerSeer = seer -> ToPlayer ())
if ( playerSeer -> IsInGroup ( _privateObjectOwner ))
return true ;
2021-03-28 16:38:04 +02:00
return false ;
}
2022-01-29 19:44:55 +01:00
SmoothPhasing * WorldObject :: GetOrCreateSmoothPhasing ()
2022-01-29 19:44:55 +01:00
{
2022-01-29 19:44:55 +01:00
if ( ! _smoothPhasing )
_smoothPhasing = std :: make_unique < SmoothPhasing > ();
2022-01-29 19:44:55 +01:00
2022-01-29 19:44:55 +01:00
return _smoothPhasing . get ();
2022-01-29 19:44:55 +01:00
}
2023-03-12 17:12:36 +01:00
bool WorldObject :: CanSeeOrDetect ( WorldObject const * obj , bool implicitDetect , bool distanceCheck , bool checkAlert ) const
2010-11-13 17:18:09 +01:00
{
if ( this == obj )
return true ;
2023-03-12 17:12:36 +01:00
if ( obj -> IsNeverVisibleFor ( this , implicitDetect ) || CanNeverSee ( obj ))
2010-11-13 17:18:09 +01:00
return false ;
2011-10-13 17:01:55 -04:00
if ( obj -> IsAlwaysVisibleFor ( this ) || CanAlwaysSee ( obj ))
2010-11-13 17:18:09 +01:00
return true ;
2021-03-28 16:38:04 +02:00
if ( ! obj -> CheckPrivateObjectOwnerVisibility ( this ))
return false ;
2022-01-29 19:44:55 +01:00
if ( SmoothPhasing const * smoothPhasing = obj -> GetSmoothPhasing ())
if ( smoothPhasing -> IsBeingReplacedForSeer ( GetGUID ()))
return false ;
2022-01-29 19:44:55 +01:00
2022-12-30 17:57:58 +01:00
if ( ! obj -> IsPrivateObject () && ! sConditionMgr -> IsObjectMeetingVisibilityByObjectIdConditions ( obj -> GetTypeId (), obj -> GetEntry (), this ))
2022-01-12 21:54:03 +01:00
return false ;
2010-11-13 17:18:09 +01:00
bool corpseVisibility = false ;
if ( distanceCheck )
{
2012-08-30 22:44:33 +01:00
bool corpseCheck = false ;
2011-12-10 13:48:41 +01:00
if ( Player const * thisPlayer = ToPlayer ())
2010-11-13 17:18:09 +01:00
{
if ( thisPlayer -> isDead () && thisPlayer -> GetHealth () > 0 && // Cheap way to check for ghost state
! ( obj -> m_serverSideVisibility . GetValue ( SERVERSIDE_VISIBILITY_GHOST ) & m_serverSideVisibility . GetValue ( SERVERSIDE_VISIBILITY_GHOST ) & GHOST_VISIBILITY_GHOST ))
{
if ( Corpse * corpse = thisPlayer -> GetCorpse ())
{
corpseCheck = true ;
if ( corpse -> IsWithinDist ( thisPlayer , GetSightRange ( obj ), false ))
if ( corpse -> IsWithinDist ( obj , GetSightRange ( obj ), false ))
corpseVisibility = true ;
}
}
2017-01-11 12:30:22 +01:00
if ( Unit const * target = obj -> ToUnit ())
{
// Don't allow to detect vehicle accessories if you can't see vehicle
if ( Unit const * vehicle = target -> GetVehicleBase ())
if ( ! thisPlayer -> HaveAtClient ( vehicle ))
return false ;
}
2010-11-13 17:18:09 +01:00
}
2012-01-02 16:33:14 -03:00
WorldObject const * viewpoint = this ;
2020-09-04 13:38:24 +02:00
if ( Player const * player = ToPlayer ())
2012-01-02 16:33:14 -03:00
viewpoint = player -> GetViewpoint ();
2012-01-03 16:34:05 -03:00
if ( ! viewpoint )
viewpoint = this ;
2012-01-02 16:33:14 -03:00
if ( ! corpseCheck && ! viewpoint -> IsWithinDist ( obj , GetSightRange ( obj ), false ))
2010-11-13 17:18:09 +01:00
return false ;
}
// GM visibility off or hidden NPC
if ( ! obj -> m_serverSideVisibility . GetValue ( SERVERSIDE_VISIBILITY_GM ))
{
// Stop checking other things for GMs
if ( m_serverSideVisibilityDetect . GetValue ( SERVERSIDE_VISIBILITY_GM ))
return true ;
}
else
return m_serverSideVisibilityDetect . GetValue ( SERVERSIDE_VISIBILITY_GM ) >= obj -> m_serverSideVisibility . GetValue ( SERVERSIDE_VISIBILITY_GM );
// Ghost players, Spirit Healers, and some other NPCs
if ( ! corpseVisibility && ! ( obj -> m_serverSideVisibility . GetValue ( SERVERSIDE_VISIBILITY_GHOST ) & m_serverSideVisibilityDetect . GetValue ( SERVERSIDE_VISIBILITY_GHOST )))
{
// Alive players can see dead players in some cases, but other objects can't do that
2011-12-10 13:48:41 +01:00
if ( Player const * thisPlayer = ToPlayer ())
2010-11-13 17:18:09 +01:00
{
2011-12-10 13:48:41 +01:00
if ( Player const * objPlayer = obj -> ToPlayer ())
2010-11-13 17:18:09 +01:00
{
2022-04-14 23:31:17 +02:00
if ( ! thisPlayer -> IsGroupVisibleFor ( objPlayer ))
2010-11-13 17:18:09 +01:00
return false ;
}
else
return false ;
}
else
return false ;
}
2022-10-21 22:11:00 +02:00
if ( obj -> IsInvisibleDueToDespawn ( this ))
2010-11-13 17:18:09 +01:00
return false ;
2023-03-12 17:12:36 +01:00
if ( ! CanDetect ( obj , implicitDetect , checkAlert ))
2010-11-13 17:18:09 +01:00
return false ;
return true ;
}
2013-03-04 19:14:58 +01:00
bool WorldObject :: CanNeverSee ( WorldObject const * obj ) const
{
2022-12-25 00:44:12 +01:00
return GetMap () != obj -> GetMap () || ! InSamePhase ( obj );
2013-03-04 11:45:57 +01:00
}
2023-03-12 17:12:36 +01:00
bool WorldObject :: CanDetect ( WorldObject const * obj , bool implicitDetect , bool checkAlert ) const
2010-11-13 17:18:09 +01:00
{
2020-09-04 13:38:24 +02:00
WorldObject const * seer = this ;
2010-11-13 17:18:09 +01:00
2021-08-08 13:22:04 +02:00
// If a unit is possessing another one, it uses the detection of the latter
2010-11-13 17:18:09 +01:00
// Pets don't have detection, they use the detection of their masters
2013-03-25 10:36:30 +01:00
if ( Unit const * thisUnit = ToUnit ())
2021-08-08 13:22:04 +02:00
{
if ( thisUnit -> isPossessing ())
{
if ( Unit * charmed = thisUnit -> GetCharmed ())
seer = charmed ;
}
else if ( Unit * controller = thisUnit -> GetCharmerOrOwner ())
2010-11-13 17:18:09 +01:00
seer = controller ;
2021-08-08 13:22:04 +02:00
}
2010-11-13 17:18:09 +01:00
2011-10-13 14:02:07 -04:00
if ( obj -> IsAlwaysDetectableFor ( seer ))
2010-11-13 17:18:09 +01:00
return true ;
2023-03-12 17:12:36 +01:00
if ( ! implicitDetect && ! seer -> CanDetectInvisibilityOf ( obj ))
2010-11-13 17:18:09 +01:00
return false ;
2023-03-12 17:12:36 +01:00
if ( ! implicitDetect && ! seer -> CanDetectStealthOf ( obj , checkAlert ))
2010-11-13 17:18:09 +01:00
return false ;
return true ;
}
2011-10-13 17:01:55 -04:00
bool WorldObject :: CanDetectInvisibilityOf ( WorldObject const * obj ) const
2010-11-13 17:18:09 +01:00
{
2022-10-07 17:08:20 +03:00
uint64 mask = obj -> m_invisibility . GetFlags () & m_invisibilityDetect . GetFlags ();
2010-11-13 17:18:09 +01:00
// Check for not detected types
if ( mask != obj -> m_invisibility . GetFlags ())
return false ;
for ( uint32 i = 0 ; i < TOTAL_INVISIBILITY_TYPES ; ++ i )
{
2022-10-07 17:08:20 +03:00
if ( ! ( mask & ( uint64 ( 1 ) << i )))
2010-11-13 17:18:09 +01:00
continue ;
int32 objInvisibilityValue = obj -> m_invisibility . GetValue ( InvisibilityType ( i ));
int32 ownInvisibilityDetectValue = m_invisibilityDetect . GetValue ( InvisibilityType ( i ));
// Too low value to detect
if ( ownInvisibilityDetectValue < objInvisibilityValue )
return false ;
}
return true ;
}
2015-07-15 18:43:19 +02:00
bool WorldObject :: CanDetectStealthOf ( WorldObject const * obj , bool checkAlert ) const
2010-11-13 17:18:09 +01:00
{
// Combat reach is the minimal distance (both in front and behind),
// and it is also used in the range calculation.
// One stealth point increases the visibility range by 0.3 yard.
if ( ! obj -> m_stealth . GetFlags ())
return true ;
float distance = GetExactDist ( obj );
float combatReach = 0.0f ;
2013-03-25 10:36:30 +01:00
Unit const * unit = ToUnit ();
if ( unit )
combatReach = unit -> GetCombatReach ();
2010-11-13 17:18:09 +01:00
if ( distance < combatReach )
return true ;
2020-04-12 15:28:01 +02:00
// Only check back for units, it does not make sense for gameobjects
if ( unit && ! HasInArc ( float ( M_PI ), obj ))
2010-11-13 17:18:09 +01:00
return false ;
2020-04-12 15:28:01 +02:00
// Traps should detect stealth always
if ( GameObject const * go = ToGameObject ())
if ( go -> GetGoType () == GAMEOBJECT_TYPE_TRAP )
return true ;
2017-01-13 17:27:53 +04:00
GameObject const * go = obj -> ToGameObject ();
2010-11-13 17:18:09 +01:00
for ( uint32 i = 0 ; i < TOTAL_STEALTH_TYPES ; ++ i )
{
if ( ! ( obj -> m_stealth . GetFlags () & ( 1 << i )))
continue ;
2013-03-25 10:36:30 +01:00
if ( unit && unit -> HasAuraTypeWithMiscvalue ( SPELL_AURA_DETECT_STEALTH , i ))
return true ;
2010-11-13 17:18:09 +01:00
// Starting points
int32 detectionValue = 30 ;
// Level difference: 5 point / level, starting from level 1.
// There may be spells for this and the starting points too, but
2012-01-08 18:22:46 +01:00
// not in the DBCs of the client.
2017-07-29 21:40:28 +02:00
detectionValue += int32 ( GetLevelForTarget ( obj ) - 1 ) * 5 ;
2010-11-13 17:18:09 +01:00
// Apply modifiers
detectionValue += m_stealthDetect . GetValue ( StealthType ( i ));
2013-03-25 10:36:30 +01:00
if ( go )
if ( Unit * owner = go -> GetOwner ())
2017-07-29 21:40:28 +02:00
detectionValue -= int32 ( owner -> GetLevelForTarget ( this ) - 1 ) * 5 ;
2012-01-08 18:22:46 +01:00
2010-11-13 17:18:09 +01:00
detectionValue -= obj -> m_stealth . GetValue ( StealthType ( i ));
// Calculate max distance
float visibilityRange = float ( detectionValue ) * 0.3f + combatReach ;
2015-07-15 18:43:19 +02:00
// If this unit is an NPC then player detect range doesn't apply
if ( unit && unit -> GetTypeId () == TYPEID_PLAYER && visibilityRange > MAX_PLAYER_STEALTH_DETECT_RANGE )
2010-11-13 17:18:09 +01:00
visibilityRange = MAX_PLAYER_STEALTH_DETECT_RANGE ;
2015-07-15 18:43:19 +02:00
// When checking for alert state, look 8% further, and then 1.5 yards more than that.
if ( checkAlert )
visibilityRange += ( visibilityRange * 0.08f ) + 1.5f ;
// If checking for alert, and creature's visibility range is greater than aggro distance, No alert
Unit const * tunit = obj -> ToUnit ();
if ( checkAlert && unit && unit -> ToCreature () && visibilityRange >= unit -> ToCreature () -> GetAttackDistance ( tunit ) + unit -> ToCreature () -> m_CombatDistance )
return false ;
2010-11-13 17:18:09 +01:00
if ( distance > visibilityRange )
return false ;
}
return true ;
}
2017-07-26 00:53:25 +02:00
void WorldObject :: SendMessageToSet ( WorldPacket const * data , bool self ) const
2011-08-30 13:46:36 +02:00
{
2012-01-14 15:34:41 +01:00
if ( IsInWorld ())
SendMessageToSetInRange ( data , GetVisibilityRange (), self );
2011-08-30 13:46:36 +02:00
}
2017-07-26 00:53:25 +02:00
void WorldObject :: SendMessageToSetInRange ( WorldPacket const * data , float dist , bool /*self*/ ) const
2008-10-11 14:16:25 -05:00
{
2021-05-01 20:16:10 +02:00
Trinity :: PacketSenderRef sender ( data );
Trinity :: MessageDistDeliverer < Trinity :: PacketSenderRef > notifier ( this , sender , dist );
2017-05-13 19:37:33 +02:00
Cell :: VisitWorldObjects ( this , notifier , dist );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-07-26 00:53:25 +02:00
void WorldObject :: SendMessageToSet ( WorldPacket const * data , Player const * skipped_rcvr ) const
2010-04-04 03:46:38 +03:00
{
2021-05-01 20:16:10 +02:00
Trinity :: PacketSenderRef sender ( data );
Trinity :: MessageDistDeliverer < Trinity :: PacketSenderRef > notifier ( this , sender , GetVisibilityRange (), false , skipped_rcvr );
2017-05-13 19:37:33 +02:00
Cell :: VisitWorldObjects ( this , notifier , GetVisibilityRange ());
2010-04-04 03:46:38 +03:00
}
2018-02-10 16:43:01 -03:00
struct CombatLogSender
{
WorldPackets :: CombatLog :: CombatLogServerPacket const * i_message ;
explicit CombatLogSender ( WorldPackets :: CombatLog :: CombatLogServerPacket * msg )
: i_message ( msg )
{
msg -> Write ();
}
void operator ()( Player const * player ) const
{
if ( player -> IsAdvancedCombatLoggingEnabled ())
player -> SendDirectMessage ( i_message -> GetFullLogPacket ());
else
player -> SendDirectMessage ( i_message -> GetBasicLogPacket ());
}
};
void WorldObject :: SendCombatLogMessage ( WorldPackets :: CombatLog :: CombatLogServerPacket * combatLog ) const
{
CombatLogSender combatLogSender ( combatLog );
if ( Player const * self = ToPlayer ())
combatLogSender ( self );
Trinity :: MessageDistDeliverer < CombatLogSender > notifier ( this , combatLogSender , GetVisibilityRange ());
Cell :: VisitWorldObjects ( this , notifier , GetVisibilityRange ());
}
2011-09-15 14:08:17 +02:00
void WorldObject :: SetMap ( Map * map )
2009-03-24 09:06:49 -06:00
{
2009-07-16 11:49:00 +08:00
ASSERT ( map );
2015-09-28 17:20:27 +02:00
ASSERT ( ! IsInWorld ());
2010-04-07 19:14:10 +02:00
if ( m_currMap == map ) // command add npc: first create, than loadfromdb
2009-08-12 13:03:44 -05:00
return ;
2010-04-07 19:14:10 +02:00
if ( m_currMap )
2009-08-26 19:35:20 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_FATAL ( "misc" , "WorldObject::SetMap: obj {} new map {} {}, old map {} {}" , ( uint32 ) GetTypeId (), map -> GetId (), map -> GetInstanceId (), m_currMap -> GetId (), m_currMap -> GetInstanceId ());
2015-09-21 15:17:05 +02:00
ABORT ();
2009-08-26 19:35:20 -05:00
}
2009-07-16 11:49:00 +08:00
m_currMap = map ;
2009-08-25 11:42:18 -05:00
m_mapId = map -> GetId ();
m_InstanceId = map -> GetInstanceId ();
2024-03-14 11:01:58 +01:00
if ( IsStoredInWorldObjectGridContainer ())
2009-08-11 16:33:33 -05:00
m_currMap -> AddWorldObject ( this );
2009-03-24 09:06:49 -06:00
}
2009-10-17 15:51:44 -07:00
2009-08-10 23:47:47 -05:00
void WorldObject :: ResetMap ()
{
ASSERT ( m_currMap );
2009-08-12 23:12:33 -05:00
ASSERT ( ! IsInWorld ());
2024-03-14 11:01:58 +01:00
if ( IsStoredInWorldObjectGridContainer ())
2009-08-11 16:33:33 -05:00
m_currMap -> RemoveWorldObject ( this );
2020-08-14 17:06:03 +02:00
m_currMap = nullptr ;
2009-08-25 19:22:45 -05:00
//maybe not for corpse
//m_mapId = 0;
//m_InstanceId = 0;
2009-08-10 23:47:47 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void WorldObject :: AddObjectToRemoveList ()
{
2009-04-08 17:23:57 -05:00
Map * map = FindMap ();
2010-04-07 19:14:10 +02:00
if ( ! map )
2008-10-11 14:16:25 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "misc" , "Object {} at attempt add to move list not have valid map (Id: {})." , GetGUID (). ToString (), GetMapId ());
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
map -> AddObjectToRemoveList ( this );
}
2009-10-17 15:51:44 -07:00
2023-08-09 11:37:17 +02:00
TempSummon * Map :: SummonCreature ( uint32 entry , Position const & pos , SummonPropertiesEntry const * properties /*= nullptr*/ , Milliseconds duration /*= 0ms*/ , WorldObject * summoner /*= nullptr*/ , uint32 spellId /*= 0*/ , uint32 vehId /*= 0*/ , ObjectGuid privateObjectOwner /*= ObjectGuid::Empty*/ , SmoothPhasingInfo const * smoothPhasingInfo /* = nullptr*/ )
2008-10-11 14:16:25 -05:00
{
2009-08-23 22:09:43 -05:00
uint32 mask = UNIT_MASK_SUMMON ;
2010-04-07 19:14:10 +02:00
if ( properties )
2009-03-20 14:01:46 -06:00
{
2018-03-12 18:20:26 +01:00
switch ( properties -> Control )
2009-08-26 19:28:21 -05:00
{
2011-05-19 22:27:18 +02:00
case SUMMON_CATEGORY_PET :
mask = UNIT_MASK_GUARDIAN ;
break ;
case SUMMON_CATEGORY_PUPPET :
mask = UNIT_MASK_PUPPET ;
break ;
case SUMMON_CATEGORY_VEHICLE :
mask = UNIT_MASK_MINION ;
break ;
case SUMMON_CATEGORY_WILD :
case SUMMON_CATEGORY_ALLY :
case SUMMON_CATEGORY_UNK :
{
2020-04-07 17:27:40 +02:00
switch ( SummonTitle ( properties -> Title ))
2009-08-26 19:28:21 -05:00
{
2020-04-07 17:27:40 +02:00
case SummonTitle :: Minion :
case SummonTitle :: Guardian :
case SummonTitle :: Runeblade :
2011-05-19 22:27:18 +02:00
mask = UNIT_MASK_GUARDIAN ;
break ;
2020-04-07 17:27:40 +02:00
case SummonTitle :: Totem :
case SummonTitle :: Lightwell :
2011-05-19 22:27:18 +02:00
mask = UNIT_MASK_TOTEM ;
break ;
2020-04-07 17:27:40 +02:00
case SummonTitle :: Vehicle :
case SummonTitle :: Mount :
2011-05-19 22:27:18 +02:00
mask = UNIT_MASK_SUMMON ;
break ;
2020-04-07 17:27:40 +02:00
case SummonTitle :: Companion :
2011-05-19 22:27:18 +02:00
mask = UNIT_MASK_MINION ;
break ;
default :
2021-11-16 11:51:05 +01:00
if ( properties -> GetFlags (). HasFlag ( SummonPropertiesFlags :: JoinSummonerSpawnGroup ))
2011-05-19 22:27:18 +02:00
mask = UNIT_MASK_GUARDIAN ;
break ;
2009-08-26 19:28:21 -05:00
}
break ;
2011-05-19 22:27:18 +02:00
}
default :
2018-01-27 21:47:37 +01:00
return nullptr ;
2009-08-26 19:28:21 -05:00
}
2009-03-20 14:01:46 -06:00
}
2009-10-17 15:51:44 -07:00
2019-05-23 21:08:29 +02:00
Unit * summonerUnit = summoner ? summoner -> ToUnit () : nullptr ;
2018-01-27 21:47:37 +01:00
TempSummon * summon = nullptr ;
2011-05-19 22:27:18 +02:00
switch ( mask )
{
case UNIT_MASK_SUMMON :
2011-12-20 17:05:08 -05:00
summon = new TempSummon ( properties , summoner , false );
2011-05-19 22:27:18 +02:00
break ;
case UNIT_MASK_GUARDIAN :
2019-05-23 21:08:29 +02:00
summon = new Guardian ( properties , summonerUnit , false );
2011-05-19 22:27:18 +02:00
break ;
case UNIT_MASK_PUPPET :
2019-05-23 21:08:29 +02:00
summon = new Puppet ( properties , summonerUnit );
2011-05-19 22:27:18 +02:00
break ;
case UNIT_MASK_TOTEM :
2019-05-23 21:08:29 +02:00
summon = new Totem ( properties , summonerUnit );
2011-05-19 22:27:18 +02:00
break ;
case UNIT_MASK_MINION :
2019-05-23 21:08:29 +02:00
summon = new Minion ( properties , summonerUnit , false );
2011-05-19 22:27:18 +02:00
break ;
2009-03-20 14:01:46 -06:00
}
2009-10-17 15:51:44 -07:00
2017-08-21 17:40:47 +02:00
if ( ! summon -> Create ( GenerateLowGuid < HighGuid :: Creature > (), this , entry , pos , nullptr , vehId , true ))
2008-10-11 14:16:25 -05:00
{
2009-03-20 14:01:46 -06:00
delete summon ;
2018-01-27 21:47:37 +01:00
return nullptr ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2022-05-25 22:14:32 +02:00
TransportBase * transport = summoner ? summoner -> GetTransport () : nullptr ;
2022-02-07 12:49:45 +01:00
if ( transport )
{
float x , y , z , o ;
pos . GetPosition ( x , y , z , o );
transport -> CalculatePassengerOffset ( x , y , z , & o );
summon -> m_movementInfo . transport . pos . Relocate ( x , y , z , o );
// This object must be added to transport before adding to map for the client to properly display it
transport -> AddPassenger ( summon );
}
2021-11-16 11:51:05 +01:00
if ( summoner && ! ( properties && properties -> GetFlags (). HasFlag ( SummonPropertiesFlags :: IgnoreSummonerPhase )))
2018-01-28 11:37:20 +01:00
PhasingHandler :: InheritPhaseShift ( summon , summoner );
2014-06-01 22:27:29 -05:00
2019-06-03 20:40:34 +02:00
summon -> SetCreatedBySpell ( spellId );
2011-05-19 22:27:18 +02:00
2009-08-29 14:58:45 -05:00
summon -> SetHomePosition ( pos );
2009-10-17 15:51:44 -07:00
2023-05-27 18:43:43 +02:00
summon -> InitStats ( summoner , duration );
2018-04-08 20:35:33 +02:00
2021-03-28 16:52:07 +02:00
summon -> SetPrivateObjectOwner ( privateObjectOwner );
2018-04-08 20:35:33 +02:00
2022-01-29 19:44:55 +01:00
if ( smoothPhasingInfo )
{
if ( summoner && smoothPhasingInfo -> ReplaceObject )
{
2022-02-08 00:03:02 +01:00
if ( WorldObject * replacedObject = ObjectAccessor :: GetWorldObject ( * summoner , * smoothPhasingInfo -> ReplaceObject ))
{
SmoothPhasingInfo originalSmoothPhasingInfo = * smoothPhasingInfo ;
originalSmoothPhasingInfo . ReplaceObject = summon -> GetGUID ();
replacedObject -> GetOrCreateSmoothPhasing () -> SetViewerDependentInfo ( privateObjectOwner , originalSmoothPhasingInfo );
2022-01-29 19:44:55 +01:00
2022-02-08 00:03:02 +01:00
summon -> SetDemonCreatorGUID ( privateObjectOwner );
}
2022-01-29 19:44:55 +01:00
}
summon -> GetOrCreateSmoothPhasing () -> SetSingleInfo ( * smoothPhasingInfo );
}
2022-01-29 19:44:55 +01:00
if ( ! AddToMap ( summon -> ToCreature ()))
{
2022-02-07 12:49:45 +01:00
// Returning false will cause the object to be deleted - remove from transport
if ( transport )
transport -> RemovePassenger ( summon );
2022-01-29 19:44:55 +01:00
delete summon ;
return nullptr ;
}
2023-05-27 18:43:43 +02:00
summon -> InitSummon ( summoner );
2009-10-17 15:51:44 -07:00
2016-03-02 23:54:37 +01:00
// call MoveInLineOfSight for nearby creatures
Trinity :: AIRelocationNotifier notifier ( * summon );
2017-05-13 19:37:33 +02:00
Cell :: VisitAllObjects ( summon , notifier , GetVisibilityRange ());
2009-10-17 15:51:44 -07:00
2009-03-20 14:01:46 -06:00
return summon ;
}
2009-10-17 15:51:44 -07:00
2013-02-23 01:50:52 +01:00
/**
* Summons group of creatures.
*
* @param group Id of group to summon.
* @param list List to store pointers to summoned creatures.
*/
2020-08-14 17:06:03 +02:00
void Map :: SummonCreatureGroup ( uint8 group , std :: list < TempSummon *>* list /*= nullptr*/ )
2013-02-23 01:50:52 +01:00
{
std :: vector < TempSummonData > const * data = sObjectMgr -> GetSummonGroup ( GetId (), SUMMONER_TYPE_MAP , group );
if ( ! data )
return ;
for ( std :: vector < TempSummonData >:: const_iterator itr = data -> begin (); itr != data -> end (); ++ itr )
2020-08-14 17:06:03 +02:00
if ( TempSummon * summon = SummonCreature ( itr -> entry , itr -> pos , nullptr , itr -> time ))
2013-02-26 02:33:08 +01:00
if ( list )
list -> push_back ( summon );
2013-02-23 01:50:52 +01:00
}
2022-04-24 00:16:13 +02:00
ZoneScript * WorldObject :: FindZoneScript () const
2009-05-30 22:15:05 -05:00
{
2011-09-15 14:08:17 +02:00
if ( Map * map = FindMap ())
2009-05-30 22:15:05 -05:00
{
2022-03-22 05:40:31 -05:00
if ( InstanceMap * instanceMap = map -> ToInstanceMap ())
2022-04-24 00:16:13 +02:00
return reinterpret_cast < ZoneScript *> ( instanceMap -> GetInstanceScript ());
2022-12-20 01:03:58 +01:00
if ( BattlegroundMap * bgMap = map -> ToBattlegroundMap ())
2024-03-28 19:29:22 +01:00
return reinterpret_cast < ZoneScript *> ( bgMap -> GetBattlegroundScript ());
2022-12-20 01:03:58 +01:00
if ( ! map -> IsBattlegroundOrArena ())
2012-03-02 22:00:26 -03:00
{
2022-07-19 23:18:19 +02:00
if ( Battlefield * bf = sBattlefieldMgr -> GetBattlefieldToZoneId ( map , GetZoneId ()))
2022-04-24 00:16:13 +02:00
return bf ;
2022-12-20 01:03:58 +01:00
return sOutdoorPvPMgr -> GetOutdoorPvPToZoneId ( map , GetZoneId ());
2012-03-02 22:00:26 -03:00
}
2009-05-30 22:15:05 -05:00
}
2022-04-24 00:16:13 +02:00
return nullptr ;
}
void WorldObject :: SetZoneScript ()
{
m_zoneScript = FindZoneScript ();
2009-05-30 22:15:05 -05:00
}
2009-10-17 15:51:44 -07:00
2016-11-06 23:30:49 +01:00
Scenario * WorldObject :: GetScenario () const
{
if ( IsInWorld ())
if ( InstanceMap * instanceMap = GetMap () -> ToInstanceMap ())
return instanceMap -> GetInstanceScenario ();
return nullptr ;
}
2020-07-26 20:44:07 +02:00
TempSummon * WorldObject :: SummonCreature ( uint32 entry , Position const & pos , TempSummonType despawnType /*= TEMPSUMMON_MANUAL_DESPAWN*/ , Milliseconds despawnTime /*= 0s*/ , uint32 vehId /*= 0*/ , uint32 spellId /*= 0*/ , ObjectGuid privateObjectOwner /* = ObjectGuid::Empty */ )
2009-03-20 14:01:46 -06:00
{
2011-09-15 14:08:17 +02:00
if ( Map * map = FindMap ())
2009-08-29 14:58:45 -05:00
{
2023-08-09 11:37:17 +02:00
if ( TempSummon * summon = map -> SummonCreature ( entry , pos , nullptr , despawnTime , this , spellId , vehId , privateObjectOwner ))
2009-08-29 14:58:45 -05:00
{
2017-11-30 20:45:44 +01:00
summon -> SetTempSummonType ( despawnType );
2009-08-29 14:58:45 -05:00
return summon ;
}
}
2009-10-17 15:51:44 -07:00
2016-07-30 18:50:44 -03:00
return nullptr ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2020-07-26 20:44:07 +02:00
TempSummon * WorldObject :: SummonCreature ( uint32 id , float x , float y , float z , float o /*= 0*/ , TempSummonType despawnType /*= TEMPSUMMON_MANUAL_DESPAWN*/ , Milliseconds despawnTime /*= 0s*/ , ObjectGuid privateObjectOwner /* = ObjectGuid::Empty */ )
2013-03-04 11:45:57 +01:00
{
if ( ! x && ! y && ! z )
2017-03-23 00:43:04 +01:00
GetClosePoint ( x , y , z , GetCombatReach ());
2017-11-30 20:45:44 +01:00
if ( ! o )
o = GetOrientation ();
2018-04-03 08:28:41 -03:00
return SummonCreature ( id , { x , y , z , o }, despawnType , despawnTime , 0 , 0 , privateObjectOwner );
2013-03-04 11:45:57 +01:00
}
2022-02-08 00:03:02 +01:00
TempSummon * WorldObject :: SummonPersonalClone ( Position const & pos , TempSummonType despawnType /*= TEMPSUMMON_MANUAL_DESPAWN*/ , Milliseconds despawnTime /*= 0s*/ , uint32 vehId /*= 0*/ , uint32 spellId /*= 0*/ , Player * privateObjectOwner /*= nullptr*/ )
2022-01-18 20:51:23 +01:00
{
2022-02-08 00:03:02 +01:00
ASSERT ( privateObjectOwner );
2022-01-18 20:51:23 +01:00
if ( Map * map = FindMap ())
{
2022-01-29 19:44:55 +01:00
SmoothPhasingInfo smoothPhasingInfo { GetGUID (), true , true };
2023-08-09 11:37:17 +02:00
if ( TempSummon * summon = map -> SummonCreature ( GetEntry (), pos , nullptr , despawnTime , privateObjectOwner , spellId , vehId , privateObjectOwner -> GetGUID (), & smoothPhasingInfo ))
2022-01-18 20:51:23 +01:00
{
summon -> SetTempSummonType ( despawnType );
2024-04-10 20:03:14 +02:00
if ( Creature * thisCreature = ToCreature ())
summon -> InheritStringIds ( thisCreature );
2022-01-18 20:51:23 +01:00
return summon ;
}
}
return nullptr ;
}
2020-07-27 00:25:18 +02:00
GameObject * WorldObject :: SummonGameObject ( uint32 entry , Position const & pos , QuaternionData const & rot , Seconds respawnTime , GOSummonType summonType )
2008-10-17 18:13:04 -05:00
{
2010-04-07 19:14:10 +02:00
if ( ! IsInWorld ())
2016-07-30 18:50:44 -03:00
return nullptr ;
2009-10-17 15:51:44 -07:00
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * goinfo = sObjectMgr -> GetGameObjectTemplate ( entry );
2010-04-07 19:14:10 +02:00
if ( ! goinfo )
2008-10-17 18:13:04 -05:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "sql.sql" , "Gameobject template {} not found in database!" , entry );
2016-07-30 18:50:44 -03:00
return nullptr ;
2008-10-17 18:13:04 -05:00
}
2012-12-31 20:43:14 +01:00
2011-09-15 14:08:17 +02:00
Map * map = GetMap ();
2018-01-27 21:47:37 +01:00
GameObject * go = GameObject :: CreateGameObject ( entry , map , pos , rot , 255 , GO_STATE_READY );
if ( ! go )
2016-07-30 18:50:44 -03:00
return nullptr ;
2012-12-31 20:43:14 +01:00
2018-01-28 11:37:20 +01:00
PhasingHandler :: InheritPhaseShift ( go , this );
2014-06-01 22:27:29 -05:00
2020-07-27 00:25:18 +02:00
go -> SetRespawnTime ( respawnTime . count ());
2018-02-16 02:19:19 +01:00
if ( GetTypeId () == TYPEID_PLAYER || ( GetTypeId () == TYPEID_UNIT && summonType == GO_SUMMON_TIMED_OR_CORPSE_DESPAWN )) //not sure how to handle this
2012-12-31 20:43:14 +01:00
ToUnit () -> AddGameObject ( go );
2008-10-18 19:59:34 -05:00
else
go -> SetSpawnedByDefault ( false );
2009-10-17 15:51:44 -07:00
2012-12-31 20:43:14 +01:00
map -> AddToMap ( go );
2008-10-17 18:13:04 -05:00
return go ;
}
2009-10-17 15:51:44 -07:00
2020-07-27 00:25:18 +02:00
GameObject * WorldObject :: SummonGameObject ( uint32 entry , float x , float y , float z , float ang , QuaternionData const & rot , Seconds respawnTime , GOSummonType summonType )
2016-07-30 18:50:44 -03:00
{
if ( ! x && ! y && ! z )
{
2017-03-23 00:43:04 +01:00
GetClosePoint ( x , y , z , GetCombatReach ());
2016-07-30 18:50:44 -03:00
ang = GetOrientation ();
}
Position pos ( x , y , z , ang );
2020-05-19 12:40:50 +03:00
return SummonGameObject ( entry , pos , rot , respawnTime , summonType );
2016-07-30 18:50:44 -03:00
}
2020-07-27 00:41:52 +02:00
Creature * WorldObject :: SummonTrigger ( float x , float y , float z , float ang , Milliseconds despawnTime , CreatureAI * ( * GetAI )( Creature * ))
2008-12-10 20:25:47 -06:00
{
2020-07-27 00:41:52 +02:00
TempSummonType summonType = ( despawnTime == 0 s ) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN ;
Creature * summon = SummonCreature ( WORLD_TRIGGER , x , y , z , ang , summonType , despawnTime );
2010-04-07 19:14:10 +02:00
if ( ! summon )
2017-02-17 21:33:18 +01:00
return nullptr ;
2009-10-17 15:51:44 -07:00
2008-12-10 20:25:47 -06:00
//summon->SetName(GetName());
2010-04-07 19:14:10 +02:00
if ( GetTypeId () == TYPEID_PLAYER || GetTypeId () == TYPEID_UNIT )
2008-12-10 20:25:47 -06:00
{
2017-04-28 02:24:04 -03:00
summon -> SetFaction ((( Unit * ) this ) -> GetFaction ());
2019-07-01 21:36:32 +02:00
summon -> SetLevel ((( Unit * ) this ) -> GetLevel ());
2008-12-10 20:25:47 -06:00
}
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( GetAI )
2008-12-10 20:25:47 -06:00
summon -> AIM_Initialize ( GetAI ( summon ));
return summon ;
}
2009-10-17 15:51:44 -07:00
2013-02-23 01:50:52 +01:00
/**
* Summons group of creatures. Should be called only by instances of Creature and GameObject classes.
*
* @param group Id of group to summon.
* @param list List to store pointers to summoned creatures.
*/
2020-08-14 17:06:03 +02:00
void WorldObject :: SummonCreatureGroup ( uint8 group , std :: list < TempSummon *>* list /*= nullptr*/ )
2013-02-23 01:50:52 +01:00
{
ASSERT (( GetTypeId () == TYPEID_GAMEOBJECT || GetTypeId () == TYPEID_UNIT ) && "Only GOs and creatures can summon npc groups!" );
std :: vector < TempSummonData > const * data = sObjectMgr -> GetSummonGroup ( GetEntry (), GetTypeId () == TYPEID_GAMEOBJECT ? SUMMONER_TYPE_GAMEOBJECT : SUMMONER_TYPE_CREATURE , group );
if ( ! data )
2016-05-14 18:00:07 +02:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_WARN ( "scripts" , "{} ({}) tried to summon non-existing summon group {}." , GetName (), GetGUID (). ToString (), group );
2013-02-23 01:50:52 +01:00
return ;
2016-05-14 18:00:07 +02:00
}
2013-02-23 01:50:52 +01:00
for ( std :: vector < TempSummonData >:: const_iterator itr = data -> begin (); itr != data -> end (); ++ itr )
2020-07-26 20:44:07 +02:00
if ( TempSummon * summon = SummonCreature ( itr -> entry , itr -> pos , itr -> type , Milliseconds ( itr -> time )))
2013-02-26 02:33:08 +01:00
if ( list )
list -> push_back ( summon );
2013-02-23 01:50:52 +01:00
}
2011-08-30 13:46:36 +02:00
Creature * WorldObject :: FindNearestCreature ( uint32 entry , float range , bool alive ) const
2009-05-29 16:49:28 -05:00
{
2017-02-17 21:33:18 +01:00
Creature * creature = nullptr ;
2009-12-17 11:40:23 +01:00
Trinity :: NearestCreatureEntryWithLiveStateInObjectRangeCheck checker ( * this , entry , alive , range );
Trinity :: CreatureLastSearcher < Trinity :: NearestCreatureEntryWithLiveStateInObjectRangeCheck > searcher ( this , creature , checker );
2022-10-30 22:29:03 +01:00
Cell :: VisitAllObjects ( this , searcher , range );
return creature ;
}
2022-12-26 20:13:13 +01:00
Creature * WorldObject :: FindNearestCreatureWithOptions ( float range , FindCreatureOptions const & options ) const
2022-10-30 22:29:03 +01:00
{
Creature * creature = nullptr ;
2022-12-27 16:39:05 +01:00
Trinity :: NearestCheckCustomizer checkCustomizer ( * this , range );
Trinity :: CreatureWithOptionsInObjectRangeCheck checker ( * this , checkCustomizer , options );
Trinity :: CreatureLastSearcher searcher ( this , creature , checker );
2022-12-26 20:13:13 +01:00
if ( options . IgnorePhases )
searcher . i_phaseShift = & PhasingHandler :: GetAlwaysVisiblePhaseShift ();
2017-05-13 19:37:33 +02:00
Cell :: VisitAllObjects ( this , searcher , range );
2009-12-17 11:40:23 +01:00
return creature ;
2009-05-29 16:49:28 -05:00
}
2009-10-17 15:51:44 -07:00
2020-09-04 22:01:25 +02:00
GameObject * WorldObject :: FindNearestGameObject ( uint32 entry , float range , bool spawnedOnly ) const
2009-05-29 16:49:28 -05:00
{
2017-02-17 21:33:18 +01:00
GameObject * go = nullptr ;
2020-09-04 22:01:25 +02:00
Trinity :: NearestGameObjectEntryInObjectRangeCheck checker ( * this , entry , range , spawnedOnly );
2009-12-17 11:40:23 +01:00
Trinity :: GameObjectLastSearcher < Trinity :: NearestGameObjectEntryInObjectRangeCheck > searcher ( this , go , checker );
2020-04-26 21:32:24 +02:00
Cell :: VisitGridObjects ( this , searcher , range );
return go ;
}
2023-07-30 13:55:34 +02:00
GameObject * WorldObject :: FindNearestGameObjectWithOptions ( float range , FindGameObjectOptions const & options ) const
{
GameObject * go = nullptr ;
Trinity :: NearestCheckCustomizer checkCustomizer ( * this , range );
Trinity :: GameObjectWithOptionsInObjectRangeCheck checker ( * this , checkCustomizer , options );
Trinity :: GameObjectLastSearcher searcher ( this , go , checker );
if ( options . IgnorePhases )
searcher . i_phaseShift = & PhasingHandler :: GetAlwaysVisiblePhaseShift ();
Cell :: VisitGridObjects ( this , searcher , range );
return go ;
}
2020-04-26 21:32:24 +02:00
GameObject * WorldObject :: FindNearestUnspawnedGameObject ( uint32 entry , float range ) const
{
GameObject * go = nullptr ;
Trinity :: NearestUnspawnedGameObjectEntryInObjectRangeCheck checker ( * this , entry , range );
Trinity :: GameObjectLastSearcher < Trinity :: NearestUnspawnedGameObjectEntryInObjectRangeCheck > searcher ( this , go , checker );
2017-05-13 19:37:33 +02:00
Cell :: VisitGridObjects ( this , searcher , range );
2009-12-17 11:40:23 +01:00
return go ;
}
2012-09-08 23:15:29 +01:00
GameObject * WorldObject :: FindNearestGameObjectOfType ( GameobjectTypes type , float range ) const
{
2017-02-17 21:33:18 +01:00
GameObject * go = nullptr ;
2012-09-08 23:15:29 +01:00
Trinity :: NearestGameObjectTypeInObjectRangeCheck checker ( * this , type , range );
Trinity :: GameObjectLastSearcher < Trinity :: NearestGameObjectTypeInObjectRangeCheck > searcher ( this , go , checker );
2017-05-13 19:37:33 +02:00
Cell :: VisitGridObjects ( this , searcher , range );
2012-09-08 23:15:29 +01:00
return go ;
}
2023-11-13 19:01:11 +01:00
Player * WorldObject :: SelectNearestPlayer ( float range ) const
2017-02-17 21:33:18 +01:00
{
Player * target = nullptr ;
2023-11-13 19:01:11 +01:00
Trinity :: NearestPlayerInObjectRangeCheck checker ( this , range );
2017-02-17 21:33:18 +01:00
Trinity :: PlayerLastSearcher < Trinity :: NearestPlayerInObjectRangeCheck > searcher ( this , target , checker );
2023-11-13 19:01:11 +01:00
Cell :: VisitWorldObjects ( this , searcher , range );
2017-02-17 21:33:18 +01:00
return target ;
}
2018-02-10 16:43:01 -03:00
ObjectGuid WorldObject :: GetCharmerOrOwnerOrOwnGUID () const
{
ObjectGuid guid = GetCharmerOrOwnerGUID ();
if ( ! guid . IsEmpty ())
return guid ;
return GetGUID ();
}
Unit * WorldObject :: GetOwner () const
{
return ObjectAccessor :: GetUnit ( * this , GetOwnerGUID ());
}
Unit * WorldObject :: GetCharmerOrOwner () const
{
if ( Unit const * unit = ToUnit ())
return unit -> GetCharmerOrOwner ();
else if ( GameObject const * go = ToGameObject ())
return go -> GetOwner ();
return nullptr ;
}
Unit * WorldObject :: GetCharmerOrOwnerOrSelf () const
{
if ( Unit * u = GetCharmerOrOwner ())
return u ;
return const_cast < WorldObject *> ( this ) -> ToUnit ();
}
Player * WorldObject :: GetCharmerOrOwnerPlayerOrPlayerItself () const
{
ObjectGuid guid = GetCharmerOrOwnerGUID ();
if ( guid . IsPlayer ())
return ObjectAccessor :: GetPlayer ( * this , guid );
return const_cast < WorldObject *> ( this ) -> ToPlayer ();
}
Player * WorldObject :: GetAffectingPlayer () const
{
if ( ! GetCharmerOrOwnerGUID ())
return const_cast < WorldObject *> ( this ) -> ToPlayer ();
if ( Unit * owner = GetCharmerOrOwner ())
return owner -> GetCharmerOrOwnerPlayerOrPlayerItself ();
return nullptr ;
}
Player * WorldObject :: GetSpellModOwner () const
{
if ( Player * player = const_cast < WorldObject *> ( this ) -> ToPlayer ())
return player ;
if ( GetTypeId () == TYPEID_UNIT )
{
Creature const * creature = ToCreature ();
if ( creature -> IsPet () || creature -> IsTotem ())
{
if ( Unit * owner = creature -> GetOwner ())
return owner -> ToPlayer ();
}
}
else if ( GetTypeId () == TYPEID_GAMEOBJECT )
{
GameObject const * go = ToGameObject ();
if ( Unit * owner = go -> GetOwner ())
return owner -> ToPlayer ();
}
return nullptr ;
}
// function uses real base points (typically value - 1)
2021-09-04 15:13:15 +02:00
int32 WorldObject :: CalculateSpellDamage ( Unit const * target , SpellEffectInfo const & spellEffectInfo , int32 const * basePoints /*= nullptr*/ , float * variance /*= nullptr*/ , uint32 castItemId /*= 0*/ , int32 itemLevel /*= -1*/ ) const
2018-02-10 16:43:01 -03:00
{
if ( variance )
* variance = 0.0f ;
2021-09-04 15:13:15 +02:00
return spellEffectInfo . CalcValue ( this , basePoints , target , variance , castItemId , itemLevel );
2018-02-10 16:43:01 -03:00
}
float WorldObject :: GetSpellMaxRangeForTarget ( Unit const * target , SpellInfo const * spellInfo ) const
{
if ( ! spellInfo -> RangeEntry )
return 0.f ;
if ( spellInfo -> RangeEntry -> RangeMax [ 0 ] == spellInfo -> RangeEntry -> RangeMax [ 1 ])
return spellInfo -> GetMaxRange ();
if ( ! target )
return spellInfo -> GetMaxRange ( true );
return spellInfo -> GetMaxRange ( ! IsHostileTo ( target ));
}
float WorldObject :: GetSpellMinRangeForTarget ( Unit const * target , SpellInfo const * spellInfo ) const
{
if ( ! spellInfo -> RangeEntry )
return 0.f ;
if ( spellInfo -> RangeEntry -> RangeMin [ 0 ] == spellInfo -> RangeEntry -> RangeMin [ 1 ])
return spellInfo -> GetMinRange ();
if ( ! target )
return spellInfo -> GetMinRange ( true );
return spellInfo -> GetMinRange ( ! IsHostileTo ( target ));
}
2022-12-14 00:24:24 +01:00
double WorldObject :: ApplyEffectModifiers ( SpellInfo const * spellInfo , uint8 effIndex , double value ) const
2018-02-10 16:43:01 -03:00
{
if ( Player * modOwner = GetSpellModOwner ())
{
modOwner -> ApplySpellMod ( spellInfo , SpellModOp :: Points , value );
switch ( effIndex )
{
case EFFECT_0 :
modOwner -> ApplySpellMod ( spellInfo , SpellModOp :: PointsIndex0 , value );
break ;
case EFFECT_1 :
modOwner -> ApplySpellMod ( spellInfo , SpellModOp :: PointsIndex1 , value );
break ;
case EFFECT_2 :
modOwner -> ApplySpellMod ( spellInfo , SpellModOp :: PointsIndex2 , value );
break ;
case EFFECT_3 :
modOwner -> ApplySpellMod ( spellInfo , SpellModOp :: PointsIndex3 , value );
break ;
case EFFECT_4 :
modOwner -> ApplySpellMod ( spellInfo , SpellModOp :: PointsIndex4 , value );
break ;
2022-12-14 00:24:24 +01:00
default :
break ;
2018-02-10 16:43:01 -03:00
}
}
return value ;
}
2023-07-31 00:11:05 +02:00
int32 WorldObject :: CalcSpellDuration ( SpellInfo const * spellInfo , std :: vector < SpellPowerCost > const * powerCosts ) const
2018-02-10 16:43:01 -03:00
{
int32 minduration = spellInfo -> GetDuration ();
2023-07-31 00:11:05 +02:00
if ( minduration <= 0 )
return minduration ;
2018-02-10 16:43:01 -03:00
int32 maxduration = spellInfo -> GetMaxDuration ();
2023-07-31 00:11:05 +02:00
if ( minduration == maxduration )
return minduration ;
2018-02-10 16:43:01 -03:00
2023-07-31 00:11:05 +02:00
Unit const * unit = ToUnit ();
if ( ! unit )
return minduration ;
if ( ! powerCosts )
return minduration ;
// we want only baseline cost here
auto itr = std :: find_if ( spellInfo -> PowerCosts . begin (), spellInfo -> PowerCosts . end (), [ = ]( SpellPowerEntry const * powerEntry )
{
return powerEntry && powerEntry -> PowerType == POWER_COMBO_POINTS && ( ! powerEntry -> RequiredAuraSpellID || unit -> HasAura ( powerEntry -> RequiredAuraSpellID ));
});
if ( itr == spellInfo -> PowerCosts . end ())
return minduration ;
auto consumedItr = std :: find_if ( powerCosts -> begin (), powerCosts -> end (),
[]( SpellPowerCost const & consumed ) { return consumed . Power == POWER_COMBO_POINTS ; });
if ( consumedItr == powerCosts -> end ())
return minduration ;
int32 baseComboCost = ( * itr ) -> ManaCost + ( * itr ) -> OptionalCost ;
if ( PowerTypeEntry const * powerTypeEntry = sDB2Manager . GetPowerTypeEntry ( POWER_COMBO_POINTS ))
baseComboCost += int32 ( CalculatePct ( powerTypeEntry -> MaxBasePower , ( * itr ) -> PowerCostPct + ( * itr ) -> OptionalCostPct ));
2018-02-10 16:43:01 -03:00
2023-07-31 00:11:05 +02:00
float durationPerComboPoint = float ( maxduration - minduration ) / baseComboCost ;
return minduration + int32 ( durationPerComboPoint * consumedItr -> Amount );
2018-02-10 16:43:01 -03:00
}
int32 WorldObject :: ModSpellDuration ( SpellInfo const * spellInfo , WorldObject const * target , int32 duration , bool positive , uint32 effectMask ) const
{
// don't mod permanent auras duration
if ( duration < 0 )
return duration ;
// some auras are not affected by duration modifiers
2023-10-21 14:49:42 +02:00
if ( spellInfo -> HasAttribute ( SPELL_ATTR7_NO_TARGET_DURATION_MOD ))
2018-02-10 16:43:01 -03:00
return duration ;
// cut duration only of negative effects
Unit const * unitTarget = target -> ToUnit ();
if ( ! unitTarget )
return duration ;
if ( ! positive )
{
2022-12-04 15:13:20 +01:00
uint64 mechanicMask = spellInfo -> GetSpellMechanicMaskByEffectMask ( effectMask );
2018-02-10 16:43:01 -03:00
auto mechanicCheck = [ mechanicMask ]( AuraEffect const * aurEff ) -> bool
{
2022-12-04 15:13:20 +01:00
if ( mechanicMask & ( UI64LIT ( 1 ) << aurEff -> GetMiscValue ()))
2018-02-10 16:43:01 -03:00
return true ;
return false ;
};
// Find total mod value (negative bonus)
int32 durationMod_always = unitTarget -> GetTotalAuraModifier ( SPELL_AURA_MECHANIC_DURATION_MOD , mechanicCheck );
// Find max mod (negative bonus)
int32 durationMod_not_stack = unitTarget -> GetMaxNegativeAuraModifier ( SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK , mechanicCheck );
// Select strongest negative mod
int32 durationMod = std :: min ( durationMod_always , durationMod_not_stack );
if ( durationMod != 0 )
AddPct ( duration , durationMod );
// there are only negative mods currently
durationMod_always = unitTarget -> GetTotalAuraModifierByMiscValue ( SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL , spellInfo -> Dispel );
durationMod_not_stack = unitTarget -> GetMaxNegativeAuraModifierByMiscValue ( SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK , spellInfo -> Dispel );
durationMod = std :: min ( durationMod_always , durationMod_not_stack );
if ( durationMod != 0 )
AddPct ( duration , durationMod );
}
else
{
// else positive mods here, there are no currently
// when there will be, change GetTotalAuraModifierByMiscValue to GetMaxPositiveAuraModifierByMiscValue
// Mixology - duration boost
if ( unitTarget -> GetTypeId () == TYPEID_PLAYER )
{
if ( spellInfo -> SpellFamilyName == SPELLFAMILY_POTION && (
sSpellMgr -> IsSpellMemberOfSpellGroup ( spellInfo -> Id , SPELL_GROUP_ELIXIR_BATTLE ) ||
sSpellMgr -> IsSpellMemberOfSpellGroup ( spellInfo -> Id , SPELL_GROUP_ELIXIR_GUARDIAN )))
{
2021-09-04 15:13:15 +02:00
SpellEffectInfo const & effect = spellInfo -> GetEffect ( EFFECT_0 );
if ( unitTarget -> HasAura ( 53042 ) && unitTarget -> HasSpell ( effect . TriggerSpell ))
2018-02-10 16:43:01 -03:00
duration *= 2 ;
}
}
}
return std :: max ( duration , 0 );
}
void WorldObject :: ModSpellCastTime ( SpellInfo const * spellInfo , int32 & castTime , Spell * spell /*= nullptr*/ ) const
{
if ( ! spellInfo || castTime < 0 )
return ;
// called from caster
if ( Player * modOwner = GetSpellModOwner ())
modOwner -> ApplySpellMod ( spellInfo , SpellModOp :: ChangeCastTime , castTime , spell );
Unit const * unitCaster = ToUnit ();
if ( ! unitCaster )
return ;
2022-07-29 20:24:28 +02:00
if ( unitCaster -> IsPlayer () && unitCaster -> ToPlayer () -> GetCommandStatus ( CHEAT_CASTTIME ))
castTime = 0 ;
else if ( ! ( spellInfo -> HasAttribute ( SPELL_ATTR0_IS_ABILITY ) || spellInfo -> HasAttribute ( SPELL_ATTR0_IS_TRADESKILL ) || spellInfo -> HasAttribute ( SPELL_ATTR3_IGNORE_CASTER_MODIFIERS )) &&
2018-02-10 16:43:01 -03:00
(( GetTypeId () == TYPEID_PLAYER && spellInfo -> SpellFamilyName ) || GetTypeId () == TYPEID_UNIT ))
castTime = unitCaster -> CanInstantCast () ? 0 : int32 ( float ( castTime ) * unitCaster -> m_unitData -> ModCastingSpeed );
2022-04-28 18:31:26 +02:00
else if ( spellInfo -> HasAttribute ( SPELL_ATTR0_USES_RANGED_SLOT ) && ! spellInfo -> HasAttribute ( SPELL_ATTR2_AUTO_REPEAT ))
2018-02-10 16:43:01 -03:00
castTime = int32 ( float ( castTime ) * unitCaster -> m_modAttackSpeedPct [ RANGED_ATTACK ]);
else if ( IsPartOfSkillLine ( SKILL_COOKING , spellInfo -> Id ) && unitCaster -> HasAura ( 67556 )) // cooking with Chef Hat.
castTime = 500 ;
}
void WorldObject :: ModSpellDurationTime ( SpellInfo const * spellInfo , int32 & duration , Spell * spell /*= nullptr*/ ) const
{
if ( ! spellInfo || duration < 0 )
return ;
2023-10-22 18:39:59 +02:00
if ( spellInfo -> IsChanneled ()
&& ! spellInfo -> HasAttribute ( SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC )
&& ! spellInfo -> HasAttribute ( SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC ))
2018-02-10 16:43:01 -03:00
return ;
// called from caster
if ( Player * modOwner = GetSpellModOwner ())
modOwner -> ApplySpellMod ( spellInfo , SpellModOp :: ChangeCastTime , duration , spell );
Unit const * unitCaster = ToUnit ();
if ( ! unitCaster )
return ;
2022-04-30 16:57:32 +02:00
if ( ! ( spellInfo -> HasAttribute ( SPELL_ATTR0_IS_ABILITY ) || spellInfo -> HasAttribute ( SPELL_ATTR0_IS_TRADESKILL ) || spellInfo -> HasAttribute ( SPELL_ATTR3_IGNORE_CASTER_MODIFIERS )) &&
2018-02-10 16:43:01 -03:00
(( GetTypeId () == TYPEID_PLAYER && spellInfo -> SpellFamilyName ) || GetTypeId () == TYPEID_UNIT ))
duration = int32 ( float ( duration ) * unitCaster -> m_unitData -> ModCastingSpeed );
2022-04-28 18:31:26 +02:00
else if ( spellInfo -> HasAttribute ( SPELL_ATTR0_USES_RANGED_SLOT ) && ! spellInfo -> HasAttribute ( SPELL_ATTR2_AUTO_REPEAT ))
2018-02-10 16:43:01 -03:00
duration = int32 ( float ( duration ) * unitCaster -> m_modAttackSpeedPct [ RANGED_ATTACK ]);
}
float WorldObject :: MeleeSpellMissChance ( Unit const * /*victim*/ , WeaponAttackType /*attType*/ , SpellInfo const * /*spellInfo*/ ) const
{
return 0.0f ;
}
SpellMissInfo WorldObject :: MeleeSpellHitResult ( Unit * /*victim*/ , SpellInfo const * /*spellInfo*/ ) const
{
return SPELL_MISS_NONE ;
}
SpellMissInfo WorldObject :: MagicSpellHitResult ( Unit * victim , SpellInfo const * spellInfo ) const
{
// Can`t miss on dead target (on skinning for example)
if ( ! victim -> IsAlive () && victim -> GetTypeId () != TYPEID_PLAYER )
return SPELL_MISS_NONE ;
2022-04-30 16:57:32 +02:00
if ( spellInfo -> HasAttribute ( SPELL_ATTR3_NO_AVOIDANCE ))
return SPELL_MISS_NONE ;
2022-02-07 20:53:32 +01:00
float missChance = [ & ]()
{
if ( spellInfo -> HasAttribute ( SPELL_ATTR7_NO_ATTACK_MISS ))
return 0.0f ;
SpellSchoolMask schoolMask = spellInfo -> GetSchoolMask ();
// PvP - PvE spell misschances per leveldif > 2
int32 lchance = victim -> GetTypeId () == TYPEID_PLAYER ? 7 : 11 ;
int32 thisLevel = GetLevelForTarget ( victim );
if ( GetTypeId () == TYPEID_UNIT && ToCreature () -> IsTrigger ())
thisLevel = std :: max < int32 > ( thisLevel , spellInfo -> SpellLevel );
int32 leveldif = int32 ( victim -> GetLevelForTarget ( this )) - thisLevel ;
int32 levelBasedHitDiff = leveldif ;
// Base hit chance from attacker and victim levels
int32 modHitChance = 100 ;
if ( levelBasedHitDiff >= 0 )
2018-02-10 16:43:01 -03:00
{
2022-02-07 20:53:32 +01:00
if ( victim -> GetTypeId () != TYPEID_PLAYER )
{
modHitChance = 94 - 3 * std :: min ( levelBasedHitDiff , 3 );
levelBasedHitDiff -= 3 ;
}
else
{
modHitChance = 96 - std :: min ( levelBasedHitDiff , 2 );
levelBasedHitDiff -= 2 ;
}
if ( levelBasedHitDiff > 0 )
modHitChance -= lchance * std :: min ( levelBasedHitDiff , 7 );
2018-02-10 16:43:01 -03:00
}
else
2022-02-07 20:53:32 +01:00
modHitChance = 97 - levelBasedHitDiff ;
// Spellmod from SpellModOp::HitChance
if ( Player * modOwner = GetSpellModOwner ())
modOwner -> ApplySpellMod ( spellInfo , SpellModOp :: HitChance , modHitChance );
// Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will ignore target's avoidance effects
2022-04-30 16:57:32 +02:00
if ( ! spellInfo -> HasAttribute ( SPELL_ATTR3_ALWAYS_HIT ))
2018-02-10 16:43:01 -03:00
{
2022-02-07 20:53:32 +01:00
// Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras
modHitChance += victim -> GetTotalAuraModifierByMiscMask ( SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE , schoolMask );
2018-02-10 16:43:01 -03:00
}
2022-02-07 20:53:32 +01:00
float HitChance = modHitChance ;
// Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings
if ( Unit const * unit = ToUnit ())
HitChance += unit -> m_modSpellHitChance ;
2018-02-10 16:43:01 -03:00
2022-02-07 20:53:32 +01:00
RoundToInterval ( HitChance , 0.0f , 100.0f );
2018-02-10 16:43:01 -03:00
2022-02-07 20:53:32 +01:00
return 100.0f - HitChance ;
}();
2018-02-10 16:43:01 -03:00
2022-02-07 20:53:32 +01:00
int32 tmp = int32 ( missChance * 100.0f );
2018-02-10 16:43:01 -03:00
int32 rand = irand ( 0 , 9999 );
if ( tmp > 0 && rand < tmp )
return SPELL_MISS_MISS ;
// Chance resist mechanic (select max value from every mechanic spell effect)
int32 resist_chance = victim -> GetMechanicResistChance ( spellInfo ) * 100 ;
// Roll chance
if ( resist_chance > 0 && rand < ( tmp += resist_chance ))
return SPELL_MISS_RESIST ;
// cast by caster in front of victim
if ( ! victim -> HasUnitState ( UNIT_STATE_CONTROLLED ) && ( victim -> HasInArc ( float ( M_PI ), this ) || victim -> HasAuraType ( SPELL_AURA_IGNORE_HIT_DIRECTION )))
{
int32 deflect_chance = victim -> GetTotalAuraModifier ( SPELL_AURA_DEFLECT_SPELLS ) * 100 ;
if ( deflect_chance > 0 && rand < ( tmp += deflect_chance ))
return SPELL_MISS_DEFLECT ;
}
return SPELL_MISS_NONE ;
}
// Calculate spell hit result can be:
// Every spell can: Evade/Immune/Reflect/Sucesful hit
// For melee based spells:
// Miss
// Dodge
// Parry
// For spells
// Resist
SpellMissInfo WorldObject :: SpellHitResult ( Unit * victim , SpellInfo const * spellInfo , bool canReflect /*= false*/ ) const
{
2018-03-15 02:59:08 -03:00
// Check for immune
if ( victim -> IsImmunedToSpell ( spellInfo , this ))
return SPELL_MISS_IMMUNE ;
// Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply
// returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit
if ( spellInfo -> HasOnlyDamageEffects () && victim -> IsImmunedToDamage ( spellInfo ))
return SPELL_MISS_IMMUNE ;
2018-02-10 16:43:01 -03:00
// All positive spells can`t miss
/// @todo client not show miss log for this spells - so need find info for this in dbc and use it!
if ( spellInfo -> IsPositive () && ! IsHostileTo ( victim )) // prevent from affecting enemy by "positive" spell
return SPELL_MISS_NONE ;
if ( this == victim )
return SPELL_MISS_NONE ;
// Return evade for units in evade mode
if ( victim -> GetTypeId () == TYPEID_UNIT && victim -> ToCreature () -> IsEvadingAttacks ())
return SPELL_MISS_EVADE ;
// Try victim reflect spell
if ( canReflect )
{
int32 reflectchance = victim -> GetTotalAuraModifier ( SPELL_AURA_REFLECT_SPELLS );
reflectchance += victim -> GetTotalAuraModifierByMiscMask ( SPELL_AURA_REFLECT_SPELLS_SCHOOL , spellInfo -> GetSchoolMask ());
if ( reflectchance > 0 && roll_chance_i ( reflectchance ))
2023-10-21 14:49:42 +02:00
return spellInfo -> HasAttribute ( SPELL_ATTR7_REFLECTION_ONLY_DEFENDS ) ? SPELL_MISS_DEFLECT : SPELL_MISS_REFLECT ;
2018-02-10 16:43:01 -03:00
}
2022-04-30 16:57:32 +02:00
if ( spellInfo -> HasAttribute ( SPELL_ATTR3_ALWAYS_HIT ))
2018-02-10 16:43:01 -03:00
return SPELL_MISS_NONE ;
switch ( spellInfo -> DmgClass )
{
case SPELL_DAMAGE_CLASS_RANGED :
case SPELL_DAMAGE_CLASS_MELEE :
return MeleeSpellHitResult ( victim , spellInfo );
case SPELL_DAMAGE_CLASS_NONE :
return SPELL_MISS_NONE ;
case SPELL_DAMAGE_CLASS_MAGIC :
return MagicSpellHitResult ( victim , spellInfo );
}
return SPELL_MISS_NONE ;
}
2018-03-10 19:51:36 -03:00
void WorldObject :: SendSpellMiss ( Unit * target , uint32 spellID , SpellMissInfo missInfo )
{
WorldPackets :: CombatLog :: SpellMissLog spellMissLog ;
spellMissLog . SpellID = spellID ;
spellMissLog . Caster = GetGUID ();
spellMissLog . Entries . emplace_back ( target -> GetGUID (), missInfo );
SendMessageToSet ( spellMissLog . Write (), true );
}
2018-02-10 16:43:01 -03:00
FactionTemplateEntry const * WorldObject :: GetFactionTemplateEntry () const
{
2019-12-14 23:28:45 +01:00
uint32 factionId = GetFaction ();
FactionTemplateEntry const * entry = sFactionTemplateStore . LookupEntry ( factionId );
2018-02-10 16:43:01 -03:00
if ( ! entry )
{
2019-12-14 23:28:45 +01:00
switch ( GetTypeId ())
{
case TYPEID_PLAYER :
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "entities.unit" , "Player {} has invalid faction (faction template id) #{}" , ToPlayer () -> GetName (), factionId );
2019-12-14 23:28:45 +01:00
break ;
case TYPEID_UNIT :
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "entities.unit" , "Creature (template id: {}) has invalid faction (faction template Id) #{}" , ToCreature () -> GetCreatureTemplate () -> Entry , factionId );
2019-12-14 23:28:45 +01:00
break ;
case TYPEID_GAMEOBJECT :
if ( factionId ) // Gameobjects may have faction template id = 0
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "entities.faction" , "GameObject (template id: {}) has invalid faction (faction template Id) #{}" , ToGameObject () -> GetGOInfo () -> entry , factionId );
2019-12-14 23:28:45 +01:00
break ;
default :
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "entities.unit" , "Object (name={}, type={}) has invalid faction (faction template Id) #{}" , GetName (), uint32 ( GetTypeId ()), factionId );
2019-12-14 23:28:45 +01:00
break ;
}
2018-02-10 16:43:01 -03:00
}
return entry ;
}
// function based on function Unit::UnitReaction from 13850 client
ReputationRank WorldObject :: GetReactionTo ( WorldObject const * target ) const
{
// always friendly to self
if ( this == target )
return REP_FRIENDLY ;
2022-12-22 23:45:35 +01:00
auto isAttackableBySummoner = [ & ]( Unit const * me , ObjectGuid const & targetGuid )
{
if ( ! me )
return false ;
TempSummon const * tempSummon = me -> ToTempSummon ();
if ( ! tempSummon || ! tempSummon -> m_Properties )
return false ;
if ( tempSummon -> m_Properties -> GetFlags (). HasFlag ( SummonPropertiesFlags :: AttackableBySummoner )
&& targetGuid == tempSummon -> GetSummonerGUID ())
return true ;
return false ;
};
if ( isAttackableBySummoner ( ToUnit (), target -> GetGUID ()) || isAttackableBySummoner ( target -> ToUnit (), GetGUID ()))
return REP_NEUTRAL ;
2018-02-10 16:43:01 -03:00
// always friendly to charmer or owner
if ( GetCharmerOrOwnerOrSelf () == target -> GetCharmerOrOwnerOrSelf ())
return REP_FRIENDLY ;
Player const * selfPlayerOwner = GetAffectingPlayer ();
Player const * targetPlayerOwner = target -> GetAffectingPlayer ();
// check forced reputation to support SPELL_AURA_FORCE_REACTION
if ( selfPlayerOwner )
{
if ( FactionTemplateEntry const * targetFactionTemplateEntry = target -> GetFactionTemplateEntry ())
if ( ReputationRank const * repRank = selfPlayerOwner -> GetReputationMgr (). GetForcedRankIfAny ( targetFactionTemplateEntry ))
return * repRank ;
}
else if ( targetPlayerOwner )
{
if ( FactionTemplateEntry const * selfFactionTemplateEntry = GetFactionTemplateEntry ())
if ( ReputationRank const * repRank = targetPlayerOwner -> GetReputationMgr (). GetForcedRankIfAny ( selfFactionTemplateEntry ))
return * repRank ;
}
2020-04-12 15:28:01 +02:00
Unit const * unit = Coalesce < const Unit > ( ToUnit (), selfPlayerOwner );
Unit const * targetUnit = Coalesce < const Unit > ( target -> ToUnit (), targetPlayerOwner );
2021-09-26 16:07:09 +02:00
if ( unit && unit -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
2018-02-10 16:43:01 -03:00
{
2021-09-26 16:07:09 +02:00
if ( targetUnit && targetUnit -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
2018-02-10 16:43:01 -03:00
{
if ( selfPlayerOwner && targetPlayerOwner )
{
// always friendly to other unit controlled by player, or to the player himself
if ( selfPlayerOwner == targetPlayerOwner )
return REP_FRIENDLY ;
// duel - always hostile to opponent
2018-09-17 12:40:42 +02:00
if ( selfPlayerOwner -> duel && selfPlayerOwner -> duel -> Opponent == targetPlayerOwner && selfPlayerOwner -> duel -> State == DUEL_STATE_IN_PROGRESS )
2018-02-10 16:43:01 -03:00
return REP_HOSTILE ;
// same group - checks dependant only on our faction - skip FFA_PVP for example
if ( selfPlayerOwner -> IsInRaidWith ( targetPlayerOwner ))
return REP_FRIENDLY ; // return true to allow config option AllowTwoSide.Interaction.Group to work
// however client seems to allow mixed group parties, because in 13850 client it works like:
// return GetFactionReactionTo(GetFactionTemplateEntry(), target);
}
// check FFA_PVP
if ( unit -> IsFFAPvP () && targetUnit -> IsFFAPvP ())
return REP_HOSTILE ;
if ( selfPlayerOwner )
{
if ( FactionTemplateEntry const * targetFactionTemplateEntry = targetUnit -> GetFactionTemplateEntry ())
{
if ( ReputationRank const * repRank = selfPlayerOwner -> GetReputationMgr (). GetForcedRankIfAny ( targetFactionTemplateEntry ))
return * repRank ;
if ( ! selfPlayerOwner -> HasUnitFlag2 ( UNIT_FLAG2_IGNORE_REPUTATION ))
{
if ( FactionEntry const * targetFactionEntry = sFactionStore . LookupEntry ( targetFactionTemplateEntry -> Faction ))
{
if ( targetFactionEntry -> CanHaveReputation ())
{
// check contested flags
2020-07-12 15:36:55 +02:00
if (( targetFactionTemplateEntry -> Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD ) &&
selfPlayerOwner -> HasPlayerFlag ( PLAYER_FLAGS_CONTESTED_PVP ))
2018-02-10 16:43:01 -03:00
return REP_HOSTILE ;
// if faction has reputation, hostile state depends only from AtWar state
if ( selfPlayerOwner -> GetReputationMgr (). IsAtWar ( targetFactionEntry ))
return REP_HOSTILE ;
return REP_FRIENDLY ;
}
}
}
}
}
}
}
// do checks dependant only on our faction
return WorldObject :: GetFactionReactionTo ( GetFactionTemplateEntry (), target );
}
/*static*/ ReputationRank WorldObject :: GetFactionReactionTo ( FactionTemplateEntry const * factionTemplateEntry , WorldObject const * target )
{
// always neutral when no template entry found
if ( ! factionTemplateEntry )
return REP_NEUTRAL ;
FactionTemplateEntry const * targetFactionTemplateEntry = target -> GetFactionTemplateEntry ();
if ( ! targetFactionTemplateEntry )
return REP_NEUTRAL ;
if ( Player const * targetPlayerOwner = target -> GetAffectingPlayer ())
{
// check contested flags
2020-07-12 15:36:55 +02:00
if (( factionTemplateEntry -> Flags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD ) &&
targetPlayerOwner -> HasPlayerFlag ( PLAYER_FLAGS_CONTESTED_PVP ))
2018-02-10 16:43:01 -03:00
return REP_HOSTILE ;
if ( ReputationRank const * repRank = targetPlayerOwner -> GetReputationMgr (). GetForcedRankIfAny ( factionTemplateEntry ))
return * repRank ;
if ( target -> IsUnit () && ! target -> ToUnit () -> HasUnitFlag2 ( UNIT_FLAG2_IGNORE_REPUTATION ))
{
if ( FactionEntry const * factionEntry = sFactionStore . LookupEntry ( factionTemplateEntry -> Faction ))
{
if ( factionEntry -> CanHaveReputation ())
{
// CvP case - check reputation, don't allow state higher than neutral when at war
ReputationRank repRank = targetPlayerOwner -> GetReputationMgr (). GetRank ( factionEntry );
if ( targetPlayerOwner -> GetReputationMgr (). IsAtWar ( factionEntry ))
repRank = std :: min ( REP_NEUTRAL , repRank );
return repRank ;
}
}
}
}
// common faction based check
if ( factionTemplateEntry -> IsHostileTo ( targetFactionTemplateEntry ))
return REP_HOSTILE ;
if ( factionTemplateEntry -> IsFriendlyTo ( targetFactionTemplateEntry ))
return REP_FRIENDLY ;
if ( targetFactionTemplateEntry -> IsFriendlyTo ( factionTemplateEntry ))
return REP_FRIENDLY ;
if ( factionTemplateEntry -> Flags & FACTION_TEMPLATE_FLAG_HOSTILE_BY_DEFAULT )
return REP_HOSTILE ;
// neutral by default
return REP_NEUTRAL ;
}
bool WorldObject :: IsHostileTo ( WorldObject const * target ) const
{
return GetReactionTo ( target ) <= REP_HOSTILE ;
}
bool WorldObject :: IsFriendlyTo ( WorldObject const * target ) const
{
return GetReactionTo ( target ) >= REP_FRIENDLY ;
}
bool WorldObject :: IsHostileToPlayers () const
{
FactionTemplateEntry const * my_faction = GetFactionTemplateEntry ();
if ( ! my_faction -> Faction )
return false ;
FactionEntry const * raw_faction = sFactionStore . LookupEntry ( my_faction -> Faction );
if ( raw_faction && raw_faction -> ReputationIndex >= 0 )
return false ;
return my_faction -> IsHostileToPlayers ();
}
bool WorldObject :: IsNeutralToAll () const
{
FactionTemplateEntry const * my_faction = GetFactionTemplateEntry ();
if ( ! my_faction -> Faction )
return true ;
FactionEntry const * raw_faction = sFactionStore . LookupEntry ( my_faction -> Faction );
if ( raw_faction && raw_faction -> ReputationIndex >= 0 )
return false ;
return my_faction -> IsNeutralToAll ();
}
2019-05-10 19:58:26 +03:00
SpellCastResult WorldObject :: CastSpell ( CastSpellTargetArg const & targets , uint32 spellId , CastSpellExtraArgs const & args /*= { }*/ )
2018-02-10 16:43:01 -03:00
{
SpellInfo const * info = sSpellMgr -> GetSpellInfo ( spellId , args . CastDifficulty != DIFFICULTY_NONE ? args . CastDifficulty : GetMap () -> GetDifficultyID ());
if ( ! info )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "entities.unit" , "CastSpell: unknown spell {} by caster {}" , spellId , GetGUID (). ToString ());
2019-05-10 19:58:26 +03:00
return SPELL_FAILED_SPELL_UNAVAILABLE ;
2018-02-10 16:43:01 -03:00
}
2021-10-03 19:58:03 +02:00
if ( ! targets . Targets )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "entities.unit" , "CastSpell: Invalid target passed to spell cast {} by {}" , spellId , GetGUID (). ToString ());
2019-05-10 19:58:26 +03:00
return SPELL_FAILED_BAD_TARGETS ;
2021-10-03 19:58:03 +02:00
}
2021-10-03 16:36:39 +02:00
Spell * spell = new Spell ( this , info , args . TriggerFlags , args . OriginalCaster , args . OriginalCastId );
2018-02-10 16:43:01 -03:00
for ( auto const & pair : args . SpellValueOverrides )
spell -> SetSpellValue ( pair . first , pair . second );
spell -> m_CastItem = args . CastItem ;
2022-02-08 21:36:31 +01:00
if ( args . OriginalCastItemLevel )
spell -> m_castItemLevel = * args . OriginalCastItemLevel ;
2022-04-28 18:31:26 +02:00
if ( ! spell -> m_CastItem && info -> HasAttribute ( SPELL_ATTR2_RETAIN_ITEM_CAST ))
{
if ( args . TriggeringSpell )
spell -> m_CastItem = args . TriggeringSpell -> m_CastItem ;
else if ( args . TriggeringAura && ! args . TriggeringAura -> GetBase () -> GetCastItemGUID (). IsEmpty ())
if ( Player const * triggeringAuraCaster = Object :: ToPlayer ( args . TriggeringAura -> GetCaster ()))
spell -> m_CastItem = triggeringAuraCaster -> GetItemByGuid ( args . TriggeringAura -> GetBase () -> GetCastItemGUID ());
}
2022-12-14 00:32:57 +01:00
spell -> m_customArg = args . CustomArg ;
2024-04-06 20:50:59 +02:00
spell -> m_scriptResult = args . ScriptResult ;
spell -> m_scriptWaitsForSpellHit = args . ScriptWaitsForSpellHit ;
2022-12-14 00:32:57 +01:00
2019-05-10 19:58:26 +03:00
return spell -> prepare ( * targets . Targets , args . TriggeringAura );
2018-02-10 16:43:01 -03:00
}
2021-12-12 19:34:00 +01:00
void WorldObject :: SendPlayOrphanSpellVisual ( ObjectGuid const & target , uint32 spellVisualId , float travelSpeed , bool speedAsTime /*= false*/ , bool withSourceOrientation /*= false*/ )
{
WorldPackets :: Spells :: PlayOrphanSpellVisual playOrphanSpellVisual ;
playOrphanSpellVisual . SourceLocation = GetPosition ();
if ( withSourceOrientation )
{
if ( IsGameObject ())
{
QuaternionData rotation = ToGameObject () -> GetWorldRotation ();
rotation . toEulerAnglesZYX ( playOrphanSpellVisual . SourceRotation . Pos . m_positionZ ,
playOrphanSpellVisual . SourceRotation . Pos . m_positionY ,
playOrphanSpellVisual . SourceRotation . Pos . m_positionX );
}
else
playOrphanSpellVisual . SourceRotation = Position ( 0.0f , 0.0f , GetOrientation ());
}
playOrphanSpellVisual . Target = target ; // exclusive with TargetLocation
playOrphanSpellVisual . SpellVisualID = spellVisualId ;
playOrphanSpellVisual . TravelSpeed = travelSpeed ;
playOrphanSpellVisual . SpeedAsTime = speedAsTime ;
playOrphanSpellVisual . LaunchDelay = 0.0f ;
SendMessageToSet ( playOrphanSpellVisual . Write (), true );
}
void WorldObject :: SendPlayOrphanSpellVisual ( Position const & targetLocation , uint32 spellVisualId , float travelSpeed , bool speedAsTime /*= false*/ , bool withSourceOrientation /*= false*/ )
{
WorldPackets :: Spells :: PlayOrphanSpellVisual playOrphanSpellVisual ;
playOrphanSpellVisual . SourceLocation = GetPosition ();
if ( withSourceOrientation )
{
if ( IsGameObject ())
{
QuaternionData rotation = ToGameObject () -> GetWorldRotation ();
rotation . toEulerAnglesZYX ( playOrphanSpellVisual . SourceRotation . Pos . m_positionZ ,
playOrphanSpellVisual . SourceRotation . Pos . m_positionY ,
playOrphanSpellVisual . SourceRotation . Pos . m_positionX );
}
else
playOrphanSpellVisual . SourceRotation = Position ( 0.0f , 0.0f , GetOrientation ());
}
playOrphanSpellVisual . TargetLocation = targetLocation ; // exclusive with Target
playOrphanSpellVisual . SpellVisualID = spellVisualId ;
playOrphanSpellVisual . TravelSpeed = travelSpeed ;
playOrphanSpellVisual . SpeedAsTime = speedAsTime ;
playOrphanSpellVisual . LaunchDelay = 0.0f ;
SendMessageToSet ( playOrphanSpellVisual . Write (), true );
}
void WorldObject :: SendCancelOrphanSpellVisual ( uint32 id )
{
WorldPackets :: Spells :: CancelOrphanSpellVisual cancelOrphanSpellVisual ;
cancelOrphanSpellVisual . SpellVisualID = id ;
SendMessageToSet ( cancelOrphanSpellVisual . Write (), true );
}
2018-02-10 16:43:01 -03:00
// function based on function Unit::CanAttack from 13850 client
2018-03-10 19:40:05 -03:00
bool WorldObject :: IsValidAttackTarget ( WorldObject const * target , SpellInfo const * bySpell /*= nullptr*/ ) const
2018-02-10 16:43:01 -03:00
{
ASSERT ( target );
2018-03-10 19:40:05 -03:00
// some positive spells can be casted at hostile target
bool isPositiveSpell = bySpell && bySpell -> IsPositive ();
// can't attack self (spells can, attribute check)
if ( ! bySpell && this == target )
return false ;
// can't attack unattackable units
Unit const * unitTarget = target -> ToUnit ();
if ( unitTarget && unitTarget -> HasUnitState ( UNIT_STATE_UNATTACKABLE ))
2018-02-10 16:43:01 -03:00
return false ;
// can't attack GMs
if ( target -> GetTypeId () == TYPEID_PLAYER && target -> ToPlayer () -> IsGameMaster ())
return false ;
Unit const * unit = ToUnit ();
2018-03-10 19:40:05 -03:00
// visibility checks (only units)
if ( unit )
{
// can't attack invisible
2022-05-06 20:23:13 +02:00
if ( ! bySpell || ! bySpell -> HasAttribute ( SPELL_ATTR6_IGNORE_PHASE_SHIFT ))
2018-03-10 19:40:05 -03:00
{
if ( ! unit -> CanSeeOrDetect ( target , bySpell && bySpell -> IsAffectingArea ()))
return false ;
}
}
// can't attack dead
if (( ! bySpell || ! bySpell -> IsAllowingDeadTarget ()) && unitTarget && ! unitTarget -> IsAlive ())
return false ;
// can't attack untargetable
2022-06-17 12:54:34 +02:00
if (( ! bySpell || ! bySpell -> HasAttribute ( SPELL_ATTR6_CAN_TARGET_UNTARGETABLE )) && unitTarget && unitTarget -> HasUnitFlag ( UNIT_FLAG_NON_ATTACKABLE_2 ))
return false ;
2023-07-20 19:09:27 +02:00
if ( unitTarget && unitTarget -> IsUninteractible ())
2018-03-10 19:40:05 -03:00
return false ;
if ( Player const * playerAttacker = ToPlayer ())
{
if ( playerAttacker -> HasPlayerFlag ( PLAYER_FLAGS_UBER ))
return false ;
}
// check flags
2022-06-17 12:54:34 +02:00
if ( unitTarget && unitTarget -> HasUnitFlag ( UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_ON_TAXI | UNIT_FLAG_NOT_ATTACKABLE_1 ))
2018-03-10 19:40:05 -03:00
return false ;
2020-07-01 22:53:28 +02:00
Unit const * unitOrOwner = unit ;
2020-07-04 15:58:41 +02:00
GameObject const * go = ToGameObject ();
if ( go && go -> GetGoType () == GAMEOBJECT_TYPE_TRAP )
unitOrOwner = go -> GetOwner ();
2020-07-01 22:53:28 +02:00
2018-03-10 19:40:05 -03:00
// ignore immunity flags when assisting
2022-05-06 20:23:13 +02:00
if ( unitOrOwner && unitTarget && ! ( isPositiveSpell && bySpell -> HasAttribute ( SPELL_ATTR6_CAN_ASSIST_IMMUNE_PC )))
2018-03-10 19:40:05 -03:00
{
2020-07-01 22:53:28 +02:00
if ( ! unitOrOwner -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ) && unitTarget -> IsImmuneToNPC ())
2018-03-10 19:40:05 -03:00
return false ;
2020-07-01 22:53:28 +02:00
if ( ! unitTarget -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ) && unitOrOwner -> IsImmuneToNPC ())
2018-03-10 19:40:05 -03:00
return false ;
2024-02-19 21:05:03 +01:00
if ( ! bySpell || ! bySpell -> HasAttribute ( SPELL_ATTR8_CAN_ATTACK_IMMUNE_PC ))
2018-03-10 19:40:05 -03:00
{
2020-07-01 22:53:28 +02:00
if ( unitOrOwner -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ) && unitTarget -> IsImmuneToPC ())
2018-03-10 19:40:05 -03:00
return false ;
2020-07-01 22:53:28 +02:00
if ( unitTarget -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ) && unitOrOwner -> IsImmuneToPC ())
2018-03-10 19:40:05 -03:00
return false ;
}
}
2018-02-10 16:43:01 -03:00
// CvC case - can attack each other only when one of them is hostile
2021-09-26 16:07:09 +02:00
if ( unit && ! unit -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ) && unitTarget && ! unitTarget -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
2020-07-04 15:58:41 +02:00
return IsHostileTo ( unitTarget ) || unitTarget -> IsHostileTo ( this );
// Traps without owner or with NPC owner versus Creature case - can attack to creature only when one of them is hostile
if ( go && go -> GetGoType () == GAMEOBJECT_TYPE_TRAP )
{
Unit const * goOwner = go -> GetOwner ();
if ( ! goOwner || ! goOwner -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
if ( unitTarget && ! unitTarget -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
return IsHostileTo ( unitTarget ) || unitTarget -> IsHostileTo ( this );
}
2018-02-10 16:43:01 -03:00
// PvP, PvC, CvP case
// can't attack friendly targets
if ( IsFriendlyTo ( target ) || target -> IsFriendlyTo ( this ))
return false ;
2023-09-10 11:54:09 +02:00
Player const * playerAffectingAttacker = ( unit && unit -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED )) || go ? GetAffectingPlayer () : nullptr ;
2020-07-04 15:58:41 +02:00
Player const * playerAffectingTarget = unitTarget && unitTarget -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ) ? unitTarget -> GetAffectingPlayer () : nullptr ;
2018-02-10 16:43:01 -03:00
2023-09-10 11:54:09 +02:00
// Pets of mounted players are immune to NPCs
if ( ! playerAffectingAttacker && unitTarget && unitTarget -> IsPet () && playerAffectingTarget && playerAffectingTarget -> IsMounted ())
return false ;
2018-02-10 16:43:01 -03:00
// Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar)
if (( playerAffectingAttacker && ! playerAffectingTarget ) || ( ! playerAffectingAttacker && playerAffectingTarget ))
{
Player const * player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget ;
2018-03-10 19:40:05 -03:00
if ( Unit const * creature = playerAffectingAttacker ? unitTarget : unit )
2018-02-10 16:43:01 -03:00
{
if ( creature -> IsContestedGuard () && player -> HasPlayerFlag ( PLAYER_FLAGS_CONTESTED_PVP ))
return true ;
if ( FactionTemplateEntry const * factionTemplate = creature -> GetFactionTemplateEntry ())
{
if ( ! ( player -> GetReputationMgr (). GetForcedRankIfAny ( factionTemplate )))
if ( FactionEntry const * factionEntry = sFactionStore . LookupEntry ( factionTemplate -> Faction ))
if ( FactionState const * repState = player -> GetReputationMgr (). GetState ( factionEntry ))
if ( ! repState -> Flags . HasFlag ( ReputationFlags :: AtWar ))
return false ;
}
}
}
if ( playerAffectingAttacker && playerAffectingTarget )
2018-09-17 12:40:42 +02:00
if ( playerAffectingAttacker -> duel && playerAffectingAttacker -> duel -> Opponent == playerAffectingTarget && playerAffectingAttacker -> duel -> State == DUEL_STATE_IN_PROGRESS )
2018-02-10 16:43:01 -03:00
return true ;
// PvP case - can't attack when attacker or target are in sanctuary
// however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp
2023-10-22 18:39:59 +02:00
if ( unitTarget && unitTarget -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED )
&& unitOrOwner && unitOrOwner -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED )
&& ( unitTarget -> IsInSanctuary () || unitOrOwner -> IsInSanctuary ())
&& ( ! bySpell || bySpell -> HasAttribute ( SPELL_ATTR8_IGNORE_SANCTUARY )))
2018-02-10 16:43:01 -03:00
return false ;
// additional checks - only PvP case
if ( playerAffectingAttacker && playerAffectingTarget )
{
2022-05-02 18:20:17 +02:00
if ( playerAffectingTarget -> IsPvP () || ( bySpell && bySpell -> HasAttribute ( SPELL_ATTR5_IGNORE_AREA_EFFECT_PVP_CHECK )))
2018-02-10 16:43:01 -03:00
return true ;
2020-04-12 15:28:01 +02:00
if ( playerAffectingAttacker -> IsFFAPvP () && playerAffectingTarget -> IsFFAPvP ())
2018-02-10 16:43:01 -03:00
return true ;
2020-04-12 15:28:01 +02:00
return playerAffectingAttacker -> HasPvpFlag ( UNIT_BYTE2_FLAG_UNK1 ) ||
playerAffectingTarget -> HasPvpFlag ( UNIT_BYTE2_FLAG_UNK1 );
2018-02-10 16:43:01 -03:00
}
return true ;
}
// function based on function Unit::CanAssist from 13850 client
2018-03-10 19:40:05 -03:00
bool WorldObject :: IsValidAssistTarget ( WorldObject const * target , SpellInfo const * bySpell /*= nullptr*/ ) const
2018-02-10 16:43:01 -03:00
{
ASSERT ( target );
2018-03-10 19:40:05 -03:00
// some negative spells can be casted at friendly target
bool isNegativeSpell = bySpell && ! bySpell -> IsPositive ();
2018-02-10 16:43:01 -03:00
// can assist to self
if ( this == target )
return true ;
// can't assist unattackable units
Unit const * unitTarget = target -> ToUnit ();
if ( unitTarget && unitTarget -> HasUnitState ( UNIT_STATE_UNATTACKABLE ))
return false ;
2018-03-10 19:40:05 -03:00
// can't assist GMs
if ( target -> GetTypeId () == TYPEID_PLAYER && target -> ToPlayer () -> IsGameMaster ())
return false ;
2018-02-10 16:43:01 -03:00
// can't assist own vehicle or passenger
Unit const * unit = ToUnit ();
if ( unit && unitTarget && unit -> GetVehicle ())
{
if ( unit -> IsOnVehicle ( unitTarget ))
return false ;
if ( unit -> GetVehicleBase () -> IsOnVehicle ( unitTarget ))
return false ;
}
// can't assist invisible
2022-05-06 20:23:13 +02:00
if (( ! bySpell || ! bySpell -> HasAttribute ( SPELL_ATTR6_IGNORE_PHASE_SHIFT )) && ! CanSeeOrDetect ( target , bySpell && bySpell -> IsAffectingArea ()))
2018-02-10 16:43:01 -03:00
return false ;
// can't assist dead
2018-03-10 19:40:05 -03:00
if (( ! bySpell || ! bySpell -> IsAllowingDeadTarget ()) && unitTarget && ! unitTarget -> IsAlive ())
2018-02-10 16:43:01 -03:00
return false ;
// can't assist untargetable
2022-06-17 12:54:34 +02:00
if (( ! bySpell || ! bySpell -> HasAttribute ( SPELL_ATTR6_CAN_TARGET_UNTARGETABLE )) && unitTarget && unitTarget -> HasUnitFlag ( UNIT_FLAG_NON_ATTACKABLE_2 ))
return false ;
2023-07-20 19:09:27 +02:00
if ( unitTarget && unitTarget -> IsUninteractible ())
2018-02-10 16:43:01 -03:00
return false ;
2018-03-10 19:40:05 -03:00
// check flags for negative spells
2022-06-17 12:54:34 +02:00
if ( isNegativeSpell && unitTarget && unitTarget -> HasUnitFlag ( UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_ON_TAXI | UNIT_FLAG_NOT_ATTACKABLE_1 ))
2018-03-10 19:40:05 -03:00
return false ;
2022-05-06 20:23:13 +02:00
if ( isNegativeSpell || ! bySpell || ! bySpell -> HasAttribute ( SPELL_ATTR6_CAN_ASSIST_IMMUNE_PC ))
2018-02-10 16:43:01 -03:00
{
2021-09-26 16:07:09 +02:00
if ( unit && unit -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
2018-02-10 16:43:01 -03:00
{
2024-02-19 21:05:03 +01:00
if ( ! bySpell || ! bySpell -> HasAttribute ( SPELL_ATTR8_CAN_ATTACK_IMMUNE_PC ))
2018-03-10 19:40:05 -03:00
if ( unitTarget && unitTarget -> IsImmuneToPC ())
return false ;
2018-02-10 16:43:01 -03:00
}
else
{
if ( unitTarget && unitTarget -> IsImmuneToNPC ())
return false ;
}
}
2018-03-10 19:40:05 -03:00
// can't assist non-friendly targets
2024-02-29 18:09:10 +01:00
if ( GetReactionTo ( target ) < REP_NEUTRAL && target -> GetReactionTo ( this ) < REP_NEUTRAL && ( ! ToCreature () || ! ToCreature () -> IsTreatedAsRaidUnit ()))
2018-03-10 19:40:05 -03:00
return false ;
2018-02-10 16:43:01 -03:00
// PvP case
2021-09-26 16:07:09 +02:00
if ( unitTarget && unitTarget -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
2018-02-10 16:43:01 -03:00
{
2021-09-26 16:07:09 +02:00
if ( unit && unit -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
2018-02-10 16:43:01 -03:00
{
Player const * selfPlayerOwner = GetAffectingPlayer ();
2019-01-09 20:41:35 +01:00
Player const * targetPlayerOwner = unitTarget -> GetAffectingPlayer ();
2018-02-10 16:43:01 -03:00
if ( selfPlayerOwner && targetPlayerOwner )
{
// can't assist player which is dueling someone
if ( selfPlayerOwner != targetPlayerOwner && targetPlayerOwner -> duel )
return false ;
}
// can't assist player in ffa_pvp zone from outside
2019-01-09 20:41:35 +01:00
if ( unitTarget -> IsFFAPvP () && ! unit -> IsFFAPvP ())
2018-02-10 16:43:01 -03:00
return false ;
// can't assist player out of sanctuary from sanctuary if has pvp enabled
2023-10-22 18:39:59 +02:00
if ( unitTarget -> IsPvP () && ( ! bySpell || bySpell -> HasAttribute ( SPELL_ATTR8_IGNORE_SANCTUARY )))
2020-08-04 09:36:47 +00:00
if ( unit -> IsInSanctuary () && ! unitTarget -> IsInSanctuary ())
2018-02-10 16:43:01 -03:00
return false ;
}
}
// PvC case - player can assist creature only if has specific type flags
// !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) &&
2021-09-26 16:07:09 +02:00
else if ( unit && unit -> HasUnitFlag ( UNIT_FLAG_PLAYER_CONTROLLED ))
2018-02-10 16:43:01 -03:00
{
2022-05-06 20:23:13 +02:00
if ( ! bySpell || ! bySpell -> HasAttribute ( SPELL_ATTR6_CAN_ASSIST_IMMUNE_PC ))
2018-02-10 16:43:01 -03:00
if ( unitTarget && ! unitTarget -> IsPvP ())
if ( Creature const * creatureTarget = target -> ToCreature ())
2024-02-29 18:09:10 +01:00
return creatureTarget -> IsTreatedAsRaidUnit () || ( creatureTarget -> GetCreatureDifficulty () -> TypeFlags & CREATURE_TYPE_FLAG_CAN_ASSIST );
2018-02-10 16:43:01 -03:00
}
return true ;
}
Unit * WorldObject :: GetMagicHitRedirectTarget ( Unit * victim , SpellInfo const * spellInfo )
{
// Patch 1.2 notes: Spell Reflection no longer reflects abilities
2022-04-25 22:16:56 +02:00
if ( spellInfo -> HasAttribute ( SPELL_ATTR0_IS_ABILITY ) || spellInfo -> HasAttribute ( SPELL_ATTR1_NO_REDIRECTION ) || spellInfo -> HasAttribute ( SPELL_ATTR0_NO_IMMUNITIES ))
2018-02-10 16:43:01 -03:00
return victim ;
Unit :: AuraEffectList const & magnetAuras = victim -> GetAuraEffectsByType ( SPELL_AURA_SPELL_MAGNET );
for ( AuraEffect const * aurEff : magnetAuras )
{
if ( Unit * magnet = aurEff -> GetBase () -> GetCaster ())
{
if ( spellInfo -> CheckExplicitTarget ( this , magnet ) == SPELL_CAST_OK && IsValidAttackTarget ( magnet , spellInfo ))
{
/// @todo handle this charge drop by proc in cast phase on explicit target
if ( spellInfo -> HasHitDelay ())
{
// Set up missile speed based delay
float hitDelay = spellInfo -> LaunchDelay ;
if ( spellInfo -> HasAttribute ( SPELL_ATTR9_SPECIAL_DELAY_CALCULATION ))
hitDelay += spellInfo -> Speed ;
else if ( spellInfo -> Speed > 0.0f )
hitDelay += std :: max ( victim -> GetDistance ( this ), 5.0f ) / spellInfo -> Speed ;
uint32 delay = uint32 ( std :: floor ( hitDelay * 1000.0f ));
// Schedule charge drop
aurEff -> GetBase () -> DropChargeDelayed ( delay , AURA_REMOVE_BY_EXPIRE );
}
else
aurEff -> GetBase () -> DropCharge ( AURA_REMOVE_BY_EXPIRE );
return magnet ;
}
}
}
return victim ;
}
uint32 WorldObject :: GetCastSpellXSpellVisualId ( SpellInfo const * spellInfo ) const
{
return spellInfo -> GetSpellXSpellVisualId ( this );
}
2016-10-13 01:23:02 -03:00
template < typename Container >
void WorldObject :: GetGameObjectListWithEntryInGrid ( Container & gameObjectContainer , uint32 entry , float maxSearchRange /*= 250.0f*/ ) const
2009-12-17 11:40:23 +01:00
{
2011-10-19 17:40:44 +01:00
Trinity :: AllGameObjectsWithEntryInRange check ( this , entry , maxSearchRange );
2016-10-13 01:23:02 -03:00
Trinity :: GameObjectListSearcher < Trinity :: AllGameObjectsWithEntryInRange > searcher ( this , gameObjectContainer , check );
2017-05-13 19:37:33 +02:00
Cell :: VisitGridObjects ( this , searcher , maxSearchRange );
2009-07-24 05:27:49 +02:00
}
2009-10-17 15:51:44 -07:00
2023-07-30 13:55:34 +02:00
template < typename Container >
void WorldObject :: GetGameObjectListWithOptionsInGrid ( Container & gameObjectContainer , float maxSearchRange , FindGameObjectOptions const & options ) const
{
Trinity :: InRangeCheckCustomizer checkCustomizer ( * this , maxSearchRange );
Trinity :: GameObjectWithOptionsInObjectRangeCheck check ( * this , checkCustomizer , options );
Trinity :: GameObjectListSearcher searcher ( this , gameObjectContainer , check );
if ( options . IgnorePhases )
searcher . i_phaseShift = & PhasingHandler :: GetAlwaysVisiblePhaseShift ();
Cell :: VisitGridObjects ( this , searcher , maxSearchRange );
}
2016-10-13 01:23:02 -03:00
template < typename Container >
void WorldObject :: GetCreatureListWithEntryInGrid ( Container & creatureContainer , uint32 entry , float maxSearchRange /*= 250.0f*/ ) const
2009-07-24 05:27:49 +02:00
{
2011-10-19 17:40:44 +01:00
Trinity :: AllCreaturesOfEntryInRange check ( this , entry , maxSearchRange );
2016-10-13 01:23:02 -03:00
Trinity :: CreatureListSearcher < Trinity :: AllCreaturesOfEntryInRange > searcher ( this , creatureContainer , check );
2017-05-13 19:37:33 +02:00
Cell :: VisitGridObjects ( this , searcher , maxSearchRange );
2009-07-24 05:27:49 +02:00
}
2009-10-17 15:51:44 -07:00
2022-12-27 16:39:05 +01:00
template < typename Container >
void WorldObject :: GetCreatureListWithOptionsInGrid ( Container & creatureContainer , float maxSearchRange , FindCreatureOptions const & options ) const
{
2023-07-30 13:55:34 +02:00
Trinity :: InRangeCheckCustomizer checkCustomizer ( * this , maxSearchRange );
2022-12-27 16:39:05 +01:00
Trinity :: CreatureWithOptionsInObjectRangeCheck check ( * this , checkCustomizer , options );
Trinity :: CreatureListSearcher searcher ( this , creatureContainer , check );
if ( options . IgnorePhases )
searcher . i_phaseShift = & PhasingHandler :: GetAlwaysVisiblePhaseShift ();
Cell :: VisitGridObjects ( this , searcher , maxSearchRange );
}
2016-10-13 01:23:02 -03:00
template < typename Container >
2021-08-10 13:08:49 +02:00
void WorldObject :: GetPlayerListInGrid ( Container & playerContainer , float maxSearchRange , bool alive /*= true*/ ) const
2015-02-10 22:16:07 -05:00
{
2021-08-10 13:08:49 +02:00
Trinity :: AnyPlayerInObjectRangeCheck checker ( this , maxSearchRange , alive );
2016-10-13 01:23:02 -03:00
Trinity :: PlayerListSearcher < Trinity :: AnyPlayerInObjectRangeCheck > searcher ( this , playerContainer , checker );
2017-05-13 19:37:33 +02:00
Cell :: VisitWorldObjects ( this , searcher , maxSearchRange );
2015-02-10 22:16:07 -05:00
}
2019-07-08 12:51:42 +02:00
void WorldObject :: GetNearPoint2D ( WorldObject const * searcher , float & x , float & y , float distance2d , float absAngle ) const
2008-10-11 14:16:25 -05:00
{
2018-04-01 18:27:29 +02:00
float effectiveReach = GetCombatReach ();
if ( searcher )
{
effectiveReach += searcher -> GetCombatReach ();
if ( this != searcher )
{
float myHover = 0.0f , searcherHover = 0.0f ;
if ( Unit const * unit = ToUnit ())
myHover = unit -> GetHoverOffset ();
if ( Unit const * searchUnit = searcher -> ToUnit ())
searcherHover = searchUnit -> GetHoverOffset ();
float hoverDelta = myHover - searcherHover ;
if ( hoverDelta != 0.0f )
2018-04-01 18:34:13 +02:00
effectiveReach = std :: sqrt ( std :: max ( effectiveReach * effectiveReach - hoverDelta * hoverDelta , 0.0f ));
2018-04-01 18:27:29 +02:00
}
}
x = GetPositionX () + ( effectiveReach + distance2d ) * std :: cos ( absAngle );
y = GetPositionY () + ( effectiveReach + distance2d ) * std :: sin ( absAngle );
2009-10-17 15:51:44 -07:00
2008-10-14 11:57:03 -05:00
Trinity :: NormalizeMapCoord ( x );
Trinity :: NormalizeMapCoord ( y );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2019-07-08 12:51:42 +02:00
void WorldObject :: GetNearPoint ( WorldObject const * searcher , float & x , float & y , float & z , float distance2d , float absAngle ) const
2008-10-11 14:16:25 -05:00
{
2018-04-01 18:27:29 +02:00
GetNearPoint2D ( searcher , x , y , distance2d , absAngle );
2009-02-09 08:16:34 -05:00
z = GetPositionZ ();
2018-04-01 13:20:49 +02:00
( searcher ? searcher : this ) -> UpdateAllowedPositionZ ( x , y , z );
2009-10-17 15:51:44 -07:00
2009-05-14 16:50:47 -05:00
// if detection disabled, return first point
2014-04-07 22:16:31 +02:00
if ( ! sWorld -> getBoolConfig ( CONFIG_DETECT_POS_COLLISION ))
2009-05-14 16:50:47 -05:00
return ;
2009-10-17 15:51:44 -07:00
2014-04-07 22:16:31 +02:00
// return if the point is already in LoS
if ( IsWithinLOS ( x , y , z ))
2009-05-14 16:50:47 -05:00
return ;
2009-10-17 15:51:44 -07:00
2014-04-07 22:16:31 +02:00
// remember first point
float first_x = x ;
float first_y = y ;
float first_z = z ;
2009-10-17 15:51:44 -07:00
2014-04-07 22:16:31 +02:00
// loop in a circle to look for a point in LoS using small steps
2014-08-21 16:56:11 +02:00
for ( float angle = float ( M_PI ) / 8 ; angle < float ( M_PI ) * 2 ; angle += float ( M_PI ) / 8 )
2009-05-14 16:50:47 -05:00
{
2018-04-01 18:27:29 +02:00
GetNearPoint2D ( searcher , x , y , distance2d , absAngle + angle );
2009-05-14 16:50:47 -05:00
z = GetPositionZ ();
2018-04-01 13:20:49 +02:00
( searcher ? searcher : this ) -> UpdateAllowedPositionZ ( x , y , z );
2011-04-29 20:47:02 +02:00
if ( IsWithinLOS ( x , y , z ))
2009-05-14 16:50:47 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2014-04-07 22:16:31 +02:00
// still not in LoS, give up and return first position found
2009-05-14 16:50:47 -05:00
x = first_x ;
y = first_y ;
2014-04-07 22:16:31 +02:00
z = first_z ;
2008-12-24 09:58:26 -06:00
}
2009-10-17 15:51:44 -07:00
2019-07-08 12:51:42 +02:00
void WorldObject :: GetClosePoint ( float & x , float & y , float & z , float size , float distance2d /*= 0*/ , float relAngle /*= 0*/ ) const
2013-03-04 11:45:57 +01:00
{
// angle calculated from current orientation
2019-07-08 12:51:42 +02:00
GetNearPoint ( nullptr , x , y , z , distance2d + size , GetOrientation () + relAngle );
2013-03-04 11:45:57 +01:00
}
2014-04-25 21:19:30 +02:00
Position WorldObject :: GetNearPosition ( float dist , float angle )
2013-03-04 11:45:57 +01:00
{
2014-04-25 21:19:30 +02:00
Position pos = GetPosition ();
2013-03-04 11:45:57 +01:00
MovePosition ( pos , dist , angle );
2014-04-25 21:19:30 +02:00
return pos ;
2013-03-04 11:45:57 +01:00
}
2014-04-25 21:19:30 +02:00
Position WorldObject :: GetFirstCollisionPosition ( float dist , float angle )
2013-03-04 11:45:57 +01:00
{
2014-04-25 21:19:30 +02:00
Position pos = GetPosition ();
2013-03-04 11:45:57 +01:00
MovePositionToFirstCollision ( pos , dist , angle );
2014-04-25 21:19:30 +02:00
return pos ;
2013-03-04 11:45:57 +01:00
}
2014-04-25 21:19:30 +02:00
Position WorldObject :: GetRandomNearPosition ( float radius )
2013-03-04 11:45:57 +01:00
{
2014-04-25 21:19:30 +02:00
Position pos = GetPosition ();
2023-10-31 20:20:00 +01:00
MovePosition ( pos , radius * rand_norm (), rand_norm () * static_cast < float > ( 2 * M_PI ));
2014-04-25 21:19:30 +02:00
return pos ;
2013-03-04 11:45:57 +01:00
}
2020-09-04 13:38:24 +02:00
void WorldObject :: GetContactPoint ( WorldObject const * obj , float & x , float & y , float & z , float distance2d /*= CONTACT_DISTANCE*/ ) const
2013-03-04 11:45:57 +01:00
{
// angle to face `obj` to `this` using distance includes size of `obj`
2018-04-01 18:27:29 +02:00
GetNearPoint ( obj , x , y , z , distance2d , GetAbsoluteAngle ( obj ));
2013-03-04 11:45:57 +01:00
}
2009-08-29 23:20:16 -05:00
void WorldObject :: MovePosition ( Position & pos , float dist , float angle )
2009-01-08 14:22:15 -06:00
{
2012-08-20 10:44:14 +02:00
angle += GetOrientation ();
2012-05-30 08:01:02 +02:00
float destx , desty , destz , ground , floor ;
2012-10-02 15:06:19 +02:00
destx = pos . m_positionX + dist * std :: cos ( angle );
desty = pos . m_positionY + dist * std :: sin ( angle );
2012-05-30 08:01:02 +02:00
// Prevent invalid coordinates here, position is unchanged
2012-09-07 04:21:11 +02:00
if ( ! Trinity :: IsValidMapCoord ( destx , desty , pos . m_positionZ ))
2012-05-30 08:01:02 +02:00
{
2023-01-08 21:16:53 +01:00
TC_LOG_FATAL ( "misc" , "WorldObject::MovePosition: Object {} has invalid coordinates X: {} and Y: {} were passed!" ,
GetGUID (). ToString (), destx , desty );
2012-05-30 08:01:02 +02:00
return ;
}
2017-12-29 22:32:07 +01:00
ground = GetMapHeight ( destx , desty , MAX_HEIGHT );
floor = GetMapHeight ( destx , desty , pos . m_positionZ );
2014-08-21 16:56:11 +02:00
destz = std :: fabs ( ground - pos . m_positionZ ) <= std :: fabs ( floor - pos . m_positionZ ) ? ground : floor ;
2012-05-30 08:01:02 +02:00
float step = dist / 10.0f ;
for ( uint8 j = 0 ; j < 10 ; ++ j )
{
// do not allow too big z changes
2014-08-21 16:56:11 +02:00
if ( std :: fabs ( pos . m_positionZ - destz ) > 6 )
2012-05-30 08:01:02 +02:00
{
2012-10-02 15:06:19 +02:00
destx -= step * std :: cos ( angle );
desty -= step * std :: sin ( angle );
2018-01-28 11:37:20 +01:00
ground = GetMap () -> GetHeight ( GetPhaseShift (), destx , desty , MAX_HEIGHT , true );
floor = GetMap () -> GetHeight ( GetPhaseShift (), destx , desty , pos . m_positionZ , true );
2014-08-21 16:56:11 +02:00
destz = std :: fabs ( ground - pos . m_positionZ ) <= std :: fabs ( floor - pos . m_positionZ ) ? ground : floor ;
2012-05-30 08:01:02 +02:00
}
// we have correct destz now
else
{
pos . Relocate ( destx , desty , destz );
break ;
}
}
2009-08-29 23:20:16 -05:00
Trinity :: NormalizeMapCoord ( pos . m_positionX );
Trinity :: NormalizeMapCoord ( pos . m_positionY );
UpdateGroundPositionZ ( pos . m_positionX , pos . m_positionY , pos . m_positionZ );
2012-08-20 10:44:14 +02:00
pos . SetOrientation ( GetOrientation ());
2009-01-08 14:22:15 -06:00
}
2009-10-17 15:51:44 -07:00
2010-07-21 02:06:51 +02:00
void WorldObject :: MovePositionToFirstCollision ( Position & pos , float dist , float angle )
{
2012-08-20 10:44:14 +02:00
angle += GetOrientation ();
2014-07-05 15:35:21 +01:00
float destx , desty , destz ;
2012-10-02 15:06:19 +02:00
destx = pos . m_positionX + dist * std :: cos ( angle );
desty = pos . m_positionY + dist * std :: sin ( angle );
2017-12-29 22:32:07 +01:00
destz = pos . m_positionZ ;
2012-05-30 08:01:02 +02:00
// Prevent invalid coordinates here, position is unchanged
if ( ! Trinity :: IsValidMapCoord ( destx , desty ))
{
2023-01-08 21:16:53 +01:00
TC_LOG_FATAL ( "misc" , "WorldObject::MovePositionToFirstCollision invalid coordinates X: {} and Y: {} were passed!" , destx , desty );
2012-05-30 08:01:02 +02:00
return ;
}
2020-03-04 20:59:04 +01:00
// Use a detour raycast to get our first collision point
PathGenerator path ( this );
2020-05-15 18:43:12 +00:00
path . SetUseRaycast ( true );
path . CalculatePath ( destx , desty , destz , false );
2020-05-01 13:43:24 +02:00
2020-05-16 17:40:08 +02:00
// Check for valid path types before we proceed
if ( ! ( path . GetPathType () & PATHFIND_NOT_USING_PATH ))
if ( path . GetPathType () & ~ ( PATHFIND_NORMAL | PATHFIND_SHORTCUT | PATHFIND_INCOMPLETE | PATHFIND_FARFROMPOLY_END ))
return ;
2020-05-01 13:43:24 +02:00
2020-03-04 20:59:04 +01:00
G3D :: Vector3 result = path . GetPath (). back ();
destx = result . x ;
desty = result . y ;
destz = result . z ;
2020-05-02 00:06:34 +02:00
// check static LOS
2020-05-01 13:43:24 +02:00
float halfHeight = GetCollisionHeight () * 0.5f ;
2020-05-16 17:40:08 +02:00
bool col = false ;
2020-05-01 13:43:24 +02:00
2020-05-16 17:40:08 +02:00
// Unit is flying, check for potential collision via vmaps
if ( path . GetPathType () & PATHFIND_NOT_USING_PATH )
2020-05-02 00:06:34 +02:00
{
2022-12-31 18:22:40 +01:00
col = VMAP :: VMapFactory :: createOrGetVMapManager () -> getObjectHitPos ( PhasingHandler :: GetTerrainMapId ( GetPhaseShift (), GetMapId (), GetMap () -> GetTerrain (), pos . m_positionX , pos . m_positionY ),
2020-05-16 17:40:08 +02:00
pos . m_positionX , pos . m_positionY , pos . m_positionZ + halfHeight ,
destx , desty , destz + halfHeight ,
destx , desty , destz , - 0.5f );
destz -= halfHeight ;
// Collided with static LOS object, move back to collision point
if ( col )
{
destx -= CONTACT_DISTANCE * std :: cos ( angle );
desty -= CONTACT_DISTANCE * std :: sin ( angle );
dist = std :: sqrt (( pos . m_positionX - destx ) * ( pos . m_positionX - destx ) + ( pos . m_positionY - desty ) * ( pos . m_positionY - desty ));
}
2020-05-01 13:43:24 +02:00
}
2010-07-21 02:06:51 +02:00
2012-02-11 19:23:18 -05:00
// check dynamic collision
2020-05-02 00:06:34 +02:00
col = GetMap () -> getObjectHitPos ( GetPhaseShift (),
2018-04-07 00:21:51 +02:00
pos . m_positionX , pos . m_positionY , pos . m_positionZ + halfHeight ,
destx , desty , destz + halfHeight ,
destx , desty , destz , - 0.5f );
2012-02-11 19:23:18 -05:00
2019-02-20 18:35:35 +01:00
destz -= halfHeight ;
2020-04-29 23:52:34 +02:00
// Collided with a gameobject, move back to collision point
2012-02-11 19:23:18 -05:00
if ( col )
{
2012-10-02 15:06:19 +02:00
destx -= CONTACT_DISTANCE * std :: cos ( angle );
desty -= CONTACT_DISTANCE * std :: sin ( angle );
2020-04-29 23:52:34 +02:00
dist = std :: sqrt (( pos . m_positionX - destx ) * ( pos . m_positionX - destx ) + ( pos . m_positionY - desty ) * ( pos . m_positionY - desty ));
2010-07-21 02:06:51 +02:00
}
2019-04-07 20:15:40 +02:00
float groundZ = VMAP_INVALID_HEIGHT_VALUE ;
2010-07-21 02:06:51 +02:00
Trinity :: NormalizeMapCoord ( pos . m_positionX );
Trinity :: NormalizeMapCoord ( pos . m_positionY );
2020-04-29 23:52:34 +02:00
UpdateAllowedPositionZ ( destx , desty , destz , & groundZ );
2012-08-20 10:44:14 +02:00
pos . SetOrientation ( GetOrientation ());
2020-04-29 23:52:34 +02:00
pos . Relocate ( destx , desty , destz );
2019-04-07 20:15:40 +02:00
// position has no ground under it (or is too far away)
if ( groundZ <= INVALID_HEIGHT )
{
if ( Unit const * unit = ToUnit ())
{
// unit can fly, ignore.
if ( unit -> CanFly ())
return ;
// fall back to gridHeight if any
float gridHeight = GetMap () -> GetGridHeight ( GetPhaseShift (), pos . m_positionX , pos . m_positionY );
if ( gridHeight > INVALID_HEIGHT )
pos . m_positionZ = gridHeight + unit -> GetHoverOffset ();
}
}
2010-07-21 02:06:51 +02:00
}
2024-03-02 00:07:47 +01:00
void WorldObject :: PlayDistanceSound ( uint32 soundId , Player const * target /*= nullptr*/ ) const
2009-03-22 19:20:03 -06:00
{
if ( target )
2016-12-03 16:45:30 +01:00
target -> SendDirectMessage ( WorldPackets :: Misc :: PlaySpeakerbotSound ( GetGUID (), soundId ). Write ());
2009-03-22 19:20:03 -06:00
else
2016-12-03 16:45:30 +01:00
SendMessageToSet ( WorldPackets :: Misc :: PlaySpeakerbotSound ( GetGUID (), soundId ). Write (), true );
2009-03-22 19:20:03 -06:00
}
2009-10-17 15:51:44 -07:00
2024-03-02 00:07:47 +01:00
void WorldObject :: StopDistanceSound ( Player const * target /*= nullptr*/ ) const
{
if ( target )
target -> SendDirectMessage ( WorldPackets :: Misc :: StopSpeakerbotSound ( GetGUID ()). Write ());
else
SendMessageToSet ( WorldPackets :: Misc :: StopSpeakerbotSound ( GetGUID ()). Write (), true );
}
void WorldObject :: PlayDirectSound ( uint32 soundId , Player const * target /*= nullptr*/ , uint32 broadcastTextId /*= 0*/ ) const
2009-03-22 19:20:03 -06:00
{
if ( target )
2020-12-27 22:43:36 +01:00
target -> SendDirectMessage ( WorldPackets :: Misc :: PlaySound ( GetGUID (), soundId , broadcastTextId ). Write ());
2009-03-22 19:20:03 -06:00
else
2020-12-27 22:43:36 +01:00
SendMessageToSet ( WorldPackets :: Misc :: PlaySound ( GetGUID (), soundId , broadcastTextId ). Write (), true );
2009-03-22 19:20:03 -06:00
}
2009-10-17 15:51:44 -07:00
2024-03-02 00:07:47 +01:00
void WorldObject :: PlayDirectMusic ( uint32 musicId , Player const * target /*= nullptr*/ ) const
2016-10-05 22:34:04 +02:00
{
if ( target )
target -> SendDirectMessage ( WorldPackets :: Misc :: PlayMusic ( musicId ). Write ());
else
SendMessageToSet ( WorldPackets :: Misc :: PlayMusic ( musicId ). Write (), true );
}
2024-03-02 00:07:47 +01:00
void WorldObject :: PlayObjectSound ( int32 soundKitId , ObjectGuid targetObjectGUID , Player const * target /*= nullptr*/ , int32 broadcastTextId /*= 0*/ ) const
2023-08-13 23:57:47 +02:00
{
WorldPackets :: Misc :: PlayObjectSound pkt ;
pkt . TargetObjectGUID = targetObjectGUID ;
pkt . SourceObjectGUID = GetGUID ();
pkt . SoundKitID = soundKitId ;
pkt . Position = GetPosition ();
pkt . BroadcastTextID = broadcastTextId ;
if ( target )
target -> SendDirectMessage ( pkt . Write ());
else
SendMessageToSet ( pkt . Write (), true );
}
2009-08-28 14:19:14 -05:00
void WorldObject :: DestroyForNearbyPlayers ()
{
2010-04-07 19:14:10 +02:00
if ( ! IsInWorld ())
2009-08-28 14:19:14 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-03-14 00:22:53 +01:00
std :: list < Player *> targets ;
2011-07-31 21:57:23 +02:00
Trinity :: AnyPlayerInObjectRangeCheck check ( this , GetVisibilityRange (), false );
2010-03-14 00:22:53 +01:00
Trinity :: PlayerListSearcher < Trinity :: AnyPlayerInObjectRangeCheck > searcher ( this , targets , check );
2017-05-13 19:37:33 +02:00
Cell :: VisitWorldObjects ( this , searcher , GetVisibilityRange ());
2010-03-14 00:22:53 +01:00
for ( std :: list < Player *>:: const_iterator iter = targets . begin (); iter != targets . end (); ++ iter )
2009-08-28 14:19:14 -05:00
{
2011-11-07 11:06:39 -06:00
Player * player = ( * iter );
2009-10-17 15:51:44 -07:00
2011-11-07 11:06:39 -06:00
if ( player == this )
2009-08-28 14:19:14 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2011-11-07 11:06:39 -06:00
if ( ! player -> HaveAtClient ( this ))
2009-08-28 14:19:14 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2024-03-14 11:37:32 +01:00
if ( Unit const * unit = ToUnit (); unit && unit -> GetCharmerGUID () == player -> GetGUID ()) /// @todo this is for puppet
2009-08-28 14:19:14 -05:00
continue ;
2009-10-17 16:20:24 -07:00
2011-11-07 11:06:39 -06:00
DestroyForPlayer ( player );
player -> m_clientGUIDs . erase ( GetGUID ());
2009-08-28 14:19:14 -05:00
}
}
2010-02-03 16:53:40 +02:00
2010-02-27 15:25:14 +02:00
void WorldObject :: UpdateObjectVisibility ( bool /*forced*/ )
2010-02-03 16:53:40 +02:00
{
2010-02-27 15:25:14 +02:00
//updates object's visibility for nearby players
2022-01-29 19:44:55 +01:00
WorldObject * objects [] = { this };
Trinity :: VisibleChangesNotifier notifier ({ std :: begin ( objects ), std :: end ( objects ) });
2017-05-13 19:37:33 +02:00
Cell :: VisitWorldObjects ( this , notifier , GetVisibilityRange ());
2010-02-03 16:53:40 +02:00
}
struct WorldObjectChangeAccumulator
{
2011-08-30 13:46:36 +02:00
UpdateDataMapType & i_updateDatas ;
WorldObject & i_object ;
2014-09-14 16:14:12 +02:00
GuidSet plr_list ;
2013-10-27 22:27:46 +01:00
WorldObjectChangeAccumulator ( WorldObject & obj , UpdateDataMapType & d ) : i_updateDatas ( d ), i_object ( obj ) { }
2010-02-03 16:53:40 +02:00
void Visit ( PlayerMapType & m )
{
2020-08-14 17:06:03 +02:00
Player * source = nullptr ;
2010-02-03 16:53:40 +02:00
for ( PlayerMapType :: iterator iter = m . begin (); iter != m . end (); ++ iter )
{
2013-06-11 21:25:12 -02:30
source = iter -> GetSource ();
2011-08-30 13:46:36 +02:00
BuildPacket ( source );
if ( ! source -> GetSharedVisionList (). empty ())
2010-02-03 16:53:40 +02:00
{
2011-08-30 13:46:36 +02:00
SharedVisionList :: const_iterator it = source -> GetSharedVisionList (). begin ();
for (; it != source -> GetSharedVisionList (). end (); ++ it )
2010-02-03 16:53:40 +02:00
BuildPacket ( * it );
}
}
}
void Visit ( CreatureMapType & m )
{
2020-08-14 17:06:03 +02:00
Creature * source = nullptr ;
2010-02-03 16:53:40 +02:00
for ( CreatureMapType :: iterator iter = m . begin (); iter != m . end (); ++ iter )
{
2013-06-11 21:25:12 -02:30
source = iter -> GetSource ();
2011-08-30 13:46:36 +02:00
if ( ! source -> GetSharedVisionList (). empty ())
2010-02-03 16:53:40 +02:00
{
2011-08-30 13:46:36 +02:00
SharedVisionList :: const_iterator it = source -> GetSharedVisionList (). begin ();
for (; it != source -> GetSharedVisionList (). end (); ++ it )
2010-02-03 16:53:40 +02:00
BuildPacket ( * it );
}
}
}
2011-08-30 13:46:36 +02:00
2010-02-03 16:53:40 +02:00
void Visit ( DynamicObjectMapType & m )
{
2020-08-14 17:06:03 +02:00
DynamicObject * source = nullptr ;
2010-02-03 16:53:40 +02:00
for ( DynamicObjectMapType :: iterator iter = m . begin (); iter != m . end (); ++ iter )
{
2013-06-11 21:25:12 -02:30
source = iter -> GetSource ();
2014-09-14 16:14:12 +02:00
ObjectGuid guid = source -> GetCasterGUID ();
2011-08-30 13:46:36 +02:00
2014-09-14 16:14:12 +02:00
if ( guid . IsPlayer ())
2010-02-03 16:53:40 +02:00
{
2020-09-04 13:38:24 +02:00
//Caster may be nullptr if DynObj is in removelist
2011-09-15 14:08:17 +02:00
if ( Player * caster = ObjectAccessor :: FindPlayer ( guid ))
2019-06-03 20:40:34 +02:00
if ( * caster -> m_activePlayerData -> FarsightObject == source -> GetGUID ())
2010-02-03 16:53:40 +02:00
BuildPacket ( caster );
}
}
}
2011-08-30 13:46:36 +02:00
2011-11-07 11:06:39 -06:00
void BuildPacket ( Player * player )
2010-02-03 16:53:40 +02:00
{
// Only send update once to a player
2011-11-07 11:06:39 -06:00
if ( plr_list . find ( player -> GetGUID ()) == plr_list . end () && player -> HaveAtClient ( & i_object ))
2010-02-03 16:53:40 +02:00
{
2011-11-07 11:06:39 -06:00
i_object . BuildFieldsUpdate ( player , i_updateDatas );
plr_list . insert ( player -> GetGUID ());
2010-02-03 16:53:40 +02:00
}
}
2013-10-27 22:27:46 +01:00
template < class SKIP > void Visit ( GridRefManager < SKIP > & ) { }
2010-02-03 16:53:40 +02:00
};
void WorldObject :: BuildUpdate ( UpdateDataMapType & data_map )
{
WorldObjectChangeAccumulator notifier ( * this , data_map );
//we must build packets for all visible players
2017-05-13 19:37:33 +02:00
Cell :: VisitWorldObjects ( this , notifier , GetVisibilityRange ());
2010-02-03 16:53:40 +02:00
ClearUpdateMask ( false );
}
2010-08-08 20:32:14 +02:00
2020-03-19 20:42:44 +01:00
bool WorldObject :: AddToObjectUpdate ()
2015-02-25 00:13:14 +01:00
{
GetMap () -> AddUpdateObject ( this );
2020-03-19 20:42:44 +01:00
return true ;
2015-02-25 00:13:14 +01:00
}
void WorldObject :: RemoveFromObjectUpdate ()
{
GetMap () -> RemoveUpdateObject ( this );
}
2014-09-14 16:14:12 +02:00
ObjectGuid WorldObject :: GetTransGUID () const
2010-08-08 20:32:14 +02:00
{
if ( GetTransport ())
2022-05-25 22:14:32 +02:00
return GetTransport () -> GetTransportGUID ();
2014-09-14 16:14:12 +02:00
return ObjectGuid :: Empty ;
2010-08-21 20:08:47 +02:00
}
2015-03-16 07:28:49 +01:00
2017-06-08 00:25:06 +02:00
float WorldObject :: GetFloorZ () const
{
if ( ! IsInWorld ())
return m_staticFloorZ ;
2021-02-21 17:29:09 +01:00
return std :: max < float > ( m_staticFloorZ , GetMap () -> GetGameObjectFloor ( GetPhaseShift (), GetPositionX (), GetPositionY (), GetPositionZ () + Z_OFFSET_FIND_HEIGHT ));
2017-12-29 22:32:07 +01:00
}
float WorldObject :: GetMapWaterOrGroundLevel ( float x , float y , float z , float * ground /* = nullptr*/ ) const
{
2024-02-29 14:43:37 +01:00
bool swimming = [ & ]()
{
if ( Creature const * creature = ToCreature ())
return ( ! creature -> CannotPenetrateWater () && ! creature -> HasAuraType ( SPELL_AURA_WATER_WALK ));
else if ( Unit const * unit = ToUnit ())
return ! unit -> HasAuraType ( SPELL_AURA_WATER_WALK );
return true ;
}();
return GetMap () -> GetWaterOrGroundLevel ( GetPhaseShift (), x , y , z , ground , swimming , GetCollisionHeight ());
2017-12-29 22:32:07 +01:00
}
float WorldObject :: GetMapHeight ( float x , float y , float z , bool vmap /* = true*/ , float distanceToSearch /* = DEFAULT_HEIGHT_SEARCH*/ ) const
{
if ( z != MAX_HEIGHT )
2021-02-21 17:29:09 +01:00
z += Z_OFFSET_FIND_HEIGHT ;
2017-12-29 22:32:07 +01:00
return GetMap () -> GetHeight ( GetPhaseShift (), x , y , z , vmap , distanceToSearch );
2017-06-08 00:25:06 +02:00
}
2018-12-14 22:01:16 +01:00
std :: string WorldObject :: GetDebugInfo () const
{
std :: stringstream sstr ;
sstr << WorldLocation :: GetDebugInfo () << " \n "
<< Object :: GetDebugInfo () << " \n "
<< "Name: " << GetName ();
return sstr . str ();
}
2016-10-13 22:28:08 -03:00
template TC_GAME_API void WorldObject :: GetGameObjectListWithEntryInGrid ( std :: list < GameObject *>& , uint32 , float ) const ;
template TC_GAME_API void WorldObject :: GetGameObjectListWithEntryInGrid ( std :: deque < GameObject *>& , uint32 , float ) const ;
template TC_GAME_API void WorldObject :: GetGameObjectListWithEntryInGrid ( std :: vector < GameObject *>& , uint32 , float ) const ;
2016-10-13 01:23:02 -03:00
2023-07-30 13:55:34 +02:00
template TC_GAME_API void WorldObject :: GetGameObjectListWithOptionsInGrid ( std :: list < GameObject *>& , float , FindGameObjectOptions const & ) const ;
template TC_GAME_API void WorldObject :: GetGameObjectListWithOptionsInGrid ( std :: deque < GameObject *>& , float , FindGameObjectOptions const & ) const ;
template TC_GAME_API void WorldObject :: GetGameObjectListWithOptionsInGrid ( std :: vector < GameObject *>& , float , FindGameObjectOptions const & ) const ;
2016-10-13 22:28:08 -03:00
template TC_GAME_API void WorldObject :: GetCreatureListWithEntryInGrid ( std :: list < Creature *>& , uint32 , float ) const ;
template TC_GAME_API void WorldObject :: GetCreatureListWithEntryInGrid ( std :: deque < Creature *>& , uint32 , float ) const ;
template TC_GAME_API void WorldObject :: GetCreatureListWithEntryInGrid ( std :: vector < Creature *>& , uint32 , float ) const ;
2016-10-13 01:23:02 -03:00
2022-12-27 16:39:05 +01:00
template TC_GAME_API void WorldObject :: GetCreatureListWithOptionsInGrid ( std :: list < Creature *>& , float , FindCreatureOptions const & ) const ;
template TC_GAME_API void WorldObject :: GetCreatureListWithOptionsInGrid ( std :: deque < Creature *>& , float , FindCreatureOptions const & ) const ;
template TC_GAME_API void WorldObject :: GetCreatureListWithOptionsInGrid ( std :: vector < Creature *>& , float , FindCreatureOptions const & ) const ;
2021-08-10 13:08:49 +02:00
template TC_GAME_API void WorldObject :: GetPlayerListInGrid ( std :: list < Player *>& , float , bool ) const ;
template TC_GAME_API void WorldObject :: GetPlayerListInGrid ( std :: deque < Player *>& , float , bool ) const ;
template TC_GAME_API void WorldObject :: GetPlayerListInGrid ( std :: vector < Player *>& , float , bool ) const ;