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
2017-05-18 23:52:58 +02:00
#include "GameObject.h"
#include "ArtifactPackets.h"
2019-11-22 11:02:04 +01:00
#include "AzeriteItem.h"
#include "AzeritePackets.h"
2013-08-07 10:31:13 +02:00
#include "Battleground.h"
2013-06-15 15:16:36 +02:00
#include "CellImpl.h"
#include "CreatureAISelector.h"
2017-05-18 23:52:58 +02:00
#include "DatabaseEnv.h"
#include "GameObjectAI.h"
2013-06-15 15:16:36 +02:00
#include "GameObjectModel.h"
2015-04-11 11:11:43 +02:00
#include "GameObjectPackets.h"
2017-03-02 02:19:25 +01:00
#include "GameTime.h"
2020-09-04 13:38:24 +02:00
#include "GossipDef.h"
2013-06-15 15:16:36 +02:00
#include "GridNotifiersImpl.h"
#include "Group.h"
2011-05-05 10:23:00 +02:00
#include "GroupMgr.h"
2017-06-04 01:00:45 +02:00
#include "Item.h"
2017-05-18 23:52:58 +02:00
#include "Log.h"
#include "LootMgr.h"
2015-03-28 18:12:26 +01:00
#include "MiscPackets.h"
2017-06-04 01:00:45 +02:00
#include "ObjectAccessor.h"
2013-06-15 15:16:36 +02:00
#include "ObjectMgr.h"
#include "OutdoorPvPMgr.h"
2018-01-28 11:37:20 +01:00
#include "PhasingHandler.h"
2010-06-25 03:50:19 -06:00
#include "PoolMgr.h"
2019-07-27 02:52:13 +02:00
#include "QueryPackets.h"
2013-06-15 15:16:36 +02:00
#include "ScriptMgr.h"
2008-10-11 14:16:25 -05:00
#include "SpellMgr.h"
2017-05-18 23:52:58 +02:00
#include "Transport.h"
2008-10-11 14:16:25 -05:00
#include "World.h"
2019-04-24 20:39:01 +02:00
#include <G3D/Box.h>
#include <G3D/CoordinateFrame.h>
2017-05-28 16:34:44 +02:00
#include <G3D/Quat.h>
2020-04-03 17:41:48 +02:00
#include <sstream>
2012-02-03 16:03:52 -05:00
2019-07-27 02:52:13 +02:00
void GameObjectTemplate :: InitializeQueryData ()
{
for ( uint8 loc = LOCALE_enUS ; loc < TOTAL_LOCALES ; ++ loc )
2018-04-19 17:35:57 +02:00
QueryData [ loc ] = BuildQueryData ( static_cast < LocaleConstant > ( loc ));
2019-07-27 02:52:13 +02:00
}
WorldPacket GameObjectTemplate :: BuildQueryData ( LocaleConstant loc ) const
{
WorldPackets :: Query :: QueryGameObjectResponse queryTemp ;
queryTemp . GameObjectID = entry ;
queryTemp . Allow = true ;
WorldPackets :: Query :: GameObjectStats & stats = queryTemp . Stats ;
stats . Type = type ;
stats . DisplayID = displayId ;
stats . Name [ 0 ] = name ;
stats . IconName = IconName ;
stats . CastBarCaption = castBarCaption ;
stats . UnkString = unk1 ;
if ( loc != LOCALE_enUS )
if ( GameObjectLocale const * gameObjectLocale = sObjectMgr -> GetGameObjectLocale ( entry ))
{
ObjectMgr :: GetLocaleString ( gameObjectLocale -> Name , loc , stats . Name [ 0 ]);
ObjectMgr :: GetLocaleString ( gameObjectLocale -> CastBarCaption , loc , stats . CastBarCaption );
ObjectMgr :: GetLocaleString ( gameObjectLocale -> Unk1 , loc , stats . UnkString );
}
stats . Size = size ;
if ( std :: vector < uint32 > const * items = sObjectMgr -> GetGameObjectQuestItemList ( entry ))
for ( int32 item : * items )
stats . QuestItems . push_back ( item );
memcpy ( stats . Data , raw . data , MAX_GAMEOBJECT_DATA * sizeof ( int32 ));
2020-12-09 00:03:10 +01:00
stats . ContentTuningId = ContentTuningId ;
2019-07-27 02:52:13 +02:00
2018-04-19 17:35:57 +02:00
queryTemp . Write ();
2018-08-22 19:44:21 +02:00
queryTemp . ShrinkToFit ();
2018-04-19 17:35:57 +02:00
return queryTemp . Move ();
2019-07-27 02:52:13 +02:00
}
2017-06-04 01:00:45 +02:00
bool QuaternionData :: isUnit () const
{
2020-09-04 13:38:24 +02:00
return fabs ( x * x + y * y + z * z + w * w - 1.0f ) < 1e-5 f ;
2017-06-04 01:00:45 +02:00
}
2018-10-28 11:07:56 +02:00
void QuaternionData :: toEulerAnglesZYX ( float & Z , float & Y , float & X ) const
{
G3D :: Matrix3 ( G3D :: Quat ( x , y , z , w )). toEulerAnglesZYX ( Z , Y , X );
}
2017-06-04 01:00:45 +02:00
QuaternionData QuaternionData :: fromEulerAnglesZYX ( float Z , float Y , float X )
{
G3D :: Quat quat ( G3D :: Matrix3 :: fromEulerAnglesZYX ( Z , Y , X ));
return QuaternionData ( quat . x , quat . y , quat . z , quat . w );
}
2013-10-16 18:37:29 +02:00
GameObject :: GameObject () : WorldObject ( false ), MapObject (),
2021-03-14 21:23:07 +02:00
m_model ( nullptr ), m_goValue (), m_AI ( nullptr ), m_respawnCompatibilityMode ( false ), _animKitId ( 0 ), _worldEffectID ( 0 )
2008-10-11 14:16:25 -05:00
{
m_objectType |= TYPEMASK_GAMEOBJECT ;
m_objectTypeId = TYPEID_GAMEOBJECT ;
2009-10-17 15:51:44 -07:00
2018-10-04 18:50:21 +02:00
m_updateFlag . Stationary = true ;
m_updateFlag . Rotation = true ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
m_respawnTime = 0 ;
2010-01-30 08:05:08 -07:00
m_respawnDelayTime = 300 ;
2018-02-13 22:08:49 +01:00
m_despawnDelay = 0 ;
2008-10-11 14:16:25 -05:00
m_lootState = GO_NOT_READY ;
m_spawnedByDefault = true ;
m_usetimes = 0 ;
m_spellId = 0 ;
m_cooldownTime = 0 ;
2016-08-28 05:25:09 -03:00
m_prevGoState = GO_STATE_ACTIVE ;
2016-05-11 23:24:26 +02:00
m_goInfo = nullptr ;
m_goData = nullptr ;
2016-07-30 11:34:02 +02:00
m_packedRotation = 0 ;
2016-08-29 19:20:06 +01:00
m_goTemplateAddon = nullptr ;
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
m_spawnId = UI64LIT ( 0 );
2009-10-28 16:31:19 -07:00
2010-03-11 22:55:02 +01:00
m_groupLootTimer = 0 ;
2017-03-02 00:19:28 +01:00
m_lootGenerationTime = 0 ;
2010-03-11 22:55:02 +01:00
2009-10-28 16:31:19 -07:00
ResetLootMode (); // restore default loot mode
2013-12-01 16:19:30 +01:00
m_stationaryPosition . Relocate ( 0.0f , 0.0f , 0.0f , 0.0f );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
GameObject ::~ GameObject ()
{
2011-10-18 09:51:55 +02:00
delete m_AI ;
2012-02-03 16:03:52 -05:00
delete m_model ;
2014-12-28 22:29:01 +01:00
if ( m_goInfo && m_goInfo -> type == GAMEOBJECT_TYPE_TRANSPORT )
2014-09-29 20:10:08 +02:00
delete m_goValue . Transport . StopFrames ;
2010-04-07 19:14:10 +02:00
//if (m_uint32Values) // field array can be not exist if GameOBject not loaded
2009-07-01 18:23:36 -05:00
// CleanupsBeforeDelete();
}
2009-10-17 15:51:44 -07:00
2016-03-11 17:09:26 +01:00
void GameObject :: AIM_Destroy ()
2010-10-23 23:31:37 +02:00
{
2014-09-19 03:30:02 +01:00
delete m_AI ;
2016-03-11 17:09:26 +01:00
m_AI = nullptr ;
}
bool GameObject :: AIM_Initialize ()
{
AIM_Destroy ();
2010-10-23 23:31:37 +02:00
m_AI = FactorySelector :: SelectGameObjectAI ( this );
2011-10-18 09:51:55 +02:00
if ( ! m_AI )
return false ;
2010-10-23 23:31:37 +02:00
m_AI -> InitializeAI ();
return true ;
}
2017-06-01 02:29:20 -03:00
std :: string const & GameObject :: GetAIName () const
2010-10-23 23:31:37 +02:00
{
2017-06-01 02:29:20 -03:00
return sObjectMgr -> GetGameObjectTemplate ( GetEntry ()) -> AIName ;
2010-10-23 23:31:37 +02:00
}
2014-06-13 11:16:17 +02:00
void GameObject :: CleanupsBeforeDelete ( bool finalCleanup )
2009-07-01 18:23:36 -05:00
{
2014-06-13 11:16:17 +02:00
WorldObject :: CleanupsBeforeDelete ( finalCleanup );
2010-02-14 19:13:14 -07:00
2019-06-03 20:40:34 +02:00
RemoveFromOwner ();
2011-10-31 10:46:39 -04:00
}
2009-10-17 15:51:44 -07:00
2011-10-31 10:46:39 -04:00
void GameObject :: RemoveFromOwner ()
{
2014-09-14 16:14:12 +02:00
ObjectGuid ownerGUID = GetOwnerGUID ();
2011-10-31 10:46:39 -04:00
if ( ! ownerGUID )
return ;
if ( Unit * owner = ObjectAccessor :: GetUnit ( * this , ownerGUID ))
{
owner -> RemoveGameObject ( this , false );
ASSERT ( ! GetOwnerGUID ());
return ;
2009-07-01 18:23:36 -05:00
}
2011-10-31 10:46:39 -04:00
2015-08-08 19:23:10 +02:00
// This happens when a mage portal is despawned after the caster changes map (for example using the portal)
TC_LOG_DEBUG ( "misc" , "Removed GameObject (%s SpellId: %u LinkedGO: %u) that just lost any reference to the owner (%s) GO list" ,
2014-10-22 19:33:06 +02:00
GetGUID (). ToString (). c_str (), m_spellId , GetGOInfo () -> GetLinkedGameObjectEntry (), ownerGUID . ToString (). c_str ());
2014-09-14 16:14:12 +02:00
SetOwnerGUID ( ObjectGuid :: Empty );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void GameObject :: AddToWorld ()
{
///- Register the gameobject for guid lookup
2010-04-07 19:14:10 +02:00
if ( ! IsInWorld ())
2009-03-25 15:54:23 -06:00
{
2010-04-07 19:14:10 +02:00
if ( m_zoneScript )
2010-12-05 18:04:10 +01:00
m_zoneScript -> OnGameObjectCreate ( this );
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
GetMap () -> GetObjectsStore (). Insert < GameObject > ( GetGUID (), this );
if ( m_spawnId )
GetMap () -> GetGameObjectBySpawnIdStore (). insert ( std :: make_pair ( m_spawnId , this ));
2012-09-04 14:04:47 -04:00
2012-05-30 08:01:02 +02:00
// The state can be changed after GameObject::Create but before GameObject::AddToWorld
2014-03-23 00:23:30 +01:00
bool toggledState = GetGoType () == GAMEOBJECT_TYPE_CHEST ? getLootState () == GO_READY : ( GetGoState () == GO_STATE_READY || IsTransport ());
2012-02-12 08:41:59 -05:00
if ( m_model )
2014-08-16 01:06:30 +02:00
{
if ( Transport * trans = ToTransport ())
trans -> SetDelayedAddModelToMap ();
else
GetMap () -> InsertGameObjectModel ( * m_model );
}
2012-02-12 08:41:59 -05:00
2012-09-04 14:04:47 -04:00
EnableCollision ( toggledState );
2009-03-25 15:54:23 -06:00
WorldObject :: AddToWorld ();
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void GameObject :: RemoveFromWorld ()
{
///- Remove the gameobject from the accessor
2010-04-07 19:14:10 +02:00
if ( IsInWorld ())
2009-03-25 15:54:23 -06:00
{
2010-04-07 19:14:10 +02:00
if ( m_zoneScript )
2010-12-05 18:04:10 +01:00
m_zoneScript -> OnGameObjectRemove ( this );
2009-10-17 15:51:44 -07:00
2011-10-31 10:46:39 -04:00
RemoveFromOwner ();
2012-02-03 16:03:52 -05:00
if ( m_model )
2012-08-19 11:07:21 -05:00
if ( GetMap () -> ContainsGameObjectModel ( * m_model ))
GetMap () -> RemoveGameObjectModel ( * m_model );
2015-02-25 00:13:14 +01:00
2009-03-25 15:54:23 -06:00
WorldObject :: RemoveFromWorld ();
2015-02-25 00:13:14 +01:00
if ( m_spawnId )
Trinity :: Containers :: MultimapErasePair ( GetMap () -> GetGameObjectBySpawnIdStore (), m_spawnId , this );
GetMap () -> GetObjectsStore (). Remove < GameObject > ( GetGUID ());
2009-03-25 15:54:23 -06:00
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
bool GameObject :: Create ( uint32 entry , Map * map , Position const & pos , QuaternionData const & rotation , uint32 animProgress , GOState goState , uint32 artKit , bool dynamic , ObjectGuid :: LowType spawnid )
2008-10-11 14:16:25 -05:00
{
2009-07-16 11:49:00 +08:00
ASSERT ( map );
SetMap ( map );
2009-10-17 15:51:44 -07:00
2016-07-30 18:50:44 -03:00
Relocate ( pos );
m_stationaryPosition . Relocate ( pos );
2010-04-07 19:14:10 +02:00
if ( ! IsPositionValid ())
2008-10-11 14:16:25 -05:00
{
2018-01-27 21:47:37 +01:00
TC_LOG_ERROR ( "misc" , "Gameobject (Spawn id: " UI64FMTD " Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)" , GetSpawnId (), entry , pos . GetPositionX (), pos . GetPositionY ());
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
// Set if this object can handle dynamic spawns
if ( ! dynamic )
SetRespawnCompatibilityMode ();
2017-07-23 00:01:11 +02:00
UpdatePositionData ();
2010-09-02 10:40:32 +02:00
SetZoneScript ();
if ( m_zoneScript )
{
2018-01-27 21:47:37 +01:00
entry = m_zoneScript -> GetGameObjectEntry ( m_spawnId , entry );
if ( ! entry )
2010-09-02 10:40:32 +02:00
return false ;
}
2018-01-27 21:47:37 +01:00
GameObjectTemplate const * goInfo = sObjectMgr -> GetGameObjectTemplate ( entry );
if ( ! goInfo )
2008-10-11 14:16:25 -05:00
{
2018-01-27 21:47:37 +01:00
TC_LOG_ERROR ( "sql.sql" , "Gameobject (Spawn id: " UI64FMTD " Entry: %u) not created: non-existing entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f)" , GetSpawnId (), entry , map -> GetId (), pos . GetPositionX (), pos . GetPositionY (), pos . GetPositionZ ());
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2018-01-27 21:47:37 +01:00
if ( goInfo -> type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT )
2015-04-23 15:47:50 +02:00
{
2018-01-27 21:47:37 +01:00
TC_LOG_ERROR ( "sql.sql" , "Gameobject (Spawn id: " UI64FMTD " Entry: %u) not created: gameobject type GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT cannot be manually created." , GetSpawnId (), entry );
2015-04-23 15:47:50 +02:00
return false ;
}
2016-06-12 00:11:00 +02:00
ObjectGuid guid ;
2018-01-27 21:47:37 +01:00
if ( goInfo -> type != GAMEOBJECT_TYPE_TRANSPORT )
guid = ObjectGuid :: Create < HighGuid :: GameObject > ( map -> GetId (), goInfo -> entry , map -> GenerateLowGuid < HighGuid :: GameObject > ());
2016-06-12 00:11:00 +02:00
else
{
guid = ObjectGuid :: Create < HighGuid :: Transport > ( map -> GenerateLowGuid < HighGuid :: Transport > ());
2018-10-04 18:50:21 +02:00
m_updateFlag . ServerTime = true ;
2016-06-12 00:11:00 +02:00
}
2013-10-16 18:37:29 +02:00
2016-06-12 00:11:00 +02:00
Object :: _Create ( guid );
2009-10-17 15:51:44 -07:00
2018-01-27 21:47:37 +01:00
m_goInfo = goInfo ;
m_goTemplateAddon = sObjectMgr -> GetGameObjectTemplateAddon ( entry );
2009-10-17 15:51:44 -07:00
2018-01-27 21:47:37 +01:00
if ( goInfo -> type >= MAX_GAMEOBJECT_TYPE )
2008-10-11 14:16:25 -05:00
{
2018-01-27 21:47:37 +01:00
TC_LOG_ERROR ( "sql.sql" , "Gameobject (%s Spawn id: " UI64FMTD " Entry: %u) not created: non-existing GO type '%u' in `gameobject_template`. It will crash client if created." , guid . ToString (). c_str (), GetSpawnId (), entry , goInfo -> type );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2019-04-24 20:39:01 +02:00
SetLocalRotation ( rotation . x , rotation . y , rotation . z , rotation . w );
2016-07-30 11:34:02 +02:00
GameObjectAddon const * gameObjectAddon = sObjectMgr -> GetGameObjectAddon ( GetSpawnId ());
2009-10-17 15:51:44 -07:00
2016-07-30 11:34:02 +02:00
// For most of gameobjects is (0, 0, 0, 1) quaternion, there are only some transports with not standard rotation
2017-05-28 16:34:44 +02:00
QuaternionData parentRotation ;
2016-07-30 11:34:02 +02:00
if ( gameObjectAddon )
parentRotation = gameObjectAddon -> ParentRotation ;
SetParentRotation ( parentRotation );
2009-10-17 15:51:44 -07:00
2018-01-27 21:47:37 +01:00
SetObjectScale ( goInfo -> size );
2009-10-17 15:51:44 -07:00
2018-02-24 20:57:55 -03:00
if ( GameObjectOverride const * goOverride = GetGameObjectOverride ())
2016-08-29 19:20:06 +01:00
{
2018-02-24 20:57:55 -03:00
SetFaction ( goOverride -> Faction );
SetFlags ( GameObjectFlags ( goOverride -> Flags ));
}
2017-11-05 20:24:45 +01:00
2018-02-24 20:57:55 -03:00
if ( m_goTemplateAddon )
{
2017-11-05 20:24:45 +01:00
if ( m_goTemplateAddon -> WorldEffectID )
{
2018-10-04 18:50:21 +02:00
m_updateFlag . GameObject = true ;
2017-11-05 20:24:45 +01:00
SetWorldEffectID ( m_goTemplateAddon -> WorldEffectID );
}
2021-01-25 17:17:20 -05:00
if ( m_goTemplateAddon -> AIAnimKitID )
_animKitId = m_goTemplateAddon -> AIAnimKitID ;
2016-08-29 19:20:06 +01:00
}
2009-10-17 15:51:44 -07:00
2018-01-27 21:47:37 +01:00
SetEntry ( goInfo -> entry );
2009-10-17 15:51:44 -07:00
2011-07-22 21:22:05 +02:00
// set name for logs usage, doesn't affect anything ingame
2018-01-27 21:47:37 +01:00
SetName ( goInfo -> name );
2011-07-22 21:22:05 +02:00
2018-01-27 21:47:37 +01:00
SetDisplayId ( goInfo -> displayId );
2009-10-17 15:51:44 -07:00
2020-07-27 08:58:40 +02:00
CreateModel ();
2009-09-04 05:11:27 +02:00
// GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3
2018-01-27 21:47:37 +01:00
SetGoType ( GameobjectTypes ( goInfo -> type ));
m_prevGoState = goState ;
SetGoState ( goState );
2012-05-30 08:01:02 +02:00
SetGoArtKit ( artKit );
2009-10-17 15:51:44 -07:00
2020-03-10 18:43:34 +01:00
SetUpdateFieldValue ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: SpawnTrackingStateAnimID ), sDB2Manager . GetEmptyAnimStateID ());
2018-10-04 18:50:21 +02:00
2018-01-27 21:47:37 +01:00
switch ( goInfo -> type )
2009-05-29 00:18:41 -05:00
{
2013-03-02 23:59:42 +01:00
case GAMEOBJECT_TYPE_FISHINGHOLE :
2018-01-27 21:47:37 +01:00
SetGoAnimProgress ( animProgress );
2014-10-30 20:02:42 +01:00
m_goValue . FishingHole . MaxOpens = urand ( GetGOInfo () -> fishingHole . minRestock , GetGOInfo () -> fishingHole . maxRestock );
2013-03-02 23:59:42 +01:00
break ;
2009-05-29 00:18:41 -05:00
case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING :
2019-06-03 20:40:34 +02:00
{
2014-10-30 20:02:42 +01:00
// TODO: Get the values somehow, no longer in gameobject_template
m_goValue . Building . Health = 20000 /*goinfo->destructibleBuilding.intactNumHits + goinfo->destructibleBuilding.damagedNumHits*/ ;
2012-10-24 13:29:34 +02:00
m_goValue . Building . MaxHealth = m_goValue . Building . Health ;
2010-05-09 06:25:06 +02:00
SetGoAnimProgress ( 255 );
2019-06-03 20:40:34 +02:00
// yes, even after the updatefield rewrite this garbage hack is still in client
QuaternionData reinterpretId ;
memcpy ( & reinterpretId . x , & m_goInfo -> destructibleBuilding . DestructibleModelRec , sizeof ( float ));
SetUpdateFieldValue ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: ParentRotation ), reinterpretId );
2009-05-29 00:18:41 -05:00
break ;
2019-06-03 20:40:34 +02:00
}
2009-11-27 01:41:26 +01:00
case GAMEOBJECT_TYPE_TRANSPORT :
2014-09-29 20:10:08 +02:00
{
2018-01-27 21:47:37 +01:00
m_goValue . Transport . AnimationInfo = sTransportMgr -> GetTransportAnimInfo ( goInfo -> entry );
2014-09-30 22:48:04 +02:00
m_goValue . Transport . PathProgress = getMSTime ();
if ( m_goValue . Transport . AnimationInfo )
m_goValue . Transport . PathProgress -= m_goValue . Transport . PathProgress % GetTransportPeriod (); // align to period
2013-10-16 18:37:29 +02:00
m_goValue . Transport . CurrentSeg = 0 ;
2014-09-29 20:10:08 +02:00
m_goValue . Transport . StateUpdateTimer = 0 ;
m_goValue . Transport . StopFrames = new std :: vector < uint32 > ();
2018-01-27 21:47:37 +01:00
if ( goInfo -> transport . Timeto2ndfloor > 0 )
m_goValue . Transport . StopFrames -> push_back ( goInfo -> transport . Timeto2ndfloor );
if ( goInfo -> transport . Timeto3rdfloor > 0 )
m_goValue . Transport . StopFrames -> push_back ( goInfo -> transport . Timeto3rdfloor );
if ( goInfo -> transport . Timeto4thfloor > 0 )
m_goValue . Transport . StopFrames -> push_back ( goInfo -> transport . Timeto4thfloor );
if ( goInfo -> transport . Timeto5thfloor > 0 )
m_goValue . Transport . StopFrames -> push_back ( goInfo -> transport . Timeto5thfloor );
if ( goInfo -> transport . Timeto6thfloor > 0 )
m_goValue . Transport . StopFrames -> push_back ( goInfo -> transport . Timeto6thfloor );
if ( goInfo -> transport . Timeto7thfloor > 0 )
m_goValue . Transport . StopFrames -> push_back ( goInfo -> transport . Timeto7thfloor );
if ( goInfo -> transport . Timeto8thfloor > 0 )
m_goValue . Transport . StopFrames -> push_back ( goInfo -> transport . Timeto8thfloor );
if ( goInfo -> transport . Timeto9thfloor > 0 )
m_goValue . Transport . StopFrames -> push_back ( goInfo -> transport . Timeto9thfloor );
if ( goInfo -> transport . Timeto10thfloor > 0 )
m_goValue . Transport . StopFrames -> push_back ( goInfo -> transport . Timeto10thfloor );
if ( goInfo -> transport . startOpen )
SetTransportState ( GO_STATE_TRANSPORT_STOPPED , goInfo -> transport . startOpen - 1 );
2014-09-29 20:10:08 +02:00
else
SetTransportState ( GO_STATE_TRANSPORT_ACTIVE );
2018-01-27 21:47:37 +01:00
SetGoAnimProgress ( animProgress );
2010-05-10 12:48:35 +02:00
break ;
2014-09-29 20:10:08 +02:00
}
2010-05-10 12:48:35 +02:00
case GAMEOBJECT_TYPE_FISHINGNODE :
2019-06-03 20:40:34 +02:00
SetLevel ( 1 );
2017-07-04 20:30:16 +02:00
SetGoAnimProgress ( 255 );
2010-11-13 17:18:09 +01:00
break ;
case GAMEOBJECT_TYPE_TRAP :
if ( GetGOInfo () -> trap . stealthed )
{
2011-06-12 02:30:32 +02:00
m_stealth . AddFlag ( STEALTH_TRAP );
2012-01-08 18:22:46 +01:00
m_stealth . AddValue ( STEALTH_TRAP , 70 );
2010-11-13 17:18:09 +01:00
}
2014-10-30 20:02:42 +01:00
if ( GetGOInfo () -> trap . stealthAffected )
2010-11-13 17:18:09 +01:00
{
2011-06-12 02:30:32 +02:00
m_invisibility . AddFlag ( INVISIBILITY_TRAP );
2012-01-08 18:22:46 +01:00
m_invisibility . AddValue ( INVISIBILITY_TRAP , 300 );
2010-11-13 17:18:09 +01:00
}
2010-05-10 12:48:35 +02:00
break ;
2018-04-09 00:05:20 +02:00
case GAMEOBJECT_TYPE_PHASEABLE_MO :
2019-06-03 20:40:34 +02:00
RemoveFlag ( GameObjectFlags ( 0xF00 ));
AddFlag ( GameObjectFlags (( m_goInfo -> phaseableMO . AreaNameSet & 0xF ) << 8 ));
2018-04-09 00:05:20 +02:00
break ;
2018-07-15 12:40:39 +02:00
case GAMEOBJECT_TYPE_CAPTURE_POINT :
2019-06-03 20:40:34 +02:00
SetUpdateFieldValue ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: SpellVisualID ), m_goInfo -> capturePoint . SpellVisual1 );
2018-07-15 12:40:39 +02:00
break ;
2010-05-10 12:48:35 +02:00
default :
2018-01-27 21:47:37 +01:00
SetGoAnimProgress ( animProgress );
2009-11-27 01:41:26 +01:00
break ;
2009-05-29 00:18:41 -05:00
}
2015-04-05 23:51:46 +02:00
2021-01-25 17:17:20 -05:00
if ( gameObjectAddon )
2015-04-05 23:51:46 +02:00
{
2021-01-25 17:17:20 -05:00
if ( gameObjectAddon -> InvisibilityValue )
{
m_invisibility . AddFlag ( gameObjectAddon -> invisibilityType );
m_invisibility . AddValue ( gameObjectAddon -> invisibilityType , gameObjectAddon -> InvisibilityValue );
}
2015-04-05 23:51:46 +02:00
2021-01-25 17:17:20 -05:00
if ( gameObjectAddon -> WorldEffectID )
{
m_updateFlag . GameObject = true ;
SetWorldEffectID ( gameObjectAddon -> WorldEffectID );
}
if ( gameObjectAddon -> AIAnimKitID )
_animKitId = gameObjectAddon -> AIAnimKitID ;
2017-11-05 20:24:45 +01:00
}
2010-05-16 17:45:45 +02:00
LastUsedScriptID = GetGOInfo () -> ScriptId ;
2010-12-06 04:52:33 +01:00
AIM_Initialize ();
2010-05-16 17:45:45 +02:00
2013-02-13 20:24:23 +01:00
// Initialize loot duplicate count depending on raid difficulty
2013-02-14 11:41:20 +01:00
if ( map -> Is25ManRaid ())
2013-02-13 20:24:23 +01:00
loot . maxDuplicates = 3 ;
2017-05-07 21:48:41 +01:00
if ( spawnid )
m_spawnId = spawnid ;
2016-11-01 22:24:03 +01:00
if ( uint32 linkedEntry = GetGOInfo () -> GetLinkedGameObjectEntry ())
{
2018-01-27 21:47:37 +01:00
if ( GameObject * linkedGo = GameObject :: CreateGameObject ( linkedEntry , map , pos , rotation , 255 , GO_STATE_READY ))
2016-11-01 22:24:03 +01:00
{
2018-01-27 21:47:37 +01:00
SetLinkedTrap ( linkedGo );
if ( ! map -> AddToMap ( linkedGo ))
delete linkedGo ;
2016-11-01 22:24:03 +01:00
}
}
2019-01-16 20:48:01 +01:00
// Check if GameObject is Infinite
if ( goInfo -> IsInfiniteGameObject ())
SetVisibilityDistanceOverride ( VisibilityDistanceType :: Infinite );
// Check if GameObject is Gigantic
if ( goInfo -> IsGiganticGameObject ())
SetVisibilityDistanceOverride ( VisibilityDistanceType :: Gigantic );
// Check if GameObject is Large
if ( goInfo -> IsLargeGameObject ())
SetVisibilityDistanceOverride ( VisibilityDistanceType :: Large );
2008-10-11 14:16:25 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2018-01-27 21:47:37 +01:00
GameObject * GameObject :: CreateGameObject ( uint32 entry , Map * map , Position const & pos , QuaternionData const & rotation , uint32 animProgress , GOState goState , uint32 artKit /*= 0*/ )
{
GameObjectTemplate const * goInfo = sObjectMgr -> GetGameObjectTemplate ( entry );
if ( ! goInfo )
return nullptr ;
GameObject * go = new GameObject ();
2017-05-07 21:48:41 +01:00
if ( ! go -> Create ( entry , map , pos , rotation , animProgress , goState , artKit , false , 0 ))
2018-01-27 21:47:37 +01:00
{
delete go ;
return nullptr ;
}
return go ;
}
GameObject * GameObject :: CreateGameObjectFromDB ( ObjectGuid :: LowType spawnId , Map * map , bool addToMap /*= true*/ )
{
GameObject * go = new GameObject ();
2017-05-07 21:48:41 +01:00
if ( ! go -> LoadFromDB ( spawnId , map , addToMap ))
2018-01-27 21:47:37 +01:00
{
delete go ;
return nullptr ;
}
return go ;
}
2010-03-11 22:55:02 +01:00
void GameObject :: Update ( uint32 diff )
2008-10-11 14:16:25 -05:00
{
2018-02-10 16:43:01 -03:00
m_Events . Update ( diff );
2013-10-16 18:37:29 +02:00
if ( AI ())
2010-10-25 18:11:52 +02:00
AI () -> UpdateAI ( diff );
2013-10-16 18:37:29 +02:00
else if ( ! AIM_Initialize ())
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "misc" , "Could not initialize GameObjectAI" );
2009-10-17 15:51:44 -07:00
2018-02-13 22:08:49 +01:00
if ( m_despawnDelay )
{
if ( m_despawnDelay > diff )
m_despawnDelay -= diff ;
else
2018-07-18 19:07:55 +02:00
{
m_despawnDelay = 0 ;
2018-02-14 02:23:55 +01:00
DespawnOrUnsummon ( 0 ms , m_despawnRespawnTime );
2018-07-18 19:07:55 +02:00
}
2018-02-13 22:08:49 +01:00
}
2008-10-11 14:16:25 -05:00
switch ( m_lootState )
{
case GO_NOT_READY :
{
2011-09-29 12:43:05 +02:00
switch ( GetGoType ())
2008-10-11 14:16:25 -05:00
{
case GAMEOBJECT_TYPE_TRAP :
{
// Arming Time for GAMEOBJECT_TYPE_TRAP (6)
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * goInfo = GetGOInfo ();
2010-02-14 19:13:14 -07:00
// Bombs
2014-10-30 20:02:42 +01:00
if ( goInfo -> trap . charges == 2 )
2014-05-05 22:28:45 +02:00
// Hardcoded tooltip value
2017-03-02 02:19:25 +01:00
m_cooldownTime = GameTime :: GetGameTimeMS () + 10 * IN_MILLISECONDS ;
2010-02-14 19:13:14 -07:00
else if ( Unit * owner = GetOwner ())
2013-06-11 19:54:27 -02:30
if ( owner -> IsInCombat ())
2017-03-02 02:19:25 +01:00
m_cooldownTime = GameTime :: GetGameTimeMS () + goInfo -> trap . startDelay * IN_MILLISECONDS ;
2014-05-05 22:28:45 +02:00
SetLootState ( GO_READY );
2008-10-11 14:16:25 -05:00
break ;
}
2013-10-16 18:37:29 +02:00
case GAMEOBJECT_TYPE_TRANSPORT :
{
if ( ! m_goValue . Transport . AnimationInfo )
break ;
2014-09-29 20:10:08 +02:00
if ( GetGoState () == GO_STATE_TRANSPORT_ACTIVE )
2013-10-16 18:37:29 +02:00
{
m_goValue . Transport . PathProgress += diff ;
2014-03-21 00:32:03 +01:00
/* TODO: Fix movement in unloaded grid - currently GO will just disappear
2014-09-29 20:10:08 +02:00
uint32 timer = m_goValue.Transport.PathProgress % GetTransportPeriod();
2013-10-16 18:37:29 +02:00
TransportAnimationEntry const* node = m_goValue.Transport.AnimationInfo->GetAnimNode(timer);
if (node && m_goValue.Transport.CurrentSeg != node->TimeSeg)
{
m_goValue.Transport.CurrentSeg = node->TimeSeg;
2017-05-16 22:16:21 +02:00
G3D::Quat rotation;
if (TransportRotationEntry const* rot = m_goValue.Transport.AnimationInfo->GetAnimRotation(timer))
rotation = G3D::Quat(rot->X, rot->Y, rot->Z, rot->W);
2013-10-16 18:37:29 +02:00
G3D::Vector3 pos = rotation.toRotationMatrix()
* G3D::Matrix3::fromEulerAnglesZYX(GetOrientation(), 0.0f, 0.0f)
* G3D::Vector3(node->X, node->Y, node->Z);
pos += G3D::Vector3(GetStationaryX(), GetStationaryY(), GetStationaryZ());
G3D::Vector3 src(GetPositionX(), GetPositionY(), GetPositionZ());
2014-03-21 00:32:03 +01:00
TC_LOG_DEBUG("misc", "Src: %s Dest: %s", src.toString().c_str(), pos.toString().c_str());
2013-10-16 18:37:29 +02:00
GetMap()->GameObjectRelocation(this, pos.x, pos.y, pos.z, GetOrientation());
}
2014-03-21 00:32:03 +01:00
*/
2014-09-29 20:10:08 +02:00
if ( ! m_goValue . Transport . StopFrames -> empty ())
{
uint32 visualStateBefore = ( m_goValue . Transport . StateUpdateTimer / 20000 ) & 1 ;
m_goValue . Transport . StateUpdateTimer += diff ;
uint32 visualStateAfter = ( m_goValue . Transport . StateUpdateTimer / 20000 ) & 1 ;
if ( visualStateBefore != visualStateAfter )
{
2019-06-03 20:40:34 +02:00
ForceUpdateFieldChange ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: Level ));
ForceUpdateFieldChange ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: State ));
2014-09-29 20:10:08 +02:00
}
}
2013-10-16 18:37:29 +02:00
}
break ;
}
2008-10-11 14:16:25 -05:00
case GAMEOBJECT_TYPE_FISHINGNODE :
{
// fishing code (bobber ready)
2017-12-10 12:39:37 +01:00
if ( GameTime :: GetGameTime () > m_respawnTime - FISHING_BOBBER_READY_TIME )
2008-10-11 14:16:25 -05:00
{
// splash bobber (bobber ready now)
Unit * caster = GetOwner ();
2010-04-07 19:14:10 +02:00
if ( caster && caster -> GetTypeId () == TYPEID_PLAYER )
2008-10-11 14:16:25 -05:00
{
2009-04-27 18:36:10 -05:00
SetGoState ( GO_STATE_ACTIVE );
2019-06-03 20:40:34 +02:00
SetFlags ( GO_FLAG_NODESPAWN );
2009-10-17 15:51:44 -07:00
2011-11-27 16:06:29 +01:00
UpdateData udata ( caster -> GetMapId ());
2008-10-11 14:16:25 -05:00
WorldPacket packet ;
2011-04-29 20:47:02 +02:00
BuildValuesUpdateBlockForPlayer ( & udata , caster -> ToPlayer ());
2008-10-11 14:16:25 -05:00
udata . BuildPacket ( & packet );
2013-08-07 10:31:13 +02:00
caster -> ToPlayer () -> SendDirectMessage ( & packet );
2009-10-17 15:51:44 -07:00
2011-03-02 16:50:09 +01:00
SendCustomAnim ( GetGoAnimProgress ());
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-11-14 16:28:45 -06:00
m_lootState = GO_READY ; // can be successfully open with some chance
2008-10-11 14:16:25 -05:00
}
return ;
}
default :
m_lootState = GO_READY ; // for other GOis same switched without delay to GO_READY
break ;
}
}
2019-03-30 17:48:36 +01:00
/* fallthrough */
2008-10-11 14:16:25 -05:00
case GO_READY :
{
2017-05-07 21:48:41 +01:00
if ( m_respawnCompatibilityMode )
2008-10-11 14:16:25 -05:00
{
2017-05-07 21:48:41 +01:00
if ( m_respawnTime > 0 ) // timer on
2008-10-11 14:16:25 -05:00
{
2017-12-10 12:39:37 +01:00
time_t now = GameTime :: GetGameTime ();
2017-05-07 21:48:41 +01:00
if ( m_respawnTime <= now ) // timer expired
2010-12-24 18:55:50 +01:00
{
2017-05-07 21:48:41 +01:00
ObjectGuid dbtableHighGuid = ObjectGuid :: Create < HighGuid :: GameObject > ( GetMapId (), GetEntry (), m_spawnId );
time_t linkedRespawntime = GetMap () -> GetLinkedRespawnTime ( dbtableHighGuid );
if ( linkedRespawntime ) // Can't respawn, the master is dead
{
ObjectGuid targetGuid = sObjectMgr -> GetLinkedRespawnGuid ( dbtableHighGuid );
if ( targetGuid == dbtableHighGuid ) // if linking self, never respawn
SetRespawnTime ( WEEK );
else
m_respawnTime = ( now > linkedRespawntime ? now : linkedRespawntime ) + urand ( 5 , MINUTE ); // else copy time from master and add a little
SaveRespawnTime (); // also save to DB immediately
return ;
}
2010-12-24 18:55:50 +01:00
2017-05-07 21:48:41 +01:00
m_respawnTime = 0 ;
m_SkillupList . clear ();
m_usetimes = 0 ;
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
switch ( GetGoType ())
2008-10-11 14:16:25 -05:00
{
2017-05-07 21:48:41 +01:00
case GAMEOBJECT_TYPE_FISHINGNODE : // can't fish now
2008-10-11 14:16:25 -05:00
{
2017-05-07 21:48:41 +01:00
Unit * caster = GetOwner ();
if ( caster && caster -> GetTypeId () == TYPEID_PLAYER )
{
caster -> ToPlayer () -> RemoveGameObject ( this , false );
caster -> ToPlayer () -> SendDirectMessage ( WorldPackets :: GameObject :: FishEscaped (). Write ());
}
// can be delete
m_lootState = GO_JUST_DEACTIVATED ;
return ;
2008-10-11 14:16:25 -05:00
}
2017-05-07 21:48:41 +01:00
case GAMEOBJECT_TYPE_DOOR :
case GAMEOBJECT_TYPE_BUTTON :
// We need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
if ( GetGoState () != GO_STATE_READY )
ResetDoorOrButton ();
break ;
case GAMEOBJECT_TYPE_FISHINGHOLE :
// Initialize a new max fish count on respawn
m_goValue . FishingHole . MaxOpens = urand ( GetGOInfo () -> fishingHole . minRestock , GetGOInfo () -> fishingHole . maxRestock );
break ;
default :
break ;
2008-10-11 14:16:25 -05:00
}
2013-03-02 23:59:42 +01:00
2017-05-07 21:48:41 +01:00
// Despawn timer
if ( ! m_spawnedByDefault )
{
// Can be despawned or destroyed
SetLootState ( GO_JUST_DEACTIVATED );
return ;
}
2014-05-05 12:35:41 +02:00
2017-05-07 21:48:41 +01:00
// Call AI Reset (required for example in SmartAI to clear one time events)
if ( AI ())
AI () -> Reset ();
2017-02-17 21:33:18 +01:00
2017-05-07 21:48:41 +01:00
// Respawn timer
uint32 poolid = GetSpawnId () ? sPoolMgr -> IsPartOfAPool < GameObject > ( GetSpawnId ()) : 0 ;
if ( poolid )
sPoolMgr -> UpdatePool < GameObject > ( poolid , GetSpawnId ());
else
GetMap () -> AddToMap ( this );
}
2008-10-11 14:16:25 -05:00
}
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
// Set respawn timer
if ( ! m_respawnCompatibilityMode && m_respawnTime > 0 )
SaveRespawnTime ( 0 , false );
2010-04-07 19:14:10 +02:00
if ( isSpawned ())
2008-10-11 14:16:25 -05:00
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * goInfo = GetGOInfo ();
2010-04-07 19:14:10 +02:00
if ( goInfo -> type == GAMEOBJECT_TYPE_TRAP )
2009-07-24 09:33:56 +08:00
{
2017-03-02 02:19:25 +01:00
if ( GameTime :: GetGameTimeMS () < m_cooldownTime )
2014-05-04 22:15:11 +02:00
break ;
2009-10-17 15:51:44 -07:00
2014-05-04 22:15:11 +02:00
// Type 2 (bomb) does not need to be triggered by a unit and despawns after casting its spell.
2014-10-30 20:02:42 +01:00
if ( goInfo -> trap . charges == 2 )
2010-02-14 19:13:14 -07:00
{
2014-05-04 22:15:11 +02:00
SetLootState ( GO_ACTIVATED );
2010-02-14 19:13:14 -07:00
break ;
}
2009-10-17 15:51:44 -07:00
2014-05-04 22:15:11 +02:00
// Type 0 despawns after being triggered, type 1 does not.
/// @todo This is activation radius. Casting radius must be selected from spell data.
float radius ;
2014-10-30 20:02:42 +01:00
if ( ! goInfo -> trap . radius )
2014-05-04 22:15:11 +02:00
{
2014-05-05 22:28:45 +02:00
// Battleground traps: data2 == 0 && data5 == 3
if ( goInfo -> trap . cooldown != 3 )
2014-05-04 22:15:11 +02:00
break ;
radius = 3.f ;
2014-05-05 10:06:44 +02:00
}
else
2014-10-30 20:02:42 +01:00
radius = goInfo -> trap . radius / 2.f ;
2009-10-17 15:51:44 -07:00
2014-05-04 22:15:11 +02:00
// Pointer to appropriate target if found any
2016-05-11 23:24:26 +02:00
Unit * target = nullptr ;
2014-05-04 22:15:11 +02:00
/// @todo this hack with search required until GO casting not implemented
2014-05-05 22:28:45 +02:00
if ( Unit * owner = GetOwner ())
2009-07-24 09:33:56 +08:00
{
2014-05-05 22:28:45 +02:00
// Hunter trap: Search units which are unfriendly to the trap's owner
2016-11-19 15:43:22 -03:00
Trinity :: NearestAttackableNoTotemUnitInObjectRangeCheck checker ( this , owner , radius );
Trinity :: UnitLastSearcher < Trinity :: NearestAttackableNoTotemUnitInObjectRangeCheck > searcher ( this , target , checker );
2017-11-11 21:45:41 +01:00
Cell :: VisitAllObjects ( this , searcher , radius );
2009-07-24 09:33:56 +08:00
}
2014-05-04 22:15:11 +02:00
else
2009-07-24 09:33:56 +08:00
{
2014-05-05 22:28:45 +02:00
// Environmental trap: Any player
2016-05-11 23:24:26 +02:00
Player * player = nullptr ;
2009-12-20 15:20:04 +01:00
Trinity :: AnyPlayerInObjectRangeCheck checker ( this , radius );
Trinity :: PlayerSearcher < Trinity :: AnyPlayerInObjectRangeCheck > searcher ( this , player , checker );
2017-05-13 19:37:33 +02:00
Cell :: VisitWorldObjects ( this , searcher , radius );
2014-05-04 22:15:11 +02:00
target = player ;
2009-07-24 09:33:56 +08:00
}
2009-10-17 15:51:44 -07:00
2014-05-04 22:15:11 +02:00
if ( target )
2014-05-05 22:28:45 +02:00
SetLootState ( GO_ACTIVATED , target );
2009-10-17 15:51:44 -07:00
2009-07-24 09:33:56 +08:00
}
2010-04-07 19:14:10 +02:00
else if ( uint32 max_charges = goInfo -> GetCharges ())
2009-07-24 09:33:56 +08:00
{
if ( m_usetimes >= max_charges )
2008-10-11 14:16:25 -05:00
{
2009-07-24 09:33:56 +08:00
m_usetimes = 0 ;
SetLootState ( GO_JUST_DEACTIVATED ); // can be despawned or destroyed
2008-10-11 14:16:25 -05:00
}
}
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
break ;
}
case GO_ACTIVATED :
{
2011-09-29 12:43:05 +02:00
switch ( GetGoType ())
2008-10-11 14:16:25 -05:00
{
case GAMEOBJECT_TYPE_DOOR :
case GAMEOBJECT_TYPE_BUTTON :
2017-03-02 02:19:25 +01:00
if ( m_cooldownTime && GameTime :: GetGameTimeMS () >= m_cooldownTime )
2009-04-27 18:36:10 -05:00
ResetDoorOrButton ();
2008-10-11 14:16:25 -05:00
break ;
2010-01-16 19:31:10 +03:00
case GAMEOBJECT_TYPE_GOOBER :
2017-03-02 02:19:25 +01:00
if ( GameTime :: GetGameTimeMS () >= m_cooldownTime )
2010-01-16 19:31:10 +03:00
{
2019-06-03 20:40:34 +02:00
RemoveFlag ( GO_FLAG_IN_USE );
2010-01-16 19:31:10 +03:00
SetLootState ( GO_JUST_DEACTIVATED );
m_cooldownTime = 0 ;
}
break ;
2010-03-11 22:55:02 +01:00
case GAMEOBJECT_TYPE_CHEST :
if ( m_groupLootTimer )
{
if ( m_groupLootTimer <= diff )
{
2014-10-22 00:35:00 +02:00
if ( Group * group = sGroupMgr -> GetGroupByGUID ( lootingGroupLowGUID ))
2017-02-04 22:37:16 +01:00
group -> EndRoll ( & loot , GetMap ());
2014-10-22 00:35:00 +02:00
2010-03-11 22:55:02 +01:00
m_groupLootTimer = 0 ;
2014-10-22 00:35:00 +02:00
lootingGroupLowGUID . Clear ();
2010-03-11 22:55:02 +01:00
}
2014-10-22 00:35:00 +02:00
else
m_groupLootTimer -= diff ;
2010-03-11 22:55:02 +01:00
}
2014-05-05 10:06:44 +02:00
break ;
2014-05-04 22:15:11 +02:00
case GAMEOBJECT_TYPE_TRAP :
{
GameObjectTemplate const * goInfo = GetGOInfo ();
2014-10-30 20:02:42 +01:00
if ( goInfo -> trap . charges == 2 && goInfo -> trap . spell )
2014-05-05 22:28:45 +02:00
{
2016-05-11 23:24:26 +02:00
/// @todo nullptr target won't work for target type 1
CastSpell ( nullptr , goInfo -> trap . spell );
2014-05-05 22:28:45 +02:00
SetLootState ( GO_JUST_DEACTIVATED );
}
2014-05-20 21:21:33 +02:00
else if ( Unit * target = ObjectAccessor :: GetUnit ( * this , m_lootStateUnitGUID ))
2014-05-05 22:28:45 +02:00
{
// Some traps do not have a spell but should be triggered
2018-02-10 16:43:01 -03:00
CastSpellExtraArgs args ;
args . SetOriginalCaster ( GetOwnerGUID ());
2014-10-30 20:02:42 +01:00
if ( goInfo -> trap . spell )
2018-02-10 16:43:01 -03:00
CastSpell ( target , goInfo -> trap . spell , args );
2014-05-04 22:15:11 +02:00
2014-05-05 22:28:45 +02:00
// Template value or 4 seconds
2017-03-02 02:19:25 +01:00
m_cooldownTime = GameTime :: GetGameTimeMS () + ( goInfo -> trap . cooldown ? goInfo -> trap . cooldown : uint32 ( 4 )) * IN_MILLISECONDS ;
2014-05-05 22:28:45 +02:00
2014-10-30 20:02:42 +01:00
if ( goInfo -> trap . charges == 1 )
2014-05-05 22:28:45 +02:00
SetLootState ( GO_JUST_DEACTIVATED );
2014-10-30 20:02:42 +01:00
else if ( ! goInfo -> trap . charges )
2014-05-11 12:35:40 +02:00
SetLootState ( GO_READY );
2014-05-04 22:15:11 +02:00
2014-05-05 22:28:45 +02:00
// Battleground gameobjects have data2 == 0 && data5 == 3
2014-10-30 20:02:42 +01:00
if ( ! goInfo -> trap . radius && goInfo -> trap . cooldown == 3 )
2014-05-06 18:46:08 +02:00
if ( Player * player = target -> ToPlayer ())
2014-05-05 22:28:45 +02:00
if ( Battleground * bg = player -> GetBattleground ())
bg -> HandleTriggerBuff ( GetGUID ());
}
2014-05-04 22:15:11 +02:00
break ;
}
2010-01-16 19:31:10 +03:00
default :
break ;
2008-10-11 14:16:25 -05:00
}
break ;
}
case GO_JUST_DEACTIVATED :
{
2016-10-30 11:20:58 +01:00
// If nearby linked trap exists, despawn it
if ( GameObject * linkedTrap = GetLinkedTrap ())
2018-03-11 18:07:44 +01:00
linkedTrap -> DespawnOrUnsummon ();
2016-10-30 11:20:58 +01:00
2008-10-11 14:16:25 -05:00
//if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
if ( GetGoType () == GAMEOBJECT_TYPE_GOOBER )
{
2014-10-30 20:02:42 +01:00
uint32 spellId = GetGOInfo () -> goober . spell ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( spellId )
2008-10-11 14:16:25 -05:00
{
2014-09-14 16:14:12 +02:00
for ( GuidSet :: const_iterator it = m_unique_users . begin (); it != m_unique_users . end (); ++ it )
2011-09-17 13:20:21 +02:00
// m_unique_users can contain only player GUIDs
if ( Player * owner = ObjectAccessor :: GetPlayer ( * this , * it ))
2010-01-16 19:31:10 +03:00
owner -> CastSpell ( owner , spellId , false );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
m_unique_users . clear ();
m_usetimes = 0 ;
}
2010-01-16 19:31:10 +03:00
SetGoState ( GO_STATE_READY );
2008-10-11 14:16:25 -05:00
//any return here in case battleground traps
2018-02-24 20:57:55 -03:00
if ( GameObjectOverride const * goOverride = GetGameObjectOverride ())
if ( goOverride -> Flags & GO_FLAG_NODESPAWN )
2016-08-29 19:20:06 +01:00
return ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-03-24 01:13:53 +02:00
loot . clear ();
2018-02-24 20:57:55 -03:00
//! If this is summoned by a spell with ie. SPELL_EFFECT_SUMMON_OBJECT_WILD, with or without owner, we check respawn criteria based on speSendObjectDeSpawnAnim(GetGUID());ll
2011-10-08 13:13:09 +02:00
//! The GetOwnerGUID() check is mostly for compatibility with hacky scripts - 99% of the time summoning should be done trough spells.
2014-10-21 19:23:32 +02:00
if ( GetSpellId () || ! GetOwnerGUID (). IsEmpty ())
2008-10-11 14:16:25 -05:00
{
2018-08-22 16:39:59 +02:00
//Don't delete spell spawned chests, which are not consumed on loot
if ( m_respawnTime > 0 && GetGoType () == GAMEOBJECT_TYPE_CHEST && ! GetGOInfo () -> IsDespawnAtAction ())
{
UpdateObjectVisibility ();
SetLootState ( GO_READY );
}
else
{
SetRespawnTime ( 0 );
Delete ();
}
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2019-03-02 15:05:28 +01:00
SetLootState ( GO_NOT_READY );
2011-03-24 01:13:53 +02:00
2008-10-11 14:16:25 -05:00
//burning flags in some battlegrounds, if you find better condition, just add it
2009-07-16 12:06:07 +08:00
if ( GetGOInfo () -> IsDespawnAtAction () || GetGoAnimProgress () > 0 )
2008-10-11 14:16:25 -05:00
{
2016-11-16 22:46:37 +01:00
SendGameObjectDespawn ();
2008-10-11 14:16:25 -05:00
//reset flags
2018-02-24 20:57:55 -03:00
if ( GameObjectOverride const * goOverride = GetGameObjectOverride ())
SetFlags ( GameObjectFlags ( goOverride -> Flags ));
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( ! m_respawnDelayTime )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2019-04-23 16:43:49 +02:00
if ( ! m_spawnedByDefault )
{
m_respawnTime = 0 ;
2019-04-24 20:33:04 +02:00
if ( m_spawnId )
DestroyForNearbyPlayers ();
else
Delete ();
2019-04-23 16:43:49 +02:00
return ;
}
2017-05-07 21:48:41 +01:00
uint32 respawnDelay = m_respawnDelayTime ;
if ( uint32 scalingMode = sWorld -> getIntConfig ( CONFIG_RESPAWN_DYNAMICMODE ))
GetMap () -> ApplyDynamicModeRespawnScaling ( this , this -> m_spawnId , respawnDelay , scalingMode );
2017-12-10 12:39:37 +01:00
m_respawnTime = GameTime :: GetGameTime () + respawnDelay ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// if option not set then object will be saved at grid unload
2017-05-07 21:48:41 +01:00
// Otherwise just save respawn time to map object memory
2010-12-23 23:25:44 +01:00
if ( sWorld -> getBoolConfig ( CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY ))
2008-10-11 14:16:25 -05:00
SaveRespawnTime ();
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
if ( ! m_respawnCompatibilityMode )
{
// Respawn time was just saved if set to save to DB
// If not, we save only to map memory
if ( ! sWorld -> getBoolConfig ( CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY ))
SaveRespawnTime ( 0 , false );
// Then despawn
AddObjectToRemoveList ();
return ;
}
2017-01-28 05:00:28 +01:00
DestroyForNearbyPlayers (); // old UpdateObjectVisibility()
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
break ;
}
}
}
2009-10-17 15:51:44 -07:00
2018-02-24 20:57:55 -03:00
GameObjectOverride const * GameObject :: GetGameObjectOverride () const
{
if ( m_spawnId )
{
if ( GameObjectOverride const * goOverride = sObjectMgr -> GetGameObjectOverride ( m_spawnId ))
return goOverride ;
}
return m_goTemplateAddon ;
}
2008-10-11 14:16:25 -05:00
void GameObject :: Refresh ()
{
2013-12-25 14:16:55 -03:30
// Do not refresh despawned GO from spellcast (GO's from spellcast are destroyed after despawn)
2010-04-07 19:14:10 +02:00
if ( m_respawnTime > 0 && m_spawnedByDefault )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( isSpawned ())
2011-10-10 17:39:34 -04:00
GetMap () -> AddToMap ( this );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void GameObject :: AddUniqueUse ( Player * player )
{
AddUse ();
2011-09-17 13:20:21 +02:00
m_unique_users . insert ( player -> GetGUID ());
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2018-07-18 17:48:15 +02:00
void GameObject :: DespawnOrUnsummon ( Milliseconds delay , Seconds forceRespawnTime )
2018-02-13 22:08:49 +01:00
{
2018-02-14 02:23:55 +01:00
if ( delay > 0 ms )
2018-02-13 22:08:49 +01:00
{
if ( ! m_despawnDelay || m_despawnDelay > delay . count ())
2018-02-14 02:23:55 +01:00
{
2018-02-13 22:08:49 +01:00
m_despawnDelay = delay . count ();
2018-02-14 02:23:55 +01:00
m_despawnRespawnTime = forceRespawnTime ;
}
2018-02-13 22:08:49 +01:00
}
else
{
2018-07-18 19:07:55 +02:00
if ( m_goData )
{
uint32 const respawnDelay = ( forceRespawnTime > 0 s ) ? forceRespawnTime . count () : m_goData -> spawntimesecs ;
2018-02-14 02:23:55 +01:00
SaveRespawnTime ( respawnDelay );
2018-07-18 19:07:55 +02:00
}
2018-02-13 22:08:49 +01:00
Delete ();
}
}
2009-09-02 14:52:52 -05:00
void GameObject :: Delete ()
2009-08-03 14:52:30 +02:00
{
2019-03-09 21:11:21 +01:00
// If nearby linked trap exists, despawn it
if ( GameObject * linkedTrap = GetLinkedTrap ())
linkedTrap -> DespawnOrUnsummon ();
2009-08-03 14:52:30 +02:00
SetLootState ( GO_NOT_READY );
2011-10-31 10:46:39 -04:00
RemoveFromOwner ();
2009-10-17 15:51:44 -07:00
2016-11-16 22:46:37 +01:00
SendGameObjectDespawn ();
2009-10-17 15:51:44 -07:00
2009-04-27 18:36:10 -05:00
SetGoState ( GO_STATE_READY );
2016-08-29 19:20:06 +01:00
2018-02-24 20:57:55 -03:00
if ( GameObjectOverride const * goOverride = GetGameObjectOverride ())
SetFlags ( GameObjectFlags ( goOverride -> Flags ));
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
uint32 poolid = GetSpawnId () ? sPoolMgr -> IsPartOfAPool < GameObject > ( GetSpawnId ()) : 0 ;
2009-02-19 18:44:20 -06:00
if ( poolid )
2015-02-25 00:13:14 +01:00
sPoolMgr -> UpdatePool < GameObject > ( poolid , GetSpawnId ());
2009-02-19 18:44:20 -06:00
else
AddObjectToRemoveList ();
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2016-11-16 22:46:37 +01:00
void GameObject :: SendGameObjectDespawn ()
{
WorldPackets :: GameObject :: GameObjectDespawn packet ;
packet . ObjectGUID = GetGUID ();
SendMessageToSet ( packet . Write (), true );
}
2011-09-15 14:08:17 +02:00
void GameObject :: getFishLoot ( Loot * fishloot , Player * loot_owner )
2008-10-11 14:16:25 -05:00
{
fishloot -> clear ();
2009-10-17 15:51:44 -07:00
2009-03-11 16:17:37 -06:00
uint32 zone , subzone ;
2014-05-10 21:35:05 +08:00
uint32 defaultzone = 1 ;
2011-04-29 20:47:02 +02:00
GetZoneAndAreaId ( zone , subzone );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// if subzone loot exist use it
2014-05-10 21:35:05 +08:00
fishloot -> FillLoot ( subzone , LootTemplates_Fishing , loot_owner , true , true );
if ( fishloot -> empty ()) //use this becase if zone or subzone has set LOOT_MODE_JUNK_FISH,Even if no normal drop, fishloot->FillLoot return true. it wrong.
{
//subzone no result,use zone loot
fishloot -> FillLoot ( zone , LootTemplates_Fishing , loot_owner , true , true );
2014-06-13 11:16:17 +02:00
//use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks.
2014-05-10 21:35:05 +08:00
if ( fishloot -> empty ())
fishloot -> FillLoot ( defaultzone , LootTemplates_Fishing , loot_owner , true , true );
}
}
void GameObject :: getFishLootJunk ( Loot * fishloot , Player * loot_owner )
{
fishloot -> clear ();
uint32 zone , subzone ;
uint32 defaultzone = 1 ;
GetZoneAndAreaId ( zone , subzone );
// if subzone loot exist use it
fishloot -> FillLoot ( subzone , LootTemplates_Fishing , loot_owner , true , true , LOOT_MODE_JUNK_FISH );
if ( fishloot -> empty ()) //use this becase if zone or subzone has normal mask drop, then fishloot->FillLoot return true.
{
//use zone loot
fishloot -> FillLoot ( zone , LootTemplates_Fishing , loot_owner , true , true , LOOT_MODE_JUNK_FISH );
if ( fishloot -> empty ())
//use zone 1 as default
fishloot -> FillLoot ( defaultzone , LootTemplates_Fishing , loot_owner , true , true , LOOT_MODE_JUNK_FISH );
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void GameObject :: SaveToDB ()
{
// this should only be used when the gameobject has already been loaded
2008-11-14 16:28:45 -06:00
// preferably after adding to map, because mapid may not be valid otherwise
2017-05-07 21:48:41 +01:00
GameObjectData const * data = sObjectMgr -> GetGameObjectData ( m_spawnId );
2010-04-07 19:14:10 +02:00
if ( ! data )
2008-10-11 14:16:25 -05:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "misc" , "GameObject::SaveToDB failed, cannot get gameobject data!" );
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2018-09-15 17:55:26 +02:00
SaveToDB ( GetMapId (), data -> spawnDifficulties );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2018-09-15 17:55:26 +02:00
void GameObject :: SaveToDB ( uint32 mapid , std :: vector < Difficulty > const & spawnDifficulties )
2008-10-11 14:16:25 -05:00
{
2020-09-04 13:38:24 +02:00
GameObjectTemplate const * goI = GetGOInfo ();
2008-10-11 14:16:25 -05:00
if ( ! goI )
return ;
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
if ( ! m_spawnId )
m_spawnId = sObjectMgr -> GenerateGameObjectSpawnId ();
2014-10-22 19:33:06 +02:00
2008-10-11 14:16:25 -05:00
// update in loaded data (changing data only in this place)
2017-05-07 21:48:41 +01:00
GameObjectData & data = sObjectMgr -> NewOrExistGameObjectData ( m_spawnId );
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
if ( ! data . spawnId )
data . spawnId = m_spawnId ;
ASSERT ( data . spawnId == m_spawnId );
2008-10-11 14:16:25 -05:00
data . id = GetEntry ();
2017-05-07 21:48:41 +01:00
data . spawnPoint . WorldRelocate ( this );
2019-04-24 20:39:01 +02:00
data . rotation = m_localRotation ;
2008-10-11 14:16:25 -05:00
data . spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : - ( int32 ) m_respawnDelayTime ;
data . animprogress = GetGoAnimProgress ();
2017-05-07 21:48:41 +01:00
data . goState = GetGoState ();
2018-09-15 17:55:26 +02:00
data . spawnDifficulties = spawnDifficulties ;
2009-08-21 12:31:52 -05:00
data . artKit = GetGoArtKit ();
2017-05-07 21:48:41 +01:00
if ( ! data . spawnGroupData )
data . spawnGroupData = sObjectMgr -> GetDefaultSpawnGroup ();
2009-10-17 15:51:44 -07:00
2018-01-10 22:01:40 +01:00
data . phaseId = GetDBPhase () > 0 ? GetDBPhase () : data . phaseId ;
data . phaseGroup = GetDBPhase () < 0 ? - GetDBPhase () : data . phaseGroup ;
2018-01-06 19:56:11 +01:00
2012-05-30 08:01:02 +02:00
// Update in DB
2019-07-27 10:52:33 +02:00
WorldDatabaseTransaction trans = WorldDatabase . BeginTransaction ();
2012-05-30 08:01:02 +02:00
uint8 index = 0 ;
2019-07-27 01:00:37 +02:00
WorldDatabasePreparedStatement * stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_GAMEOBJECT );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( 0 , m_spawnId );
2012-05-30 08:01:02 +02:00
trans -> Append ( stmt );
stmt = WorldDatabase . GetPreparedStatement ( WORLD_INS_GAMEOBJECT );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( index ++ , m_spawnId );
2012-05-30 08:01:02 +02:00
stmt -> setUInt32 ( index ++ , GetEntry ());
stmt -> setUInt16 ( index ++ , uint16 ( mapid ));
2020-04-03 17:41:48 +02:00
stmt -> setString ( index ++ , [ & data ]() -> std :: string
{
if ( data . spawnDifficulties . empty ())
return "" ;
std :: ostringstream os ;
auto itr = data . spawnDifficulties . begin ();
os << int32 ( * itr ++ );
for (; itr != data . spawnDifficulties . end (); ++ itr )
os << ',' << int32 ( * itr );
return os . str ();
}());
2018-01-06 19:56:11 +01:00
stmt -> setUInt32 ( index ++ , data . phaseId );
stmt -> setUInt32 ( index ++ , data . phaseGroup );
2012-05-30 08:01:02 +02:00
stmt -> setFloat ( index ++ , GetPositionX ());
stmt -> setFloat ( index ++ , GetPositionY ());
stmt -> setFloat ( index ++ , GetPositionZ ());
stmt -> setFloat ( index ++ , GetOrientation ());
2019-04-24 20:39:01 +02:00
stmt -> setFloat ( index ++ , m_localRotation . x );
stmt -> setFloat ( index ++ , m_localRotation . y );
stmt -> setFloat ( index ++ , m_localRotation . z );
stmt -> setFloat ( index ++ , m_localRotation . w );
2012-05-30 08:01:02 +02:00
stmt -> setInt32 ( index ++ , int32 ( m_respawnDelayTime ));
stmt -> setUInt8 ( index ++ , GetGoAnimProgress ());
stmt -> setUInt8 ( index ++ , uint8 ( GetGoState ()));
trans -> Append ( stmt );
2010-08-21 03:19:25 +02:00
WorldDatabase . CommitTransaction ( trans );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-05-07 21:48:41 +01:00
bool GameObject :: LoadFromDB ( ObjectGuid :: LowType spawnId , Map * map , bool addToMap , bool )
2008-10-11 14:16:25 -05:00
{
2017-05-07 21:48:41 +01:00
GameObjectData const * data = sObjectMgr -> GetGameObjectData ( spawnId );
2010-04-07 22:59:46 +02:00
if ( ! data )
2008-10-11 14:16:25 -05:00
{
2015-02-25 00:13:14 +01:00
TC_LOG_ERROR ( "sql.sql" , "Gameobject (GUID: " UI64FMTD ") not found in table `gameobject`, can't load. " , spawnId );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
uint32 entry = data -> id ;
2008-12-24 09:58:26 -06:00
//uint32 map_id = data->mapid; // already used before call
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
uint32 animprogress = data -> animprogress ;
2017-05-07 21:48:41 +01:00
GOState go_state = data -> goState ;
2009-08-21 12:31:52 -05:00
uint32 artKit = data -> artKit ;
2009-10-17 15:51:44 -07:00
2015-02-25 00:13:14 +01:00
m_spawnId = spawnId ;
2017-05-07 21:48:41 +01:00
m_respawnCompatibilityMode = (( data -> spawnGroupData -> flags & SPAWNGROUP_FLAG_COMPATIBILITY_MODE ) != 0 );
if ( ! Create ( entry , map , data -> spawnPoint , data -> rotation , animprogress , go_state , artKit , ! m_respawnCompatibilityMode , spawnId ))
2008-10-11 14:16:25 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2018-01-28 11:37:20 +01:00
PhasingHandler :: InitDbPhaseShift ( GetPhaseShift (), data -> phaseUseFlags , data -> phaseId , data -> phaseGroup );
PhasingHandler :: InitDbVisibleMapId ( GetPhaseShift (), data -> terrainSwapMap );
2014-06-01 22:27:29 -05:00
2010-04-07 19:14:10 +02:00
if ( data -> spawntimesecs >= 0 )
2008-10-11 14:16:25 -05:00
{
2009-04-08 16:35:13 -05:00
m_spawnedByDefault = true ;
2009-10-17 16:20:24 -07:00
2010-04-07 19:14:10 +02:00
if ( ! GetGOInfo () -> GetDespawnPossibility () && ! GetGOInfo () -> IsDespawnAtAction ())
2009-08-17 13:58:43 +02:00
{
2019-06-03 20:40:34 +02:00
AddFlag ( GO_FLAG_NODESPAWN );
2009-08-17 13:58:43 +02:00
m_respawnDelayTime = 0 ;
m_respawnTime = 0 ;
}
else
2009-04-08 16:35:13 -05:00
{
m_respawnDelayTime = data -> spawntimesecs ;
2015-02-25 00:13:14 +01:00
m_respawnTime = GetMap () -> GetGORespawnTime ( m_spawnId );
2009-10-17 15:51:44 -07:00
2009-04-08 16:35:13 -05:00
// ready to respawn
2017-12-10 12:39:37 +01:00
if ( m_respawnTime && m_respawnTime <= GameTime :: GetGameTime ())
2008-10-11 14:16:25 -05:00
{
m_respawnTime = 0 ;
2017-05-07 21:48:41 +01:00
GetMap () -> RemoveRespawnTime ( SPAWN_TYPE_GAMEOBJECT , m_spawnId );
2008-10-11 14:16:25 -05:00
}
2009-04-08 16:35:13 -05:00
}
2009-08-17 13:58:43 +02:00
}
else
{
2018-02-23 19:58:22 +01:00
if ( ! m_respawnCompatibilityMode )
{
TC_LOG_WARN ( "sql.sql" , "GameObject %u (SpawnID " UI64FMTD ") is not spawned by default, but tries to use a non-hack spawn system. This will not work. Defaulting to compatibility mode." , entry , spawnId );
m_respawnCompatibilityMode = true ;
}
2009-08-17 13:58:43 +02:00
m_spawnedByDefault = false ;
m_respawnDelayTime = - data -> spawntimesecs ;
m_respawnTime = 0 ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2009-05-31 21:04:04 -05:00
m_goData = data ;
2009-10-17 15:51:44 -07:00
2011-11-28 14:05:25 -05:00
if ( addToMap && ! GetMap () -> AddToMap ( this ))
return false ;
2008-10-11 14:16:25 -05:00
return true ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void GameObject :: DeleteFromDB ()
{
2017-05-07 21:48:41 +01:00
GetMap () -> RemoveRespawnTime ( SPAWN_TYPE_GAMEOBJECT , m_spawnId );
sObjectMgr -> DeleteGameObjectData ( m_spawnId );
2011-12-27 00:29:17 +01:00
2017-05-07 21:48:41 +01:00
WorldDatabaseTransaction trans = WorldDatabase . BeginTransaction ();
2011-12-27 00:29:17 +01:00
2017-05-07 21:48:41 +01:00
WorldDatabasePreparedStatement * stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_GAMEOBJECT );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( 0 , m_spawnId );
2017-05-07 21:48:41 +01:00
trans -> Append ( stmt );
2011-12-27 00:29:17 +01:00
2017-05-07 21:48:41 +01:00
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_SPAWNGROUP_MEMBER );
stmt -> setUInt8 ( 0 , uint8 ( SPAWN_TYPE_GAMEOBJECT ));
stmt -> setUInt64 ( 1 , m_spawnId );
trans -> Append ( stmt );
2011-12-27 00:29:17 +01:00
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_EVENT_GAMEOBJECT );
2015-02-25 00:13:14 +01:00
stmt -> setUInt64 ( 0 , m_spawnId );
2017-05-07 21:48:41 +01:00
trans -> Append ( stmt );
2011-12-27 00:29:17 +01:00
2018-01-08 00:32:22 +01:00
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_LINKED_RESPAWN );
stmt -> setUInt64 ( 0 , m_spawnId );
stmt -> setUInt32 ( 1 , LINKED_RESPAWN_GO_TO_GO );
trans -> Append ( stmt );
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_LINKED_RESPAWN );
stmt -> setUInt64 ( 0 , m_spawnId );
stmt -> setUInt32 ( 1 , LINKED_RESPAWN_GO_TO_CREATURE );
trans -> Append ( stmt );
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_LINKED_RESPAWN_MASTER );
stmt -> setUInt64 ( 0 , m_spawnId );
stmt -> setUInt32 ( 1 , LINKED_RESPAWN_GO_TO_GO );
trans -> Append ( stmt );
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_LINKED_RESPAWN_MASTER );
stmt -> setUInt64 ( 0 , m_spawnId );
stmt -> setUInt32 ( 1 , LINKED_RESPAWN_CREATURE_TO_GO );
trans -> Append ( stmt );
2019-04-17 21:01:32 +02:00
stmt = WorldDatabase . GetPreparedStatement ( WORLD_DEL_GAMEOBJECT_ADDON );
stmt -> setUInt32 ( 0 , m_spawnId );
trans -> Append ( stmt );
2017-05-07 21:48:41 +01:00
WorldDatabase . CommitTransaction ( trans );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
/*********************************************************/
/*** QUEST SYSTEM ***/
/*********************************************************/
bool GameObject :: hasQuest ( uint32 quest_id ) const
{
2010-12-22 21:25:23 +01:00
QuestRelationBounds qr = sObjectMgr -> GetGOQuestRelationBounds ( GetEntry ());
2010-09-13 13:18:27 +02:00
for ( QuestRelations :: const_iterator itr = qr . first ; itr != qr . second ; ++ itr )
2008-10-11 14:16:25 -05:00
{
2010-04-07 23:25:02 +02:00
if ( itr -> second == quest_id )
2008-10-11 14:16:25 -05:00
return true ;
}
return false ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
bool GameObject :: hasInvolvedQuest ( uint32 quest_id ) const
{
2010-12-22 21:25:23 +01:00
QuestRelationBounds qir = sObjectMgr -> GetGOQuestInvolvedRelationBounds ( GetEntry ());
2010-09-13 13:18:27 +02:00
for ( QuestRelations :: const_iterator itr = qir . first ; itr != qir . second ; ++ itr )
2008-10-11 14:16:25 -05:00
{
2010-04-07 23:25:02 +02:00
if ( itr -> second == quest_id )
2008-10-11 14:16:25 -05:00
return true ;
}
return false ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
bool GameObject :: IsTransport () const
{
// If something is marked as a transport, don't transmit an out of range packet for it.
2011-06-11 22:35:29 +02:00
GameObjectTemplate const * gInfo = GetGOInfo ();
2012-05-30 08:01:02 +02:00
if ( ! gInfo )
return false ;
2014-10-06 18:37:38 -04:00
return gInfo -> type == GAMEOBJECT_TYPE_TRANSPORT || gInfo -> type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2009-11-27 01:41:26 +01:00
// is Dynamic transport = non-stop Transport
bool GameObject :: IsDynTransport () const
{
// If something is marked as a transport, don't transmit an out of range packet for it.
2011-06-11 22:35:29 +02:00
GameObjectTemplate const * gInfo = GetGOInfo ();
2012-05-30 08:01:02 +02:00
if ( ! gInfo )
return false ;
2014-10-06 18:37:38 -04:00
return gInfo -> type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT || ( gInfo -> type == GAMEOBJECT_TYPE_TRANSPORT && m_goValue . Transport . StopFrames -> empty ());
2009-11-27 01:41:26 +01:00
}
2012-08-21 23:40:38 +01:00
bool GameObject :: IsDestructibleBuilding () const
{
GameObjectTemplate const * gInfo = GetGOInfo ();
2013-08-07 10:31:13 +02:00
if ( ! gInfo )
return false ;
2012-08-21 23:40:38 +01:00
return gInfo -> type == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING ;
}
2017-05-07 21:48:41 +01:00
void GameObject :: SaveRespawnTime ( uint32 forceDelay , bool savetodb )
2008-10-11 14:16:25 -05:00
{
2018-02-13 22:08:49 +01:00
if ( m_goData && ( forceDelay || m_respawnTime > GameTime :: GetGameTime ()) && m_spawnedByDefault )
2017-05-07 21:48:41 +01:00
{
if ( m_respawnCompatibilityMode )
{
GetMap () -> SaveRespawnTimeDB ( SPAWN_TYPE_GAMEOBJECT , m_spawnId , m_respawnTime );
return ;
}
2017-12-10 12:39:37 +01:00
uint32 thisRespawnTime = forceDelay ? GameTime :: GetGameTime () + forceDelay : m_respawnTime ;
2017-05-07 21:48:41 +01:00
GetMap () -> SaveRespawnTime ( SPAWN_TYPE_GAMEOBJECT , m_spawnId , GetEntry (), thisRespawnTime , GetZoneId (), Trinity :: ComputeGridCoord ( GetPositionX (), GetPositionY ()). GetId (), m_goData -> dbData ? savetodb : false );
}
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2017-05-05 13:31:25 +02:00
bool GameObject :: IsNeverVisibleFor ( WorldObject const * seer ) const
2014-07-07 21:33:58 +02:00
{
2017-05-05 13:31:25 +02:00
if ( WorldObject :: IsNeverVisibleFor ( seer ))
2014-07-07 21:33:58 +02:00
return true ;
if ( GetGoType () == GAMEOBJECT_TYPE_SPELL_FOCUS && GetGOInfo () -> spellFocus . serverOnly == 1 )
return true ;
2019-06-03 20:40:34 +02:00
if ( ! GetDisplayId ())
2016-06-07 19:56:40 +02:00
return true ;
2014-07-07 21:33:58 +02:00
return false ;
}
2011-10-13 14:02:07 -04:00
bool GameObject :: IsAlwaysVisibleFor ( WorldObject const * seer ) const
2008-10-11 14:16:25 -05:00
{
2011-10-13 14:02:07 -04:00
if ( WorldObject :: IsAlwaysVisibleFor ( seer ))
2008-10-11 14:16:25 -05:00
return true ;
2009-10-17 15:51:44 -07:00
2012-08-21 23:40:38 +01:00
if ( IsTransport () || IsDestructibleBuilding ())
2010-11-13 17:18:09 +01:00
return true ;
2010-12-13 22:37:56 +01:00
2012-01-08 18:22:46 +01:00
if ( ! seer )
return false ;
// Always seen by owner and friendly units
2014-10-21 20:27:00 +02:00
if ( ! GetOwnerGUID (). IsEmpty ())
2012-01-08 18:22:46 +01:00
{
2014-10-21 20:27:00 +02:00
if ( seer -> GetGUID () == GetOwnerGUID ())
2012-01-08 18:22:46 +01:00
return true ;
Unit * owner = GetOwner ();
2014-09-14 16:14:12 +02:00
if ( Unit const * unitSeer = seer -> ToUnit ())
if ( owner && owner -> IsFriendlyTo ( unitSeer ))
return true ;
2012-01-08 18:22:46 +01:00
}
2010-11-13 17:18:09 +01:00
return false ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-10-13 13:26:27 -04:00
bool GameObject :: IsInvisibleDueToDespawn () const
2009-02-04 19:43:00 +01:00
{
2011-10-13 13:26:27 -04:00
if ( WorldObject :: IsInvisibleDueToDespawn ())
return true ;
2009-10-17 15:51:44 -07:00
2010-11-13 17:18:09 +01:00
// Despawned
if ( ! isSpawned ())
2011-10-13 13:26:27 -04:00
return true ;
2009-10-17 15:51:44 -07:00
2011-10-13 13:26:27 -04:00
return false ;
2009-02-04 19:43:00 +01:00
}
2009-10-17 15:51:44 -07:00
2017-07-29 21:40:28 +02:00
uint8 GameObject :: GetLevelForTarget ( WorldObject const * target ) const
2017-05-22 00:05:03 +02:00
{
if ( Unit * owner = GetOwner ())
2017-07-29 21:40:28 +02:00
return owner -> GetLevelForTarget ( target );
2017-05-22 00:05:03 +02:00
return 1 ;
}
2017-12-10 12:39:37 +01:00
time_t GameObject :: GetRespawnTimeEx () const
{
time_t now = GameTime :: GetGameTime ();
if ( m_respawnTime > now )
return m_respawnTime ;
else
return now ;
}
void GameObject :: SetRespawnTime ( int32 respawn )
{
m_respawnTime = respawn > 0 ? GameTime :: GetGameTime () + respawn : 0 ;
m_respawnDelayTime = respawn > 0 ? respawn : 0 ;
2018-02-23 19:52:32 +01:00
if ( respawn && ! m_spawnedByDefault )
UpdateObjectVisibility ( true );
2017-12-10 12:39:37 +01:00
}
2008-10-11 14:16:25 -05:00
void GameObject :: Respawn ()
{
2010-04-07 19:14:10 +02:00
if ( m_spawnedByDefault && m_respawnTime > 0 )
2008-10-11 14:16:25 -05:00
{
2017-12-10 12:39:37 +01:00
m_respawnTime = GameTime :: GetGameTime ();
2017-05-07 21:48:41 +01:00
GetMap () -> RemoveRespawnTime ( SPAWN_TYPE_GAMEOBJECT , m_spawnId , true );
2008-10-11 14:16:25 -05:00
}
}
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
bool GameObject :: ActivateToQuest ( Player const * target ) const
2008-10-11 14:16:25 -05:00
{
2011-10-07 19:45:43 -05:00
if ( target -> HasQuestForGO ( GetEntry ()))
2010-10-06 16:46:18 -06:00
return true ;
2010-12-22 21:25:23 +01:00
if ( ! sObjectMgr -> IsGameObjectForQuests ( GetEntry ()))
2008-10-11 14:16:25 -05:00
return false ;
2009-10-17 15:51:44 -07:00
2009-11-21 17:59:24 -08:00
switch ( GetGoType ())
2008-10-11 14:16:25 -05:00
{
2014-02-06 21:23:18 +01:00
case GAMEOBJECT_TYPE_QUESTGIVER :
{
GameObject * go = const_cast < GameObject *> ( this );
2019-06-03 20:40:34 +02:00
QuestGiverStatus questStatus = const_cast < Player *> ( target ) -> GetQuestDialogStatus ( go );
2020-12-11 22:19:19 +01:00
if ( questStatus != QuestGiverStatus :: None && questStatus != QuestGiverStatus :: Future )
2014-02-06 21:23:18 +01:00
return true ;
break ;
}
2008-10-11 14:16:25 -05:00
case GAMEOBJECT_TYPE_CHEST :
{
2014-02-06 21:23:18 +01:00
// scan GO chest with loot including quest items
2011-10-07 19:45:43 -05:00
if ( LootTemplates_Gameobject . HaveQuestLootForPlayer ( GetGOInfo () -> GetLootId (), target ))
2008-11-21 19:45:49 -06:00
{
2013-08-07 10:31:13 +02:00
if ( Battleground const * bg = target -> GetBattleground ())
return bg -> CanActivateGO ( GetEntry (), target -> GetTeam ());
2008-10-11 14:16:25 -05:00
return true ;
2008-11-21 19:45:49 -06:00
}
2008-10-11 14:16:25 -05:00
break ;
}
2010-10-06 16:36:50 -06:00
case GAMEOBJECT_TYPE_GENERIC :
{
2015-04-15 12:47:17 +02:00
if ( target -> GetQuestStatus ( GetGOInfo () -> generic . questID ) == QUEST_STATUS_INCOMPLETE )
2010-10-06 16:36:50 -06:00
return true ;
break ;
}
2008-10-11 14:16:25 -05:00
case GAMEOBJECT_TYPE_GOOBER :
{
2015-04-15 12:47:17 +02:00
if ( target -> GetQuestStatus ( GetGOInfo () -> goober . questID ) == QUEST_STATUS_INCOMPLETE )
2008-10-11 14:16:25 -05:00
return true ;
break ;
}
default :
break ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
return false ;
}
2009-10-17 15:51:44 -07:00
2009-11-21 17:59:24 -08:00
void GameObject :: TriggeringLinkedGameObject ( uint32 trapEntry , Unit * target )
2008-10-11 14:16:25 -05:00
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * trapInfo = sObjectMgr -> GetGameObjectTemplate ( trapEntry );
2009-11-21 17:59:24 -08:00
if ( ! trapInfo || trapInfo -> type != GAMEOBJECT_TYPE_TRAP )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2020-06-12 01:29:18 +02:00
SpellInfo const * trapSpell = sSpellMgr -> GetSpellInfo ( trapInfo -> trap . spell , GetMap () -> GetDifficultyID ());
2010-04-07 19:14:10 +02:00
if ( ! trapSpell ) // checked at load already
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2016-11-16 04:59:43 -03:00
if ( GameObject * trapGO = GetLinkedTrap ())
trapGO -> CastSpell ( target , trapSpell -> Id );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
GameObject * GameObject :: LookupFishingHoleAround ( float range )
{
2016-05-11 23:24:26 +02:00
GameObject * ok = nullptr ;
2009-12-20 15:20:04 +01:00
Trinity :: NearestGameObjectFishingHole u_check ( * this , range );
Trinity :: GameObjectSearcher < Trinity :: NearestGameObjectFishingHole > checker ( this , ok , u_check );
2017-05-13 19:37:33 +02:00
Cell :: VisitGridObjects ( this , checker , range );
2008-10-11 14:16:25 -05:00
return ok ;
}
2009-10-17 15:51:44 -07:00
2009-04-27 18:36:10 -05:00
void GameObject :: ResetDoorOrButton ()
{
if ( m_lootState == GO_READY || m_lootState == GO_JUST_DEACTIVATED )
return ;
2009-10-17 15:51:44 -07:00
2019-06-03 20:40:34 +02:00
RemoveFlag ( GO_FLAG_IN_USE );
2016-08-28 05:25:09 -03:00
SetGoState ( m_prevGoState );
2009-04-27 18:36:10 -05:00
SetLootState ( GO_JUST_DEACTIVATED );
m_cooldownTime = 0 ;
}
2009-10-17 15:51:44 -07:00
2016-05-11 23:24:26 +02:00
void GameObject :: UseDoorOrButton ( uint32 time_to_restore , bool alternative /* = false */ , Unit * user /*=nullptr*/ )
2008-10-11 14:16:25 -05:00
{
2010-04-07 19:14:10 +02:00
if ( m_lootState != GO_READY )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( ! time_to_restore )
2009-07-16 12:06:07 +08:00
time_to_restore = GetGOInfo () -> GetAutoCloseTime ();
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
SwitchDoorOrButton ( true , alternative );
2012-01-22 20:36:01 -05:00
SetLootState ( GO_ACTIVATED , user );
2009-10-17 15:51:44 -07:00
2017-03-05 17:14:29 +01:00
m_cooldownTime = time_to_restore ? ( GameTime :: GetGameTimeMS () + time_to_restore ) : 0 ;
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2009-01-04 17:14:34 -06:00
void GameObject :: SetGoArtKit ( uint8 kit )
{
2019-06-03 20:40:34 +02:00
SetUpdateFieldValue ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: ArtKit ), kit );
2017-05-07 21:48:41 +01:00
GameObjectData * data = const_cast < GameObjectData *> ( sObjectMgr -> GetGameObjectData ( m_spawnId ));
2010-04-07 19:14:10 +02:00
if ( data )
2009-08-21 12:31:52 -05:00
data -> artKit = kit ;
}
2009-10-17 15:51:44 -07:00
2014-10-26 02:57:28 +02:00
void GameObject :: SetGoArtKit ( uint8 artkit , GameObject * go , ObjectGuid :: LowType lowguid )
2009-08-21 12:31:52 -05:00
{
2020-09-04 13:38:24 +02:00
GameObjectData const * data = nullptr ;
2010-04-07 19:14:10 +02:00
if ( go )
2009-08-21 12:31:52 -05:00
{
go -> SetGoArtKit ( artkit );
2017-05-07 21:48:41 +01:00
data = go -> GetGameObjectData ();
2009-08-21 12:31:52 -05:00
}
2010-04-07 19:14:10 +02:00
else if ( lowguid )
2017-05-07 21:48:41 +01:00
data = sObjectMgr -> GetGameObjectData ( lowguid );
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( data )
2009-08-21 12:31:52 -05:00
const_cast < GameObjectData *> ( data ) -> artKit = artkit ;
2009-01-04 17:14:34 -06:00
}
2009-10-17 15:51:44 -07:00
2009-04-27 18:36:10 -05:00
void GameObject :: SwitchDoorOrButton ( bool activate , bool alternative /* = false */ )
2008-10-11 14:16:25 -05:00
{
2010-04-07 19:14:10 +02:00
if ( activate )
2019-06-03 20:40:34 +02:00
AddFlag ( GO_FLAG_IN_USE );
2008-10-11 14:16:25 -05:00
else
2019-06-03 20:40:34 +02:00
RemoveFlag ( GO_FLAG_IN_USE );
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( GetGoState () == GO_STATE_READY ) //if closed -> open
2009-04-27 18:36:10 -05:00
SetGoState ( alternative ? GO_STATE_ACTIVE_ALTERNATIVE : GO_STATE_ACTIVE );
2008-10-11 14:16:25 -05:00
else //if open -> close
2009-04-27 18:36:10 -05:00
SetGoState ( GO_STATE_READY );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
void GameObject :: Use ( Unit * user )
{
// by default spell caster is user
Unit * spellCaster = user ;
uint32 spellId = 0 ;
2009-02-22 17:48:48 -06:00
bool triggered = false ;
2009-10-17 15:51:44 -07:00
2010-12-26 16:35:29 +01:00
if ( Player * playerUser = user -> ToPlayer ())
{
2017-12-16 18:00:02 -03:00
if ( ! m_goInfo -> IsUsableMounted ())
2019-03-30 21:30:58 -03:00
playerUser -> RemoveAurasByType ( SPELL_AURA_MOUNTED );
2017-12-16 05:00:35 -03:00
2017-05-01 18:19:36 -03:00
playerUser -> PlayerTalkClass -> ClearMenus ();
2017-06-06 20:36:01 +02:00
if ( AI () -> GossipHello ( playerUser ))
2012-09-22 21:28:37 +02:00
return ;
2010-12-26 16:35:29 +01:00
}
2011-03-14 23:16:34 +01:00
// If cooldown data present in template
if ( uint32 cooldown = GetGOInfo () -> GetCooldown ())
{
2017-03-02 02:19:25 +01:00
if ( m_cooldownTime > GameTime :: GetGameTime ())
2011-03-14 23:16:34 +01:00
return ;
2017-03-02 02:19:25 +01:00
m_cooldownTime = GameTime :: GetGameTimeMS () + cooldown * IN_MILLISECONDS ;
2011-03-14 23:16:34 +01:00
}
switch ( GetGoType ())
2008-10-11 14:16:25 -05:00
{
case GAMEOBJECT_TYPE_DOOR : //0
case GAMEOBJECT_TYPE_BUTTON : //1
//doors/buttons never really despawn, only reset to default state/flags
2012-01-22 20:36:01 -05:00
UseDoorOrButton ( 0 , false , user );
2008-10-11 14:16:25 -05:00
return ;
case GAMEOBJECT_TYPE_QUESTGIVER : //2
{
2009-12-20 13:35:08 +02:00
if ( user -> GetTypeId () != TYPEID_PLAYER )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2011-02-22 01:46:19 +06:00
Player * player = user -> ToPlayer ();
2009-10-17 15:51:44 -07:00
2013-04-04 19:32:45 +02:00
player -> PrepareGossipMenu ( this , GetGOInfo () -> questgiver . gossipID , true );
2009-12-20 13:35:08 +02:00
player -> SendPreparedGossip ( this );
2008-10-11 14:16:25 -05:00
return ;
}
2012-10-20 21:56:48 +02:00
case GAMEOBJECT_TYPE_TRAP : //6
{
GameObjectTemplate const * goInfo = GetGOInfo ();
2014-10-30 20:02:42 +01:00
if ( goInfo -> trap . spell )
CastSpell ( user , goInfo -> trap . spell );
2012-10-20 21:56:48 +02:00
2017-03-02 02:19:25 +01:00
m_cooldownTime = GameTime :: GetGameTimeMS () + ( goInfo -> trap . cooldown ? goInfo -> trap . cooldown : uint32 ( 4 )) * IN_MILLISECONDS ; // template or 4 seconds
2012-10-20 21:56:48 +02:00
2014-10-30 20:02:42 +01:00
if ( goInfo -> trap . charges == 1 ) // Deactivate after trigger
2012-10-20 21:56:48 +02:00
SetLootState ( GO_JUST_DEACTIVATED );
return ;
}
2008-10-11 14:16:25 -05:00
//Sitting: Wooden bench, chairs enzz
case GAMEOBJECT_TYPE_CHAIR : //7
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * info = GetGOInfo ();
2010-04-07 19:14:10 +02:00
if ( ! info )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( user -> GetTypeId () != TYPEID_PLAYER )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2011-07-01 14:58:44 +02:00
if ( ChairListSlots . empty ()) // this is called once at first chair use to make list of available slots
2010-08-23 07:51:19 +02:00
{
2014-10-30 20:02:42 +01:00
if ( info -> chair . chairslots > 0 ) // sometimes chairs in DB have error in fields and we dont know number of slots
for ( uint32 i = 0 ; i < info -> chair . chairslots ; ++ i )
2014-09-14 16:14:12 +02:00
ChairListSlots [ i ]. Clear (); // Last user of current slot set to 0 (none sit here yet)
2009-10-28 15:59:45 -07:00
else
2014-09-14 16:14:12 +02:00
ChairListSlots [ 0 ]. Clear (); // error in DB, make one default slot
2010-08-23 07:51:19 +02:00
}
2009-10-28 15:59:45 -07:00
2011-02-22 01:46:19 +06:00
Player * player = user -> ToPlayer ();
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
2009-10-17 15:51:44 -07:00
2009-10-28 15:59:45 -07:00
float lowestDist = DEFAULT_VISIBILITY_DISTANCE ;
uint32 nearest_slot = 0 ;
float x_lowest = GetPositionX ();
float y_lowest = GetPositionY ();
// the object orientation + 1/2 pi
// every slot will be on that straight line
2014-08-21 16:56:11 +02:00
float orthogonalOrientation = GetOrientation () + float ( M_PI ) * 0.5f ;
2009-10-28 15:59:45 -07:00
// find nearest slot
bool found_free_slot = false ;
for ( ChairSlotAndUser :: iterator itr = ChairListSlots . begin (); itr != ChairListSlots . end (); ++ itr )
2008-10-11 14:16:25 -05:00
{
2009-10-28 15:59:45 -07:00
// the distance between this slot and the center of the go - imagine a 1D space
2014-10-30 20:02:42 +01:00
float relativeDistance = ( info -> size * itr -> first ) - ( info -> size * ( info -> chair . chairslots - 1 ) / 2.0f );
2009-10-17 15:51:44 -07:00
2012-10-02 15:06:19 +02:00
float x_i = GetPositionX () + relativeDistance * std :: cos ( orthogonalOrientation );
float y_i = GetPositionY () + relativeDistance * std :: sin ( orthogonalOrientation );
2009-10-17 15:51:44 -07:00
2014-10-21 20:27:00 +02:00
if ( ! itr -> second . IsEmpty ())
2010-08-23 07:51:19 +02:00
{
2017-02-04 22:37:16 +01:00
if ( Player * ChairUser = ObjectAccessor :: GetPlayer ( * this , itr -> second ))
2013-10-16 18:37:29 +02:00
{
2015-01-28 19:29:31 +00:00
if ( ChairUser -> IsSitState () && ChairUser -> GetStandState () != UNIT_STAND_STATE_SIT && ChairUser -> GetExactDist2d ( x_i , y_i ) < 0.1f )
continue ; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->GetStandState() != UNIT_STAND_STATE_SIT check is required.
2009-10-28 15:59:45 -07:00
else
2014-09-14 16:14:12 +02:00
itr -> second . Clear (); // This seat is unoccupied.
2013-10-16 18:37:29 +02:00
}
2009-10-28 15:59:45 -07:00
else
2014-09-14 16:14:12 +02:00
itr -> second . Clear (); // The seat may of had an occupant, but they're offline.
2010-08-23 07:51:19 +02:00
}
2009-10-17 15:51:44 -07:00
2009-10-28 15:59:45 -07:00
found_free_slot = true ;
2009-10-17 15:51:44 -07:00
2009-10-28 15:59:45 -07:00
// calculate the distance between the player and this slot
float thisDistance = player -> GetDistance2d ( x_i , y_i );
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( thisDistance <= lowestDist )
2009-10-28 15:59:45 -07:00
{
nearest_slot = itr -> first ;
lowestDist = thisDistance ;
x_lowest = x_i ;
y_lowest = y_i ;
2008-10-11 14:16:25 -05:00
}
}
2009-10-28 15:59:45 -07:00
2010-04-07 19:14:10 +02:00
if ( found_free_slot )
2008-10-11 14:16:25 -05:00
{
2009-10-28 15:59:45 -07:00
ChairSlotAndUser :: iterator itr = ChairListSlots . find ( nearest_slot );
if ( itr != ChairListSlots . end ())
{
itr -> second = player -> GetGUID (); //this slot in now used by player
2011-04-29 20:47:02 +02:00
player -> TeleportTo ( GetMapId (), x_lowest , y_lowest , GetPositionZ (), GetOrientation (), TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET );
2015-01-28 19:29:31 +00:00
player -> SetStandState ( UnitStandStateType ( UNIT_STAND_STATE_SIT_LOW_CHAIR + info -> chair . chairheight ));
2009-10-28 15:59:45 -07:00
return ;
}
2008-10-11 14:16:25 -05:00
}
2009-10-28 15:59:45 -07:00
2008-10-11 14:16:25 -05:00
return ;
}
//big gun, its a spell/aura
case GAMEOBJECT_TYPE_GOOBER : //10
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * info = GetGOInfo ();
2009-10-17 15:51:44 -07:00
2014-04-26 03:40:30 +02:00
if ( Player * player = user -> ToPlayer ())
2008-10-11 14:16:25 -05:00
{
2014-10-30 20:02:42 +01:00
if ( info -> goober . pageID ) // show page...
2008-10-11 14:16:25 -05:00
{
2015-04-11 11:11:43 +02:00
WorldPackets :: GameObject :: PageText data ;
data . GameObjectGUID = GetGUID ();
player -> SendDirectMessage ( data . Write ());
2008-10-11 14:16:25 -05:00
}
2010-03-11 22:55:02 +01:00
else if ( info -> goober . gossipID )
2009-12-20 13:35:08 +02:00
{
player -> PrepareGossipMenu ( this , info -> goober . gossipID );
player -> SendPreparedGossip ( this );
}
2009-10-17 15:51:44 -07:00
2014-10-30 20:02:42 +01:00
if ( info -> goober . eventID )
2010-01-16 19:31:10 +03:00
{
2015-02-25 00:13:14 +01:00
TC_LOG_DEBUG ( "maps.script" , "Goober ScriptStart id %u for GO entry %u (GUID " UI64FMTD ")." , info -> goober . eventID , GetEntry (), GetSpawnId ());
2014-10-30 20:02:42 +01:00
GetMap () -> ScriptsStart ( sEventScripts , info -> goober . eventID , player , this );
EventInform ( info -> goober . eventID , user );
2010-01-16 19:31:10 +03:00
}
// possible quest objective for active quests
2014-10-30 20:02:42 +01:00
if ( info -> goober . questID && sObjectMgr -> GetQuestTemplate ( info -> goober . questID ))
2010-01-16 19:31:10 +03:00
{
//Quest require to be active for GO using
2014-10-30 20:02:42 +01:00
if ( player -> GetQuestStatus ( info -> goober . questID ) != QUEST_STATUS_INCOMPLETE )
2010-01-16 19:31:10 +03:00
break ;
}
2016-01-17 23:30:18 +01:00
if ( Group * group = player -> GetGroup ())
{
for ( GroupReference const * itr = group -> GetFirstMember (); itr != nullptr ; itr = itr -> next ())
if ( Player * member = itr -> GetSource ())
if ( member -> IsAtGroupRewardDistance ( this ))
member -> KillCreditGO ( info -> entry , GetGUID ());
}
else
player -> KillCreditGO ( info -> entry , GetGUID ());
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2014-10-30 20:02:42 +01:00
if ( uint32 trapEntry = info -> goober . linkedTrap )
2010-01-16 19:31:10 +03:00
TriggeringLinkedGameObject ( trapEntry , user );
2019-06-03 20:40:34 +02:00
AddFlag ( GO_FLAG_IN_USE );
2012-01-22 20:36:01 -05:00
SetLootState ( GO_ACTIVATED , user );
2010-01-16 19:31:10 +03:00
// this appear to be ok, however others exist in addition to this that should have custom (ex: 190510, 188692, 187389)
2011-10-07 12:05:31 +03:00
if ( info -> goober . customAnim )
2011-03-02 16:50:09 +01:00
SendCustomAnim ( GetGoAnimProgress ());
2010-01-16 19:31:10 +03:00
else
SetGoState ( GO_STATE_ACTIVE );
2017-03-02 02:19:25 +01:00
m_cooldownTime = GameTime :: GetGameTimeMS () + info -> GetAutoCloseTime ();
2010-01-16 19:31:10 +03:00
2008-10-11 14:16:25 -05:00
// cast this spell later if provided
2014-10-30 20:02:42 +01:00
spellId = info -> goober . spell ;
2016-05-11 23:24:26 +02:00
spellCaster = nullptr ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
break ;
}
case GAMEOBJECT_TYPE_CAMERA : //13
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * info = GetGOInfo ();
2010-04-07 19:14:10 +02:00
if ( ! info )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( user -> GetTypeId () != TYPEID_PLAYER )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2011-02-22 01:46:19 +06:00
Player * player = user -> ToPlayer ();
2009-10-17 15:51:44 -07:00
2014-10-30 20:02:42 +01:00
if ( info -> camera . camera )
player -> SendCinematicStart ( info -> camera . camera );
2009-10-17 15:51:44 -07:00
2009-06-01 21:50:12 -05:00
if ( info -> camera . eventID )
2016-02-10 02:47:55 -02:00
{
2009-07-31 10:48:34 +08:00
GetMap () -> ScriptsStart ( sEventScripts , info -> camera . eventID , player , this );
2016-02-10 02:47:55 -02:00
EventInform ( info -> camera . eventID , user );
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
return ;
}
//fishing bobber
case GAMEOBJECT_TYPE_FISHINGNODE : //17
{
2010-12-26 16:35:29 +01:00
Player * player = user -> ToPlayer ();
if ( ! player )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( player -> GetGUID () != GetOwnerGUID ())
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-12-26 16:35:29 +01:00
switch ( getLootState ())
2008-10-11 14:16:25 -05:00
{
case GO_READY : // ready for loot
{
2009-03-11 16:17:37 -06:00
uint32 zone , subzone ;
2011-04-29 20:47:02 +02:00
GetZoneAndAreaId ( zone , subzone );
2009-10-17 15:51:44 -07:00
2010-12-22 21:25:23 +01:00
int32 zone_skill = sObjectMgr -> GetFishingBaseSkillLevel ( subzone );
2010-04-07 19:14:10 +02:00
if ( ! zone_skill )
2010-12-22 21:25:23 +01:00
zone_skill = sObjectMgr -> GetFishingBaseSkillLevel ( zone );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
//provide error, no fishable zone or area should be 0
2010-04-07 19:14:10 +02:00
if ( ! zone_skill )
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "sql.sql" , "Fishable areaId %u are not properly defined in `skill_fishing_base_level`." , subzone );
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
int32 skill = player -> GetSkillValue ( SKILL_FISHING );
2010-04-04 00:30:11 +02:00
int32 chance ;
if ( skill < zone_skill )
{
2011-04-29 20:47:02 +02:00
chance = int32 ( pow (( double ) skill / zone_skill , 2 ) * 100 );
2010-04-04 00:30:11 +02:00
if ( chance < 1 )
chance = 1 ;
}
else
chance = 100 ;
2011-04-29 20:47:02 +02:00
int32 roll = irand ( 1 , 100 );
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_DEBUG ( "misc" , "Fishing check (skill: %i zone min skill: %i chance %i roll: %i" , skill , zone_skill , chance , roll );
2009-10-17 15:51:44 -07:00
2016-07-05 23:23:22 +02:00
player -> UpdateFishingSkill ();
2016-09-12 19:37:24 +02:00
/// @todo find reasonable value for fishing hole search
GameObject * fishingPool = LookupFishingHoleAround ( 20.0f + CONTACT_DISTANCE );
// If fishing skill is high enough, or if fishing on a pool, send correct loot.
// Fishing pools have no skill requirement as of patch 3.3.0 (undocumented change).
if ( chance >= roll || fishingPool )
2008-10-11 14:16:25 -05:00
{
2013-03-08 21:41:30 +01:00
/// @todo I do not understand this hack. Need some explanation.
2010-04-04 00:30:11 +02:00
// prevent removing GO at spell cancel
2011-10-31 10:46:39 -04:00
RemoveFromOwner ();
2010-04-04 00:30:11 +02:00
SetOwnerGUID ( player -> GetGUID ());
2014-06-07 17:58:45 +01:00
SetSpellId ( 0 ); // prevent removing unintended auras at Unit::RemoveGameObject
2010-04-04 00:30:11 +02:00
2016-09-12 19:37:24 +02:00
if ( fishingPool )
2010-04-04 00:30:11 +02:00
{
2016-09-12 19:37:24 +02:00
fishingPool -> Use ( player );
2010-04-04 00:30:11 +02:00
SetLootState ( GO_JUST_DEACTIVATED );
2008-10-11 14:16:25 -05:00
}
2010-04-04 00:30:11 +02:00
else
2011-04-29 20:47:02 +02:00
player -> SendLoot ( GetGUID (), LOOT_FISHING );
2008-10-11 14:16:25 -05:00
}
2016-09-12 19:37:24 +02:00
else // If fishing skill is too low, send junk loot.
2014-05-10 21:35:05 +08:00
player -> SendLoot ( GetGUID (), LOOT_FISHING_JUNK );
2008-10-11 14:16:25 -05:00
break ;
}
case GO_JUST_DEACTIVATED : // nothing to do, will be deleted at next update
break ;
default :
{
SetLootState ( GO_JUST_DEACTIVATED );
2016-02-11 00:56:39 +01:00
player -> SendDirectMessage ( WorldPackets :: GameObject :: FishNotHooked (). Write ());
2008-10-11 14:16:25 -05:00
break ;
}
}
2009-10-17 15:51:44 -07:00
2009-08-19 16:52:51 -05:00
player -> FinishSpell ( CURRENT_CHANNELED_SPELL );
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2014-10-06 18:37:38 -04:00
case GAMEOBJECT_TYPE_RITUAL : //18
2008-10-11 14:16:25 -05:00
{
2010-04-07 19:14:10 +02:00
if ( user -> GetTypeId () != TYPEID_PLAYER )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-12-26 00:14:13 +01:00
Player * player = user -> ToPlayer ();
2009-10-17 15:51:44 -07:00
2010-08-09 22:50:30 +06:00
Unit * owner = GetOwner ();
2009-10-17 15:51:44 -07:00
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * info = GetGOInfo ();
2009-10-17 15:51:44 -07:00
2016-05-11 23:24:26 +02:00
Player * m_ritualOwner = nullptr ;
2014-10-21 20:27:00 +02:00
if ( ! m_ritualOwnerGUID . IsEmpty ())
2014-04-10 20:41:34 +02:00
m_ritualOwner = ObjectAccessor :: FindPlayer ( m_ritualOwnerGUID );
2010-08-09 22:50:30 +06:00
// ritual owner is set for GO's without owner (not summoned)
if ( ! m_ritualOwner && ! owner )
2014-04-10 20:41:34 +02:00
{
m_ritualOwnerGUID = player -> GetGUID ();
2010-08-09 22:50:30 +06:00
m_ritualOwner = player ;
2014-04-10 20:41:34 +02:00
}
2009-10-17 15:51:44 -07:00
2010-08-09 22:50:30 +06:00
if ( owner )
{
if ( owner -> GetTypeId () != TYPEID_PLAYER )
return ;
2009-10-17 15:51:44 -07:00
2010-08-09 22:50:30 +06:00
// accept only use by player from same group as owner, excluding owner itself (unique use already added in spell effect)
2014-10-30 20:02:42 +01:00
if ( player == owner -> ToPlayer () || ( info -> ritual . castersGrouped && ! player -> IsInSameRaidWith ( owner -> ToPlayer ())))
2010-08-09 22:50:30 +06:00
return ;
2009-10-17 15:51:44 -07:00
2010-08-09 22:50:30 +06:00
// expect owner to already be channeling, so if not...
if ( ! owner -> GetCurrentSpell ( CURRENT_CHANNELED_SPELL ))
return ;
2009-10-17 15:51:44 -07:00
2010-08-09 22:50:30 +06:00
// in case summoning ritual caster is GO creator
spellCaster = owner ;
}
else
{
2014-10-30 20:02:42 +01:00
if ( player != m_ritualOwner && ( info -> ritual . castersGrouped && ! player -> IsInSameRaidWith ( m_ritualOwner )))
2010-08-09 22:50:30 +06:00
return ;
2009-10-17 15:51:44 -07:00
2010-08-09 22:50:30 +06:00
spellCaster = player ;
}
AddUniqueUse ( player );
2009-10-17 15:51:44 -07:00
2014-10-30 20:02:42 +01:00
if ( info -> ritual . animSpell )
2009-02-22 17:48:48 -06:00
{
2014-10-30 20:02:42 +01:00
player -> CastSpell ( player , info -> ritual . animSpell , true );
2010-08-09 22:50:30 +06:00
// for this case, summoningRitual.spellId is always triggered
2009-02-22 17:48:48 -06:00
triggered = true ;
}
2009-10-17 15:51:44 -07:00
2010-08-09 22:50:30 +06:00
// full amount unique participants including original summoner
2014-10-30 20:02:42 +01:00
if ( GetUniqueUseCount () == info -> ritual . casters )
2010-08-09 22:50:30 +06:00
{
2012-08-30 22:44:33 +01:00
if ( m_ritualOwner )
spellCaster = m_ritualOwner ;
2010-08-09 22:50:30 +06:00
2014-10-30 20:02:42 +01:00
spellId = info -> ritual . spell ;
2009-10-17 15:51:44 -07:00
2010-08-09 22:50:30 +06:00
if ( spellId == 62330 ) // GO store nonexistent spell, replace by expected
{
// spell have reagent and mana cost but it not expected use its
2013-12-25 14:16:55 -03:30
// it triggered spell in fact cast at currently channeled GO
2010-08-09 22:50:30 +06:00
spellId = 61993 ;
triggered = true ;
}
2011-09-15 17:51:17 +01:00
// Cast casterTargetSpell at a random GO user
// on the current DB there is only one gameobject that uses this (Ritual of Doom)
// and its required target number is 1 (outter for loop will run once)
2014-10-30 20:02:42 +01:00
if ( info -> ritual . casterTargetSpell && info -> ritual . casterTargetSpell != 1 ) // No idea why this field is a bool in some cases
for ( uint32 i = 0 ; i < info -> ritual . casterTargetSpellTargets ; i ++ )
2011-09-17 13:20:21 +02:00
// m_unique_users can contain only player GUIDs
2012-05-30 08:01:02 +02:00
if ( Player * target = ObjectAccessor :: GetPlayer ( * this , Trinity :: Containers :: SelectRandomContainerElement ( m_unique_users )))
2014-10-30 20:02:42 +01:00
spellCaster -> CastSpell ( target , info -> ritual . casterTargetSpell , true );
2011-09-15 17:51:17 +01:00
2010-08-09 22:50:30 +06:00
// finish owners spell
if ( owner )
owner -> FinishSpell ( CURRENT_CHANNELED_SPELL );
// can be deleted now, if
2014-10-30 20:02:42 +01:00
if ( ! info -> ritual . ritualPersistent )
2010-08-09 22:50:30 +06:00
SetLootState ( GO_JUST_DEACTIVATED );
else
{
// reset ritual for this GO
2014-09-14 16:14:12 +02:00
m_ritualOwnerGUID . Clear ();
2010-08-09 22:50:30 +06:00
m_unique_users . clear ();
m_usetimes = 0 ;
}
}
else
return ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
// go to end function to spell casting
break ;
}
case GAMEOBJECT_TYPE_SPELLCASTER : //22
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * info = GetGOInfo ();
2010-04-07 19:14:10 +02:00
if ( ! info )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2014-10-30 20:02:42 +01:00
if ( info -> spellCaster . partyOnly )
2008-10-11 14:16:25 -05:00
{
Unit * caster = GetOwner ();
2010-04-07 22:59:46 +02:00
if ( ! caster || caster -> GetTypeId () != TYPEID_PLAYER )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( user -> GetTypeId () != TYPEID_PLAYER || ! user -> ToPlayer () -> IsInSameRaidWith ( caster -> ToPlayer ()))
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2010-12-19 11:56:37 +01:00
user -> RemoveAurasByType ( SPELL_AURA_MOUNTED );
2014-10-30 20:02:42 +01:00
spellId = info -> spellCaster . spell ;
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
AddUse ();
break ;
}
case GAMEOBJECT_TYPE_MEETINGSTONE : //23
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * info = GetGOInfo ();
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if ( user -> GetTypeId () != TYPEID_PLAYER )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2011-02-22 01:46:19 +06:00
Player * player = user -> ToPlayer ();
2009-10-17 15:51:44 -07:00
2013-10-03 17:07:25 +02:00
Player * targetPlayer = ObjectAccessor :: FindPlayer ( player -> GetTarget ());
2009-10-17 15:51:44 -07:00
2010-07-24 04:50:35 +02:00
// accept only use by player from same raid as caster, except caster itself
if ( ! targetPlayer || targetPlayer == player || ! targetPlayer -> IsInSameRaidWith ( player ))
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
//required lvl checks!
2020-12-09 00:03:10 +01:00
if ( Optional < ContentTuningLevels > userLevels = sDB2Manager . GetContentTuningData ( info -> ContentTuningId , player -> m_playerData -> CtrOptions -> ContentTuningConditionMask ))
if ( player -> getLevel () < userLevels -> MaxLevel )
return ;
if ( Optional < ContentTuningLevels > targetLevels = sDB2Manager . GetContentTuningData ( info -> ContentTuningId , targetPlayer -> m_playerData -> CtrOptions -> ContentTuningConditionMask ))
if ( targetPlayer -> getLevel () < targetLevels -> MaxLevel )
return ;
2009-10-17 15:51:44 -07:00
2011-04-28 22:54:30 +02:00
if ( info -> entry == 194097 )
2009-02-22 17:48:19 -06:00
spellId = 61994 ; // Ritual of Summoning
else
spellId = 59782 ; // Summoning Stone Effect
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
break ;
}
2009-10-17 15:51:44 -07:00
2008-10-11 14:16:25 -05:00
case GAMEOBJECT_TYPE_FLAGSTAND : // 24
{
2009-11-01 17:53:07 -08:00
if ( user -> GetTypeId () != TYPEID_PLAYER )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2011-02-22 01:46:19 +06:00
Player * player = user -> ToPlayer ();
2009-10-17 15:51:44 -07:00
2012-10-17 16:56:35 +02:00
if ( player -> CanUseBattlegroundObject ( this ))
2008-10-11 14:16:25 -05:00
{
// in battleground check
2011-09-15 14:08:17 +02:00
Battleground * bg = player -> GetBattleground ();
2010-04-07 19:14:10 +02:00
if ( ! bg )
2008-10-11 14:16:25 -05:00
return ;
2012-10-17 16:56:35 +02:00
2010-04-30 00:24:25 +02:00
if ( player -> GetVehicle ())
return ;
2012-10-17 16:56:35 +02:00
player -> RemoveAurasByType ( SPELL_AURA_MOD_STEALTH );
player -> RemoveAurasByType ( SPELL_AURA_MOD_INVISIBILITY );
2008-10-11 14:16:25 -05:00
// BG flag click
// AB:
// 15001
// 15002
// 15003
// 15004
// 15005
bg -> EventPlayerClickedOnFlag ( player , this );
return ; //we don;t need to delete flag ... it is despawned!
}
break ;
}
2010-08-09 22:50:30 +06:00
case GAMEOBJECT_TYPE_FISHINGHOLE : // 25
{
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
2011-02-22 01:46:19 +06:00
Player * player = user -> ToPlayer ();
2010-08-09 22:50:30 +06:00
player -> SendLoot ( GetGUID (), LOOT_FISHINGHOLE );
2021-09-08 22:07:01 +02:00
player -> UpdateCriteria ( CriteriaType :: CatchFishInFishingHole , GetGOInfo () -> entry );
2010-08-09 22:50:30 +06:00
return ;
}
2008-10-11 14:16:25 -05:00
case GAMEOBJECT_TYPE_FLAGDROP : // 26
{
2009-11-01 17:53:07 -08:00
if ( user -> GetTypeId () != TYPEID_PLAYER )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2011-02-22 01:46:19 +06:00
Player * player = user -> ToPlayer ();
2009-10-17 15:51:44 -07:00
2012-10-17 16:56:35 +02:00
if ( player -> CanUseBattlegroundObject ( this ))
2008-10-11 14:16:25 -05:00
{
// in battleground check
2011-06-12 02:30:32 +02:00
Battleground * bg = player -> GetBattleground ();
2009-11-01 17:53:07 -08:00
if ( ! bg )
2008-10-11 14:16:25 -05:00
return ;
2012-10-17 16:56:35 +02:00
2011-06-12 02:30:32 +02:00
if ( player -> GetVehicle ())
2010-04-30 00:24:25 +02:00
return ;
2012-10-17 16:56:35 +02:00
player -> RemoveAurasByType ( SPELL_AURA_MOD_STEALTH );
player -> RemoveAurasByType ( SPELL_AURA_MOD_INVISIBILITY );
2008-10-11 14:16:25 -05:00
// BG flag dropped
// WS:
// 179785 - Silverwing Flag
// 179786 - Warsong Flag
// EotS:
// 184142 - Netherstorm Flag
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * info = GetGOInfo ();
2009-11-01 17:53:07 -08:00
if ( info )
2008-10-11 14:16:25 -05:00
{
2011-09-29 12:43:05 +02:00
switch ( info -> entry )
2008-10-11 14:16:25 -05:00
{
case 179785 : // Silverwing Flag
case 179786 : // Warsong Flag
2011-10-18 09:48:11 +02:00
if ( bg -> GetTypeID ( true ) == BATTLEGROUND_WS )
2008-10-11 14:16:25 -05:00
bg -> EventPlayerClickedOnFlag ( player , this );
break ;
case 184142 : // Netherstorm Flag
2011-10-18 09:48:11 +02:00
if ( bg -> GetTypeID ( true ) == BATTLEGROUND_EY )
2008-10-11 14:16:25 -05:00
bg -> EventPlayerClickedOnFlag ( player , this );
break ;
}
}
//this cause to call return, all flags must be deleted here!!
spellId = 0 ;
Delete ();
}
break ;
}
2008-12-24 09:58:26 -06:00
case GAMEOBJECT_TYPE_BARBER_CHAIR : //32
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const * info = GetGOInfo ();
2009-11-01 17:53:07 -08:00
if ( ! info )
2008-12-24 09:58:26 -06:00
return ;
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( user -> GetTypeId () != TYPEID_PLAYER )
2008-12-24 09:58:26 -06:00
return ;
2009-10-17 15:51:44 -07:00
2011-02-22 01:46:19 +06:00
Player * player = user -> ToPlayer ();
2009-10-17 15:51:44 -07:00
2015-03-28 18:12:26 +01:00
WorldPackets :: Misc :: EnableBarberShop packet ;
player -> SendDirectMessage ( packet . Write ());
2009-10-17 15:51:44 -07:00
2015-07-12 22:34:25 +02:00
// fallback, will always work
player -> TeleportTo ( GetMapId (), GetPositionX (), GetPositionY (), GetPositionZ (), GetOrientation (), TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET );
player -> SetStandState ( UnitStandStateType ( UNIT_STAND_STATE_SIT_LOW_CHAIR + info -> barberChair . chairheight ), info -> barberChair . SitAnimKit );
2008-12-24 09:58:26 -06:00
return ;
}
2021-11-20 20:36:56 +01:00
case GAMEOBJECT_TYPE_NEW_FLAG :
{
GameObjectTemplate const * info = GetGOInfo ();
if ( ! info )
return ;
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
spellId = info -> newflag . pickupSpell ;
break ;
}
2019-07-13 20:56:02 +02:00
case GAMEOBJECT_TYPE_ITEM_FORGE :
2016-08-15 00:03:38 +02:00
{
GameObjectTemplate const * info = GetGOInfo ();
if ( ! info )
return ;
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = user -> ToPlayer ();
2019-07-13 20:56:02 +02:00
if ( PlayerConditionEntry const * playerCondition = sPlayerConditionStore . LookupEntry ( info -> itemForge . conditionID1 ))
2016-08-15 00:03:38 +02:00
if ( ! sConditionMgr -> IsPlayerMeetingCondition ( player , playerCondition ))
return ;
2019-11-22 11:02:04 +01:00
switch ( info -> itemForge . ForgeType )
2016-08-15 00:03:38 +02:00
{
2019-11-22 11:02:04 +01:00
case 0 : // Artifact Forge
case 1 : // Relic Forge
{
Aura const * artifactAura = player -> GetAura ( ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE );
Item const * item = artifactAura ? player -> GetItemByGuid ( artifactAura -> GetCastItemGUID ()) : nullptr ;
if ( ! item )
{
player -> SendDirectMessage ( WorldPackets :: Misc :: DisplayGameError ( GameError :: ERR_MUST_EQUIP_ARTIFACT ). Write ());
return ;
}
2020-09-19 12:44:42 +02:00
WorldPackets :: Artifact :: OpenArtifactForge openArtifactForge ;
openArtifactForge . ArtifactGUID = item -> GetGUID ();
openArtifactForge . ForgeGUID = GetGUID ();
player -> SendDirectMessage ( openArtifactForge . Write ());
2019-11-22 11:02:04 +01:00
break ;
}
case 2 : // Heart Forge
{
2021-04-21 11:02:42 +03:00
Item const * item = player -> GetItemByEntry ( ITEM_ID_HEART_OF_AZEROTH , ItemSearchLocation :: Everywhere );
2019-11-22 11:02:04 +01:00
if ( ! item )
return ;
2016-08-15 00:03:38 +02:00
2020-09-19 12:44:42 +02:00
WorldPackets :: Azerite :: OpenHeartForge openHeartForge ;
openHeartForge . ForgeGUID = GetGUID ();
player -> SendDirectMessage ( openHeartForge . Write ());
2019-11-22 11:02:04 +01:00
break ;
}
default :
break ;
}
2016-08-15 00:03:38 +02:00
return ;
}
2018-01-04 01:15:33 +01:00
case GAMEOBJECT_TYPE_UI_LINK :
{
Player * player = user -> ToPlayer ();
if ( ! player )
return ;
2020-09-19 12:44:42 +02:00
WorldPackets :: GameObject :: GameObjectUILink gameObjectUILink ;
gameObjectUILink . ObjectGUID = GetGUID ();
gameObjectUILink . UILink = GetGOInfo () -> UILink . UILinkType ;
player -> SendDirectMessage ( gameObjectUILink . Write ());
2018-01-04 01:15:33 +01:00
return ;
}
2008-10-11 14:16:25 -05:00
default :
2011-08-15 15:41:06 +02:00
if ( GetGoType () >= MAX_GAMEOBJECT_TYPE )
2014-10-22 19:33:06 +02:00
TC_LOG_ERROR ( "misc" , "GameObject::Use(): unit (type: %u, %s, name: %s) tries to use object (%s, name: %s) of unknown type (%u)" ,
user -> GetTypeId (), user -> GetGUID (). ToString (). c_str (), user -> GetName (). c_str (), GetGUID (). ToString (). c_str (), GetGOInfo () -> name . c_str (), GetGoType ());
2008-10-11 14:16:25 -05:00
break ;
}
2009-10-17 15:51:44 -07:00
2009-11-01 17:53:07 -08:00
if ( ! spellId )
2008-10-11 14:16:25 -05:00
return ;
2009-10-17 15:51:44 -07:00
2017-12-30 20:28:41 +01:00
if ( ! sSpellMgr -> GetSpellInfo ( spellId , GetMap () -> GetDifficultyID ()))
2008-10-11 14:16:25 -05:00
{
2011-02-22 01:46:19 +06:00
if ( user -> GetTypeId () != TYPEID_PLAYER || ! sOutdoorPvPMgr -> HandleCustomSpell ( user -> ToPlayer (), spellId , this ))
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "misc" , "WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u)" , spellId , GetEntry (), GetGoType ());
2008-10-14 11:57:03 -05:00
else
2013-11-08 10:50:51 +01:00
TC_LOG_DEBUG ( "outdoorpvp" , "WORLD: %u non-dbc spell was handled by OutdoorPvP" , spellId );
2008-10-11 14:16:25 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2015-05-22 15:12:52 +02:00
if ( Player * player = user -> ToPlayer ())
sOutdoorPvPMgr -> HandleCustomSpell ( player , spellId , this );
2010-04-01 20:08:22 -02:30
if ( spellCaster )
2017-12-30 20:28:41 +01:00
spellCaster -> CastSpell ( user , spellId , triggered );
2010-04-01 20:08:22 -02:30
else
CastSpell ( user , spellId );
2008-10-11 14:16:25 -05:00
}
2009-10-17 15:51:44 -07:00
2011-03-02 16:50:09 +01:00
void GameObject :: SendCustomAnim ( uint32 anim )
2009-04-30 12:01:08 -05:00
{
2016-02-28 23:14:27 +01:00
WorldPackets :: GameObject :: GameObjectCustomAnim customAnim ;
customAnim . ObjectGUID = GetGUID ();
customAnim . CustomAnim = anim ;
SendMessageToSet ( customAnim . Write (), true );
2009-04-30 12:01:08 -05:00
}
2009-10-17 15:51:44 -07:00
2009-05-21 10:01:03 -05:00
bool GameObject :: IsInRange ( float x , float y , float z , float radius ) const
{
2011-10-04 20:14:30 -03:00
GameObjectDisplayInfoEntry const * info = sGameObjectDisplayInfoStore . LookupEntry ( m_goInfo -> displayId );
2009-11-01 17:53:07 -08:00
if ( ! info )
2009-05-21 10:01:03 -05:00
return IsWithinDist3d ( x , y , z , radius );
2009-10-17 15:51:44 -07:00
2012-10-02 15:06:19 +02:00
float sinA = std :: sin ( GetOrientation ());
float cosA = std :: cos ( GetOrientation ());
2009-05-21 10:01:03 -05:00
float dx = x - GetPositionX ();
float dy = y - GetPositionY ();
float dz = z - GetPositionZ ();
2014-08-21 16:56:11 +02:00
float dist = std :: sqrt ( dx * dx + dy * dy );
2012-09-08 15:24:59 -05:00
//! Check if the distance between the 2 objects is 0, can happen if both objects are on the same position.
//! The code below this check wont crash if dist is 0 because 0/0 in float operations is valid, and returns infinite
if ( G3D :: fuzzyEq ( dist , 0.0f ))
return true ;
2009-05-21 10:01:03 -05:00
float sinB = dx / dist ;
float cosB = dy / dist ;
dx = dist * ( cosA * cosB + sinA * sinB );
dy = dist * ( cosA * sinB - sinA * cosB );
2014-11-08 20:21:17 +02:00
return dx < info -> GeoBoxMax . X + radius && dx > info -> GeoBoxMin . X - radius
&& dy < info -> GeoBoxMax . Y + radius && dy > info -> GeoBoxMin . Y - radius
&& dz < info -> GeoBoxMax . Z + radius && dz > info -> GeoBoxMin . Z - radius ;
2009-05-21 10:01:03 -05:00
}
2009-10-17 15:51:44 -07:00
2016-05-11 23:24:26 +02:00
void GameObject :: EventInform ( uint32 eventId , WorldObject * invoker /*= nullptr*/ )
2009-06-01 12:48:12 -05:00
{
2012-06-11 11:10:46 +02:00
if ( ! eventId )
return ;
if ( AI ())
AI () -> EventInform ( eventId );
2014-04-26 03:40:30 +02:00
if ( GetZoneScript ())
GetZoneScript () -> ProcessEvent ( this , eventId );
if ( BattlegroundMap * bgMap = GetMap () -> ToBattlegroundMap ())
if ( bgMap -> GetBG ())
bgMap -> GetBG () -> ProcessEvent ( this , eventId , invoker );
2009-06-01 12:48:12 -05:00
}
2009-10-17 15:51:44 -07:00
2017-03-18 11:31:31 +01:00
uint32 GameObject :: GetScriptId () const
{
2017-05-07 21:48:41 +01:00
if ( GameObjectData const * gameObjectData = GetGameObjectData ())
if ( uint32 scriptId = gameObjectData -> scriptId )
2017-03-31 01:58:15 -03:00
return scriptId ;
2017-03-18 11:31:31 +01:00
return GetGOInfo () -> ScriptId ;
}
2008-11-14 17:03:03 -06:00
// overwrite WorldObject function for proper name localization
2020-04-18 10:59:35 +02:00
std :: string GameObject :: GetNameForLocaleIdx ( LocaleConstant locale ) const
2008-11-14 17:03:03 -06:00
{
2020-04-18 10:59:35 +02:00
if ( locale != DEFAULT_LOCALE )
2011-09-15 14:08:17 +02:00
if ( GameObjectLocale const * cl = sObjectMgr -> GetGameObjectLocale ( GetEntry ()))
2020-04-18 10:59:35 +02:00
if ( cl -> Name . size () > locale && ! cl -> Name [ locale ]. empty ())
return cl -> Name [ locale ];
2009-10-17 15:51:44 -07:00
2008-11-14 17:03:03 -06:00
return GetName ();
}
2009-10-17 15:51:44 -07:00
2016-07-30 11:34:02 +02:00
void GameObject :: UpdatePackedRotation ()
2009-03-18 20:46:21 -06:00
{
2016-07-30 11:34:02 +02:00
static const int32 PACK_YZ = 1 << 20 ;
static const int32 PACK_X = PACK_YZ << 1 ;
2009-10-17 15:51:44 -07:00
2016-07-30 11:34:02 +02:00
static const int32 PACK_YZ_MASK = ( PACK_YZ << 1 ) - 1 ;
static const int32 PACK_X_MASK = ( PACK_X << 1 ) - 1 ;
2009-10-17 15:51:44 -07:00
2019-04-24 20:39:01 +02:00
int8 w_sign = ( m_localRotation . w >= 0.f ? 1 : - 1 );
int64 x = int32 ( m_localRotation . x * PACK_X ) * w_sign & PACK_X_MASK ;
int64 y = int32 ( m_localRotation . y * PACK_YZ ) * w_sign & PACK_YZ_MASK ;
int64 z = int32 ( m_localRotation . z * PACK_YZ ) * w_sign & PACK_YZ_MASK ;
2016-07-30 11:34:02 +02:00
m_packedRotation = z | ( y << 21 ) | ( x << 42 );
}
2009-10-17 15:51:44 -07:00
2019-04-24 20:39:01 +02:00
void GameObject :: SetLocalRotation ( float qx , float qy , float qz , float qw )
2016-07-30 11:34:02 +02:00
{
2017-05-28 16:34:44 +02:00
G3D :: Quat rotation ( qx , qy , qz , qw );
rotation . unitize ();
2019-04-24 20:39:01 +02:00
m_localRotation . x = rotation . x ;
m_localRotation . y = rotation . y ;
m_localRotation . z = rotation . z ;
m_localRotation . w = rotation . w ;
2016-07-30 11:34:02 +02:00
UpdatePackedRotation ();
}
2009-10-17 15:51:44 -07:00
2017-05-28 16:34:44 +02:00
void GameObject :: SetParentRotation ( QuaternionData const & rotation )
2016-07-30 11:34:02 +02:00
{
2019-06-03 20:40:34 +02:00
SetUpdateFieldValue ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: ParentRotation ), rotation );
2016-07-30 11:34:02 +02:00
}
2009-10-17 15:51:44 -07:00
2019-04-24 20:39:01 +02:00
void GameObject :: SetLocalRotationAngles ( float z_rot , float y_rot , float x_rot )
2016-07-30 11:34:02 +02:00
{
2017-05-28 16:34:44 +02:00
G3D :: Quat quat ( G3D :: Matrix3 :: fromEulerAnglesZYX ( z_rot , y_rot , x_rot ));
2019-04-24 20:39:01 +02:00
SetLocalRotation ( quat . x , quat . y , quat . z , quat . w );
}
QuaternionData GameObject :: GetWorldRotation () const
{
QuaternionData localRotation = GetLocalRotation ();
if ( Transport * transport = GetTransport ())
{
QuaternionData worldRotation = transport -> GetWorldRotation ();
G3D :: Quat worldRotationQuat ( worldRotation . x , worldRotation . y , worldRotation . z , worldRotation . w );
G3D :: Quat localRotationQuat ( localRotation . x , localRotation . y , localRotation . z , localRotation . w );
G3D :: Quat resultRotation = localRotationQuat * worldRotationQuat ;
return QuaternionData ( resultRotation . x , resultRotation . y , resultRotation . z , resultRotation . w );
}
return localRotation ;
2009-03-18 20:46:21 -06:00
}
2011-07-22 21:22:05 +02:00
2018-02-10 16:43:01 -03:00
void GameObject :: ModifyHealth ( int32 change , WorldObject * attackerOrHealer /*= nullptr*/ , uint32 spellId /*= 0*/ )
2011-07-22 21:22:05 +02:00
{
2012-10-24 13:29:34 +02:00
if ( ! m_goValue . Building . MaxHealth || ! change )
2011-07-22 21:22:05 +02:00
return ;
2011-07-24 23:55:14 +02:00
// prevent double destructions of the same object
2012-10-24 13:29:34 +02:00
if ( change < 0 && ! m_goValue . Building . Health )
2011-07-24 23:55:14 +02:00
return ;
2012-10-24 13:29:34 +02:00
if ( int32 ( m_goValue . Building . Health ) + change <= 0 )
m_goValue . Building . Health = 0 ;
else if ( int32 ( m_goValue . Building . Health ) + change >= int32 ( m_goValue . Building . MaxHealth ))
m_goValue . Building . Health = m_goValue . Building . MaxHealth ;
2011-07-22 21:22:05 +02:00
else
2012-10-24 13:29:34 +02:00
m_goValue . Building . Health += change ;
2011-07-22 21:22:05 +02:00
2011-07-24 23:55:14 +02:00
// Set the health bar, value = 255 * healthPct;
2012-10-24 13:29:34 +02:00
SetGoAnimProgress ( m_goValue . Building . Health * 255 / m_goValue . Building . MaxHealth );
2011-07-22 21:22:05 +02:00
// dealing damage, send packet
2018-02-10 16:43:01 -03:00
if ( Player * player = attackerOrHealer ? attackerOrHealer -> GetCharmerOrOwnerPlayerOrPlayerItself () : nullptr )
2011-07-22 21:22:05 +02:00
{
2016-01-01 12:03:31 +01:00
WorldPackets :: GameObject :: DestructibleBuildingDamage packet ;
packet . Caster = attackerOrHealer -> GetGUID (); // todo: this can be a GameObject
packet . Target = GetGUID ();
packet . Damage = - change ;
packet . Owner = player -> GetGUID ();
packet . SpellID = spellId ;
player -> SendDirectMessage ( packet . Write ());
2011-07-22 21:22:05 +02:00
}
2011-07-24 23:55:14 +02:00
GameObjectDestructibleState newState = GetDestructibleState ();
2012-10-24 13:29:34 +02:00
if ( ! m_goValue . Building . Health )
2011-07-24 23:55:14 +02:00
newState = GO_DESTRUCTIBLE_DESTROYED ;
2014-10-30 20:02:42 +01:00
else if ( m_goValue . Building . Health <= 10000 /*GetGOInfo() -> destructibleBuilding . damagedNumHits */ ) // TODO: Get health somewhere
2011-07-24 23:55:14 +02:00
newState = GO_DESTRUCTIBLE_DAMAGED ;
2012-10-24 13:29:34 +02:00
else if ( m_goValue . Building . Health == m_goValue . Building . MaxHealth )
2011-07-24 23:55:14 +02:00
newState = GO_DESTRUCTIBLE_INTACT ;
if ( newState == GetDestructibleState ())
return ;
2018-02-10 16:43:01 -03:00
SetDestructibleState ( newState , attackerOrHealer , false );
2011-07-22 21:22:05 +02:00
}
2018-02-10 16:43:01 -03:00
void GameObject :: SetDestructibleState ( GameObjectDestructibleState state , WorldObject * attackerOrHealer /*= nullptr*/ , bool setHealth /*= false*/ )
2011-07-22 21:22:05 +02:00
{
// the user calling this must know he is already operating on destructible gameobject
ASSERT ( GetGoType () == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING );
switch ( state )
{
case GO_DESTRUCTIBLE_INTACT :
2019-06-03 20:40:34 +02:00
RemoveFlag ( GameObjectFlags ( GO_FLAG_DAMAGED | GO_FLAG_DESTROYED ));
2012-02-03 16:03:52 -05:00
SetDisplayId ( m_goInfo -> displayId );
2011-07-22 21:22:05 +02:00
if ( setHealth )
{
2012-10-24 13:29:34 +02:00
m_goValue . Building . Health = m_goValue . Building . MaxHealth ;
2011-07-22 21:22:05 +02:00
SetGoAnimProgress ( 255 );
}
2012-05-30 08:01:02 +02:00
EnableCollision ( true );
2011-07-22 21:22:05 +02:00
break ;
case GO_DESTRUCTIBLE_DAMAGED :
{
2018-02-10 16:43:01 -03:00
EventInform ( m_goInfo -> destructibleBuilding . DamagedEvent , attackerOrHealer );
AI () -> Damaged ( attackerOrHealer , m_goInfo -> destructibleBuilding . DamagedEvent );
2011-07-22 21:22:05 +02:00
2019-06-03 20:40:34 +02:00
RemoveFlag ( GO_FLAG_DESTROYED );
AddFlag ( GO_FLAG_DAMAGED );
2011-07-22 21:22:05 +02:00
2014-10-30 20:02:42 +01:00
uint32 modelId = m_goInfo -> displayId ;
if ( DestructibleModelDataEntry const * modelData = sDestructibleModelDataStore . LookupEntry ( m_goInfo -> destructibleBuilding . DestructibleModelRec ))
2018-03-28 22:01:22 +02:00
if ( modelData -> State1Wmo )
modelId = modelData -> State1Wmo ;
2012-02-03 16:03:52 -05:00
SetDisplayId ( modelId );
2011-07-22 21:22:05 +02:00
if ( setHealth )
{
2014-10-30 20:02:42 +01:00
m_goValue . Building . Health = 10000 /*m_goInfo->destructibleBuilding.damagedNumHits*/ ;
2012-10-24 13:29:34 +02:00
uint32 maxHealth = m_goValue . Building . MaxHealth ;
2011-07-22 21:22:05 +02:00
// in this case current health is 0 anyway so just prevent crashing here
if ( ! maxHealth )
maxHealth = 1 ;
2012-10-24 13:29:34 +02:00
SetGoAnimProgress ( m_goValue . Building . Health * 255 / maxHealth );
2011-07-22 21:22:05 +02:00
}
break ;
}
case GO_DESTRUCTIBLE_DESTROYED :
{
2018-02-10 16:43:01 -03:00
EventInform ( m_goInfo -> destructibleBuilding . DestroyedEvent , attackerOrHealer );
AI () -> Destroyed ( attackerOrHealer , m_goInfo -> destructibleBuilding . DestroyedEvent );
2018-07-11 18:14:09 +02:00
if ( Player * player = attackerOrHealer ? attackerOrHealer -> GetCharmerOrOwnerPlayerOrPlayerItself () : nullptr )
if ( Battleground * bg = player -> GetBattleground ())
bg -> DestroyGate ( player , this );
2011-07-22 21:22:05 +02:00
2019-06-03 20:40:34 +02:00
RemoveFlag ( GO_FLAG_DAMAGED );
AddFlag ( GO_FLAG_DESTROYED );
2011-07-22 21:22:05 +02:00
2014-10-30 20:02:42 +01:00
uint32 modelId = m_goInfo -> displayId ;
if ( DestructibleModelDataEntry const * modelData = sDestructibleModelDataStore . LookupEntry ( m_goInfo -> destructibleBuilding . DestructibleModelRec ))
2018-03-28 22:01:22 +02:00
if ( modelData -> State2Wmo )
modelId = modelData -> State2Wmo ;
2012-02-03 16:03:52 -05:00
SetDisplayId ( modelId );
2011-07-22 21:22:05 +02:00
if ( setHealth )
{
2012-10-24 13:29:34 +02:00
m_goValue . Building . Health = 0 ;
2011-07-22 21:22:05 +02:00
SetGoAnimProgress ( 0 );
}
2012-05-30 08:01:02 +02:00
EnableCollision ( false );
2011-07-22 21:22:05 +02:00
break ;
}
case GO_DESTRUCTIBLE_REBUILDING :
2011-07-24 23:55:14 +02:00
{
2018-02-10 16:43:01 -03:00
EventInform ( m_goInfo -> destructibleBuilding . RebuildingEvent , attackerOrHealer );
2019-06-03 20:40:34 +02:00
RemoveFlag ( GameObjectFlags ( GO_FLAG_DAMAGED | GO_FLAG_DESTROYED ));
2011-07-24 23:55:14 +02:00
uint32 modelId = m_goInfo -> displayId ;
2014-10-30 20:02:42 +01:00
if ( DestructibleModelDataEntry const * modelData = sDestructibleModelDataStore . LookupEntry ( m_goInfo -> destructibleBuilding . DestructibleModelRec ))
2018-03-28 22:01:22 +02:00
if ( modelData -> State3Wmo )
modelId = modelData -> State3Wmo ;
2012-02-03 16:03:52 -05:00
SetDisplayId ( modelId );
2011-07-24 23:55:14 +02:00
2011-07-22 21:22:05 +02:00
// restores to full health
if ( setHealth )
{
2012-10-24 13:29:34 +02:00
m_goValue . Building . Health = m_goValue . Building . MaxHealth ;
2011-07-22 21:22:05 +02:00
SetGoAnimProgress ( 255 );
}
2012-05-30 08:01:02 +02:00
EnableCollision ( true );
2011-07-22 21:22:05 +02:00
break ;
2011-07-24 23:55:14 +02:00
}
2011-07-22 21:22:05 +02:00
}
}
2012-01-06 11:09:01 -05:00
2012-02-03 16:03:52 -05:00
void GameObject :: SetLootState ( LootState state , Unit * unit )
2012-01-06 11:09:01 -05:00
{
2012-02-03 16:03:52 -05:00
m_lootState = state ;
2014-09-14 16:14:12 +02:00
if ( unit )
m_lootStateUnitGUID = unit -> GetGUID ();
else
m_lootStateUnitGUID . Clear ();
2017-05-01 18:19:36 -03:00
AI () -> OnLootStateChanged ( state , unit );
2014-05-25 11:35:51 +01:00
if ( GetGoType () == GAMEOBJECT_TYPE_DOOR ) // only set collision for doors on SetGoState
return ;
2012-02-03 16:03:52 -05:00
if ( m_model )
{
2012-09-04 14:04:47 -04:00
bool collision = false ;
2012-05-30 08:01:02 +02:00
// Use the current go state
2012-09-04 12:07:20 -04:00
if (( GetGoState () != GO_STATE_READY && ( state == GO_ACTIVATED || state == GO_JUST_DEACTIVATED )) || state == GO_READY )
2012-09-04 14:04:47 -04:00
collision = ! collision ;
2012-02-03 16:03:52 -05:00
2012-09-04 14:04:47 -04:00
EnableCollision ( collision );
2012-02-03 16:03:52 -05:00
}
2012-01-14 15:34:41 +01:00
}
2012-02-03 16:03:52 -05:00
2017-12-10 12:39:37 +01:00
void GameObject :: SetLootGenerationTime ()
{
m_lootGenerationTime = GameTime :: GetGameTime ();
}
2012-02-03 16:03:52 -05:00
void GameObject :: SetGoState ( GOState state )
{
2019-06-03 20:40:34 +02:00
SetUpdateFieldValue ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: State ), state );
2017-05-01 18:19:36 -03:00
if ( AI ())
AI () -> OnStateChanged ( state );
2014-03-23 00:23:30 +01:00
if ( m_model && ! IsTransport ())
2012-02-03 16:03:52 -05:00
{
if ( ! IsInWorld ())
return ;
// startOpen determines whether we are going to add or remove the LoS on activation
2012-09-04 14:04:47 -04:00
bool collision = false ;
if ( state == GO_STATE_READY )
2012-09-04 19:48:29 +01:00
collision = ! collision ;
2012-02-03 16:03:52 -05:00
2012-09-04 14:04:47 -04:00
EnableCollision ( collision );
2012-02-03 16:03:52 -05:00
}
}
2014-09-29 20:10:08 +02:00
uint32 GameObject :: GetTransportPeriod () const
{
ASSERT ( GetGOInfo () -> type == GAMEOBJECT_TYPE_TRANSPORT );
if ( m_goValue . Transport . AnimationInfo )
return m_goValue . Transport . AnimationInfo -> TotalTime ;
2015-04-23 15:47:50 +02:00
return 0 ;
2014-09-29 20:10:08 +02:00
}
void GameObject :: SetTransportState ( GOState state , uint32 stopFrame /*= 0*/ )
{
if ( GetGoState () == state )
return ;
ASSERT ( GetGOInfo () -> type == GAMEOBJECT_TYPE_TRANSPORT );
ASSERT ( state >= GO_STATE_TRANSPORT_ACTIVE );
if ( state == GO_STATE_TRANSPORT_ACTIVE )
{
m_goValue . Transport . StateUpdateTimer = 0 ;
2014-09-30 22:48:04 +02:00
m_goValue . Transport . PathProgress = getMSTime ();
if ( GetGoState () >= GO_STATE_TRANSPORT_STOPPED )
2015-05-21 14:02:50 +02:00
m_goValue . Transport . PathProgress += m_goValue . Transport . StopFrames -> at ( GetGoState () - GO_STATE_TRANSPORT_STOPPED );
2014-09-29 20:10:08 +02:00
SetGoState ( GO_STATE_TRANSPORT_ACTIVE );
}
else
{
2014-12-01 17:20:41 +01:00
ASSERT ( state < GOState ( GO_STATE_TRANSPORT_STOPPED + MAX_GO_STATE_TRANSPORT_STOP_FRAMES ));
2014-09-29 20:10:08 +02:00
ASSERT ( stopFrame < m_goValue . Transport . StopFrames -> size ());
m_goValue . Transport . PathProgress = getMSTime () + m_goValue . Transport . StopFrames -> at ( stopFrame );
SetGoState ( GOState ( GO_STATE_TRANSPORT_STOPPED + stopFrame ));
}
}
2012-02-03 16:03:52 -05:00
void GameObject :: SetDisplayId ( uint32 displayid )
{
2019-06-03 20:40:34 +02:00
SetUpdateFieldValue ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: DisplayID ), displayid );
2012-02-03 16:03:52 -05:00
UpdateModel ();
}
2018-03-28 22:01:22 +02:00
uint8 GameObject :: GetNameSetId () const
{
switch ( GetGoType ())
{
case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING :
if ( DestructibleModelDataEntry const * modelData = sDestructibleModelDataStore . LookupEntry ( m_goInfo -> destructibleBuilding . DestructibleModelRec ))
{
switch ( GetDestructibleState ())
{
case GO_DESTRUCTIBLE_INTACT :
return modelData -> State0NameSet ;
case GO_DESTRUCTIBLE_DAMAGED :
return modelData -> State1NameSet ;
case GO_DESTRUCTIBLE_DESTROYED :
return modelData -> State2NameSet ;
case GO_DESTRUCTIBLE_REBUILDING :
return modelData -> State3NameSet ;
default :
break ;
}
}
break ;
case GAMEOBJECT_TYPE_GARRISON_BUILDING :
case GAMEOBJECT_TYPE_GARRISON_PLOT :
case GAMEOBJECT_TYPE_PHASEABLE_MO :
2019-06-03 20:40:34 +02:00
return (( * m_gameObjectData -> Flags ) >> 8 ) & 0xF ;
2018-03-28 22:01:22 +02:00
default :
break ;
}
return 0 ;
}
2012-02-03 16:03:52 -05:00
void GameObject :: EnableCollision ( bool enable )
2012-01-06 11:09:01 -05:00
{
2012-02-03 16:03:52 -05:00
if ( ! m_model )
return ;
2012-02-10 10:24:56 +01:00
2012-08-19 11:07:21 -05:00
/*if (enable && !GetMap()->ContainsGameObjectModel(*m_model))
GetMap()->InsertGameObjectModel(*m_model);*/
2012-02-03 16:03:52 -05:00
2017-05-05 20:48:08 +02:00
m_model -> enableCollision ( enable );
2012-02-03 16:03:52 -05:00
}
void GameObject :: UpdateModel ()
{
if ( ! IsInWorld ())
return ;
if ( m_model )
2012-08-19 11:07:21 -05:00
if ( GetMap () -> ContainsGameObjectModel ( * m_model ))
GetMap () -> RemoveGameObjectModel ( * m_model );
2020-07-27 08:58:40 +02:00
RemoveFlag ( GO_FLAG_MAP_OBJECT );
2012-02-03 16:03:52 -05:00
delete m_model ;
2020-07-27 08:58:40 +02:00
m_model = nullptr ;
CreateModel ();
2012-02-03 16:03:52 -05:00
if ( m_model )
2012-08-19 11:07:21 -05:00
GetMap () -> InsertGameObjectModel ( * m_model );
2012-01-14 15:34:41 +01:00
}
2012-05-30 08:01:02 +02:00
Player * GameObject :: GetLootRecipient () const
{
if ( ! m_lootRecipient )
2016-05-11 23:24:26 +02:00
return nullptr ;
2014-10-16 22:47:02 +02:00
return ObjectAccessor :: FindConnectedPlayer ( m_lootRecipient );
2012-05-30 08:01:02 +02:00
}
Group * GameObject :: GetLootRecipientGroup () const
{
if ( ! m_lootRecipientGroup )
2016-05-11 23:24:26 +02:00
return nullptr ;
2012-05-30 08:01:02 +02:00
return sGroupMgr -> GetGroupByGUID ( m_lootRecipientGroup );
}
2016-09-24 14:43:18 +02:00
void GameObject :: SetLootRecipient ( Unit * unit , Group * group )
2012-05-30 08:01:02 +02:00
{
// set the player whose group should receive the right
// to loot the creature after it dies
2016-05-11 23:24:26 +02:00
// should be set to nullptr after the loot disappears
2012-05-30 08:01:02 +02:00
if ( ! unit )
{
2014-09-14 16:14:12 +02:00
m_lootRecipient . Clear ();
2016-09-24 14:43:18 +02:00
m_lootRecipientGroup = group ? group -> GetGUID () : ObjectGuid :: Empty ;
2012-05-30 08:01:02 +02:00
return ;
}
if ( unit -> GetTypeId () != TYPEID_PLAYER && ! unit -> IsVehicle ())
return ;
Player * player = unit -> GetCharmerOrOwnerPlayerOrPlayerItself ();
if ( ! player ) // normal creature, no player involved
return ;
m_lootRecipient = player -> GetGUID ();
2016-09-24 14:43:18 +02:00
// either get the group from the passed parameter or from unit's one
if ( group )
2014-10-22 00:35:00 +02:00
m_lootRecipientGroup = group -> GetGUID ();
2016-09-24 14:43:18 +02:00
else if ( Group * unitGroup = player -> GetGroup ())
m_lootRecipientGroup = unitGroup -> GetGUID ();
2012-05-30 08:01:02 +02:00
}
bool GameObject :: IsLootAllowedFor ( Player const * player ) const
{
if ( ! m_lootRecipient && ! m_lootRecipientGroup )
return true ;
if ( player -> GetGUID () == m_lootRecipient )
return true ;
Group const * playerGroup = player -> GetGroup ();
if ( ! playerGroup || playerGroup != GetLootRecipientGroup ()) // if we dont have a group we arent the recipient
return false ; // if go doesnt have group bound it means it was solo killed by someone else
return true ;
}
2013-06-15 15:16:36 +02:00
2016-10-30 11:20:58 +01:00
GameObject * GameObject :: GetLinkedTrap ()
{
return ObjectAccessor :: GetGameObject ( * this , m_linkedTrap );
}
2019-06-03 20:40:34 +02:00
void GameObject :: BuildValuesCreate ( ByteBuffer * data , Player const * target ) const
2013-06-15 15:16:36 +02:00
{
2019-06-03 20:40:34 +02:00
UF :: UpdateFieldFlag flags = GetUpdateFieldFlagsFor ( target );
std :: size_t sizePos = data -> wpos ();
* data << uint32 ( 0 );
* data << uint8 ( flags );
m_objectData -> WriteCreate ( * data , flags , this , target );
m_gameObjectData -> WriteCreate ( * data , flags , this , target );
data -> put < uint32 > ( sizePos , data -> wpos () - sizePos - 4 );
}
2013-06-15 15:16:36 +02:00
2019-06-03 20:40:34 +02:00
void GameObject :: BuildValuesUpdate ( ByteBuffer * data , Player const * target ) const
{
UF :: UpdateFieldFlag flags = GetUpdateFieldFlagsFor ( target );
std :: size_t sizePos = data -> wpos ();
* data << uint32 ( 0 );
* data << uint32 ( m_values . GetChangedObjectTypeMask ());
2013-06-15 15:16:36 +02:00
2019-06-03 20:40:34 +02:00
if ( m_values . HasChanged ( TYPEID_OBJECT ))
m_objectData -> WriteUpdate ( * data , flags , this , target );
2013-06-15 15:16:36 +02:00
2019-06-03 20:40:34 +02:00
if ( m_values . HasChanged ( TYPEID_GAMEOBJECT ))
m_gameObjectData -> WriteUpdate ( * data , flags , this , target );
2016-07-10 00:11:19 +02:00
2019-06-03 20:40:34 +02:00
data -> put < uint32 > ( sizePos , data -> wpos () - sizePos - 4 );
}
2014-09-29 20:10:08 +02:00
2020-04-23 20:22:53 +02:00
void GameObject :: BuildValuesUpdateForPlayerWithMask ( UpdateData * data , UF :: ObjectData :: Mask const & requestedObjectMask ,
UF :: GameObjectData :: Mask const & requestedGameObjectMask , Player const * target ) const
{
UpdateMask < NUM_CLIENT_OBJECT_TYPES > valuesMask ;
if ( requestedObjectMask . IsAnySet ())
valuesMask . Set ( TYPEID_OBJECT );
if ( requestedGameObjectMask . IsAnySet ())
valuesMask . Set ( TYPEID_GAMEOBJECT );
ByteBuffer buffer = PrepareValuesUpdateBuffer ();
std :: size_t sizePos = buffer . wpos ();
buffer << uint32 ( 0 );
buffer << uint32 ( valuesMask . GetBlock ( 0 ));
if ( valuesMask [ TYPEID_OBJECT ])
m_objectData -> WriteUpdate ( buffer , requestedObjectMask , true , this , target );
if ( valuesMask [ TYPEID_GAMEOBJECT ])
m_gameObjectData -> WriteUpdate ( buffer , requestedGameObjectMask , true , this , target );
buffer . put < uint32 > ( sizePos , buffer . wpos () - sizePos - 4 );
data -> AddUpdateBlock ( buffer );
}
2019-06-03 20:40:34 +02:00
void GameObject :: ClearUpdateMask ( bool remove )
{
m_values . ClearChangesMask ( & GameObject :: m_gameObjectData );
Object :: ClearUpdateMask ( remove );
2013-06-15 15:16:36 +02:00
}
2013-10-16 18:37:29 +02:00
2016-05-11 23:24:26 +02:00
void GameObject :: GetRespawnPosition ( float & x , float & y , float & z , float * ori /* = nullptr*/ ) const
2013-10-16 18:37:29 +02:00
{
2017-05-07 21:48:41 +01:00
if ( m_goData )
2013-10-16 18:37:29 +02:00
{
2017-05-07 21:48:41 +01:00
if ( ori )
m_goData -> spawnPoint . GetPosition ( x , y , z , * ori );
else
m_goData -> spawnPoint . GetPosition ( x , y , z );
}
else
{
if ( ori )
GetPosition ( x , y , z , * ori );
else
GetPosition ( x , y , z );
2013-10-16 18:37:29 +02:00
}
}
2014-01-25 16:59:01 +01:00
2014-01-27 18:44:07 +01:00
float GameObject :: GetInteractionDistance () const
2014-01-25 16:59:01 +01:00
{
switch ( GetGoType ())
{
/// @todo find out how the client calculates the maximal usage distance to spellless working
// gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary choosen number
case GAMEOBJECT_TYPE_GUILD_BANK :
case GAMEOBJECT_TYPE_MAILBOX :
return 10.0f ;
case GAMEOBJECT_TYPE_FISHINGHOLE :
2014-01-26 22:46:10 +02:00
case GAMEOBJECT_TYPE_FISHINGNODE :
2014-01-25 16:59:01 +01:00
return 20.0f + CONTACT_DISTANCE ; // max spell range
default :
return INTERACTION_DISTANCE ;
}
}
2014-03-23 00:23:30 +01:00
void GameObject :: UpdateModelPosition ()
{
if ( ! m_model )
return ;
if ( GetMap () -> ContainsGameObjectModel ( * m_model ))
{
GetMap () -> RemoveGameObjectModel ( * m_model );
2015-06-03 19:53:28 +02:00
m_model -> UpdatePosition ();
2014-03-23 00:23:30 +01:00
GetMap () -> InsertGameObjectModel ( * m_model );
}
}
2015-07-25 15:37:35 +02:00
2015-09-16 17:06:24 +02:00
void GameObject :: SetAnimKitId ( uint16 animKitId , bool oneshot )
{
if ( _animKitId == animKitId )
return ;
if ( animKitId && ! sAnimKitStore . LookupEntry ( animKitId ))
return ;
if ( ! oneshot )
_animKitId = animKitId ;
else
_animKitId = 0 ;
WorldPackets :: GameObject :: GameObjectActivateAnimKit activateAnimKit ;
activateAnimKit . ObjectGUID = GetGUID ();
activateAnimKit . AnimKitID = animKitId ;
activateAnimKit . Maintain = ! oneshot ;
SendMessageToSet ( activateAnimKit . Write (), true );
}
2021-03-14 15:40:42 +01:00
void GameObject :: SetSpellVisualId ( int32 spellVisualId , ObjectGuid activatorGuid )
{
SetUpdateFieldValue ( m_values . ModifyValue ( & GameObject :: m_gameObjectData ). ModifyValue ( & UF :: GameObjectData :: SpellVisualID ), spellVisualId );
WorldPackets :: GameObject :: GameObjectPlaySpellVisual packet ;
packet . ObjectGUID = GetGUID ();
packet . ActivatorGUID = activatorGuid ;
packet . SpellVisualID = spellVisualId ;
SendMessageToSet ( packet . Write (), true );
}
2015-07-25 15:37:35 +02:00
class GameObjectModelOwnerImpl : public GameObjectModelOwnerBase
{
public :
2018-04-08 20:35:33 +02:00
explicit GameObjectModelOwnerImpl ( GameObject * owner ) : _owner ( owner ) { }
2018-01-28 23:34:14 +01:00
virtual ~ GameObjectModelOwnerImpl () = default ;
2015-07-25 15:37:35 +02:00
2018-03-28 22:01:22 +02:00
bool IsSpawned () const override { return _owner -> isSpawned (); }
uint32 GetDisplayId () const override { return _owner -> GetDisplayId (); }
uint8 GetNameSetId () const override { return _owner -> GetNameSetId (); }
bool IsInPhase ( PhaseShift const & phaseShift ) const override { return _owner -> GetPhaseShift (). CanSee ( phaseShift ); }
G3D :: Vector3 GetPosition () const override { return G3D :: Vector3 ( _owner -> GetPositionX (), _owner -> GetPositionY (), _owner -> GetPositionZ ()); }
float GetOrientation () const override { return _owner -> GetOrientation (); }
float GetScale () const override { return _owner -> GetObjectScale (); }
void DebugVisualizeCorner ( G3D :: Vector3 const & corner ) const override { _owner -> SummonCreature ( 1 , corner . x , corner . y , corner . z , 0 , TEMPSUMMON_MANUAL_DESPAWN ); }
2015-07-25 15:37:35 +02:00
private :
2018-04-08 20:35:33 +02:00
GameObject * _owner ;
2015-07-25 15:37:35 +02:00
};
2020-07-27 08:58:40 +02:00
void GameObject :: CreateModel ()
2015-07-25 15:37:35 +02:00
{
2020-07-27 08:58:40 +02:00
m_model = GameObjectModel :: Create ( std :: make_unique < GameObjectModelOwnerImpl > ( this ), sWorld -> GetDataPath ());
if ( m_model && m_model -> isMapObject ())
AddFlag ( GO_FLAG_MAP_OBJECT );
2015-07-25 15:37:35 +02:00
}
2018-12-14 22:01:16 +01:00
std :: string GameObject :: GetDebugInfo () const
{
std :: stringstream sstr ;
sstr << WorldObject :: GetDebugInfo () << " \n "
<< "SpawnId: " << GetSpawnId () << " GoState: " << std :: to_string ( GetGoState ()) << " ScriptId: " << GetScriptId () << " AIName: " << GetAIName ();
return sstr . str ();
}
2019-04-24 20:39:01 +02:00
bool GameObject :: IsAtInteractDistance ( Player const * player , SpellInfo const * spell ) const
{
if ( spell || ( spell = GetSpellForLock ( player )))
{
float maxRange = spell -> GetMaxRange ( spell -> IsPositive ());
if ( GetGoType () == GAMEOBJECT_TYPE_SPELL_FOCUS )
return maxRange * maxRange >= GetExactDistSq ( player );
if ( GameObjectDisplayInfoEntry const * displayInfo = sGameObjectDisplayInfoStore . LookupEntry ( GetGOInfo () -> displayId ))
return IsAtInteractDistance ( * player , maxRange );
}
float distance ;
switch ( GetGoType ())
{
case GAMEOBJECT_TYPE_AREADAMAGE :
distance = 0.0f ;
break ;
case GAMEOBJECT_TYPE_QUESTGIVER :
case GAMEOBJECT_TYPE_TEXT :
case GAMEOBJECT_TYPE_FLAGSTAND :
case GAMEOBJECT_TYPE_FLAGDROP :
case GAMEOBJECT_TYPE_MINI_GAME :
distance = 5.5555553f ;
break ;
case GAMEOBJECT_TYPE_BINDER :
distance = 10.0f ;
break ;
case GAMEOBJECT_TYPE_CHAIR :
case GAMEOBJECT_TYPE_BARBER_CHAIR :
distance = 3.0f ;
break ;
case GAMEOBJECT_TYPE_FISHINGNODE :
distance = 100.0f ;
break ;
case GAMEOBJECT_TYPE_CAMERA :
case GAMEOBJECT_TYPE_MAP_OBJECT :
case GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY :
case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING :
case GAMEOBJECT_TYPE_DOOR :
default :
distance = 5.0f ;
break ;
// Following values are not blizzlike
case GAMEOBJECT_TYPE_MAILBOX :
// Successful mailbox interaction is rather critical to the client, failing it will start a minute-long cooldown until the next mail query may be executed.
// And since movement info update is not sent with mailbox interaction query, server may find the player outside of interaction range. Thus we increase it.
distance = 10.0f ; // 5.0f is blizzlike
break ;
}
return IsAtInteractDistance ( * player , distance );
}
bool GameObject :: IsAtInteractDistance ( Position const & pos , float radius ) const
{
if ( GameObjectDisplayInfoEntry const * displayInfo = sGameObjectDisplayInfoStore . LookupEntry ( GetGOInfo () -> displayId ))
{
float scale = GetObjectScale ();
float minX = displayInfo -> GeoBoxMin . X * scale - radius ;
float minY = displayInfo -> GeoBoxMin . Y * scale - radius ;
float minZ = displayInfo -> GeoBoxMin . Z * scale - radius ;
float maxX = displayInfo -> GeoBoxMax . X * scale + radius ;
float maxY = displayInfo -> GeoBoxMax . Y * scale + radius ;
float maxZ = displayInfo -> GeoBoxMax . Z * scale + radius ;
QuaternionData localRotation = GetLocalRotation ();
G3D :: Quat localRotationQuat ( localRotation . x , localRotation . y , localRotation . z , localRotation . w );
return G3D :: CoordinateFrame { { localRotationQuat }, { GetPositionX (), GetPositionY (), GetPositionZ () } }
. toWorldSpace ( G3D :: Box { { minX , minY , minZ }, { maxX , maxY , maxZ } })
. contains ({ pos . GetPositionX (), pos . GetPositionY (), pos . GetPositionZ () });
}
return GetExactDist ( & pos ) <= radius ;
}
SpellInfo const * GameObject :: GetSpellForLock ( Player const * player ) const
{
if ( ! player )
return nullptr ;
uint32 lockId = GetGOInfo () -> GetLockId ();
if ( ! lockId )
return nullptr ;
LockEntry const * lock = sLockStore . LookupEntry ( lockId );
if ( ! lock )
return nullptr ;
for ( uint8 i = 0 ; i < MAX_LOCK_CASE ; ++ i )
{
if ( ! lock -> Type [ i ])
continue ;
if ( lock -> Type [ i ] == LOCK_KEY_SKILL )
if ( SpellInfo const * spell = sSpellMgr -> GetSpellInfo ( lock -> Index [ i ], GetMap () -> GetDifficultyID ()))
return spell ;
if ( lock -> Type [ i ] != LOCK_KEY_SKILL )
break ;
for ( auto && playerSpell : player -> GetSpellMap ())
if ( SpellInfo const * spell = sSpellMgr -> GetSpellInfo ( playerSpell . first , GetMap () -> GetDifficultyID ()))
for ( auto && effect : spell -> GetEffects ())
if ( effect . Effect == SPELL_EFFECT_OPEN_LOCK && effect . MiscValue == lock -> Index [ i ])
if ( effect . CalcValue ( player ) >= int32 ( lock -> Skill [ i ]))
return spell ;
}
return nullptr ;
}