2008-10-11 14:16:25 -05:00
/*
2009-02-04 12:42:26 +01:00
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
2008-10-14 11:57:03 -05:00
*
2009-02-04 12:04:12 +01:00
* Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/>
2008-10-11 14:16:25 -05: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
2008-10-14 11:57:03 -05:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2008-10-11 14:16:25 -05:00
* 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, write to the Free Software
2008-10-14 11:57:03 -05:00
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2008-10-11 14:16:25 -05:00
*/
#include "Common.h"
#include "QuestDef.h"
#include "GameObject.h"
#include "ObjectMgr.h"
2009-02-19 18:44:20 -06:00
#include "PoolHandler.h"
2008-10-11 14:16:25 -05:00
#include "SpellMgr.h"
#include "Spell.h"
#include "UpdateMask.h"
#include "Opcodes.h"
#include "WorldPacket.h"
#include "World.h"
#include "Database/DatabaseEnv.h"
#include "LootMgr.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
#include "InstanceData.h"
#include "BattleGround.h"
#include "Util.h"
2008-11-21 19:45:49 -06:00
#include "OutdoorPvPMgr.h"
#include "BattleGroundAV.h"
2008-10-11 14:16:25 -05:00
2009-05-29 00:18:41 -05:00
GameObject :: GameObject () : WorldObject (), m_goValue ( new GameObjectValue )
2008-10-11 14:16:25 -05:00
{
m_objectType |= TYPEMASK_GAMEOBJECT ;
m_objectTypeId = TYPEID_GAMEOBJECT ;
2009-06-11 00:45:59 -05:00
m_updateFlag = ( UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_POSITION | UPDATEFLAG_ROTATION );
2008-10-11 14:16:25 -05:00
m_valuesCount = GAMEOBJECT_END ;
m_respawnTime = 0 ;
m_respawnDelayTime = 25 ;
m_lootState = GO_NOT_READY ;
m_spawnedByDefault = true ;
m_usetimes = 0 ;
m_spellId = 0 ;
m_cooldownTime = 0 ;
m_goInfo = NULL ;
2009-05-31 21:04:04 -05:00
m_goData = NULL ;
2008-10-11 14:16:25 -05:00
m_DBTableGuid = 0 ;
2009-06-11 00:45:59 -05:00
m_rotation = 0 ;
2008-10-11 14:16:25 -05:00
}
GameObject ::~ GameObject ()
{
2009-07-01 18:23:36 -05:00
//if(m_uint32Values) // field array can be not exist if GameOBject not loaded
// CleanupsBeforeDelete();
}
void GameObject :: CleanupsBeforeDelete ()
{
if ( m_uint32Values ) // field array can be not exist if GameOBject not loaded
2008-10-11 14:16:25 -05:00
{
2009-02-19 18:44:20 -06:00
// Possible crash at access to deleted GO in Unit::m_gameobj
2009-07-01 18:23:36 -05:00
if ( uint64 owner_guid = GetOwnerGUID ())
2008-10-11 14:16:25 -05:00
{
2009-07-01 18:36:03 -05:00
Unit * owner = NULL ;
2009-07-04 21:07:06 +02:00
// Object may be deleted while player is not in world, skip this check for now.
/*if(IS_PLAYER_GUID(owner_guid))
2009-07-01 18:36:03 -05:00
owner = ObjectAccessor::GetObjectInWorld(owner_guid, (Player*)NULL);
2009-07-04 21:07:06 +02:00
else*/
2009-07-01 18:36:03 -05:00
owner = ObjectAccessor :: GetUnit ( * this , owner_guid );
if ( owner )
2008-10-11 14:16:25 -05:00
owner -> RemoveGameObject ( this , false );
2009-06-16 11:19:59 -05:00
else
{
2009-07-12 17:52:27 +08:00
const char * ownerType = "creature" ;
2009-06-16 11:19:59 -05:00
if ( IS_PLAYER_GUID ( owner_guid ))
ownerType = "player" ;
else if ( IS_PET_GUID ( owner_guid ))
ownerType = "pet" ;
sLog . outError ( "Delete GameObject (GUID: %u Entry: %u SpellId %u LinkedGO %u) that lost references to owner (GUID %u Type '%s') GO list. Crash possible later." ,
2009-07-16 12:06:07 +08:00
GetGUIDLow (), GetGOInfo () -> id , m_spellId , GetGOInfo () -> GetLinkedGameObjectEntry (), GUID_LOPART ( owner_guid ), ownerType );
2009-06-16 11:19:59 -05:00
}
2008-10-11 14:16:25 -05:00
}
2009-07-01 18:23:36 -05:00
}
2008-10-11 14:16:25 -05:00
}
void GameObject :: AddToWorld ()
{
///- Register the gameobject for guid lookup
2009-03-25 15:54:23 -06:00
if ( ! IsInWorld ())
{
2009-05-30 22:15:05 -05:00
if ( m_zoneScript )
m_zoneScript -> OnGameObjectCreate ( this , true );
2009-03-25 15:54:23 -06:00
ObjectAccessor :: Instance (). AddObject ( this );
WorldObject :: AddToWorld ();
}
2008-10-11 14:16:25 -05:00
}
void GameObject :: RemoveFromWorld ()
{
///- Remove the gameobject from the accessor
2009-03-25 15:54:23 -06:00
if ( IsInWorld ())
{
2009-05-30 22:15:05 -05:00
if ( m_zoneScript )
m_zoneScript -> OnGameObjectCreate ( this , false );
2009-05-29 00:18:41 -05:00
2009-03-31 09:18:39 -06:00
// Possible crash at access to deleted GO in Unit::m_gameobj
if ( uint64 owner_guid = GetOwnerGUID ())
{
2009-08-01 20:23:23 +02:00
if ( Unit * owner = GetOwner ( false ))
2009-03-31 09:18:39 -06:00
owner -> RemoveGameObject ( this , false );
else if ( ! IS_PLAYER_GUID ( owner_guid ))
sLog . outError ( "Delete GameObject (GUID: %u Entry: %u ) that have references in not found creature %u GO list. Crash possible later." , GetGUIDLow (), GetGOInfo () -> id , GUID_LOPART ( owner_guid ));
}
2009-03-25 15:54:23 -06:00
WorldObject :: RemoveFromWorld ();
2009-03-28 16:04:37 -06:00
ObjectAccessor :: Instance (). RemoveObject ( this );
2009-03-25 15:54:23 -06:00
}
2008-10-11 14:16:25 -05:00
}
2009-04-27 18:36:10 -05:00
bool GameObject :: Create ( uint32 guidlow , uint32 name_id , Map * map , uint32 phaseMask , float x , float y , float z , float ang , float rotation0 , float rotation1 , float rotation2 , float rotation3 , uint32 animprogress , GOState go_state , uint32 ArtKit )
2008-10-11 14:16:25 -05:00
{
2009-07-16 11:49:00 +08:00
ASSERT ( map );
SetMap ( map );
2008-10-11 14:16:25 -05:00
2009-08-01 00:20:32 +02:00
Relocate ( x , y , z , ang );
2008-10-11 14:16:25 -05:00
if ( ! IsPositionValid ())
{
2009-03-21 14:28:02 -06:00
sLog . outError ( "Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)" , guidlow , name_id , x , y );
2008-10-11 14:16:25 -05:00
return false ;
}
2009-08-01 00:20:32 +02:00
SetPhaseMask ( phaseMask , false );
2008-10-11 14:16:25 -05:00
GameObjectInfo const * goinfo = objmgr . GetGameObjectInfo ( name_id );
if ( ! goinfo )
{
sLog . outErrorDb ( "Gameobject (GUID: %u Entry: %u) not created: it have not exist entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f) ang: %f rotation0: %f rotation1: %f rotation2: %f rotation3: %f" , guidlow , name_id , map -> GetId (), x , y , z , ang , rotation0 , rotation1 , rotation2 , rotation3 );
return false ;
}
Object :: _Create ( guidlow , goinfo -> id , HIGHGUID_GAMEOBJECT );
m_goInfo = goinfo ;
if ( goinfo -> type >= MAX_GAMEOBJECT_TYPE )
{
sLog . outErrorDb ( "Gameobject (GUID: %u Entry: %u) not created: it have not exist GO type '%u' in `gameobject_template`. It's will crash client if created." , guidlow , name_id , goinfo -> type );
return false ;
}
2008-12-24 09:58:26 -06:00
SetFloatValue ( GAMEOBJECT_PARENTROTATION + 0 , rotation0 );
SetFloatValue ( GAMEOBJECT_PARENTROTATION + 1 , rotation1 );
2009-03-18 20:46:21 -06:00
UpdateRotationFields ( rotation2 , rotation3 ); // GAMEOBJECT_FACING, GAMEOBJECT_ROTATION, GAMEOBJECT_PARENTROTATION+2/3
2008-10-11 14:16:25 -05:00
SetFloatValue ( OBJECT_FIELD_SCALE_X , goinfo -> size );
SetUInt32Value ( GAMEOBJECT_FACTION , goinfo -> faction );
SetUInt32Value ( GAMEOBJECT_FLAGS , goinfo -> flags );
2008-12-24 09:58:26 -06:00
SetEntry ( goinfo -> id );
2008-10-11 14:16:25 -05:00
SetUInt32Value ( GAMEOBJECT_DISPLAYID , goinfo -> displayId );
SetGoState ( go_state );
SetGoType ( GameobjectTypes ( goinfo -> type ));
SetGoAnimProgress ( animprogress );
2009-01-04 17:14:34 -06:00
SetByteValue ( GAMEOBJECT_BYTES_1 , 2 , ArtKit );
2009-05-29 00:18:41 -05:00
switch ( goinfo -> type )
{
case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING :
2009-06-04 18:32:24 +02:00
m_goValue -> building . health = goinfo -> building . intactNumHits + goinfo -> building . damagedNumHits ;
2009-05-29 00:18:41 -05:00
break ;
}
2008-10-11 14:16:25 -05:00
2009-05-30 22:15:05 -05:00
SetZoneScript ();
2008-10-11 14:16:25 -05:00
return true ;
}
void GameObject :: Update ( uint32 /*p_time*/ )
{
if ( IS_MO_TRANSPORT ( GetGUID ()))
{
//((Transport*)this)->Update(p_time);
return ;
}
switch ( m_lootState )
{
case GO_NOT_READY :
{
switch ( GetGoType ())
{
case GAMEOBJECT_TYPE_TRAP :
{
// Arming Time for GAMEOBJECT_TYPE_TRAP (6)
Unit * owner = GetOwner ();
2009-05-09 10:48:14 -05:00
if ( owner && owner -> isInCombat ())
2008-10-11 14:16:25 -05:00
m_cooldownTime = time ( NULL ) + GetGOInfo () -> trap . startDelay ;
m_lootState = GO_READY ;
break ;
}
case GAMEOBJECT_TYPE_FISHINGNODE :
{
// fishing code (bobber ready)
if ( time ( NULL ) > m_respawnTime - FISHING_BOBBER_READY_TIME )
{
// splash bobber (bobber ready now)
Unit * caster = GetOwner ();
if ( caster && caster -> GetTypeId () == TYPEID_PLAYER )
{
2009-04-27 18:36:10 -05:00
SetGoState ( GO_STATE_ACTIVE );
2008-12-12 11:21:28 -06:00
SetUInt32Value ( GAMEOBJECT_FLAGS , GO_FLAG_NODESPAWN );
2008-10-11 14:16:25 -05:00
UpdateData udata ;
WorldPacket packet ;
BuildValuesUpdateBlockForPlayer ( & udata ,(( Player * ) caster ));
udata . BuildPacket ( & packet );
(( Player * ) caster ) -> GetSession () -> SendPacket ( & packet );
2009-04-30 12:01:08 -05:00
SendCustomAnim ();
2008-10-11 14:16:25 -05: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 ;
}
// NO BREAK for switch (m_lootState)
}
case GO_READY :
{
if ( m_respawnTime > 0 ) // timer on
{
if ( m_respawnTime <= time ( NULL )) // timer expired
{
m_respawnTime = 0 ;
m_SkillupList . clear ();
m_usetimes = 0 ;
switch ( GetGoType ())
{
case GAMEOBJECT_TYPE_FISHINGNODE : // can't fish now
{
Unit * caster = GetOwner ();
if ( caster && caster -> GetTypeId () == TYPEID_PLAYER )
{
2009-08-19 16:52:51 -05:00
caster -> FinishSpell ( CURRENT_CHANNELED_SPELL );
2008-10-11 14:16:25 -05:00
WorldPacket data ( SMSG_FISH_NOT_HOOKED , 0 );
(( Player * ) caster ) -> GetSession () -> SendPacket ( & data );
}
// can be delete
m_lootState = GO_JUST_DEACTIVATED ;
return ;
}
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)
2009-04-27 18:36:10 -05:00
if ( GetGoState () != GO_STATE_READY )
ResetDoorOrButton ();
2008-10-11 14:16:25 -05:00
//flags in AB are type_button and we need to add them here so no break!
default :
2009-04-27 18:36:10 -05:00
if ( ! m_spawnedByDefault ) // despawn timer
2008-10-11 14:16:25 -05:00
{
// can be despawned or destroyed
SetLootState ( GO_JUST_DEACTIVATED );
return ;
}
// respawn timer
2009-02-19 18:44:20 -06:00
uint16 poolid = poolhandler . IsPartOfAPool ( GetGUIDLow (), TYPEID_GAMEOBJECT );
if ( poolid )
poolhandler . UpdatePool ( poolid , GetGUIDLow (), TYPEID_GAMEOBJECT );
else
GetMap () -> Add ( this );
2008-10-11 14:16:25 -05:00
break ;
}
}
}
2009-07-24 09:33:56 +08:00
if ( isSpawned ())
2008-10-11 14:16:25 -05:00
{
2009-07-24 09:33:56 +08:00
// traps can have time and can not have
GameObjectInfo const * goInfo = GetGOInfo ();
if ( goInfo -> type == GAMEOBJECT_TYPE_TRAP )
{
if ( m_cooldownTime >= time ( NULL ))
return ;
2008-10-11 14:16:25 -05:00
2009-07-24 09:33:56 +08:00
// traps
Unit * owner = GetOwner ();
Unit * ok = NULL ; // pointer to appropriate target if found any
2008-10-11 14:16:25 -05:00
2009-07-24 09:33:56 +08:00
bool IsBattleGroundTrap = false ;
//FIXME: this is activation radius (in different casting radius that must be selected from spell data)
//TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state
float radius = goInfo -> trap . radius ;
if ( ! radius )
2008-10-11 14:16:25 -05:00
{
2009-07-24 09:33:56 +08:00
if ( goInfo -> trap . cooldown != 3 ) // cast in other case (at some triggering/linked go/etc explicit call)
return ;
else
{
if ( m_respawnTime > 0 )
break ;
radius = goInfo -> trap . cooldown ; // battlegrounds gameobjects has data2 == 0 && data5 == 3
IsBattleGroundTrap = true ;
2008-10-11 14:16:25 -05:00
2009-07-24 09:33:56 +08:00
if ( ! radius )
return ;
}
2008-10-11 14:16:25 -05:00
}
2009-07-24 09:33:56 +08:00
// Note: this hack with search required until GO casting not implemented
// search unfriendly creature
if ( owner ) // hunter trap
{
Trinity :: AnyUnfriendlyNoTotemUnitInObjectRangeCheck checker ( this , owner , radius );
Trinity :: UnitSearcher < Trinity :: AnyUnfriendlyNoTotemUnitInObjectRangeCheck > searcher ( this , ok , checker );
VisitNearbyGridObject ( radius , searcher );
if ( ! ok ) VisitNearbyWorldObject ( radius , searcher );
}
else // environmental trap
{
// environmental damage spells already have around enemies targeting but this not help in case not existed GO casting support
// affect only players
Player * player = NULL ;
MaNGOS :: AnyPlayerInObjectRangeCheck checker ( this , radius );
MaNGOS :: PlayerSearcher < MaNGOS :: AnyPlayerInObjectRangeCheck > searcher ( this , player , checker );
VisitNearbyWorldObject ( radius , searcher );
ok = player ;
}
if ( ok )
{
// some traps do not have spell but should be triggered
if ( goInfo -> trap . spellId )
CastSpell ( ok , goInfo -> trap . spellId );
2008-10-11 14:16:25 -05:00
2009-06-03 23:24:57 -05:00
m_cooldownTime = time ( NULL ) + 4 ; // 4 seconds
2008-10-11 14:16:25 -05:00
2009-07-24 09:33:56 +08:00
// count charges
//if(goInfo->trap.charges > 0)
// AddUse();
if ( owner )
SetLootState ( GO_JUST_DEACTIVATED );
2008-10-11 14:16:25 -05:00
2009-07-24 09:33:56 +08:00
if ( IsBattleGroundTrap && ok -> GetTypeId () == TYPEID_PLAYER )
{
//BattleGround gameobjects case
if ((( Player * ) ok ) -> InBattleGround ())
if ( BattleGround * bg = (( Player * ) ok ) -> GetBattleGround ())
bg -> HandleTriggerBuff ( GetGUID ());
}
}
}
else if ( uint32 max_charges = goInfo -> GetCharges ())
{
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
}
}
}
break ;
}
case GO_ACTIVATED :
{
switch ( GetGoType ())
{
case GAMEOBJECT_TYPE_DOOR :
case GAMEOBJECT_TYPE_BUTTON :
2009-07-16 12:06:07 +08:00
if ( GetGOInfo () -> GetAutoCloseTime () && ( m_cooldownTime < time ( NULL )))
2009-04-27 18:36:10 -05:00
ResetDoorOrButton ();
2008-10-11 14:16:25 -05:00
break ;
}
break ;
}
case GO_JUST_DEACTIVATED :
{
//if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
if ( GetGoType () == GAMEOBJECT_TYPE_GOOBER )
{
uint32 spellId = GetGOInfo () -> goober . spellId ;
if ( spellId )
{
2009-04-29 00:26:07 -05:00
std :: set < uint32 >:: const_iterator it = m_unique_users . begin ();
std :: set < uint32 >:: const_iterator end = m_unique_users . end ();
2008-10-11 14:16:25 -05:00
for (; it != end ; it ++ )
{
Unit * owner = Unit :: GetUnit ( * this , uint64 ( * it ));
2009-05-25 21:18:02 -05:00
// For now we do not support go cast
//if (owner) owner->CastSpell(owner, spellId, false, 0, 0, GetGUID());
if ( owner ) owner -> CastSpell ( owner , spellId , false );
2008-10-11 14:16:25 -05:00
}
m_unique_users . clear ();
m_usetimes = 0 ;
}
//any return here in case battleground traps
}
if ( GetOwnerGUID ())
{
2009-08-03 14:52:30 +02:00
if ( Unit * owner = GetOwner ( false ))
2009-07-12 13:38:38 +02:00
{
2009-06-06 20:24:50 -05:00
owner -> RemoveGameObject ( this , false );
2009-07-12 13:38:38 +02:00
SetRespawnTime ( 0 );
Delete ();
}
2008-10-11 14:16:25 -05:00
return ;
}
//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
{
2008-12-24 09:58:26 -06:00
SendObjectDeSpawnAnim ( GetGUID ());
2008-10-11 14:16:25 -05:00
//reset flags
SetUInt32Value ( GAMEOBJECT_FLAGS , GetGOInfo () -> flags );
}
loot . clear ();
SetLootState ( GO_READY );
if ( ! m_respawnDelayTime )
return ;
if ( ! m_spawnedByDefault )
{
m_respawnTime = 0 ;
2009-08-17 13:58:43 +02:00
ObjectAccessor :: UpdateObjectVisibility ( this );
2008-10-11 14:16:25 -05:00
return ;
}
m_respawnTime = time ( NULL ) + m_respawnDelayTime ;
// if option not set then object will be saved at grid unload
2008-11-14 16:28:45 -06:00
if ( sWorld . getConfig ( CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY ))
2008-10-11 14:16:25 -05:00
SaveRespawnTime ();
ObjectAccessor :: UpdateObjectVisibility ( this );
break ;
}
}
}
void GameObject :: Refresh ()
{
// not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway)
if ( m_respawnTime > 0 && m_spawnedByDefault )
return ;
if ( isSpawned ())
2008-12-24 09:58:26 -06:00
GetMap () -> Add ( this );
2008-10-11 14:16:25 -05:00
}
void GameObject :: AddUniqueUse ( Player * player )
{
AddUse ();
m_unique_users . insert ( player -> GetGUIDLow ());
}
2009-08-03 14:52:30 +02:00
void GameObject :: DeleteObjectWithOwner ()
{
SetLootState ( GO_NOT_READY );
if ( GetOwnerGUID ())
if ( Unit * owner = GetOwner ( false ))
owner -> RemoveGameObject ( this , false );
Delete ();
}
2008-10-11 14:16:25 -05:00
void GameObject :: Delete ()
{
2009-07-11 21:32:41 +02:00
assert ( ! GetOwnerGUID ());
2008-10-11 14:16:25 -05:00
SendObjectDeSpawnAnim ( GetGUID ());
2009-04-27 18:36:10 -05:00
SetGoState ( GO_STATE_READY );
2008-10-11 14:16:25 -05:00
SetUInt32Value ( GAMEOBJECT_FLAGS , GetGOInfo () -> flags );
2009-02-19 18:44:20 -06:00
uint16 poolid = poolhandler . IsPartOfAPool ( GetGUIDLow (), TYPEID_GAMEOBJECT );
if ( poolid )
poolhandler . UpdatePool ( poolid , GetGUIDLow (), TYPEID_GAMEOBJECT );
else
AddObjectToRemoveList ();
2008-10-11 14:16:25 -05:00
}
2009-02-08 21:45:31 -06:00
void GameObject :: getFishLoot ( Loot * fishloot , Player * loot_owner )
2008-10-11 14:16:25 -05:00
{
fishloot -> clear ();
2009-03-11 16:17:37 -06:00
uint32 zone , subzone ;
GetZoneAndAreaId ( zone , subzone );
2008-10-11 14:16:25 -05:00
// if subzone loot exist use it
if ( LootTemplates_Fishing . HaveLootFor ( subzone ))
2009-02-08 21:45:31 -06:00
fishloot -> FillLoot ( subzone , LootTemplates_Fishing , loot_owner , true );
2008-10-11 14:16:25 -05:00
// else use zone loot
else
2009-03-11 16:17:37 -06:00
fishloot -> FillLoot ( zone , LootTemplates_Fishing , loot_owner , true );
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
2008-10-11 14:16:25 -05:00
GameObjectData const * data = objmgr . GetGOData ( m_DBTableGuid );
if ( ! data )
{
sLog . outError ( "GameObject::SaveToDB failed, cannot get gameobject data!" );
return ;
}
2009-01-31 16:38:50 -06:00
SaveToDB ( GetMapId (), data -> spawnMask , data -> phaseMask );
2008-10-11 14:16:25 -05:00
}
2009-01-31 16:38:50 -06:00
void GameObject :: SaveToDB ( uint32 mapid , uint8 spawnMask , uint32 phaseMask )
2008-10-11 14:16:25 -05:00
{
const GameObjectInfo * goI = GetGOInfo ();
if ( ! goI )
return ;
2008-12-24 09:58:26 -06:00
2008-10-11 14:16:25 -05:00
if ( ! m_DBTableGuid )
m_DBTableGuid = GetGUIDLow ();
// update in loaded data (changing data only in this place)
GameObjectData & data = objmgr . NewGOData ( m_DBTableGuid );
// data->guid = guid don't must be update at save
data . id = GetEntry ();
data . mapid = mapid ;
2009-01-31 16:38:50 -06:00
data . phaseMask = phaseMask ;
2009-06-11 00:45:59 -05:00
data . posX = GetPositionX ();
data . posY = GetPositionY ();
data . posZ = GetPositionZ ();
data . orientation = GetOrientation ();
2008-12-24 09:58:26 -06:00
data . rotation0 = GetFloatValue ( GAMEOBJECT_PARENTROTATION + 0 );
data . rotation1 = GetFloatValue ( GAMEOBJECT_PARENTROTATION + 1 );
data . rotation2 = GetFloatValue ( GAMEOBJECT_PARENTROTATION + 2 );
data . rotation3 = GetFloatValue ( GAMEOBJECT_PARENTROTATION + 3 );
2008-10-11 14:16:25 -05:00
data . spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : - ( int32 ) m_respawnDelayTime ;
data . animprogress = GetGoAnimProgress ();
data . go_state = GetGoState ();
data . spawnMask = spawnMask ;
2009-01-04 17:14:34 -06:00
data . ArtKit = GetGoArtKit ();
2008-10-11 14:16:25 -05:00
// updated in DB
std :: ostringstream ss ;
ss << "INSERT INTO gameobject VALUES ( "
<< m_DBTableGuid << ", "
2008-12-24 09:58:26 -06:00
<< GetEntry () << ", "
2008-10-11 14:16:25 -05:00
<< mapid << ", "
2009-02-27 12:26:40 -06:00
<< uint32 ( spawnMask ) << "," // cast to prevent save as symbol
<< uint16 ( GetPhaseMask ()) << "," // prevent out of range error
2009-06-11 00:45:59 -05:00
<< GetPositionX () << ", "
<< GetPositionY () << ", "
<< GetPositionZ () << ", "
<< GetOrientation () << ", "
2008-12-24 09:58:26 -06:00
<< GetFloatValue ( GAMEOBJECT_PARENTROTATION ) << ", "
<< GetFloatValue ( GAMEOBJECT_PARENTROTATION + 1 ) << ", "
<< GetFloatValue ( GAMEOBJECT_PARENTROTATION + 2 ) << ", "
<< GetFloatValue ( GAMEOBJECT_PARENTROTATION + 3 ) << ", "
2008-10-11 14:16:25 -05:00
<< m_respawnDelayTime << ", "
2009-04-27 18:36:10 -05:00
<< uint32 ( GetGoAnimProgress ()) << ", "
<< uint32 ( GetGoState ()) << ")" ;
2008-10-11 14:16:25 -05:00
WorldDatabase . BeginTransaction ();
WorldDatabase . PExecuteLog ( "DELETE FROM gameobject WHERE guid = '%u'" , m_DBTableGuid );
WorldDatabase . PExecuteLog ( ss . str ( ). c_str ( ) );
WorldDatabase . CommitTransaction ();
}
bool GameObject :: LoadFromDB ( uint32 guid , Map * map )
{
GameObjectData const * data = objmgr . GetGOData ( guid );
if ( ! data )
{
2009-03-21 14:28:02 -06:00
sLog . outErrorDb ( "Gameobject (GUID: %u) not found in table `gameobject`, can't load. " , guid );
2008-10-11 14:16:25 -05:00
return false ;
}
uint32 entry = data -> id ;
2008-12-24 09:58:26 -06:00
//uint32 map_id = data->mapid; // already used before call
2009-01-31 16:38:50 -06:00
uint32 phaseMask = data -> phaseMask ;
2008-10-11 14:16:25 -05:00
float x = data -> posX ;
float y = data -> posY ;
float z = data -> posZ ;
float ang = data -> orientation ;
float rotation0 = data -> rotation0 ;
float rotation1 = data -> rotation1 ;
float rotation2 = data -> rotation2 ;
float rotation3 = data -> rotation3 ;
uint32 animprogress = data -> animprogress ;
2009-04-27 18:36:10 -05:00
GOState go_state = data -> go_state ;
2009-01-04 17:14:34 -06:00
uint32 ArtKit = data -> ArtKit ;
2008-10-11 14:16:25 -05:00
m_DBTableGuid = guid ;
if ( map -> GetInstanceId () != 0 ) guid = objmgr . GenerateLowGuid ( HIGHGUID_GAMEOBJECT );
2009-01-31 16:38:50 -06:00
if ( ! Create ( guid , entry , map , phaseMask , x , y , z , ang , rotation0 , rotation1 , rotation2 , rotation3 , animprogress , go_state , ArtKit ) )
2008-10-11 14:16:25 -05:00
return false ;
2009-08-17 13:58:43 +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-08-17 13:58:43 +02:00
if ( ! GetGOInfo () -> GetDespawnPossibility () && ! GetGOInfo () -> IsDespawnAtAction ())
{
SetFlag ( GAMEOBJECT_FLAGS , GO_FLAG_NODESPAWN );
m_respawnDelayTime = 0 ;
m_respawnTime = 0 ;
}
else
2009-04-08 16:35:13 -05:00
{
m_respawnDelayTime = data -> spawntimesecs ;
m_respawnTime = objmgr . GetGORespawnTime ( m_DBTableGuid , map -> GetInstanceId ());
2008-10-11 14:16:25 -05:00
2009-04-08 16:35:13 -05:00
// ready to respawn
if ( m_respawnTime && m_respawnTime <= time ( NULL ))
2008-10-11 14:16:25 -05:00
{
m_respawnTime = 0 ;
2009-04-08 16:35:13 -05:00
objmgr . SaveGORespawnTime ( m_DBTableGuid , GetInstanceId (), 0 );
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
{
m_spawnedByDefault = false ;
m_respawnDelayTime = - data -> spawntimesecs ;
m_respawnTime = 0 ;
2008-10-11 14:16:25 -05:00
}
2009-05-31 21:04:04 -05:00
m_goData = data ;
2009-05-31 14:26:57 -05:00
2008-10-11 14:16:25 -05:00
return true ;
}
void GameObject :: DeleteFromDB ()
{
objmgr . SaveGORespawnTime ( m_DBTableGuid , GetInstanceId (), 0 );
objmgr . DeleteGOData ( m_DBTableGuid );
WorldDatabase . PExecuteLog ( "DELETE FROM gameobject WHERE guid = '%u'" , m_DBTableGuid );
WorldDatabase . PExecuteLog ( "DELETE FROM game_event_gameobject WHERE guid = '%u'" , m_DBTableGuid );
}
GameObject * GameObject :: GetGameObject ( WorldObject & object , uint64 guid )
{
2009-04-20 20:28:19 -05:00
return object . GetMap () -> GetGameObject ( guid );
2008-10-11 14:16:25 -05:00
}
/*********************************************************/
/*** QUEST SYSTEM ***/
/*********************************************************/
bool GameObject :: hasQuest ( uint32 quest_id ) const
{
QuestRelations const & qr = objmgr . mGOQuestRelations ;
for ( QuestRelations :: const_iterator itr = qr . lower_bound ( GetEntry ()); itr != qr . upper_bound ( GetEntry ()); ++ itr )
{
if ( itr -> second == quest_id )
return true ;
}
return false ;
}
bool GameObject :: hasInvolvedQuest ( uint32 quest_id ) const
{
QuestRelations const & qr = objmgr . mGOQuestInvolvedRelations ;
for ( QuestRelations :: const_iterator itr = qr . lower_bound ( GetEntry ()); itr != qr . upper_bound ( GetEntry ()); ++ itr )
{
if ( itr -> second == quest_id )
return true ;
}
return false ;
}
bool GameObject :: IsTransport () const
{
// If something is marked as a transport, don't transmit an out of range packet for it.
GameObjectInfo const * gInfo = GetGOInfo ();
if ( ! gInfo ) return false ;
return gInfo -> type == GAMEOBJECT_TYPE_TRANSPORT || gInfo -> type == GAMEOBJECT_TYPE_MO_TRANSPORT ;
}
2009-08-01 20:23:23 +02:00
Unit * GameObject :: GetOwner ( bool inWorld ) const
2008-10-11 14:16:25 -05:00
{
2009-08-01 20:23:23 +02:00
if ( inWorld )
return ObjectAccessor :: GetUnit ( * this , GetOwnerGUID ());
return ObjectAccessor :: GetUnitInOrOutOfWorld ( * this , GetOwnerGUID ());
2008-10-11 14:16:25 -05:00
}
void GameObject :: SaveRespawnTime ()
{
2009-05-31 21:04:04 -05:00
if ( m_goData && m_goData -> dbData && m_respawnTime > time ( NULL ) && m_spawnedByDefault )
2008-10-11 14:16:25 -05:00
objmgr . SaveGORespawnTime ( m_DBTableGuid , GetInstanceId (), m_respawnTime );
}
bool GameObject :: isVisibleForInState ( Player const * u , bool inVisibleList ) const
{
// Not in world
if ( ! IsInWorld () || ! u -> IsInWorld ())
return false ;
// Transport always visible at this step implementation
if ( IsTransport () && IsInMap ( u ))
return true ;
// quick check visibility false cases for non-GM-mode
if ( ! u -> isGameMaster ())
{
// despawned and then not visible for non-GM in GM-mode
if ( ! isSpawned ())
return false ;
// special invisibility cases
2009-02-04 16:30:20 -06:00
if ( GetGOInfo () -> type == GAMEOBJECT_TYPE_TRAP && GetGOInfo () -> trap . stealthed )
2008-10-11 14:16:25 -05:00
{
2009-02-04 16:30:20 -06:00
Unit * owner = GetOwner ();
if ( owner && u -> IsHostileTo ( owner ) && ! canDetectTrap ( u , GetDistance ( u )))
2008-10-11 14:16:25 -05:00
return false ;
2009-02-04 19:43:00 +01:00
}
2008-10-11 14:16:25 -05:00
}
// check distance
2009-03-10 15:26:17 -06:00
return IsWithinDistInMap ( u -> m_seer , World :: GetMaxVisibleDistanceForObject () +
2008-12-06 14:01:44 -06:00
( inVisibleList ? World :: GetVisibleObjectGreyDistance () : 0.0f ), false );
2008-10-11 14:16:25 -05:00
}
2009-02-04 19:43:00 +01:00
bool GameObject :: canDetectTrap ( Player const * u , float distance ) const
{
if ( u -> hasUnitState ( UNIT_STAT_STUNNED ))
return false ;
if ( distance < GetGOInfo () -> size ) //collision
return true ;
if ( ! u -> HasInArc ( M_PI , this )) //behind
return false ;
if ( u -> HasAuraType ( SPELL_AURA_DETECT_STEALTH ))
return true ;
//Visible distance is modified by -Level Diff (every level diff = 0.25f in visible distance)
float visibleDistance = ( int32 ( u -> getLevel ()) - int32 ( GetOwner () -> getLevel ())) * 0.25f ;
//GetModifier for trap (miscvalue 1)
//35y for aura 2836
//WARNING: these values are guessed, may be not blizzlike
visibleDistance += u -> GetTotalAuraModifierByMiscValue ( SPELL_AURA_MOD_DETECT , 1 ) * 0.5f ;
return distance < visibleDistance ;
}
2008-10-11 14:16:25 -05:00
void GameObject :: Respawn ()
{
if ( m_spawnedByDefault && m_respawnTime > 0 )
{
m_respawnTime = time ( NULL );
objmgr . SaveGORespawnTime ( m_DBTableGuid , GetInstanceId (), 0 );
}
}
bool GameObject :: ActivateToQuest ( Player * pTarget ) const
{
if ( ! objmgr . IsGameObjectForQuests ( GetEntry ()))
return false ;
switch ( GetGoType ())
{
// scan GO chest with loot including quest items
case GAMEOBJECT_TYPE_CHEST :
{
2009-07-16 12:06:07 +08:00
if ( LootTemplates_Gameobject . HaveQuestLootForPlayer ( GetGOInfo () -> GetLootId (), pTarget ))
2008-11-21 19:45:49 -06:00
{
//TODO: fix this hack
//look for battlegroundAV for some objects which are only activated after mine gots captured by own team
if ( GetEntry () == BG_AV_OBJECTID_MINE_N || GetEntry () == BG_AV_OBJECTID_MINE_S )
if ( BattleGround * bg = pTarget -> GetBattleGround ())
if ( bg -> GetTypeID () == BATTLEGROUND_AV && ! ((( BattleGroundAV * ) bg ) -> PlayerCanDoMineQuest ( GetEntry (), pTarget -> GetTeam ())))
return false ;
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 ;
}
case GAMEOBJECT_TYPE_GOOBER :
{
if ( pTarget -> GetQuestStatus ( GetGOInfo () -> goober . questId ) == QUEST_STATUS_INCOMPLETE )
return true ;
break ;
}
default :
break ;
}
return false ;
}
void GameObject :: TriggeringLinkedGameObject ( uint32 trapEntry , Unit * target )
{
GameObjectInfo const * trapInfo = sGOStorage . LookupEntry < GameObjectInfo > ( trapEntry );
if ( ! trapInfo || trapInfo -> type != GAMEOBJECT_TYPE_TRAP )
return ;
SpellEntry const * trapSpell = sSpellStore . LookupEntry ( trapInfo -> trap . spellId );
if ( ! trapSpell ) // checked at load already
return ;
2009-02-14 15:56:04 +01:00
float range ;
SpellRangeEntry const * srentry = sSpellRangeStore . LookupEntry ( trapSpell -> rangeIndex );
//get owner to check hostility of GameObject
if ( GetSpellMaxRangeForHostile ( srentry ) == GetSpellMaxRangeForHostile ( srentry ))
range = GetSpellMaxRangeForHostile ( srentry );
else
{
Unit * owner = GetOwner ();
if ( owner )
range = owner -> GetSpellMaxRangeForTarget ( target , srentry );
else
//if no owner assume that object is hostile to target
range = GetSpellMaxRangeForHostile ( srentry );
}
2008-10-11 14:16:25 -05:00
// search nearest linked GO
GameObject * trapGO = NULL ;
{
// using original GO distance
2008-10-14 11:57:03 -05:00
CellPair p ( Trinity :: ComputeCellPair ( GetPositionX (), GetPositionY ()));
2008-10-11 14:16:25 -05:00
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
2009-01-31 16:38:50 -06:00
MaNGOS :: NearestGameObjectEntryInObjectRangeCheck go_check ( * target , trapEntry , range );
MaNGOS :: GameObjectLastSearcher < MaNGOS :: NearestGameObjectEntryInObjectRangeCheck > checker ( this , trapGO , go_check );
2008-10-11 14:16:25 -05:00
2008-10-14 11:57:03 -05:00
TypeContainerVisitor < Trinity :: GameObjectLastSearcher < Trinity :: NearestGameObjectEntryInObjectRangeCheck > , GridTypeMapContainer > object_checker ( checker );
2008-10-11 14:16:25 -05:00
CellLock < GridReadGuard > cell_lock ( cell , p );
2009-03-08 13:05:56 -06:00
cell_lock -> Visit ( cell_lock , object_checker , * GetMap ());
2008-10-11 14:16:25 -05:00
}
// found correct GO
// FIXME: when GO casting will be implemented trap must cast spell to target
if ( trapGO )
2009-01-25 16:35:14 -06:00
target -> CastSpell ( target , trapSpell , true , 0 , 0 , GetGUID ());
2008-10-11 14:16:25 -05:00
}
GameObject * GameObject :: LookupFishingHoleAround ( float range )
{
GameObject * ok = NULL ;
2008-10-14 11:57:03 -05:00
CellPair p ( Trinity :: ComputeCellPair ( GetPositionX (), GetPositionY ()));
2008-10-11 14:16:25 -05:00
Cell cell ( p );
cell . data . Part . reserved = ALL_DISTRICT ;
2009-01-31 16:38:50 -06:00
MaNGOS :: NearestGameObjectFishingHole u_check ( * this , range );
MaNGOS :: GameObjectSearcher < MaNGOS :: NearestGameObjectFishingHole > checker ( this , ok , u_check );
2008-10-11 14:16:25 -05:00
CellLock < GridReadGuard > cell_lock ( cell , p );
2008-10-14 11:57:03 -05:00
TypeContainerVisitor < Trinity :: GameObjectSearcher < Trinity :: NearestGameObjectFishingHole > , GridTypeMapContainer > grid_object_checker ( checker );
2009-03-08 13:05:56 -06:00
cell_lock -> Visit ( cell_lock , grid_object_checker , * GetMap ());
2008-10-11 14:16:25 -05:00
return ok ;
}
2009-04-27 18:36:10 -05:00
void GameObject :: ResetDoorOrButton ()
{
if ( m_lootState == GO_READY || m_lootState == GO_JUST_DEACTIVATED )
return ;
SwitchDoorOrButton ( false );
SetLootState ( GO_JUST_DEACTIVATED );
m_cooldownTime = 0 ;
}
void GameObject :: UseDoorOrButton ( uint32 time_to_restore , bool alternative /* = false */ )
2008-10-11 14:16:25 -05:00
{
if ( m_lootState != GO_READY )
return ;
if ( ! time_to_restore )
2009-07-16 12:06:07 +08:00
time_to_restore = GetGOInfo () -> GetAutoCloseTime ();
2008-10-11 14:16:25 -05:00
2009-04-27 18:36:10 -05:00
SwitchDoorOrButton ( true , alternative );
2008-10-11 14:16:25 -05:00
SetLootState ( GO_ACTIVATED );
m_cooldownTime = time ( NULL ) + time_to_restore ;
}
2009-01-04 17:14:34 -06:00
void GameObject :: SetGoArtKit ( uint8 kit )
{
SetByteValue ( GAMEOBJECT_BYTES_1 , 2 , kit );
GameObjectData * data = const_cast < GameObjectData *> ( objmgr . GetGOData ( m_DBTableGuid ));
if ( data )
data -> ArtKit = kit ;
}
2009-04-27 18:36:10 -05:00
void GameObject :: SwitchDoorOrButton ( bool activate , bool alternative /* = false */ )
2008-10-11 14:16:25 -05:00
{
if ( activate )
SetFlag ( GAMEOBJECT_FLAGS , GO_FLAG_IN_USE );
else
RemoveFlag ( GAMEOBJECT_FLAGS , GO_FLAG_IN_USE );
2009-04-27 18:36:10 -05:00
if ( GetGoState () == GO_STATE_READY ) //if closed -> open
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
}
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 ;
2008-10-11 14:16:25 -05:00
switch ( GetGoType ())
{
case GAMEOBJECT_TYPE_DOOR : //0
case GAMEOBJECT_TYPE_BUTTON : //1
//doors/buttons never really despawn, only reset to default state/flags
UseDoorOrButton ();
// activate script
2009-07-31 10:48:34 +08:00
GetMap () -> ScriptsStart ( sGameObjectScripts , GetDBTableGUIDLow (), spellCaster , this );
2008-10-11 14:16:25 -05:00
return ;
case GAMEOBJECT_TYPE_QUESTGIVER : //2
{
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = ( Player * ) user ;
player -> PrepareQuestMenu ( GetGUID () );
player -> SendPreparedQuest ( GetGUID () );
return ;
}
//Sitting: Wooden bench, chairs enzz
case GAMEOBJECT_TYPE_CHAIR : //7
{
GameObjectInfo const * info = GetGOInfo ();
if ( ! info )
return ;
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = ( Player * ) user ;
// a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
// check if the db is sane
if ( info -> chair . slots > 0 )
{
float lowestDist = DEFAULT_VISIBILITY_DISTANCE ;
float x_lowest = GetPositionX ();
float y_lowest = GetPositionY ();
// the object orientation + 1/2 pi
// every slot will be on that straight line
float orthogonalOrientation = GetOrientation () + M_PI * 0.5f ;
// find nearest slot
2009-04-29 00:26:07 -05:00
for ( uint32 i = 0 ; i < info -> chair . slots ; ++ i )
2008-10-11 14:16:25 -05:00
{
// the distance between this slot and the center of the go - imagine a 1D space
float relativeDistance = ( info -> size * i ) - ( info -> size * ( info -> chair . slots - 1 ) / 2.0f );
float x_i = GetPositionX () + relativeDistance * cos ( orthogonalOrientation );
float y_i = GetPositionY () + relativeDistance * sin ( orthogonalOrientation );
// calculate the distance between the player and this slot
float thisDistance = player -> GetDistance2d ( x_i , y_i );
/* debug code. It will spawn a npc on each slot to visualize them.
Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000);
std::ostringstream output;
output << i << ": thisDist: " << thisDistance;
helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0);
*/
if ( thisDistance <= lowestDist )
{
lowestDist = thisDistance ;
x_lowest = x_i ;
y_lowest = y_i ;
}
}
player -> TeleportTo ( GetMapId (), x_lowest , y_lowest , GetPositionZ (), GetOrientation (), TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET );
}
else
{
// 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 );
}
2009-01-31 14:41:36 -06:00
player -> SetStandState ( UNIT_STAND_STATE_SIT_LOW_CHAIR + info -> chair . height );
2008-10-11 14:16:25 -05:00
return ;
}
//big gun, its a spell/aura
case GAMEOBJECT_TYPE_GOOBER : //10
{
GameObjectInfo const * info = GetGOInfo ();
if ( user -> GetTypeId () == TYPEID_PLAYER )
{
Player * player = ( Player * ) user ;
// show page
if ( info -> goober . pageId )
{
WorldPacket data ( SMSG_GAMEOBJECT_PAGETEXT , 8 );
data << GetGUID ();
player -> GetSession () -> SendPacket ( & data );
}
// possible quest objective for active quests
player -> CastedCreatureOrGO ( info -> id , GetGUID (), 0 );
2009-06-01 21:50:12 -05:00
if ( info -> goober . eventId )
2009-07-31 10:48:34 +08:00
GetMap () -> ScriptsStart ( sEventScripts , info -> goober . eventId , player , this );
2008-10-11 14:16:25 -05:00
}
// cast this spell later if provided
spellId = info -> goober . spellId ;
break ;
}
case GAMEOBJECT_TYPE_CAMERA : //13
{
GameObjectInfo const * info = GetGOInfo ();
if ( ! info )
return ;
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = ( Player * ) user ;
2009-04-05 16:25:00 -05:00
if ( info -> camera . cinematicId )
player -> SendCinematicStart ( info -> camera . cinematicId );
2009-06-01 21:50:12 -05:00
if ( info -> camera . eventID )
2009-07-31 10:48:34 +08:00
GetMap () -> ScriptsStart ( sEventScripts , info -> camera . eventID , player , this );
2009-06-01 21:50:12 -05:00
2008-10-11 14:16:25 -05:00
return ;
}
//fishing bobber
case GAMEOBJECT_TYPE_FISHINGNODE : //17
{
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = ( Player * ) user ;
if ( player -> GetGUID () != GetOwnerGUID ())
return ;
switch ( getLootState ())
{
case GO_READY : // ready for loot
{
// 1) skill must be >= base_zone_skill
// 2) if skill == base_zone_skill => 5% chance
// 3) chance is linear dependence from (base_zone_skill-skill)
2009-03-11 16:17:37 -06:00
uint32 zone , subzone ;
GetZoneAndAreaId ( zone , subzone );
2008-10-11 14:16:25 -05:00
int32 zone_skill = objmgr . GetFishingBaseSkillLevel ( subzone );
if ( ! zone_skill )
2009-03-11 16:17:37 -06:00
zone_skill = objmgr . GetFishingBaseSkillLevel ( zone );
2008-10-11 14:16:25 -05:00
//provide error, no fishable zone or area should be 0
if ( ! zone_skill )
sLog . outErrorDb ( "Fishable areaId %u are not properly defined in `skill_fishing_base_level`." , subzone );
int32 skill = player -> GetSkillValue ( SKILL_FISHING );
int32 chance = skill - zone_skill + 5 ;
int32 roll = irand ( 1 , 100 );
DEBUG_LOG ( "Fishing check (skill: %i zone min skill: %i chance %i roll: %i" , skill , zone_skill , chance , roll );
if ( skill >= zone_skill && chance >= roll )
{
// prevent removing GO at spell cancel
player -> RemoveGameObject ( this , false );
SetOwnerGUID ( player -> GetGUID ());
//fish catched
player -> UpdateFishingSkill ();
GameObject * ok = LookupFishingHoleAround ( DEFAULT_VISIBILITY_DISTANCE );
if ( ok )
{
player -> SendLoot ( ok -> GetGUID (), LOOT_FISHINGHOLE );
2009-04-17 15:22:27 -05:00
player -> UpdateAchievementCriteria ( ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT , ok -> GetGOInfo () -> id );
2008-10-11 14:16:25 -05:00
SetLootState ( GO_JUST_DEACTIVATED );
}
else
player -> SendLoot ( GetGUID (), LOOT_FISHING );
}
else
{
// fish escaped, can be deleted now
SetLootState ( GO_JUST_DEACTIVATED );
WorldPacket data ( SMSG_FISH_ESCAPED , 0 );
player -> GetSession () -> SendPacket ( & data );
}
break ;
}
case GO_JUST_DEACTIVATED : // nothing to do, will be deleted at next update
break ;
default :
{
SetLootState ( GO_JUST_DEACTIVATED );
WorldPacket data ( SMSG_FISH_NOT_HOOKED , 0 );
player -> GetSession () -> SendPacket ( & data );
break ;
}
}
2009-08-19 16:52:51 -05:00
player -> FinishSpell ( CURRENT_CHANNELED_SPELL );
2008-10-11 14:16:25 -05:00
return ;
}
case GAMEOBJECT_TYPE_SUMMONING_RITUAL : //18
{
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = ( Player * ) user ;
Unit * caster = GetOwner ();
GameObjectInfo const * info = GetGOInfo ();
if ( ! caster || caster -> GetTypeId () != TYPEID_PLAYER )
return ;
// accept only use by player from same group for caster except caster itself
if ((( Player * ) caster ) == player || ! (( Player * ) caster ) -> IsInSameRaidWith ( player ))
return ;
AddUniqueUse ( player );
// full amount unique participants including original summoner
if ( GetUniqueUseCount () < info -> summoningRitual . reqParticipants )
return ;
// in case summoning ritual caster is GO creator
spellCaster = caster ;
2009-08-19 16:52:51 -05:00
if ( ! caster -> GetCurrentSpell ( CURRENT_CHANNELED_SPELL ))
2008-10-11 14:16:25 -05:00
return ;
spellId = info -> summoningRitual . spellId ;
2009-02-22 17:48:48 -06:00
if ( spellId == 62330 ) // GO store not existed spell, replace by expected
{
// spell have reagent and mana cost but it not expected use its
// it triggered spell in fact casted at currently channeled GO
spellId = 61993 ;
triggered = true ;
}
2008-10-11 14:16:25 -05:00
// finish spell
2009-08-19 16:52:51 -05:00
player -> FinishSpell ( CURRENT_CHANNELED_SPELL );
2008-10-11 14:16:25 -05:00
// can be deleted now
SetLootState ( GO_JUST_DEACTIVATED );
// go to end function to spell casting
break ;
}
case GAMEOBJECT_TYPE_SPELLCASTER : //22
{
SetUInt32Value ( GAMEOBJECT_FLAGS , 2 );
GameObjectInfo const * info = GetGOInfo ();
if ( ! info )
return ;
if ( info -> spellcaster . partyOnly )
{
Unit * caster = GetOwner ();
if ( ! caster || caster -> GetTypeId () != TYPEID_PLAYER )
return ;
if ( user -> GetTypeId () != TYPEID_PLAYER || ! (( Player * ) user ) -> IsInSameRaidWith (( Player * ) caster ))
return ;
}
spellId = info -> spellcaster . spellId ;
AddUse ();
break ;
}
case GAMEOBJECT_TYPE_MEETINGSTONE : //23
{
GameObjectInfo const * info = GetGOInfo ();
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = ( Player * ) user ;
Player * targetPlayer = ObjectAccessor :: FindPlayer ( player -> GetSelection ());
// accept only use by player from same group for caster except caster itself
if ( ! targetPlayer || targetPlayer == player || ! targetPlayer -> IsInSameGroupWith ( player ))
return ;
//required lvl checks!
uint8 level = player -> getLevel ();
if ( level < info -> meetingstone . minLevel || level > info -> meetingstone . maxLevel )
return ;
level = targetPlayer -> getLevel ();
if ( level < info -> meetingstone . minLevel || level > info -> meetingstone . maxLevel )
return ;
2009-02-22 17:48:19 -06:00
if ( info -> id == 194097 )
spellId = 61994 ; // Ritual of Summoning
else
spellId = 59782 ; // Summoning Stone Effect
2008-10-11 14:16:25 -05:00
break ;
}
case GAMEOBJECT_TYPE_FLAGSTAND : // 24
{
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = ( Player * ) user ;
2009-03-09 18:00:56 -06:00
if ( player -> CanUseBattleGroundObject () )
2008-10-11 14:16:25 -05:00
{
// in battleground check
BattleGround * bg = player -> GetBattleGround ();
if ( ! bg )
return ;
// 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 ;
}
case GAMEOBJECT_TYPE_FLAGDROP : // 26
{
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = ( Player * ) user ;
2009-03-09 18:00:56 -06:00
if ( player -> CanUseBattleGroundObject () )
2008-10-11 14:16:25 -05:00
{
// in battleground check
BattleGround * bg = player -> GetBattleGround ();
if ( ! bg )
return ;
// BG flag dropped
// WS:
// 179785 - Silverwing Flag
// 179786 - Warsong Flag
// EotS:
// 184142 - Netherstorm Flag
GameObjectInfo const * info = GetGOInfo ();
if ( info )
{
switch ( info -> id )
{
case 179785 : // Silverwing Flag
// check if it's correct bg
if ( bg -> GetTypeID () == BATTLEGROUND_WS )
bg -> EventPlayerClickedOnFlag ( player , this );
break ;
case 179786 : // Warsong Flag
if ( bg -> GetTypeID () == BATTLEGROUND_WS )
bg -> EventPlayerClickedOnFlag ( player , this );
break ;
case 184142 : // Netherstorm Flag
if ( bg -> GetTypeID () == BATTLEGROUND_EY )
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
{
GameObjectInfo const * info = GetGOInfo ();
if ( ! info )
return ;
if ( user -> GetTypeId () != TYPEID_PLAYER )
return ;
Player * player = ( Player * ) user ;
// 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 );
2009-07-08 14:29:40 +02:00
WorldPacket data ( SMSG_ENABLE_BARBER_SHOP , 0 );
2008-12-24 09:58:26 -06:00
player -> GetSession () -> SendPacket ( & data );
2009-01-31 14:41:36 -06:00
player -> SetStandState ( UNIT_STAND_STATE_SIT_LOW_CHAIR + info -> barberChair . chairheight );
2008-12-24 09:58:26 -06:00
return ;
}
2008-10-11 14:16:25 -05:00
default :
sLog . outDebug ( "Unknown Object Type %u" , GetGoType ());
break ;
}
if ( ! spellId )
return ;
SpellEntry const * spellInfo = sSpellStore . LookupEntry ( spellId );
if ( ! spellInfo )
{
2008-10-14 11:57:03 -05:00
if ( user -> GetTypeId () != TYPEID_PLAYER || ! sOutdoorPvPMgr . HandleCustomSpell (( Player * ) user , spellId , this ))
sLog . outError ( "WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u )" , spellId , GetEntry (), GetGoType ());
else
sLog . outDebug ( "WORLD: %u non-dbc spell was handled by OutdoorPvP" , spellId );
2008-10-11 14:16:25 -05:00
return ;
}
2009-02-22 17:48:48 -06:00
Spell * spell = new Spell ( spellCaster , spellInfo , triggered );
2009-07-16 12:06:07 +08:00
//Spell *spell = new Spell(spellCaster, spellInfo, triggered,GetGUID());
2008-10-11 14:16:25 -05:00
// spell target is user of GO
SpellCastTargets targets ;
targets . setUnitTarget ( user );
spell -> prepare ( & targets );
}
2008-11-02 00:59:44 -05:00
2009-03-14 21:30:03 -06:00
void GameObject :: CastSpell ( Unit * target , uint32 spellId )
2008-11-02 00:59:44 -05:00
{
2009-03-14 21:30:03 -06:00
SpellEntry const * spellInfo = sSpellStore . LookupEntry ( spellId );
if ( ! spellInfo )
return ;
bool self = false ;
2009-06-18 17:21:25 +02:00
for ( uint8 i = 0 ; i < 3 ; ++ i )
2009-03-14 21:30:03 -06:00
{
if ( spellInfo -> EffectImplicitTargetA [ i ] == TARGET_UNIT_CASTER )
{
self = true ;
break ;
}
}
if ( self )
{
2009-05-06 00:06:38 -05:00
if ( target )
target -> CastSpell ( target , spellInfo , true );
2009-03-14 21:30:03 -06:00
return ;
}
2008-11-02 00:59:44 -05:00
//summon world trigger
2008-12-10 20:25:47 -06:00
Creature * trigger = SummonTrigger ( GetPositionX (), GetPositionY (), GetPositionZ (), 0 , 1 );
2008-11-02 00:59:44 -05:00
if ( ! trigger ) return ;
trigger -> SetVisibility ( VISIBILITY_OFF ); //should this be true?
2008-11-02 08:44:15 -06:00
if ( Unit * owner = GetOwner ())
{
trigger -> setFaction ( owner -> getFaction ());
2009-03-14 21:30:03 -06:00
trigger -> CastSpell ( target , spellInfo , true , 0 , 0 , owner -> GetGUID ());
2008-11-02 08:44:15 -06:00
}
else
{
trigger -> setFaction ( 14 );
2009-06-13 14:01:10 +02:00
// Set owner guid for target if no owner avalible - needed by trigger auras
// - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell())
2009-06-15 09:49:16 -05:00
trigger -> CastSpell ( target , spellInfo , true , 0 , 0 , target ? target -> GetGUID () : 0 );
2008-11-02 08:44:15 -06:00
}
2008-11-09 14:54:13 -06:00
//trigger->setDeathState(JUST_DIED);
//trigger->RemoveCorpse();
2008-11-14 16:28:45 -06:00
}
2008-11-14 17:03:03 -06:00
2009-04-30 12:01:08 -05:00
void GameObject :: SendCustomAnim ()
{
WorldPacket data ( SMSG_GAMEOBJECT_CUSTOM_ANIM , 8 + 4 );
data << GetGUID ();
data << ( uint32 ) 0 ;
SendMessageToSet ( & data , true );
}
2009-05-21 10:01:03 -05:00
bool GameObject :: IsInRange ( float x , float y , float z , float radius ) const
{
GameObjectDisplayInfoEntry const * info = sGameObjectDisplayInfoStore . LookupEntry ( GetUInt32Value ( GAMEOBJECT_DISPLAYID ));
if ( ! info )
return IsWithinDist3d ( x , y , z , radius );
float sinA = sin ( GetOrientation ());
float cosA = cos ( GetOrientation ());
float dx = x - GetPositionX ();
float dy = y - GetPositionY ();
float dz = z - GetPositionZ ();
float dist = sqrt ( dx * dx + dy * dy );
float sinB = dx / dist ;
float cosB = dy / dist ;
dx = dist * ( cosA * cosB + sinA * sinB );
dy = dist * ( cosA * sinB - sinA * cosB );
return dx < info -> maxX + radius && dx > info -> minX - radius
&& dy < info -> maxY + radius && dy > info -> minY - radius
&& dz < info -> maxZ + radius && dz > info -> minZ - radius ;
}
void GameObject :: TakenDamage ( uint32 damage )
{
2009-06-01 23:45:55 -05:00
if ( ! m_goValue -> building . health )
2009-05-21 10:01:03 -05:00
return ;
2009-06-01 23:45:55 -05:00
if ( m_goValue -> building . health > damage )
m_goValue -> building . health -= damage ;
else
m_goValue -> building . health = 0 ;
2009-05-21 10:01:03 -05:00
if ( HasFlag ( GAMEOBJECT_FLAGS , GO_FLAG_DAMAGED )) // from damaged to destroyed
{
2009-06-01 23:45:55 -05:00
if ( ! m_goValue -> building . health )
{
RemoveFlag ( GAMEOBJECT_FLAGS , GO_FLAG_DAMAGED );
SetFlag ( GAMEOBJECT_FLAGS , GO_FLAG_DESTROYED );
SetUInt32Value ( GAMEOBJECT_DISPLAYID , m_goInfo -> building . destroyedDisplayId );
2009-06-04 18:32:24 +02:00
EventInform ( m_goInfo -> building . damagedEvent );
2009-06-01 23:45:55 -05:00
}
2009-05-21 10:01:03 -05:00
}
2009-06-01 23:45:55 -05:00
else // from intact to damaged
2009-05-21 10:01:03 -05:00
{
2009-06-04 18:32:24 +02:00
if ( m_goValue -> building . health <= m_goInfo -> building . damagedNumHits )
2009-05-21 10:01:03 -05:00
{
2009-06-01 23:45:55 -05:00
if ( ! m_goInfo -> building . destroyedDisplayId )
m_goValue -> building . health = 0 ;
else if ( ! m_goValue -> building . health )
m_goValue -> building . health = 1 ;
SetFlag ( GAMEOBJECT_FLAGS , GO_FLAG_DAMAGED );
SetUInt32Value ( GAMEOBJECT_DISPLAYID , m_goInfo -> building . damagedDisplayId );
2009-06-04 18:32:24 +02:00
EventInform ( m_goInfo -> building . intactEvent );
2009-05-21 10:01:03 -05:00
}
}
}
void GameObject :: Rebuild ()
{
RemoveFlag ( GAMEOBJECT_FLAGS , GO_FLAG_DAMAGED + GO_FLAG_DESTROYED );
SetUInt32Value ( GAMEOBJECT_DISPLAYID , m_goInfo -> displayId );
2009-06-04 18:32:24 +02:00
m_goValue -> building . health = m_goInfo -> building . intactNumHits + m_goInfo -> building . damagedNumHits ;
2009-05-21 10:01:03 -05:00
}
2009-06-01 12:48:12 -05:00
void GameObject :: EventInform ( uint32 eventId )
{
if ( eventId && m_zoneScript )
m_zoneScript -> ProcessEvent ( this , eventId );
}
2008-11-14 17:03:03 -06:00
// overwrite WorldObject function for proper name localization
const char * GameObject :: GetNameForLocaleIdx ( int32 loc_idx ) const
{
if ( loc_idx >= 0 )
{
GameObjectLocale const * cl = objmgr . GetGameObjectLocale ( GetEntry ());
if ( cl )
{
if ( cl -> Name . size () > loc_idx && ! cl -> Name [ loc_idx ]. empty ())
return cl -> Name [ loc_idx ]. c_str ();
}
}
return GetName ();
}
2009-02-17 20:07:49 -06:00
2009-03-18 20:46:21 -06:00
void GameObject :: UpdateRotationFields ( float rotation2 /*=0.0f*/ , float rotation3 /*=0.0f*/ )
{
2009-03-19 15:16:02 -06:00
static double const atan_pow = atan ( pow ( 2.0f , - 20.0f ));
2009-03-18 20:46:21 -06:00
double f_rot1 = sin ( GetOrientation () / 2.0f );
2009-03-19 15:16:02 -06:00
double f_rot2 = cos ( GetOrientation () / 2.0f );
2009-03-18 20:46:21 -06:00
2009-03-19 15:16:02 -06:00
int64 i_rot1 = int64 ( f_rot1 / atan_pow * ( f_rot2 >= 0 ? 1.0f : - 1.0f ));
int64 rotation = ( i_rot1 << 43 >> 43 ) & 0x00000000001FFFFF ;
2009-06-11 00:45:59 -05:00
//float f_rot2 = sin(0.0f / 2.0f);
//int64 i_rot2 = f_rot2 / atan(pow(2.0f, -20.0f));
//rotation |= (((i_rot2 << 22) >> 32) >> 11) & 0x000003FFFFE00000;
//float f_rot3 = sin(0.0f / 2.0f);
//int64 i_rot3 = f_rot3 / atan(pow(2.0f, -21.0f));
//rotation |= (i_rot3 >> 42) & 0x7FFFFC0000000000;
m_rotation = rotation ;
2009-03-18 20:46:21 -06:00
if ( rotation2 == 0.0f && rotation3 == 0.0f )
{
2009-03-19 15:16:02 -06:00
rotation2 = f_rot1 ;
rotation3 = f_rot2 ;
2009-03-18 20:46:21 -06:00
}
SetFloatValue ( GAMEOBJECT_PARENTROTATION + 2 , rotation2 );
SetFloatValue ( GAMEOBJECT_PARENTROTATION + 3 , rotation3 );
}