Files
TCPlayerbotCore/src/server/game/Entities/GameObject/GameObject.cpp
T

2393 lines
85 KiB
C++
Raw Normal View History

/*
2014-01-01 00:07:53 +01:00
* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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/>.
*/
2009-10-17 15:51:44 -07:00
#include "GameObjectAI.h"
#include "Battleground.h"
2013-06-15 15:16:36 +02:00
#include "CellImpl.h"
#include "CreatureAISelector.h"
#include "DynamicTree.h"
#include "GameObjectModel.h"
#include "GridNotifiersImpl.h"
#include "Group.h"
#include "GroupMgr.h"
2013-06-15 15:16:36 +02:00
#include "ObjectMgr.h"
#include "OutdoorPvPMgr.h"
#include "PoolMgr.h"
2013-06-15 15:16:36 +02:00
#include "ScriptMgr.h"
#include "SpellMgr.h"
2013-06-15 15:16:36 +02:00
#include "UpdateFieldFlags.h"
#include "World.h"
2013-10-16 18:37:29 +02:00
#include "Transport.h"
2014-07-20 16:06:35 +02:00
#include <G3D/Quat.h>
2013-10-16 18:37:29 +02:00
GameObject::GameObject() : WorldObject(false), MapObject(),
m_model(NULL), m_goValue(), m_AI(NULL)
{
m_objectType |= TYPEMASK_GAMEOBJECT;
m_objectTypeId = TYPEID_GAMEOBJECT;
2009-10-17 15:51:44 -07:00
m_updateFlag = (UPDATEFLAG_STATIONARY_POSITION | UPDATEFLAG_ROTATION);
2009-10-17 15:51:44 -07:00
m_valuesCount = GAMEOBJECT_END;
m_respawnTime = 0;
m_respawnDelayTime = 300;
m_lootState = GO_NOT_READY;
m_spawnedByDefault = true;
m_usetimes = 0;
m_spellId = 0;
m_cooldownTime = 0;
m_goInfo = NULL;
m_goData = NULL;
2009-10-17 15:51:44 -07:00
m_DBTableGuid = 0;
2009-06-11 00:45:59 -05:00
m_rotation = 0;
m_lootRecipientGroup = 0;
m_groupLootTimer = 0;
2010-08-02 17:28:47 +02:00
lootingGroupLowGUID = 0;
ResetLootMode(); // restore default loot mode
2013-12-01 16:19:30 +01:00
m_stationaryPosition.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
}
2009-10-17 15:51:44 -07:00
GameObject::~GameObject()
{
delete m_AI;
delete m_model;
if (m_goInfo->type == GAMEOBJECT_TYPE_TRANSPORT)
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
// CleanupsBeforeDelete();
}
2009-10-17 15:51:44 -07:00
bool GameObject::AIM_Initialize()
{
delete m_AI;
m_AI = FactorySelector::SelectGameObjectAI(this);
if (!m_AI)
return false;
m_AI->InitializeAI();
return true;
}
std::string GameObject::GetAIName() const
{
if (GameObjectTemplate const* got = sObjectMgr->GetGameObjectTemplate(GetEntry()))
return got->AIName;
return "";
}
void GameObject::CleanupsBeforeDelete(bool finalCleanup)
{
WorldObject::CleanupsBeforeDelete(finalCleanup);
2010-02-14 19:13:14 -07:00
2010-04-07 19:14:10 +02:00
if (m_uint32Values) // field array can be not exist if GameOBject not loaded
2011-10-31 10:46:39 -04:00
RemoveFromOwner();
}
2009-10-17 15:51:44 -07:00
2011-10-31 10:46:39 -04:00
void GameObject::RemoveFromOwner()
{
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;
}
2011-10-31 10:46:39 -04:00
TC_LOG_FATAL("misc", "Removed GameObject (GUID: %u Entry: %u SpellId: %u LinkedGO: %u) that just lost any reference to the owner (%s) GO list",
GetGUIDLow(), GetGOInfo()->entry, m_spellId, GetGOInfo()->GetLinkedGameObjectEntry(), ownerGUID.ToString().c_str());
SetOwnerGUID(ObjectGuid::Empty);
}
2009-10-17 15:51:44 -07:00
void GameObject::AddToWorld()
{
///- Register the gameobject for guid lookup
2010-04-07 19:14:10 +02:00
if (!IsInWorld())
{
2010-04-07 19:14:10 +02:00
if (m_zoneScript)
m_zoneScript->OnGameObjectCreate(this);
2009-10-17 15:51:44 -07:00
sObjectAccessor->AddObject(this);
// 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)
{
if (Transport* trans = ToTransport())
trans->SetDelayedAddModelToMap();
else
GetMap()->InsertGameObjectModel(*m_model);
}
2012-02-12 08:41:59 -05:00
EnableCollision(toggledState);
WorldObject::AddToWorld();
}
}
2009-10-17 15:51:44 -07:00
void GameObject::RemoveFromWorld()
{
///- Remove the gameobject from the accessor
2010-04-07 19:14:10 +02:00
if (IsInWorld())
{
2010-04-07 19:14:10 +02:00
if (m_zoneScript)
m_zoneScript->OnGameObjectRemove(this);
2009-10-17 15:51:44 -07:00
2011-10-31 10:46:39 -04:00
RemoveFromOwner();
if (m_model)
if (GetMap()->ContainsGameObjectModel(*m_model))
GetMap()->RemoveGameObjectModel(*m_model);
WorldObject::RemoveFromWorld();
sObjectAccessor->RemoveObject(this);
}
}
2009-10-17 15:51:44 -07:00
2014-06-28 22:43:35 +02: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)
{
ASSERT(map);
SetMap(map);
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
Relocate(x, y, z, ang);
2013-10-16 18:37:29 +02:00
m_stationaryPosition.Relocate(x, y, z, ang);
2010-04-07 19:14:10 +02:00
if (!IsPositionValid())
{
TC_LOG_ERROR("misc", "Gameobject (GUID: %u Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", guidlow, name_id, x, y);
return false;
}
2009-10-17 15:51:44 -07:00
SetZoneScript();
if (m_zoneScript)
{
name_id = m_zoneScript->GetGameObjectEntry(guidlow, name_id);
if (!name_id)
return false;
}
2011-04-28 22:54:30 +02:00
GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(name_id);
if (!goinfo)
{
TC_LOG_ERROR("sql.sql", "Gameobject (GUID: %u Entry: %u) not created: non-existing entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f)", guidlow, name_id, map->GetId(), x, y, z);
return false;
}
2009-10-17 15:51:44 -07:00
2013-10-16 18:37:29 +02:00
if (goinfo->type == GAMEOBJECT_TYPE_TRANSPORT)
m_updateFlag |= UPDATEFLAG_TRANSPORT;
2013-10-16 18:37:29 +02:00
2011-04-28 22:54:30 +02:00
Object::_Create(guidlow, goinfo->entry, HIGHGUID_GAMEOBJECT);
2009-10-17 15:51:44 -07:00
m_goInfo = goinfo;
2009-10-17 15:51:44 -07:00
if (goinfo->type >= MAX_GAMEOBJECT_TYPE)
{
TC_LOG_ERROR("sql.sql", "Gameobject (GUID: %u Entry: %u) not created: non-existing GO type '%u' in `gameobject_template`. It will crash client if created.", guidlow, name_id, goinfo->type);
return false;
}
2009-10-17 15:51:44 -07:00
SetFloatValue(GAMEOBJECT_PARENTROTATION+0, rotation0);
SetFloatValue(GAMEOBJECT_PARENTROTATION+1, rotation1);
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
UpdateRotationFields(rotation2, rotation3); // GAMEOBJECT_FACING, GAMEOBJECT_ROTATION, GAMEOBJECT_PARENTROTATION+2/3
2009-10-17 15:51:44 -07:00
SetObjectScale(goinfo->size);
2009-10-17 15:51:44 -07:00
SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
2009-10-17 15:51:44 -07:00
2011-04-28 22:54:30 +02:00
SetEntry(goinfo->entry);
2009-10-17 15:51:44 -07:00
// set name for logs usage, doesn't affect anything ingame
SetName(goinfo->name);
SetDisplayId(goinfo->displayId);
2009-10-17 15:51:44 -07:00
m_model = GameObjectModel::Create(*this);
// GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3
SetGoType(GameobjectTypes(goinfo->type));
SetGoState(go_state);
SetGoArtKit(artKit);
2009-10-17 15:51:44 -07:00
switch (goinfo->type)
{
2013-03-02 23:59:42 +01:00
case GAMEOBJECT_TYPE_FISHINGHOLE:
SetGoAnimProgress(animprogress);
m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishinghole.minSuccessOpens, GetGOInfo()->fishinghole.maxSuccessOpens);
break;
case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING:
m_goValue.Building.Health = goinfo->building.intactNumHits + goinfo->building.damagedNumHits;
m_goValue.Building.MaxHealth = m_goValue.Building.Health;
2010-05-09 06:25:06 +02:00
SetGoAnimProgress(255);
SetUInt32Value(GAMEOBJECT_PARENTROTATION, m_goInfo->building.destructibleData);
break;
2009-11-27 01:41:26 +01:00
case GAMEOBJECT_TYPE_TRANSPORT:
{
2013-10-16 18:37:29 +02:00
m_goValue.Transport.AnimationInfo = sTransportMgr->GetTransportAnimInfo(goinfo->entry);
m_goValue.Transport.PathProgress = (getMSTime() / GetTransportPeriod());
m_goValue.Transport.PathProgress *= GetTransportPeriod();
2013-10-16 18:37:29 +02:00
m_goValue.Transport.CurrentSeg = 0;
m_goValue.Transport.StateUpdateTimer = 0;
m_goValue.Transport.StopFrames = new std::vector<uint32>();
if (goinfo->transport.stopFrame1 > 0)
m_goValue.Transport.StopFrames->push_back(goinfo->transport.stopFrame1);
if (goinfo->transport.stopFrame2 > 0)
m_goValue.Transport.StopFrames->push_back(goinfo->transport.stopFrame3);
if (goinfo->transport.stopFrame3 > 0)
m_goValue.Transport.StopFrames->push_back(goinfo->transport.stopFrame3);
if (goinfo->transport.stopFrame4 > 0)
m_goValue.Transport.StopFrames->push_back(goinfo->transport.stopFrame4);
if (goinfo->transport.startOpen)
SetTransportState(GO_STATE_TRANSPORT_STOPPED, goinfo->transport.startOpen - 1);
else
SetTransportState(GO_STATE_TRANSPORT_ACTIVE);
SetGoAnimProgress(animprogress);
break;
}
case GAMEOBJECT_TYPE_FISHINGNODE:
SetGoAnimProgress(0);
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
}
if (GetGOInfo()->trap.invisible)
{
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
}
break;
default:
SetGoAnimProgress(animprogress);
2009-11-27 01:41:26 +01:00
break;
}
LastUsedScriptID = GetGOInfo()->ScriptId;
AIM_Initialize();
// Initialize loot duplicate count depending on raid difficulty
if (map->Is25ManRaid())
loot.maxDuplicates = 3;
return true;
}
2009-10-17 15:51:44 -07:00
void GameObject::Update(uint32 diff)
{
2013-10-16 18:37:29 +02:00
if (AI())
AI()->UpdateAI(diff);
2013-10-16 18:37:29 +02:00
else if (!AIM_Initialize())
TC_LOG_ERROR("misc", "Could not initialize GameObjectAI");
2009-10-17 15:51:44 -07:00
switch (m_lootState)
{
case GO_NOT_READY:
{
2011-09-29 12:43:05 +02:00
switch (GetGoType())
{
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
if (goInfo->trap.type == 2)
// Hardcoded tooltip value
m_cooldownTime = time(NULL) + 10;
2010-02-14 19:13:14 -07:00
else if (Unit* owner = GetOwner())
2013-06-11 19:54:27 -02:30
if (owner->IsInCombat())
m_cooldownTime = time(NULL) + goInfo->trap.startDelay;
SetLootState(GO_READY);
break;
}
2013-10-16 18:37:29 +02:00
case GAMEOBJECT_TYPE_TRANSPORT:
{
if (!m_goValue.Transport.AnimationInfo)
break;
if (GetGoState() == GO_STATE_TRANSPORT_ACTIVE)
2013-10-16 18:37:29 +02:00
{
m_goValue.Transport.PathProgress += diff;
/* TODO: Fix movement in unloaded grid - currently GO will just disappear
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;
G3D::Quat rotation = m_goValue.Transport.AnimationInfo->GetAnimRotation(timer);
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());
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());
}
*/
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)
{
ForceValuesUpdateAtIndex(GAMEOBJECT_LEVEL);
ForceValuesUpdateAtIndex(GAMEOBJECT_BYTES_1);
}
}
2013-10-16 18:37:29 +02:00
}
break;
}
case GAMEOBJECT_TYPE_FISHINGNODE:
{
// fishing code (bobber ready)
2010-04-07 22:59:46 +02:00
if (time(NULL) > m_respawnTime - FISHING_BOBBER_READY_TIME)
{
// splash bobber (bobber ready now)
Unit* caster = GetOwner();
2010-04-07 19:14:10 +02:00
if (caster && caster->GetTypeId() == TYPEID_PLAYER)
{
SetGoState(GO_STATE_ACTIVE);
2008-12-12 11:21:28 -06:00
SetUInt32Value(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
2009-10-17 15:51:44 -07:00
UpdateData udata(caster->GetMapId());
WorldPacket packet;
2011-04-29 20:47:02 +02:00
BuildValuesUpdateBlockForPlayer(&udata, caster->ToPlayer());
udata.BuildPacket(&packet);
caster->ToPlayer()->SendDirectMessage(&packet);
2009-10-17 15:51:44 -07:00
SendCustomAnim(GetGoAnimProgress());
}
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
}
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
{
2010-12-24 18:55:50 +01:00
time_t now = time(NULL);
if (m_respawnTime <= now) // timer expired
{
ObjectGuid dbtableHighGuid(HIGHGUID_GAMEOBJECT, GetEntry(), m_DBTableGuid);
time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
2010-12-24 18:55:50 +01:00
if (linkedRespawntime) // Can't respawn, the master is dead
{
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
2010-12-24 18:55:50 +01:00
SetRespawnTime(DAY);
else
m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
2010-12-24 18:55:50 +01:00
SaveRespawnTime(); // also save to DB immediately
return;
}
m_respawnTime = 0;
m_SkillupList.clear();
m_usetimes = 0;
2009-10-17 15:51:44 -07:00
switch (GetGoType())
{
case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
{
Unit* caster = GetOwner();
2010-04-07 19:14:10 +02:00
if (caster && caster->GetTypeId() == TYPEID_PLAYER)
{
caster->ToPlayer()->RemoveGameObject(this, false);
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
WorldPacket data(SMSG_FISH_ESCAPED, 0);
caster->ToPlayer()->SendDirectMessage(&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)
if (GetGoState() != GO_STATE_READY)
ResetDoorOrButton();
2013-03-02 23:59:42 +01:00
break;
case GAMEOBJECT_TYPE_FISHINGHOLE:
// Initialize a new max fish count on respawn
m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishinghole.minSuccessOpens, GetGOInfo()->fishinghole.maxSuccessOpens);
break;
default:
break;
}
2013-03-02 23:59:42 +01:00
// Despawn timer
if (!m_spawnedByDefault)
2013-03-02 23:59:42 +01:00
{
// Can be despawned or destroyed
2013-03-02 23:59:42 +01:00
SetLootState(GO_JUST_DEACTIVATED);
return;
}
// Respawn timer
2013-03-02 23:59:42 +01:00
uint32 poolid = GetDBTableGUIDLow() ? sPoolMgr->IsPartOfAPool<GameObject>(GetDBTableGUIDLow()) : 0;
if (poolid)
sPoolMgr->UpdatePool<GameObject>(poolid, GetDBTableGUIDLow());
else
GetMap()->AddToMap(this);
}
}
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (isSpawned())
{
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)
{
2010-04-07 19:14:10 +02:00
if (m_cooldownTime >= time(NULL))
break;
2009-10-17 15:51:44 -07:00
// Type 2 (bomb) does not need to be triggered by a unit and despawns after casting its spell.
if (goInfo->trap.type == 2)
2010-02-14 19:13:14 -07:00
{
SetLootState(GO_ACTIVATED);
2010-02-14 19:13:14 -07:00
break;
}
2009-10-17 15:51:44 -07: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;
if (!goInfo->trap.diameter)
{
// Battleground traps: data2 == 0 && data5 == 3
if (goInfo->trap.cooldown != 3)
break;
radius = 3.f;
}
else
radius = goInfo->trap.diameter / 2.f;
2009-10-17 15:51:44 -07:00
// Pointer to appropriate target if found any
Unit* target = NULL;
/// @todo this hack with search required until GO casting not implemented
if (Unit* owner = GetOwner())
{
// Hunter trap: Search units which are unfriendly to the trap's owner
Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck checker(this, owner, radius);
Trinity::UnitSearcher<Trinity::AnyUnfriendlyNoTotemUnitInObjectRangeCheck> searcher(this, target, checker);
VisitNearbyGridObject(radius, searcher);
if (!target)
VisitNearbyWorldObject(radius, searcher);
}
else
{
// Environmental trap: Any player
Player* player = NULL;
2009-12-20 15:20:04 +01:00
Trinity::AnyPlayerInObjectRangeCheck checker(this, radius);
Trinity::PlayerSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(this, player, checker);
VisitNearbyWorldObject(radius, searcher);
target = player;
}
2009-10-17 15:51:44 -07:00
if (target)
SetLootState(GO_ACTIVATED, target);
2009-10-17 15:51:44 -07:00
}
2010-04-07 19:14:10 +02:00
else if (uint32 max_charges = goInfo->GetCharges())
{
if (m_usetimes >= max_charges)
{
m_usetimes = 0;
SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
}
}
}
2009-10-17 15:51:44 -07:00
break;
}
case GO_ACTIVATED:
{
2011-09-29 12:43:05 +02:00
switch (GetGoType())
{
case GAMEOBJECT_TYPE_DOOR:
case GAMEOBJECT_TYPE_BUTTON:
2014-04-28 22:26:43 +02:00
if (m_cooldownTime && (m_cooldownTime < time(NULL)))
ResetDoorOrButton();
break;
case GAMEOBJECT_TYPE_GOOBER:
if (m_cooldownTime < time(NULL))
{
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
SetLootState(GO_JUST_DEACTIVATED);
m_cooldownTime = 0;
}
break;
case GAMEOBJECT_TYPE_CHEST:
if (m_groupLootTimer)
{
if (m_groupLootTimer <= diff)
{
Group* group = sGroupMgr->GetGroupByGUID(lootingGroupLowGUID);
if (group)
group->EndRoll(&loot);
m_groupLootTimer = 0;
2010-08-02 17:28:47 +02:00
lootingGroupLowGUID = 0;
}
else m_groupLootTimer -= diff;
}
break;
case GAMEOBJECT_TYPE_TRAP:
{
GameObjectTemplate const* goInfo = GetGOInfo();
if (goInfo->trap.type == 2 && goInfo->trap.spellId)
{
/// @todo NULL target won't work for target type 1
CastSpell(NULL, goInfo->trap.spellId);
SetLootState(GO_JUST_DEACTIVATED);
}
else if (Unit* target = ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID))
{
// Some traps do not have a spell but should be triggered
if (goInfo->trap.spellId)
CastSpell(target, goInfo->trap.spellId);
// Template value or 4 seconds
m_cooldownTime = time(NULL) + (goInfo->trap.cooldown ? goInfo->trap.cooldown : uint32(4));
if (goInfo->trap.type == 1)
SetLootState(GO_JUST_DEACTIVATED);
else if (!goInfo->trap.type)
SetLootState(GO_READY);
// Battleground gameobjects have data2 == 0 && data5 == 3
if (!goInfo->trap.diameter && goInfo->trap.cooldown == 3)
if (Player* player = target->ToPlayer())
if (Battleground* bg = player->GetBattleground())
bg->HandleTriggerBuff(GetGUID());
}
break;
}
default:
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;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (spellId)
{
for (GuidSet::const_iterator it = m_unique_users.begin(); it != m_unique_users.end(); ++it)
// m_unique_users can contain only player GUIDs
if (Player* owner = ObjectAccessor::GetPlayer(*this, *it))
owner->CastSpell(owner, spellId, false);
2009-10-17 15:51:44 -07:00
m_unique_users.clear();
m_usetimes = 0;
}
SetGoState(GO_STATE_READY);
//any return here in case battleground traps
if (GetGOInfo()->flags & GO_FLAG_NODESPAWN)
return;
}
2009-10-17 15:51:44 -07:00
loot.clear();
//! If this is summoned by a spell with ie. SPELL_EFFECT_SUMMON_OBJECT_WILD, with or without owner, we check respawn criteria based on spell
//! The GetOwnerGUID() check is mostly for compatibility with hacky scripts - 99% of the time summoning should be done trough spells.
if (GetSpellId() || GetOwnerGUID())
{
SetRespawnTime(0);
Delete();
return;
}
2009-10-17 15:51:44 -07:00
SetLootState(GO_READY);
//burning flags in some battlegrounds, if you find better condition, just add it
if (GetGOInfo()->IsDespawnAtAction() || GetGoAnimProgress() > 0)
{
SendObjectDeSpawnAnim(GetGUID());
//reset flags
SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
}
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (!m_respawnDelayTime)
return;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (!m_spawnedByDefault)
{
m_respawnTime = 0;
UpdateObjectVisibility();
return;
}
2009-10-17 15:51:44 -07:00
m_respawnTime = time(NULL) + m_respawnDelayTime;
2009-10-17 15:51:44 -07:00
// if option not set then object will be saved at grid unload
if (sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY))
SaveRespawnTime();
2009-10-17 15:51:44 -07:00
UpdateObjectVisibility();
2009-10-17 15:51:44 -07:00
break;
}
}
sScriptMgr->OnGameObjectUpdate(this, diff);
}
2009-10-17 15:51:44 -07:00
void GameObject::Refresh()
{
// 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)
return;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (isSpawned())
GetMap()->AddToMap(this);
}
2009-10-17 15:51:44 -07:00
void GameObject::AddUniqueUse(Player* player)
{
AddUse();
m_unique_users.insert(player->GetGUID());
}
2009-10-17 15:51:44 -07:00
void GameObject::Delete()
{
SetLootState(GO_NOT_READY);
2011-10-31 10:46:39 -04:00
RemoveFromOwner();
2009-10-17 15:51:44 -07:00
SendObjectDeSpawnAnim(GetGUID());
2009-10-17 15:51:44 -07:00
SetGoState(GO_STATE_READY);
SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
2009-10-17 15:51:44 -07:00
uint32 poolid = GetDBTableGUIDLow() ? sPoolMgr->IsPartOfAPool<GameObject>(GetDBTableGUIDLow()) : 0;
if (poolid)
sPoolMgr->UpdatePool<GameObject>(poolid, GetDBTableGUIDLow());
else
AddObjectToRemoveList();
}
2009-10-17 15:51:44 -07:00
void GameObject::getFishLoot(Loot* fishloot, Player* loot_owner)
{
fishloot->clear();
2009-10-17 15:51:44 -07:00
uint32 zone, subzone;
uint32 defaultzone = 1;
2011-04-29 20:47:02 +02:00
GetZoneAndAreaId(zone, subzone);
2009-10-17 15:51:44 -07:00
// if subzone loot exist use it
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);
//use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks.
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);
}
}
2009-10-17 15:51:44 -07: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
GameObjectData const* data = sObjectMgr->GetGOData(m_DBTableGuid);
2010-04-07 19:14:10 +02:00
if (!data)
{
TC_LOG_ERROR("misc", "GameObject::SaveToDB failed, cannot get gameobject data!");
return;
}
2009-10-17 15:51:44 -07:00
SaveToDB(GetMapId(), data->spawnMask, data->phaseMask);
}
2009-10-17 15:51:44 -07:00
void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
{
const GameObjectTemplate* goI = GetGOInfo();
2009-10-17 15:51:44 -07:00
if (!goI)
return;
2009-10-17 15:51:44 -07:00
if (!m_DBTableGuid)
m_DBTableGuid = GetGUIDLow();
// update in loaded data (changing data only in this place)
GameObjectData& data = sObjectMgr->NewGOData(m_DBTableGuid);
2009-10-17 15:51:44 -07:00
// data->guid = guid must not be updated at save
data.id = GetEntry();
data.mapid = mapid;
data.phaseMask = phaseMask;
2009-06-11 00:45:59 -05:00
data.posX = GetPositionX();
data.posY = GetPositionY();
data.posZ = GetPositionZ();
data.orientation = GetOrientation();
data.rotation0 = GetFloatValue(GAMEOBJECT_PARENTROTATION+0);
data.rotation1 = GetFloatValue(GAMEOBJECT_PARENTROTATION+1);
data.rotation2 = GetFloatValue(GAMEOBJECT_PARENTROTATION+2);
data.rotation3 = GetFloatValue(GAMEOBJECT_PARENTROTATION+3);
data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
data.animprogress = GetGoAnimProgress();
data.go_state = GetGoState();
data.spawnMask = spawnMask;
data.artKit = GetGoArtKit();
2009-10-17 15:51:44 -07:00
// Update in DB
2010-08-21 03:19:25 +02:00
SQLTransaction trans = WorldDatabase.BeginTransaction();
uint8 index = 0;
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
stmt->setUInt32(0, m_DBTableGuid);
trans->Append(stmt);
stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAMEOBJECT);
stmt->setUInt32(index++, m_DBTableGuid);
stmt->setUInt32(index++, GetEntry());
stmt->setUInt16(index++, uint16(mapid));
stmt->setUInt8(index++, spawnMask);
stmt->setUInt32(index++, GetPhaseMask());
stmt->setFloat(index++, GetPositionX());
stmt->setFloat(index++, GetPositionY());
stmt->setFloat(index++, GetPositionZ());
stmt->setFloat(index++, GetOrientation());
stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION));
stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION+1));
stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION+2));
stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION+3));
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);
}
2009-10-17 15:51:44 -07:00
bool GameObject::LoadGameObjectFromDB(uint32 guid, Map* map, bool addToMap)
{
GameObjectData const* data = sObjectMgr->GetGOData(guid);
2009-10-17 15:51:44 -07:00
2010-04-07 22:59:46 +02:00
if (!data)
{
TC_LOG_ERROR("sql.sql", "Gameobject (GUID: %u) not found in table `gameobject`, can't load. ", guid);
return false;
}
2009-10-17 15:51:44 -07:00
uint32 entry = data->id;
//uint32 map_id = data->mapid; // already used before call
uint32 phaseMask = data->phaseMask;
float x = data->posX;
float y = data->posY;
float z = data->posZ;
float ang = data->orientation;
2009-10-17 15:51:44 -07:00
float rotation0 = data->rotation0;
float rotation1 = data->rotation1;
float rotation2 = data->rotation2;
float rotation3 = data->rotation3;
2009-10-17 15:51:44 -07:00
uint32 animprogress = data->animprogress;
GOState go_state = data->go_state;
uint32 artKit = data->artKit;
2009-10-17 15:51:44 -07:00
m_DBTableGuid = guid;
if (map->GetInstanceId() != 0) guid = sObjectMgr->GenerateLowGuid(HIGHGUID_GAMEOBJECT);
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
if (!Create(guid, entry, map, phaseMask, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, artKit))
return false;
2009-10-17 15:51:44 -07:00
if (data->phaseid)
SetInPhase(data->phaseid, false, true);
2014-07-11 10:22:27 +02:00
if (data->phaseGroup)
{
// Set the gameobject in all the phases of the phasegroup
for (auto ph : GetPhasesForGroup(data->phaseGroup))
SetInPhase(ph, false, true);
}
2010-04-07 19:14:10 +02:00
if (data->spawntimesecs >= 0)
{
m_spawnedByDefault = true;
2009-10-17 16:20:24 -07:00
2010-04-07 19:14:10 +02:00
if (!GetGOInfo()->GetDespawnPossibility() && !GetGOInfo()->IsDespawnAtAction())
{
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
m_respawnDelayTime = 0;
m_respawnTime = 0;
}
else
{
m_respawnDelayTime = data->spawntimesecs;
m_respawnTime = GetMap()->GetGORespawnTime(m_DBTableGuid);
2009-10-17 15:51:44 -07:00
// ready to respawn
2010-04-07 19:14:10 +02:00
if (m_respawnTime && m_respawnTime <= time(NULL))
{
m_respawnTime = 0;
GetMap()->RemoveGORespawnTime(m_DBTableGuid);
}
}
}
else
{
m_spawnedByDefault = false;
m_respawnDelayTime = -data->spawntimesecs;
m_respawnTime = 0;
}
2009-10-17 15:51:44 -07:00
m_goData = data;
2009-10-17 15:51:44 -07:00
if (addToMap && !GetMap()->AddToMap(this))
return false;
return true;
}
2009-10-17 15:51:44 -07:00
void GameObject::DeleteFromDB()
{
GetMap()->RemoveGORespawnTime(m_DBTableGuid);
sObjectMgr->DeleteGOData(m_DBTableGuid);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
stmt->setUInt32(0, m_DBTableGuid);
WorldDatabase.Execute(stmt);
stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_EVENT_GAMEOBJECT);
stmt->setUInt32(0, m_DBTableGuid);
WorldDatabase.Execute(stmt);
}
2009-10-17 15:51:44 -07:00
/*********************************************************/
/*** QUEST SYSTEM ***/
/*********************************************************/
bool GameObject::hasQuest(uint32 quest_id) const
{
QuestRelationBounds qr = sObjectMgr->GetGOQuestRelationBounds(GetEntry());
for (QuestRelations::const_iterator itr = qr.first; itr != qr.second; ++itr)
{
2010-04-07 23:25:02 +02:00
if (itr->second == quest_id)
return true;
}
return false;
}
2009-10-17 15:51:44 -07:00
bool GameObject::hasInvolvedQuest(uint32 quest_id) const
{
QuestRelationBounds qir = sObjectMgr->GetGOQuestInvolvedRelationBounds(GetEntry());
for (QuestRelations::const_iterator itr = qir.first; itr != qir.second; ++itr)
{
2010-04-07 23:25:02 +02:00
if (itr->second == quest_id)
return true;
}
return false;
}
2009-10-17 15:51:44 -07: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();
if (!gInfo)
return false;
return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT;
}
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();
if (!gInfo)
return false;
return gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT || (gInfo->type == GAMEOBJECT_TYPE_TRANSPORT && m_goValue.Transport.StopFrames->empty());
2009-11-27 01:41:26 +01:00
}
bool GameObject::IsDestructibleBuilding() const
{
GameObjectTemplate const* gInfo = GetGOInfo();
if (!gInfo)
return false;
return gInfo->type == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING;
}
Unit* GameObject::GetOwner() const
{
return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
}
2009-10-17 15:51:44 -07:00
void GameObject::SaveRespawnTime()
{
2010-04-07 19:14:10 +02:00
if (m_goData && m_goData->dbData && m_respawnTime > time(NULL) && m_spawnedByDefault)
GetMap()->SaveGORespawnTime(m_DBTableGuid, m_respawnTime);
}
2009-10-17 15:51:44 -07:00
bool GameObject::IsNeverVisible() const
{
if (WorldObject::IsNeverVisible())
return true;
if (GetGoType() == GAMEOBJECT_TYPE_SPELL_FOCUS && GetGOInfo()->spellFocus.serverOnly == 1)
return true;
return false;
}
2011-10-13 14:02:07 -04:00
bool GameObject::IsAlwaysVisibleFor(WorldObject const* seer) const
{
2011-10-13 14:02:07 -04:00
if (WorldObject::IsAlwaysVisibleFor(seer))
return true;
2009-10-17 15:51:44 -07:00
if (IsTransport() || IsDestructibleBuilding())
2010-11-13 17:18:09 +01:00
return true;
2012-01-08 18:22:46 +01:00
if (!seer)
return false;
// Always seen by owner and friendly units
if (ObjectGuid guid = GetOwnerGUID())
2012-01-08 18:22:46 +01:00
{
if (seer->GetGUID() == guid)
return true;
Unit* owner = GetOwner();
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;
}
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
void GameObject::Respawn()
{
2010-04-07 19:14:10 +02:00
if (m_spawnedByDefault && m_respawnTime > 0)
{
m_respawnTime = time(NULL);
GetMap()->RemoveGORespawnTime(m_DBTableGuid);
}
}
2009-10-17 15:51:44 -07:00
2011-10-07 19:45:43 -05:00
bool GameObject::ActivateToQuest(Player* target) const
{
2011-10-07 19:45:43 -05:00
if (target->HasQuestForGO(GetEntry()))
return true;
if (!sObjectMgr->IsGameObjectForQuests(GetEntry()))
return false;
2009-10-17 15:51:44 -07:00
switch (GetGoType())
{
case GAMEOBJECT_TYPE_QUESTGIVER:
{
GameObject* go = const_cast<GameObject*>(this);
QuestGiverStatus questStatus = target->GetQuestDialogStatus(go);
if (questStatus > DIALOG_STATUS_UNAVAILABLE)
return true;
break;
}
case GAMEOBJECT_TYPE_CHEST:
{
// scan GO chest with loot including quest items
2011-10-07 19:45:43 -05:00
if (LootTemplates_Gameobject.HaveQuestLootForPlayer(GetGOInfo()->GetLootId(), target))
{
if (Battleground const* bg = target->GetBattleground())
return bg->CanActivateGO(GetEntry(), target->GetTeam());
return true;
}
break;
}
case GAMEOBJECT_TYPE_GENERIC:
{
2011-10-07 19:45:43 -05:00
if (GetGOInfo()->_generic.questID == -1 || target->GetQuestStatus(GetGOInfo()->_generic.questID) == QUEST_STATUS_INCOMPLETE)
return true;
break;
}
case GAMEOBJECT_TYPE_GOOBER:
{
2011-10-07 19:45:43 -05:00
if (GetGOInfo()->goober.questId == -1 || target->GetQuestStatus(GetGOInfo()->goober.questId) == QUEST_STATUS_INCOMPLETE)
return true;
break;
}
default:
break;
}
2009-10-17 15:51:44 -07:00
return false;
}
2009-10-17 15:51:44 -07:00
void GameObject::TriggeringLinkedGameObject(uint32 trapEntry, Unit* target)
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(trapEntry);
if (!trapInfo || trapInfo->type != GAMEOBJECT_TYPE_TRAP)
return;
2009-10-17 15:51:44 -07:00
SpellInfo const* trapSpell = sSpellMgr->GetSpellInfo(trapInfo->trap.spellId);
2010-04-07 19:14:10 +02:00
if (!trapSpell) // checked at load already
return;
2009-10-17 15:51:44 -07:00
float range = float(target->GetSpellMaxRangeForTarget(GetOwner(), trapSpell));
2009-10-17 15:51:44 -07:00
// search nearest linked GO
GameObject* trapGO = NULL;
{
// using original GO distance
2011-10-18 10:53:34 -04:00
CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY()));
Cell cell(p);
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
Trinity::NearestGameObjectEntryInObjectRangeCheck go_check(*target, trapEntry, range);
Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> checker(this, trapGO, go_check);
2009-10-17 15:51:44 -07:00
2008-10-14 11:57:03 -05:00
TypeContainerVisitor<Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
cell.Visit(p, object_checker, *GetMap(), *target, range);
}
2009-10-17 15:51:44 -07:00
// found correct GO
2010-04-07 19:14:10 +02:00
if (trapGO)
2010-04-30 13:09:38 +02:00
trapGO->CastSpell(target, trapInfo->trap.spellId);
}
2009-10-17 15:51:44 -07:00
GameObject* GameObject::LookupFishingHoleAround(float range)
{
GameObject* ok = NULL;
2009-10-17 15:51:44 -07:00
2011-10-18 10:53:34 -04:00
CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY()));
Cell cell(p);
2009-12-20 15:20:04 +01:00
Trinity::NearestGameObjectFishingHole u_check(*this, range);
Trinity::GameObjectSearcher<Trinity::NearestGameObjectFishingHole> checker(this, ok, u_check);
2009-10-17 15:51:44 -07:00
2008-10-14 11:57:03 -05:00
TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker);
cell.Visit(p, grid_object_checker, *GetMap(), *this, range);
2009-10-17 15:51:44 -07:00
return ok;
}
2009-10-17 15:51:44 -07:00
void GameObject::ResetDoorOrButton()
{
if (m_lootState == GO_READY || m_lootState == GO_JUST_DEACTIVATED)
return;
2009-10-17 15:51:44 -07:00
SwitchDoorOrButton(false);
SetLootState(GO_JUST_DEACTIVATED);
m_cooldownTime = 0;
}
2009-10-17 15:51:44 -07:00
void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */, Unit* user /*=NULL*/)
{
2010-04-07 19:14:10 +02:00
if (m_lootState != GO_READY)
return;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (!time_to_restore)
time_to_restore = GetGOInfo()->GetAutoCloseTime();
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
SwitchDoorOrButton(true, alternative);
SetLootState(GO_ACTIVATED, user);
2009-10-17 15:51:44 -07:00
m_cooldownTime = time_to_restore ? (time(NULL) + time_to_restore) : 0;
}
2009-10-17 15:51:44 -07:00
void GameObject::SetGoArtKit(uint8 kit)
{
SetByteValue(GAMEOBJECT_BYTES_1, 2, kit);
GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGOData(m_DBTableGuid));
2010-04-07 19:14:10 +02:00
if (data)
data->artKit = kit;
}
2009-10-17 15:51:44 -07:00
void GameObject::SetGoArtKit(uint8 artkit, GameObject* go, uint32 lowguid)
{
const GameObjectData* data = NULL;
2010-04-07 19:14:10 +02:00
if (go)
{
go->SetGoArtKit(artkit);
data = go->GetGOData();
}
2010-04-07 19:14:10 +02:00
else if (lowguid)
data = sObjectMgr->GetGOData(lowguid);
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (data)
const_cast<GameObjectData*>(data)->artKit = artkit;
}
2009-10-17 15:51:44 -07:00
void GameObject::SwitchDoorOrButton(bool activate, bool alternative /* = false */)
{
2010-04-07 19:14:10 +02:00
if (activate)
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
else
RemoveFlag(GAMEOBJECT_FLAGS, 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
SetGoState(alternative ? GO_STATE_ACTIVE_ALTERNATIVE : GO_STATE_ACTIVE);
else //if open -> close
SetGoState(GO_STATE_READY);
}
2009-10-17 15:51:44 -07:00
void GameObject::Use(Unit* user)
{
// by default spell caster is user
Unit* spellCaster = user;
uint32 spellId = 0;
bool triggered = false;
2009-10-17 15:51:44 -07:00
if (Player* playerUser = user->ToPlayer())
{
if (sScriptMgr->OnGossipHello(playerUser, this))
return;
if (AI()->GossipHello(playerUser))
return;
}
// If cooldown data present in template
if (uint32 cooldown = GetGOInfo()->GetCooldown())
{
if (m_cooldownTime > sWorld->GetGameTime())
return;
m_cooldownTime = sWorld->GetGameTime() + cooldown;
}
switch (GetGoType())
{
case GAMEOBJECT_TYPE_DOOR: //0
case GAMEOBJECT_TYPE_BUTTON: //1
//doors/buttons never really despawn, only reset to default state/flags
UseDoorOrButton(0, false, user);
return;
case GAMEOBJECT_TYPE_QUESTGIVER: //2
{
if (user->GetTypeId() != TYPEID_PLAYER)
return;
2009-10-17 15:51:44 -07:00
Player* player = user->ToPlayer();
2009-10-17 15:51:44 -07:00
player->PrepareGossipMenu(this, GetGOInfo()->questgiver.gossipID, true);
player->SendPreparedGossip(this);
return;
}
2012-10-20 21:56:48 +02:00
case GAMEOBJECT_TYPE_TRAP: //6
{
GameObjectTemplate const* goInfo = GetGOInfo();
if (goInfo->trap.spellId)
CastSpell(user, goInfo->trap.spellId);
m_cooldownTime = time(NULL) + (goInfo->trap.cooldown ? goInfo->trap.cooldown : uint32(4)); // template or 4 seconds
if (goInfo->trap.type == 1) // Deactivate after trigger
SetLootState(GO_JUST_DEACTIVATED);
return;
}
//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)
return;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (user->GetTypeId() != TYPEID_PLAYER)
return;
2009-10-17 15:51:44 -07: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
{
if (info->chair.slots > 0) // sometimes chairs in DB have error in fields and we dont know number of slots
for (uint32 i = 0; i < info->chair.slots; ++i)
ChairListSlots[i].Clear(); // Last user of current slot set to 0 (none sit here yet)
else
ChairListSlots[0].Clear(); // error in DB, make one default slot
2010-08-23 07:51:19 +02:00
}
Player* player = user->ToPlayer();
2009-10-17 15:51:44 -07: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
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;
// find nearest slot
bool found_free_slot = false;
for (ChairSlotAndUser::iterator itr = ChairListSlots.begin(); itr != ChairListSlots.end(); ++itr)
{
// the distance between this slot and the center of the go - imagine a 1D space
float relativeDistance = (info->size*itr->first)-(info->size*(info->chair.slots-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
2010-04-07 19:14:10 +02:00
if (itr->second)
2010-08-23 07:51:19 +02:00
{
if (Player* ChairUser = ObjectAccessor::FindPlayer(itr->second))
2013-10-16 18:37:29 +02:00
{
2010-04-07 19:14:10 +02: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.
else
itr->second.Clear(); // This seat is unoccupied.
2013-10-16 18:37:29 +02:00
}
else
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
found_free_slot = true;
2009-10-17 15:51:44 -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)
{
nearest_slot = itr->first;
lowestDist = thisDistance;
x_lowest = x_i;
y_lowest = y_i;
}
}
2010-04-07 19:14:10 +02:00
if (found_free_slot)
{
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);
player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR+info->chair.height);
return;
}
}
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())
{
if (info->goober.pageId) // show page...
{
WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8);
data << GetGUID();
player->SendDirectMessage(&data);
}
else if (info->goober.gossipID)
{
player->PrepareGossipMenu(this, info->goober.gossipID);
player->SendPreparedGossip(this);
}
2009-10-17 15:51:44 -07:00
if (info->goober.eventId)
{
TC_LOG_DEBUG("maps.script", "Goober ScriptStart id %u for GO entry %u (GUID %u).", info->goober.eventId, GetEntry(), GetDBTableGUIDLow());
GetMap()->ScriptsStart(sEventScripts, info->goober.eventId, player, this);
2014-04-26 03:40:30 +02:00
EventInform(info->goober.eventId, user);
}
// possible quest objective for active quests
if (info->goober.questId && sObjectMgr->GetQuestTemplate(info->goober.questId))
{
//Quest require to be active for GO using
if (player->GetQuestStatus(info->goober.questId) != QUEST_STATUS_INCOMPLETE)
break;
}
player->KillCreditGO(info->entry, GetGUID());
}
2009-10-17 15:51:44 -07:00
if (uint32 trapEntry = info->goober.linkedTrapId)
TriggeringLinkedGameObject(trapEntry, user);
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
SetLootState(GO_ACTIVATED, user);
// this appear to be ok, however others exist in addition to this that should have custom (ex: 190510, 188692, 187389)
if (info->goober.customAnim)
SendCustomAnim(GetGoAnimProgress());
else
SetGoState(GO_STATE_ACTIVE);
m_cooldownTime = time(NULL) + info->GetAutoCloseTime();
// cast this spell later if provided
spellId = info->goober.spellId;
2010-04-01 20:08:22 -02:30
spellCaster = NULL;
2009-10-17 15:51:44 -07: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)
return;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (user->GetTypeId() != TYPEID_PLAYER)
return;
2009-10-17 15:51:44 -07:00
Player* player = user->ToPlayer();
2009-10-17 15:51:44 -07:00
if (info->camera.cinematicId)
player->SendCinematicStart(info->camera.cinematicId);
2009-10-17 15:51:44 -07:00
if (info->camera.eventID)
GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this);
2009-10-17 15:51:44 -07:00
return;
}
//fishing bobber
case GAMEOBJECT_TYPE_FISHINGNODE: //17
{
Player* player = user->ToPlayer();
if (!player)
return;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (player->GetGUID() != GetOwnerGUID())
return;
2009-10-17 15:51:44 -07:00
switch (getLootState())
{
case GO_READY: // ready for loot
{
uint32 zone, subzone;
2011-04-29 20:47:02 +02:00
GetZoneAndAreaId(zone, subzone);
2009-10-17 15:51:44 -07:00
int32 zone_skill = sObjectMgr->GetFishingBaseSkillLevel(subzone);
2010-04-07 19:14:10 +02:00
if (!zone_skill)
zone_skill = sObjectMgr->GetFishingBaseSkillLevel(zone);
2009-10-17 15:51:44 -07:00
//provide error, no fishable zone or area should be 0
2010-04-07 19:14:10 +02:00
if (!zone_skill)
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
int32 skill = player->GetSkillValue(SKILL_FISHING);
int32 chance;
if (skill < zone_skill)
{
2011-04-29 20:47:02 +02:00
chance = int32(pow((double)skill/zone_skill, 2) * 100);
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
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
// but you will likely cause junk in areas that require a high fishing skill (not yet implemented)
if (chance >= roll)
{
player->UpdateFishingSkill();
2009-10-17 15:51:44 -07:00
/// @todo I do not understand this hack. Need some explanation.
// prevent removing GO at spell cancel
2011-10-31 10:46:39 -04:00
RemoveFromOwner();
SetOwnerGUID(player->GetGUID());
SetSpellId(0); // prevent removing unintended auras at Unit::RemoveGameObject
/// @todo find reasonable value for fishing hole search
GameObject* ok = LookupFishingHoleAround(20.0f + CONTACT_DISTANCE);
if (ok)
{
ok->Use(player);
SetLootState(GO_JUST_DEACTIVATED);
}
else
2011-04-29 20:47:02 +02:00
player->SendLoot(GetGUID(), LOOT_FISHING);
}
else // else: junk
player->SendLoot(GetGUID(), LOOT_FISHING_JUNK);
break;
}
case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update
break;
default:
{
SetLootState(GO_JUST_DEACTIVATED);
2009-10-17 15:51:44 -07:00
WorldPacket data(SMSG_FISH_NOT_HOOKED, 0);
player->SendDirectMessage(&data);
break;
}
}
2009-10-17 15:51:44 -07:00
player->FinishSpell(CURRENT_CHANNELED_SPELL);
return;
}
2009-10-17 15:51:44 -07:00
case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
{
2010-04-07 19:14:10 +02:00
if (user->GetTypeId() != TYPEID_PLAYER)
return;
2009-10-17 15:51:44 -07:00
Player* player = user->ToPlayer();
2009-10-17 15:51:44 -07: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
Player* m_ritualOwner = NULL;
if (m_ritualOwnerGUID)
m_ritualOwner = ObjectAccessor::FindPlayer(m_ritualOwnerGUID);
// ritual owner is set for GO's without owner (not summoned)
if (!m_ritualOwner && !owner)
{
m_ritualOwnerGUID = player->GetGUID();
m_ritualOwner = player;
}
2009-10-17 15:51:44 -07:00
if (owner)
{
if (owner->GetTypeId() != TYPEID_PLAYER)
return;
2009-10-17 15:51:44 -07:00
// accept only use by player from same group as owner, excluding owner itself (unique use already added in spell effect)
if (player == owner->ToPlayer() || (info->summoningRitual.castersGrouped && !player->IsInSameRaidWith(owner->ToPlayer())))
return;
2009-10-17 15:51:44 -07: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
// in case summoning ritual caster is GO creator
spellCaster = owner;
}
else
{
if (player != m_ritualOwner && (info->summoningRitual.castersGrouped && !player->IsInSameRaidWith(m_ritualOwner)))
return;
2009-10-17 15:51:44 -07:00
spellCaster = player;
}
AddUniqueUse(player);
2009-10-17 15:51:44 -07:00
if (info->summoningRitual.animSpell)
{
player->CastSpell(player, info->summoningRitual.animSpell, true);
// for this case, summoningRitual.spellId is always triggered
triggered = true;
}
2009-10-17 15:51:44 -07:00
// full amount unique participants including original summoner
if (GetUniqueUseCount() == info->summoningRitual.reqParticipants)
{
if (m_ritualOwner)
spellCaster = m_ritualOwner;
spellId = info->summoningRitual.spellId;
2009-10-17 15:51:44 -07:00
if (spellId == 62330) // GO store nonexistent spell, replace by expected
{
// spell have reagent and mana cost but it not expected use its
// it triggered spell in fact cast at currently channeled GO
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)
if (info->summoningRitual.casterTargetSpell && info->summoningRitual.casterTargetSpell != 1) // No idea why this field is a bool in some cases
for (uint32 i = 0; i < info->summoningRitual.casterTargetSpellTargets; i++)
// m_unique_users can contain only player GUIDs
if (Player* target = ObjectAccessor::GetPlayer(*this, Trinity::Containers::SelectRandomContainerElement(m_unique_users)))
2011-09-15 17:51:17 +01:00
spellCaster->CastSpell(target, info->summoningRitual.casterTargetSpell, true);
// finish owners spell
if (owner)
owner->FinishSpell(CURRENT_CHANNELED_SPELL);
// can be deleted now, if
if (!info->summoningRitual.ritualPersistent)
SetLootState(GO_JUST_DEACTIVATED);
else
{
// reset ritual for this GO
m_ritualOwnerGUID.Clear();
m_unique_users.clear();
m_usetimes = 0;
}
}
else
return;
2009-10-17 15:51:44 -07: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)
return;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (info->spellcaster.partyOnly)
{
Unit* caster = GetOwner();
2010-04-07 22:59:46 +02:00
if (!caster || caster->GetTypeId() != TYPEID_PLAYER)
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()))
return;
}
2009-10-17 15:51:44 -07:00
user->RemoveAurasByType(SPELL_AURA_MOUNTED);
spellId = info->spellcaster.spellId;
2009-10-17 15:51:44 -07: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)
return;
2009-10-17 15:51:44 -07:00
Player* player = user->ToPlayer();
2009-10-17 15:51:44 -07:00
Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetTarget());
2009-10-17 15:51:44 -07:00
// accept only use by player from same raid as caster, except caster itself
if (!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameRaidWith(player))
return;
2009-10-17 15:51:44 -07:00
//required lvl checks!
uint8 level = player->getLevel();
if (level < info->meetingstone.minLevel)
return;
level = targetPlayer->getLevel();
if (level < info->meetingstone.minLevel)
return;
2009-10-17 15:51:44 -07:00
2011-04-28 22:54:30 +02:00
if (info->entry == 194097)
spellId = 61994; // Ritual of Summoning
else
spellId = 59782; // Summoning Stone Effect
2009-10-17 15:51:44 -07:00
break;
}
2009-10-17 15:51:44 -07:00
case GAMEOBJECT_TYPE_FLAGSTAND: // 24
{
if (user->GetTypeId() != TYPEID_PLAYER)
return;
2009-10-17 15:51:44 -07: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))
{
// in battleground check
Battleground* bg = player->GetBattleground();
2010-04-07 19:14:10 +02:00
if (!bg)
return;
2012-10-17 16:56:35 +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);
// 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_FISHINGHOLE: // 25
{
if (user->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = user->ToPlayer();
player->SendLoot(GetGUID(), LOOT_FISHINGHOLE);
2011-04-28 22:54:30 +02:00
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT, GetGOInfo()->entry);
return;
}
case GAMEOBJECT_TYPE_FLAGDROP: // 26
{
if (user->GetTypeId() != TYPEID_PLAYER)
return;
2009-10-17 15:51:44 -07: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))
{
// in battleground check
2011-06-12 02:30:32 +02:00
Battleground* bg = player->GetBattleground();
if (!bg)
return;
2012-10-17 16:56:35 +02:00
2011-06-12 02:30:32 +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);
// 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();
if (info)
{
2011-09-29 12:43:05 +02:00
switch (info->entry)
{
case 179785: // Silverwing Flag
case 179786: // Warsong Flag
if (bg->GetTypeID(true) == BATTLEGROUND_WS)
bg->EventPlayerClickedOnFlag(player, this);
break;
case 184142: // Netherstorm Flag
if (bg->GetTypeID(true) == BATTLEGROUND_EY)
bg->EventPlayerClickedOnFlag(player, this);
break;
}
}
//this cause to call return, all flags must be deleted here!!
spellId = 0;
Delete();
}
break;
}
case GAMEOBJECT_TYPE_BARBER_CHAIR: //32
{
2011-04-28 22:54:30 +02:00
GameObjectTemplate const* info = GetGOInfo();
if (!info)
return;
2009-10-17 15:51:44 -07:00
if (user->GetTypeId() != TYPEID_PLAYER)
return;
2009-10-17 15:51:44 -07:00
Player* player = user->ToPlayer();
2009-10-17 15:51:44 -07:00
// fallback, will always work
2011-04-29 20:47:02 +02:00
player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
2009-10-17 15:51:44 -07:00
2009-07-08 14:29:40 +02:00
WorldPacket data(SMSG_ENABLE_BARBER_SHOP, 0);
player->SendDirectMessage(&data);
2009-10-17 15:51:44 -07:00
2009-01-31 14:41:36 -06:00
player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR+info->barberChair.chairheight);
return;
}
default:
if (GetGoType() >= MAX_GAMEOBJECT_TYPE)
TC_LOG_ERROR("misc", "GameObject::Use(): unit (type: %u, guid: %u, name: %s) tries to use object (guid: %u, entry: %u, name: %s) of unknown type (%u)",
user->GetTypeId(), user->GetGUIDLow(), user->GetName().c_str(), GetGUIDLow(), GetEntry(), GetGOInfo()->name.c_str(), GetGoType());
break;
}
2009-10-17 15:51:44 -07:00
if (!spellId)
return;
2009-10-17 15:51:44 -07:00
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
{
if (user->GetTypeId() != TYPEID_PLAYER || !sOutdoorPvPMgr->HandleCustomSpell(user->ToPlayer(), spellId, this))
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
TC_LOG_DEBUG("outdoorpvp", "WORLD: %u non-dbc spell was handled by OutdoorPvP", spellId);
return;
}
2009-10-17 15:51:44 -07:00
2010-04-01 20:08:22 -02:30
if (spellCaster)
spellCaster->CastSpell(user, spellInfo, triggered);
else
CastSpell(user, spellId);
}
2009-10-17 15:51:44 -07:00
void GameObject::CastSpell(Unit* target, uint32 spellId, bool triggered /*= true*/)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
return;
2009-10-17 15:51:44 -07:00
bool self = false;
2010-09-28 08:21:51 +03:00
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_CASTER)
{
self = true;
break;
}
}
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (self)
{
2010-04-07 19:14:10 +02:00
if (target)
target->CastSpell(target, spellInfo, triggered);
return;
}
2009-10-17 15:51:44 -07:00
//summon world trigger
Creature* trigger = SummonTrigger(GetPositionX(), GetPositionY(), GetPositionZ(), 0, spellInfo->CalcCastTime() + 100);
if (!trigger)
return;
2009-10-17 15:51:44 -07:00
if (Unit* owner = GetOwner())
{
trigger->setFaction(owner->getFaction());
// needed for GO casts for proper target validation checks
2012-11-26 11:11:30 +01:00
trigger->SetOwnerGUID(owner->GetGUID());
trigger->CastSpell(target ? target : trigger, spellInfo, triggered, nullptr, nullptr, owner->GetGUID());
}
else
{
trigger->setFaction(14);
// Set owner guid for target if no owner available - needed by trigger auras
// - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell())
trigger->CastSpell(target ? target : trigger, spellInfo, triggered, nullptr, nullptr, target ? target->GetGUID() : ObjectGuid::Empty);
}
2008-11-14 16:28:45 -06:00
}
2009-10-17 15:51:44 -07:00
void GameObject::SendCustomAnim(uint32 anim)
2009-04-30 12:01:08 -05:00
{
2011-04-29 20:47:02 +02:00
WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM, 8+4);
2009-04-30 12:01:08 -05:00
data << GetGUID();
data << uint32(anim);
2009-04-30 12:01:08 -05:00
SendMessageToSet(&data, true);
}
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
{
GameObjectDisplayInfoEntry const* info = sGameObjectDisplayInfoStore.LookupEntry(m_goInfo->displayId);
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);
//! 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);
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;
}
2009-10-17 15:51:44 -07:00
2014-04-26 03:40:30 +02:00
void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= NULL*/)
{
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-10-17 15:51:44 -07:00
2008-11-14 17:03:03 -06:00
// overwrite WorldObject function for proper name localization
std::string const & GameObject::GetNameForLocaleIdx(LocaleConstant loc_idx) const
2008-11-14 17:03:03 -06:00
{
2010-09-17 07:04:29 +02:00
if (loc_idx != DEFAULT_LOCALE)
2010-08-23 07:51:19 +02:00
{
uint8 uloc_idx = uint8(loc_idx);
if (GameObjectLocale const* cl = sObjectMgr->GetGameObjectLocale(GetEntry()))
2010-08-23 07:51:19 +02:00
if (cl->Name.size() > uloc_idx && !cl->Name[uloc_idx].empty())
return cl->Name[uloc_idx];
2010-08-23 07:51:19 +02:00
}
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
void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 /*=0.0f*/)
{
static double const atan_pow = atan(pow(2.0f, -20.0f));
2009-10-17 15:51:44 -07:00
2012-10-02 15:06:19 +02:00
double f_rot1 = std::sin(GetOrientation() / 2.0f);
double f_rot2 = std::cos(GetOrientation() / 2.0f);
2009-10-17 15:51:44 -07:00
int64 i_rot1 = int64(f_rot1 / atan_pow *(f_rot2 >= 0 ? 1.0f : -1.0f));
int64 rotation = (i_rot1 << 43 >> 43) & 0x00000000001FFFFF;
2009-10-17 15:51:44 -07:00
2012-10-02 15:06:19 +02:00
//float f_rot2 = std::sin(0.0f / 2.0f);
2009-06-11 00:45:59 -05:00
//int64 i_rot2 = f_rot2 / atan(pow(2.0f, -20.0f));
//rotation |= (((i_rot2 << 22) >> 32) >> 11) & 0x000003FFFFE00000;
2009-10-17 15:51:44 -07:00
2012-10-02 15:06:19 +02:00
//float f_rot3 = std::sin(0.0f / 2.0f);
2009-06-11 00:45:59 -05:00
//int64 i_rot3 = f_rot3 / atan(pow(2.0f, -21.0f));
//rotation |= (i_rot3 >> 42) & 0x7FFFFC0000000000;
2009-10-17 15:51:44 -07:00
2009-06-11 00:45:59 -05:00
m_rotation = rotation;
2009-10-17 15:51:44 -07:00
2010-04-07 19:14:10 +02:00
if (rotation2 == 0.0f && rotation3 == 0.0f)
{
rotation2 = (float)f_rot1;
rotation3 = (float)f_rot2;
}
2009-10-17 15:51:44 -07:00
SetFloatValue(GAMEOBJECT_PARENTROTATION+2, rotation2);
SetFloatValue(GAMEOBJECT_PARENTROTATION+3, rotation3);
}
void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, uint32 spellId /*= 0*/)
{
if (!m_goValue.Building.MaxHealth || !change)
return;
// prevent double destructions of the same object
if (change < 0 && !m_goValue.Building.Health)
return;
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;
else
m_goValue.Building.Health += change;
// Set the health bar, value = 255 * healthPct;
SetGoAnimProgress(m_goValue.Building.Health * 255 / m_goValue.Building.MaxHealth);
2014-04-26 03:40:30 +02:00
Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : NULL;
// dealing damage, send packet
if (player)
{
WorldPacket data(SMSG_DESTRUCTIBLE_BUILDING_DAMAGE, 8 + 8 + 8 + 4 + 4);
2014-09-13 01:07:21 +02:00
data << GetPackGUID();
data << attackerOrHealer->GetPackGUID();
data << player->GetPackGUID();
data << uint32(-change); // change < 0 triggers SPELL_BUILDING_HEAL combat log event
// change >= 0 triggers SPELL_BUILDING_DAMAGE event
data << uint32(spellId);
player->SendDirectMessage(&data);
}
GameObjectDestructibleState newState = GetDestructibleState();
if (!m_goValue.Building.Health)
newState = GO_DESTRUCTIBLE_DESTROYED;
else if (m_goValue.Building.Health <= GetGOInfo()->building.damagedNumHits)
newState = GO_DESTRUCTIBLE_DAMAGED;
else if (m_goValue.Building.Health == m_goValue.Building.MaxHealth)
newState = GO_DESTRUCTIBLE_INTACT;
if (newState == GetDestructibleState())
return;
/// @todo: pass attackerOrHealer instead of player
SetDestructibleState(newState, player, false);
}
void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker /*= NULL*/, bool setHealth /*= false*/)
{
// 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:
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED | GO_FLAG_DESTROYED);
SetDisplayId(m_goInfo->displayId);
if (setHealth)
{
m_goValue.Building.Health = m_goValue.Building.MaxHealth;
SetGoAnimProgress(255);
}
EnableCollision(true);
break;
case GO_DESTRUCTIBLE_DAMAGED:
{
2014-04-26 03:40:30 +02:00
EventInform(m_goInfo->building.damagedEvent, eventInvoker);
sScriptMgr->OnGameObjectDamaged(this, eventInvoker);
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED);
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED);
uint32 modelId = m_goInfo->building.damagedDisplayId;
if (DestructibleModelDataEntry const* modelData = sDestructibleModelDataStore.LookupEntry(m_goInfo->building.destructibleData))
if (modelData->DamagedDisplayId)
modelId = modelData->DamagedDisplayId;
SetDisplayId(modelId);
if (setHealth)
{
m_goValue.Building.Health = m_goInfo->building.damagedNumHits;
uint32 maxHealth = m_goValue.Building.MaxHealth;
// in this case current health is 0 anyway so just prevent crashing here
if (!maxHealth)
maxHealth = 1;
SetGoAnimProgress(m_goValue.Building.Health * 255 / maxHealth);
}
break;
}
case GO_DESTRUCTIBLE_DESTROYED:
{
sScriptMgr->OnGameObjectDestroyed(this, eventInvoker);
2014-04-26 03:40:30 +02:00
EventInform(m_goInfo->building.destroyedEvent, eventInvoker);
if (eventInvoker)
if (Battleground* bg = eventInvoker->GetBattleground())
bg->DestroyGate(eventInvoker, this);
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED);
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED);
uint32 modelId = m_goInfo->building.destroyedDisplayId;
if (DestructibleModelDataEntry const* modelData = sDestructibleModelDataStore.LookupEntry(m_goInfo->building.destructibleData))
if (modelData->DestroyedDisplayId)
modelId = modelData->DestroyedDisplayId;
SetDisplayId(modelId);
if (setHealth)
{
m_goValue.Building.Health = 0;
SetGoAnimProgress(0);
}
EnableCollision(false);
break;
}
case GO_DESTRUCTIBLE_REBUILDING:
{
2014-04-26 03:40:30 +02:00
EventInform(m_goInfo->building.rebuildingEvent, eventInvoker);
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED | GO_FLAG_DESTROYED);
uint32 modelId = m_goInfo->displayId;
if (DestructibleModelDataEntry const* modelData = sDestructibleModelDataStore.LookupEntry(m_goInfo->building.destructibleData))
if (modelData->RebuildingDisplayId)
modelId = modelData->RebuildingDisplayId;
SetDisplayId(modelId);
// restores to full health
if (setHealth)
{
m_goValue.Building.Health = m_goValue.Building.MaxHealth;
SetGoAnimProgress(255);
}
EnableCollision(true);
break;
}
}
}
void GameObject::SetLootState(LootState state, Unit* unit)
{
m_lootState = state;
if (unit)
m_lootStateUnitGUID = unit->GetGUID();
else
m_lootStateUnitGUID.Clear();
AI()->OnStateChanged(state, unit);
sScriptMgr->OnGameObjectLootStateChanged(this, state, unit);
if (GetGoType() == GAMEOBJECT_TYPE_DOOR) // only set collision for doors on SetGoState
return;
if (m_model)
{
bool collision = false;
// Use the current go state
if ((GetGoState() != GO_STATE_READY && (state == GO_ACTIVATED || state == GO_JUST_DEACTIVATED)) || state == GO_READY)
collision = !collision;
EnableCollision(collision);
}
}
void GameObject::SetGoState(GOState state)
{
SetByteValue(GAMEOBJECT_BYTES_1, 0, state);
sScriptMgr->OnGameObjectStateChanged(this, state);
2014-03-23 00:23:30 +01:00
if (m_model && !IsTransport())
{
if (!IsInWorld())
return;
// startOpen determines whether we are going to add or remove the LoS on activation
bool collision = false;
if (state == GO_STATE_READY)
2012-09-04 19:48:29 +01:00
collision = !collision;
EnableCollision(collision);
}
}
uint32 GameObject::GetTransportPeriod() const
{
ASSERT(GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT);
if (m_goValue.Transport.AnimationInfo)
return m_goValue.Transport.AnimationInfo->TotalTime;
// return something that will nicely divide for GAMEOBJECT_DYNAMIC value calculation
return m_goValue.Transport.PathProgress;
}
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;
m_goValue.Transport.PathProgress = getMSTime() + m_goValue.Transport.StopFrames->at(GetGoState() - GO_STATE_TRANSPORT_STOPPED);
SetGoState(GO_STATE_TRANSPORT_ACTIVE);
}
else
{
ASSERT(state < GO_STATE_TRANSPORT_STOPPED + MAX_GO_STATE_TRANSPORT_STOP_FRAMES);
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));
}
}
void GameObject::SetDisplayId(uint32 displayid)
{
SetUInt32Value(GAMEOBJECT_DISPLAYID, displayid);
UpdateModel();
}
void GameObject::SetInPhase(uint32 id, bool update, bool apply)
{
WorldObject::SetInPhase(id, update, apply);
if (m_model && m_model->isEnabled())
EnableCollision(true);
}
void GameObject::EnableCollision(bool enable)
{
if (!m_model)
return;
/*if (enable && !GetMap()->ContainsGameObjectModel(*m_model))
GetMap()->InsertGameObjectModel(*m_model);*/
m_model->enable(enable ? GetPhaseMask() : 0);
}
void GameObject::UpdateModel()
{
if (!IsInWorld())
return;
if (m_model)
if (GetMap()->ContainsGameObjectModel(*m_model))
GetMap()->RemoveGameObjectModel(*m_model);
delete m_model;
m_model = GameObjectModel::Create(*this);
if (m_model)
GetMap()->InsertGameObjectModel(*m_model);
}
Player* GameObject::GetLootRecipient() const
{
if (!m_lootRecipient)
return NULL;
return ObjectAccessor::FindPlayer(m_lootRecipient);
}
Group* GameObject::GetLootRecipientGroup() const
{
if (!m_lootRecipientGroup)
return NULL;
return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup);
}
void GameObject::SetLootRecipient(Unit* unit)
{
// set the player whose group should receive the right
// to loot the creature after it dies
// should be set to NULL after the loot disappears
if (!unit)
{
m_lootRecipient.Clear();
m_lootRecipientGroup = 0;
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();
if (Group* group = player->GetGroup())
m_lootRecipientGroup = group->GetLowGUID();
}
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
void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const
{
if (!target)
return;
bool isStoppableTransport = GetGoType() == GAMEOBJECT_TYPE_TRANSPORT && !m_goValue.Transport.StopFrames->empty();
2013-06-15 15:16:36 +02:00
bool forcedFlags = GetGoType() == GAMEOBJECT_TYPE_CHEST && GetGOInfo()->chest.groupLootRules && HasLootRecipient();
bool targetIsGM = target->IsGameMaster();
ByteBuffer fieldBuffer;
UpdateMask updateMask;
updateMask.SetCount(m_valuesCount);
uint32* flags = GameObjectUpdateFieldFlags;
uint32 visibleFlag = UF_FLAG_PUBLIC;
if (GetOwnerGUID() == target->GetGUID())
visibleFlag |= UF_FLAG_OWNER;
for (uint16 index = 0; index < m_valuesCount; ++index)
{
if (_fieldNotifyFlags & flags[index] ||
((updateType == UPDATETYPE_VALUES ? _changesMask.GetBit(index) : m_uint32Values[index]) && (flags[index] & visibleFlag)) ||
(index == GAMEOBJECT_FLAGS && forcedFlags))
{
updateMask.SetBit(index);
if (index == GAMEOBJECT_DYNAMIC)
{
uint16 dynFlags = 0;
2013-10-16 18:37:29 +02:00
int16 pathProgress = -1;
2013-06-15 15:16:36 +02:00
switch (GetGoType())
{
case GAMEOBJECT_TYPE_QUESTGIVER:
if (ActivateToQuest(target))
dynFlags |= GO_DYNFLAG_LO_ACTIVATE;
break;
2013-06-15 15:16:36 +02:00
case GAMEOBJECT_TYPE_CHEST:
case GAMEOBJECT_TYPE_GOOBER:
if (ActivateToQuest(target))
dynFlags |= GO_DYNFLAG_LO_ACTIVATE | GO_DYNFLAG_LO_SPARKLE;
else if (targetIsGM)
dynFlags |= GO_DYNFLAG_LO_ACTIVATE;
break;
case GAMEOBJECT_TYPE_GENERIC:
if (ActivateToQuest(target))
dynFlags |= GO_DYNFLAG_LO_SPARKLE;
break;
case GAMEOBJECT_TYPE_TRANSPORT:
{
float timer = float(m_goValue.Transport.PathProgress % GetTransportPeriod());
pathProgress = int16(timer / float(GetTransportPeriod()) * 65535.0f);
break;
}
2013-10-16 18:37:29 +02:00
case GAMEOBJECT_TYPE_MO_TRANSPORT:
{
float timer = float(m_goValue.Transport.PathProgress % GetUInt32Value(GAMEOBJECT_LEVEL));
pathProgress = int16(timer / float(GetUInt32Value(GAMEOBJECT_LEVEL)) * 65535.0f);
2013-06-18 16:51:13 +02:00
break;
}
2013-06-18 16:51:13 +02:00
default:
break;
2013-06-15 15:16:36 +02:00
}
fieldBuffer << uint16(dynFlags);
2013-10-16 18:37:29 +02:00
fieldBuffer << int16(pathProgress);
2013-06-15 15:16:36 +02:00
}
else if (index == GAMEOBJECT_FLAGS)
{
uint32 flags = m_uint32Values[GAMEOBJECT_FLAGS];
if (GetGoType() == GAMEOBJECT_TYPE_CHEST)
if (GetGOInfo()->chest.groupLootRules && !IsLootAllowedFor(target))
flags |= GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE;
fieldBuffer << flags;
}
else if (index == GAMEOBJECT_LEVEL)
{
if (isStoppableTransport)
fieldBuffer << uint32(m_goValue.Transport.PathProgress);
else
fieldBuffer << m_uint32Values[index];
}
else if (index == GAMEOBJECT_BYTES_1)
{
uint32 bytes1 = m_uint32Values[index];
if (isStoppableTransport && GetGoState() == GO_STATE_TRANSPORT_ACTIVE)
{
if ((m_goValue.Transport.StateUpdateTimer / 20000) & 1)
{
bytes1 &= 0xFFFFFF00;
bytes1 |= GO_STATE_TRANSPORT_STOPPED;
}
}
fieldBuffer << bytes1;
}
2013-06-15 15:16:36 +02:00
else
fieldBuffer << m_uint32Values[index]; // other cases
}
}
*data << uint8(updateMask.GetBlockCount());
updateMask.AppendToPacket(data);
data->append(fieldBuffer);
}
2013-10-16 18:37:29 +02:00
void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = NULL*/) const
{
if (m_DBTableGuid)
{
if (GameObjectData const* data = sObjectMgr->GetGOData(GetDBTableGUIDLow()))
{
x = data->posX;
y = data->posY;
z = data->posZ;
if (ori)
*ori = data->orientation;
return;
}
}
x = GetPositionX();
y = GetPositionY();
z = GetPositionZ();
if (ori)
*ori = GetOrientation();
}
float GameObject::GetInteractionDistance() const
{
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:
case GAMEOBJECT_TYPE_FISHINGNODE:
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);
m_model->Relocate(*this);
GetMap()->InsertGameObjectModel(*m_model);
}
}