2 * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
3 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "GameObject.h"
20 #include "ArtifactPackets.h"
21 #include "Battleground.h"
23 #include "CreatureAISelector.h"
24 #include "DatabaseEnv.h"
25 #include "GameObjectAI.h"
26 #include "GameObjectModel.h"
27 #include "GameObjectPackets.h"
28 #include "GridNotifiersImpl.h"
34 #include "MiscPackets.h"
35 #include "ObjectAccessor.h"
36 #include "ObjectMgr.h"
37 #include "OutdoorPvPMgr.h"
38 #include "PhasingHandler.h"
40 #include "ScriptMgr.h"
42 #include "Transport.h"
43 #include "UpdateFieldFlags.h"
47 bool QuaternionData::isUnit() const
49 return fabs(x
* x
+ y
* y
+ z
* z
+ w
* w
- 1.0f
) < 1e-5;
52 QuaternionData
QuaternionData::fromEulerAnglesZYX(float Z
, float Y
, float X
)
54 G3D::Quat
quat(G3D::Matrix3::fromEulerAnglesZYX(Z
, Y
, X
));
55 return QuaternionData(quat
.x
, quat
.y
, quat
.z
, quat
.w
);
58 GameObject::GameObject() : WorldObject(false), MapObject(),
59 m_model(nullptr), m_goValue(), m_AI(nullptr), _animKitId(0), _worldEffectID(0)
61 m_objectType
|= TYPEMASK_GAMEOBJECT
;
62 m_objectTypeId
= TYPEID_GAMEOBJECT
;
64 m_updateFlag
= (UPDATEFLAG_STATIONARY_POSITION
| UPDATEFLAG_ROTATION
);
66 m_valuesCount
= GAMEOBJECT_END
;
67 _dynamicValuesCount
= GAMEOBJECT_DYNAMIC_END
;
69 m_respawnDelayTime
= 300;
70 m_lootState
= GO_NOT_READY
;
71 m_spawnedByDefault
= true;
75 m_prevGoState
= GO_STATE_ACTIVE
;
79 m_goTemplateAddon
= nullptr;
81 m_spawnId
= UI64LIT(0);
85 ResetLootMode(); // restore default loot mode
86 m_stationaryPosition
.Relocate(0.0f
, 0.0f
, 0.0f
, 0.0f
);
89 GameObject::~GameObject()
93 if (m_goInfo
&& m_goInfo
->type
== GAMEOBJECT_TYPE_TRANSPORT
)
94 delete m_goValue
.Transport
.StopFrames
;
95 //if (m_uint32Values) // field array can be not exist if GameOBject not loaded
96 // CleanupsBeforeDelete();
99 void GameObject::AIM_Destroy()
105 bool GameObject::AIM_Initialize()
109 m_AI
= FactorySelector::SelectGameObjectAI(this);
114 m_AI
->InitializeAI();
118 std::string
GameObject::GetAIName() const
120 if (GameObjectTemplate
const* got
= sObjectMgr
->GetGameObjectTemplate(GetEntry()))
126 void GameObject::CleanupsBeforeDelete(bool finalCleanup
)
128 WorldObject::CleanupsBeforeDelete(finalCleanup
);
130 if (m_uint32Values
) // field array can be not exist if GameOBject not loaded
134 void GameObject::RemoveFromOwner()
136 ObjectGuid ownerGUID
= GetOwnerGUID();
140 if (Unit
* owner
= ObjectAccessor::GetUnit(*this, ownerGUID
))
142 owner
->RemoveGameObject(this, false);
143 ASSERT(!GetOwnerGUID());
147 // This happens when a mage portal is despawned after the caster changes map (for example using the portal)
148 TC_LOG_DEBUG("misc", "Removed GameObject (%s SpellId: %u LinkedGO: %u) that just lost any reference to the owner (%s) GO list",
149 GetGUID().ToString().c_str(), m_spellId
, GetGOInfo()->GetLinkedGameObjectEntry(), ownerGUID
.ToString().c_str());
150 SetOwnerGUID(ObjectGuid::Empty
);
153 void GameObject::AddToWorld()
155 ///- Register the gameobject for guid lookup
159 m_zoneScript
->OnGameObjectCreate(this);
161 GetMap()->GetObjectsStore().Insert
<GameObject
>(GetGUID(), this);
163 GetMap()->GetGameObjectBySpawnIdStore().insert(std::make_pair(m_spawnId
, this));
165 // The state can be changed after GameObject::Create but before GameObject::AddToWorld
166 bool toggledState
= GetGoType() == GAMEOBJECT_TYPE_CHEST
? getLootState() == GO_READY
: (GetGoState() == GO_STATE_READY
|| IsTransport());
169 if (Transport
* trans
= ToTransport())
170 trans
->SetDelayedAddModelToMap();
172 GetMap()->InsertGameObjectModel(*m_model
);
175 EnableCollision(toggledState
);
176 WorldObject::AddToWorld();
180 void GameObject::RemoveFromWorld()
182 ///- Remove the gameobject from the accessor
186 m_zoneScript
->OnGameObjectRemove(this);
190 if (GetMap()->ContainsGameObjectModel(*m_model
))
191 GetMap()->RemoveGameObjectModel(*m_model
);
193 WorldObject::RemoveFromWorld();
196 Trinity::Containers::MultimapErasePair(GetMap()->GetGameObjectBySpawnIdStore(), m_spawnId
, this);
197 GetMap()->GetObjectsStore().Remove
<GameObject
>(GetGUID());
201 bool GameObject::Create(uint32 entry
, Map
* map
, Position
const& pos
, QuaternionData
const& rotation
, uint32 animProgress
, GOState goState
, uint32 artKit
)
207 m_stationaryPosition
.Relocate(pos
);
208 if (!IsPositionValid())
210 TC_LOG_ERROR("misc", "Gameobject (Spawn id: " UI64FMTD
" Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", GetSpawnId(), entry
, pos
.GetPositionX(), pos
.GetPositionY());
217 entry
= m_zoneScript
->GetGameObjectEntry(m_spawnId
, entry
);
222 GameObjectTemplate
const* goInfo
= sObjectMgr
->GetGameObjectTemplate(entry
);
225 TC_LOG_ERROR("sql.sql", "Gameobject (Spawn id: " UI64FMTD
" Entry: %u) not created: non-existing entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f)", GetSpawnId(), entry
, map
->GetId(), pos
.GetPositionX(), pos
.GetPositionY(), pos
.GetPositionZ());
229 if (goInfo
->type
== GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT
)
231 TC_LOG_ERROR("sql.sql", "Gameobject (Spawn id: " UI64FMTD
" Entry: %u) not created: gameobject type GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT cannot be manually created.", GetSpawnId(), entry
);
236 if (goInfo
->type
!= GAMEOBJECT_TYPE_TRANSPORT
)
237 guid
= ObjectGuid::Create
<HighGuid::GameObject
>(map
->GetId(), goInfo
->entry
, map
->GenerateLowGuid
<HighGuid::GameObject
>());
240 guid
= ObjectGuid::Create
<HighGuid::Transport
>(map
->GenerateLowGuid
<HighGuid::Transport
>());
241 m_updateFlag
|= UPDATEFLAG_TRANSPORT
;
244 Object::_Create(guid
);
247 m_goTemplateAddon
= sObjectMgr
->GetGameObjectTemplateAddon(entry
);
249 if (goInfo
->type
>= MAX_GAMEOBJECT_TYPE
)
251 TC_LOG_ERROR("sql.sql", "Gameobject (%s Spawn id: " UI64FMTD
" Entry: %u) not created: non-existing GO type '%u' in `gameobject_template`. It will crash client if created.", guid
.ToString().c_str(), GetSpawnId(), entry
, goInfo
->type
);
255 SetWorldRotation(rotation
.x
, rotation
.y
, rotation
.z
, rotation
.w
);
256 GameObjectAddon
const* gameObjectAddon
= sObjectMgr
->GetGameObjectAddon(GetSpawnId());
258 // For most of gameobjects is (0, 0, 0, 1) quaternion, there are only some transports with not standard rotation
259 QuaternionData parentRotation
;
261 parentRotation
= gameObjectAddon
->ParentRotation
;
263 SetParentRotation(parentRotation
);
265 SetObjectScale(goInfo
->size
);
267 if (m_goTemplateAddon
)
269 SetUInt32Value(GAMEOBJECT_FACTION
, m_goTemplateAddon
->faction
);
270 SetUInt32Value(GAMEOBJECT_FLAGS
, m_goTemplateAddon
->flags
);
272 if (m_goTemplateAddon
->WorldEffectID
)
274 m_updateFlag
|= UPDATEFLAG_GAMEOBJECT
;
275 SetWorldEffectID(m_goTemplateAddon
->WorldEffectID
);
279 SetEntry(goInfo
->entry
);
281 // set name for logs usage, doesn't affect anything ingame
282 SetName(goInfo
->name
);
284 SetDisplayId(goInfo
->displayId
);
286 m_model
= CreateModel();
287 // GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3
288 SetGoType(GameobjectTypes(goInfo
->type
));
289 m_prevGoState
= goState
;
293 switch (goInfo
->type
)
295 case GAMEOBJECT_TYPE_FISHINGHOLE
:
296 SetGoAnimProgress(animProgress
);
297 m_goValue
.FishingHole
.MaxOpens
= urand(GetGOInfo()->fishingHole
.minRestock
, GetGOInfo()->fishingHole
.maxRestock
);
299 case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
:
300 // TODO: Get the values somehow, no longer in gameobject_template
301 m_goValue
.Building
.Health
= 20000/*goinfo->destructibleBuilding.intactNumHits + goinfo->destructibleBuilding.damagedNumHits*/;
302 m_goValue
.Building
.MaxHealth
= m_goValue
.Building
.Health
;
303 SetGoAnimProgress(255);
304 SetUInt32Value(GAMEOBJECT_PARENTROTATION
, m_goInfo
->destructibleBuilding
.DestructibleModelRec
);
306 case GAMEOBJECT_TYPE_TRANSPORT
:
308 m_goValue
.Transport
.AnimationInfo
= sTransportMgr
->GetTransportAnimInfo(goInfo
->entry
);
309 m_goValue
.Transport
.PathProgress
= getMSTime();
310 if (m_goValue
.Transport
.AnimationInfo
)
311 m_goValue
.Transport
.PathProgress
-= m_goValue
.Transport
.PathProgress
% GetTransportPeriod(); // align to period
312 m_goValue
.Transport
.CurrentSeg
= 0;
313 m_goValue
.Transport
.StateUpdateTimer
= 0;
314 m_goValue
.Transport
.StopFrames
= new std::vector
<uint32
>();
315 if (goInfo
->transport
.Timeto2ndfloor
> 0)
316 m_goValue
.Transport
.StopFrames
->push_back(goInfo
->transport
.Timeto2ndfloor
);
317 if (goInfo
->transport
.Timeto3rdfloor
> 0)
318 m_goValue
.Transport
.StopFrames
->push_back(goInfo
->transport
.Timeto3rdfloor
);
319 if (goInfo
->transport
.Timeto4thfloor
> 0)
320 m_goValue
.Transport
.StopFrames
->push_back(goInfo
->transport
.Timeto4thfloor
);
321 if (goInfo
->transport
.Timeto5thfloor
> 0)
322 m_goValue
.Transport
.StopFrames
->push_back(goInfo
->transport
.Timeto5thfloor
);
323 if (goInfo
->transport
.Timeto6thfloor
> 0)
324 m_goValue
.Transport
.StopFrames
->push_back(goInfo
->transport
.Timeto6thfloor
);
325 if (goInfo
->transport
.Timeto7thfloor
> 0)
326 m_goValue
.Transport
.StopFrames
->push_back(goInfo
->transport
.Timeto7thfloor
);
327 if (goInfo
->transport
.Timeto8thfloor
> 0)
328 m_goValue
.Transport
.StopFrames
->push_back(goInfo
->transport
.Timeto8thfloor
);
329 if (goInfo
->transport
.Timeto9thfloor
> 0)
330 m_goValue
.Transport
.StopFrames
->push_back(goInfo
->transport
.Timeto9thfloor
);
331 if (goInfo
->transport
.Timeto10thfloor
> 0)
332 m_goValue
.Transport
.StopFrames
->push_back(goInfo
->transport
.Timeto10thfloor
);
333 if (goInfo
->transport
.startOpen
)
334 SetTransportState(GO_STATE_TRANSPORT_STOPPED
, goInfo
->transport
.startOpen
- 1);
336 SetTransportState(GO_STATE_TRANSPORT_ACTIVE
);
338 SetGoAnimProgress(animProgress
);
341 case GAMEOBJECT_TYPE_FISHINGNODE
:
342 SetUInt32Value(GAMEOBJECT_LEVEL
, 1);
343 SetGoAnimProgress(255);
345 case GAMEOBJECT_TYPE_TRAP
:
346 if (GetGOInfo()->trap
.stealthed
)
348 m_stealth
.AddFlag(STEALTH_TRAP
);
349 m_stealth
.AddValue(STEALTH_TRAP
, 70);
352 if (GetGOInfo()->trap
.stealthAffected
)
354 m_invisibility
.AddFlag(INVISIBILITY_TRAP
);
355 m_invisibility
.AddValue(INVISIBILITY_TRAP
, 300);
359 SetGoAnimProgress(animProgress
);
363 if (gameObjectAddon
&& gameObjectAddon
->InvisibilityValue
)
365 m_invisibility
.AddFlag(gameObjectAddon
->invisibilityType
);
366 m_invisibility
.AddValue(gameObjectAddon
->invisibilityType
, gameObjectAddon
->InvisibilityValue
);
369 if (gameObjectAddon
&& gameObjectAddon
->WorldEffectID
)
371 m_updateFlag
|= UPDATEFLAG_GAMEOBJECT
;
372 SetWorldEffectID(gameObjectAddon
->WorldEffectID
);
375 LastUsedScriptID
= GetGOInfo()->ScriptId
;
378 // Initialize loot duplicate count depending on raid difficulty
379 if (map
->Is25ManRaid())
380 loot
.maxDuplicates
= 3;
382 if (uint32 linkedEntry
= GetGOInfo()->GetLinkedGameObjectEntry())
384 if (GameObject
* linkedGo
= GameObject::CreateGameObject(linkedEntry
, map
, pos
, rotation
, 255, GO_STATE_READY
))
386 SetLinkedTrap(linkedGo
);
387 if (!map
->AddToMap(linkedGo
))
395 GameObject
* GameObject::CreateGameObject(uint32 entry
, Map
* map
, Position
const& pos
, QuaternionData
const& rotation
, uint32 animProgress
, GOState goState
, uint32 artKit
/*= 0*/)
397 GameObjectTemplate
const* goInfo
= sObjectMgr
->GetGameObjectTemplate(entry
);
401 GameObject
* go
= new GameObject();
402 if (!go
->Create(entry
, map
, pos
, rotation
, animProgress
, goState
, artKit
))
411 GameObject
* GameObject::CreateGameObjectFromDB(ObjectGuid::LowType spawnId
, Map
* map
, bool addToMap
/*= true*/)
413 GameObject
* go
= new GameObject();
414 if (!go
->LoadGameObjectFromDB(spawnId
, map
, addToMap
))
423 void GameObject::Update(uint32 diff
)
426 AI()->UpdateAI(diff
);
427 else if (!AIM_Initialize())
428 TC_LOG_ERROR("misc", "Could not initialize GameObjectAI");
436 case GAMEOBJECT_TYPE_TRAP
:
438 // Arming Time for GAMEOBJECT_TYPE_TRAP (6)
439 GameObjectTemplate
const* goInfo
= GetGOInfo();
441 if (goInfo
->trap
.charges
== 2)
442 // Hardcoded tooltip value
443 m_cooldownTime
= time(NULL
) + 10;
444 else if (Unit
* owner
= GetOwner())
445 if (owner
->IsInCombat())
446 m_cooldownTime
= time(NULL
) + goInfo
->trap
.startDelay
;
448 SetLootState(GO_READY
);
451 case GAMEOBJECT_TYPE_TRANSPORT
:
453 if (!m_goValue
.Transport
.AnimationInfo
)
456 if (GetGoState() == GO_STATE_TRANSPORT_ACTIVE
)
458 m_goValue
.Transport
.PathProgress
+= diff
;
459 /* TODO: Fix movement in unloaded grid - currently GO will just disappear
460 uint32 timer = m_goValue.Transport.PathProgress % GetTransportPeriod();
461 TransportAnimationEntry const* node = m_goValue.Transport.AnimationInfo->GetAnimNode(timer);
462 if (node && m_goValue.Transport.CurrentSeg != node->TimeSeg)
464 m_goValue.Transport.CurrentSeg = node->TimeSeg;
467 if (TransportRotationEntry const* rot = m_goValue.Transport.AnimationInfo->GetAnimRotation(timer))
468 rotation = G3D::Quat(rot->X, rot->Y, rot->Z, rot->W);
470 G3D::Vector3 pos = rotation.toRotationMatrix()
471 * G3D::Matrix3::fromEulerAnglesZYX(GetOrientation(), 0.0f, 0.0f)
472 * G3D::Vector3(node->X, node->Y, node->Z);
474 pos += G3D::Vector3(GetStationaryX(), GetStationaryY(), GetStationaryZ());
476 G3D::Vector3 src(GetPositionX(), GetPositionY(), GetPositionZ());
478 TC_LOG_DEBUG("misc", "Src: %s Dest: %s", src.toString().c_str(), pos.toString().c_str());
480 GetMap()->GameObjectRelocation(this, pos.x, pos.y, pos.z, GetOrientation());
484 if (!m_goValue
.Transport
.StopFrames
->empty())
486 uint32 visualStateBefore
= (m_goValue
.Transport
.StateUpdateTimer
/ 20000) & 1;
487 m_goValue
.Transport
.StateUpdateTimer
+= diff
;
488 uint32 visualStateAfter
= (m_goValue
.Transport
.StateUpdateTimer
/ 20000) & 1;
489 if (visualStateBefore
!= visualStateAfter
)
491 ForceValuesUpdateAtIndex(GAMEOBJECT_LEVEL
);
492 ForceValuesUpdateAtIndex(GAMEOBJECT_BYTES_1
);
498 case GAMEOBJECT_TYPE_FISHINGNODE
:
500 // fishing code (bobber ready)
501 if (time(NULL
) > m_respawnTime
- FISHING_BOBBER_READY_TIME
)
503 // splash bobber (bobber ready now)
504 Unit
* caster
= GetOwner();
505 if (caster
&& caster
->GetTypeId() == TYPEID_PLAYER
)
507 SetGoState(GO_STATE_ACTIVE
);
508 SetUInt32Value(GAMEOBJECT_FLAGS
, GO_FLAG_NODESPAWN
);
510 UpdateData
udata(caster
->GetMapId());
512 BuildValuesUpdateBlockForPlayer(&udata
, caster
->ToPlayer());
513 udata
.BuildPacket(&packet
);
514 caster
->ToPlayer()->SendDirectMessage(&packet
);
516 SendCustomAnim(GetGoAnimProgress());
519 m_lootState
= GO_READY
; // can be successfully open with some chance
524 m_lootState
= GO_READY
; // for other GOis same switched without delay to GO_READY
527 // NO BREAK for switch (m_lootState)
531 if (m_respawnTime
> 0) // timer on
533 time_t now
= time(NULL
);
534 if (m_respawnTime
<= now
) // timer expired
536 ObjectGuid dbtableHighGuid
= ObjectGuid::Create
<HighGuid::GameObject
>(GetMapId(), GetEntry(), m_spawnId
);
537 time_t linkedRespawntime
= GetMap()->GetLinkedRespawnTime(dbtableHighGuid
);
538 if (linkedRespawntime
) // Can't respawn, the master is dead
540 ObjectGuid targetGuid
= sObjectMgr
->GetLinkedRespawnGuid(dbtableHighGuid
);
541 if (targetGuid
== dbtableHighGuid
) // if linking self, never respawn (check delayed to next day)
544 m_respawnTime
= (now
> linkedRespawntime
? now
: linkedRespawntime
) + urand(5, MINUTE
); // else copy time from master and add a little
545 SaveRespawnTime(); // also save to DB immediately
550 m_SkillupList
.clear();
553 // If nearby linked trap exists, respawn it
554 if (GameObject
* linkedTrap
= GetLinkedTrap())
555 linkedTrap
->SetLootState(GO_READY
);
559 case GAMEOBJECT_TYPE_FISHINGNODE
: // can't fish now
561 Unit
* caster
= GetOwner();
562 if (caster
&& caster
->GetTypeId() == TYPEID_PLAYER
)
564 caster
->ToPlayer()->RemoveGameObject(this, false);
565 caster
->ToPlayer()->SendDirectMessage(WorldPackets::GameObject::FishEscaped().Write());
568 m_lootState
= GO_JUST_DEACTIVATED
;
571 case GAMEOBJECT_TYPE_DOOR
:
572 case GAMEOBJECT_TYPE_BUTTON
:
573 // 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)
574 if (GetGoState() != GO_STATE_READY
)
577 case GAMEOBJECT_TYPE_FISHINGHOLE
:
578 // Initialize a new max fish count on respawn
579 m_goValue
.FishingHole
.MaxOpens
= urand(GetGOInfo()->fishingHole
.minRestock
, GetGOInfo()->fishingHole
.maxRestock
);
586 if (!m_spawnedByDefault
)
588 // Can be despawned or destroyed
589 SetLootState(GO_JUST_DEACTIVATED
);
594 uint32 poolid
= GetSpawnId() ? sPoolMgr
->IsPartOfAPool
<GameObject
>(GetSpawnId()) : 0;
596 sPoolMgr
->UpdatePool
<GameObject
>(poolid
, GetSpawnId());
598 GetMap()->AddToMap(this);
604 GameObjectTemplate
const* goInfo
= GetGOInfo();
605 if (goInfo
->type
== GAMEOBJECT_TYPE_TRAP
)
607 if (m_cooldownTime
>= time(NULL
))
610 // Type 2 (bomb) does not need to be triggered by a unit and despawns after casting its spell.
611 if (goInfo
->trap
.charges
== 2)
613 SetLootState(GO_ACTIVATED
);
617 // Type 0 despawns after being triggered, type 1 does not.
618 /// @todo This is activation radius. Casting radius must be selected from spell data.
620 if (!goInfo
->trap
.radius
)
622 // Battleground traps: data2 == 0 && data5 == 3
623 if (goInfo
->trap
.cooldown
!= 3)
629 radius
= goInfo
->trap
.radius
/ 2.f
;
631 // Pointer to appropriate target if found any
632 Unit
* target
= nullptr;
634 /// @todo this hack with search required until GO casting not implemented
635 if (Unit
* owner
= GetOwner())
637 // Hunter trap: Search units which are unfriendly to the trap's owner
638 Trinity::NearestAttackableNoTotemUnitInObjectRangeCheck
checker(this, owner
, radius
);
639 Trinity::UnitLastSearcher
<Trinity::NearestAttackableNoTotemUnitInObjectRangeCheck
> searcher(this, target
, checker
);
640 Cell::VisitAllObjects(this, searcher
, radius
);
644 // Environmental trap: Any player
645 Player
* player
= nullptr;
646 Trinity::AnyPlayerInObjectRangeCheck
checker(this, radius
);
647 Trinity::PlayerSearcher
<Trinity::AnyPlayerInObjectRangeCheck
> searcher(this, player
, checker
);
648 Cell::VisitWorldObjects(this, searcher
, radius
);
653 SetLootState(GO_ACTIVATED
, target
);
656 else if (uint32 max_charges
= goInfo
->GetCharges())
658 if (m_usetimes
>= max_charges
)
661 SetLootState(GO_JUST_DEACTIVATED
); // can be despawned or destroyed
672 case GAMEOBJECT_TYPE_DOOR
:
673 case GAMEOBJECT_TYPE_BUTTON
:
674 if (m_cooldownTime
&& (m_cooldownTime
< time(NULL
)))
677 case GAMEOBJECT_TYPE_GOOBER
:
678 if (m_cooldownTime
< time(NULL
))
680 RemoveFlag(GAMEOBJECT_FLAGS
, GO_FLAG_IN_USE
);
682 SetLootState(GO_JUST_DEACTIVATED
);
686 case GAMEOBJECT_TYPE_CHEST
:
687 if (m_groupLootTimer
)
689 if (m_groupLootTimer
<= diff
)
691 if (Group
* group
= sGroupMgr
->GetGroupByGUID(lootingGroupLowGUID
))
692 group
->EndRoll(&loot
);
694 m_groupLootTimer
= 0;
695 lootingGroupLowGUID
.Clear();
698 m_groupLootTimer
-= diff
;
701 case GAMEOBJECT_TYPE_TRAP
:
703 GameObjectTemplate
const* goInfo
= GetGOInfo();
704 if (goInfo
->trap
.charges
== 2 && goInfo
->trap
.spell
)
706 /// @todo nullptr target won't work for target type 1
707 CastSpell(nullptr, goInfo
->trap
.spell
);
708 SetLootState(GO_JUST_DEACTIVATED
);
710 else if (Unit
* target
= ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID
))
712 // Some traps do not have a spell but should be triggered
713 if (goInfo
->trap
.spell
)
714 CastSpell(target
, goInfo
->trap
.spell
);
716 // Template value or 4 seconds
717 m_cooldownTime
= time(NULL
) + (goInfo
->trap
.cooldown
? goInfo
->trap
.cooldown
: uint32(4));
719 if (goInfo
->trap
.charges
== 1)
720 SetLootState(GO_JUST_DEACTIVATED
);
721 else if (!goInfo
->trap
.charges
)
722 SetLootState(GO_READY
);
724 // Battleground gameobjects have data2 == 0 && data5 == 3
725 if (!goInfo
->trap
.radius
&& goInfo
->trap
.cooldown
== 3)
726 if (Player
* player
= target
->ToPlayer())
727 if (Battleground
* bg
= player
->GetBattleground())
728 bg
->HandleTriggerBuff(GetGUID());
737 case GO_JUST_DEACTIVATED
:
739 // If nearby linked trap exists, despawn it
740 if (GameObject
* linkedTrap
= GetLinkedTrap())
741 linkedTrap
->SetLootState(GO_JUST_DEACTIVATED
);
743 //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
744 if (GetGoType() == GAMEOBJECT_TYPE_GOOBER
)
746 uint32 spellId
= GetGOInfo()->goober
.spell
;
750 for (GuidSet::const_iterator it
= m_unique_users
.begin(); it
!= m_unique_users
.end(); ++it
)
751 // m_unique_users can contain only player GUIDs
752 if (Player
* owner
= ObjectAccessor::GetPlayer(*this, *it
))
753 owner
->CastSpell(owner
, spellId
, false);
755 m_unique_users
.clear();
759 SetGoState(GO_STATE_READY
);
761 //any return here in case battleground traps
762 if (GameObjectTemplateAddon
const* addon
= GetTemplateAddon())
763 if (addon
->flags
& GO_FLAG_NODESPAWN
)
769 //! 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
770 //! The GetOwnerGUID() check is mostly for compatibility with hacky scripts - 99% of the time summoning should be done trough spells.
771 if (GetSpellId() || !GetOwnerGUID().IsEmpty())
778 SetLootState(GO_READY
);
780 //burning flags in some battlegrounds, if you find better condition, just add it
781 if (GetGOInfo()->IsDespawnAtAction() || GetGoAnimProgress() > 0)
783 SendGameObjectDespawn();
785 if (GameObjectTemplateAddon
const* addon
= GetTemplateAddon())
786 SetUInt32Value(GAMEOBJECT_FLAGS
, addon
->flags
);
789 if (!m_respawnDelayTime
)
792 if (!m_spawnedByDefault
)
795 UpdateObjectVisibility();
799 m_respawnTime
= time(NULL
) + m_respawnDelayTime
;
801 // if option not set then object will be saved at grid unload
802 if (sWorld
->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY
))
805 UpdateObjectVisibility();
810 sScriptMgr
->OnGameObjectUpdate(this, diff
);
813 void GameObject::Refresh()
815 // Do not refresh despawned GO from spellcast (GO's from spellcast are destroyed after despawn)
816 if (m_respawnTime
> 0 && m_spawnedByDefault
)
820 GetMap()->AddToMap(this);
823 void GameObject::AddUniqueUse(Player
* player
)
826 m_unique_users
.insert(player
->GetGUID());
829 void GameObject::Delete()
831 SetLootState(GO_NOT_READY
);
834 SendGameObjectDespawn();
836 SetGoState(GO_STATE_READY
);
838 if (GameObjectTemplateAddon
const* addon
= GetTemplateAddon())
839 SetUInt32Value(GAMEOBJECT_FLAGS
, addon
->flags
);
841 uint32 poolid
= GetSpawnId() ? sPoolMgr
->IsPartOfAPool
<GameObject
>(GetSpawnId()) : 0;
843 sPoolMgr
->UpdatePool
<GameObject
>(poolid
, GetSpawnId());
845 AddObjectToRemoveList();
848 void GameObject::SendGameObjectDespawn()
850 WorldPackets::GameObject::GameObjectDespawn packet
;
851 packet
.ObjectGUID
= GetGUID();
852 SendMessageToSet(packet
.Write(), true);
855 void GameObject::getFishLoot(Loot
* fishloot
, Player
* loot_owner
)
859 uint32 zone
, subzone
;
860 uint32 defaultzone
= 1;
861 GetZoneAndAreaId(zone
, subzone
);
863 // if subzone loot exist use it
864 fishloot
->FillLoot(subzone
, LootTemplates_Fishing
, loot_owner
, true, true);
865 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.
867 //subzone no result,use zone loot
868 fishloot
->FillLoot(zone
, LootTemplates_Fishing
, loot_owner
, true, true);
869 //use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks.
870 if (fishloot
->empty())
871 fishloot
->FillLoot(defaultzone
, LootTemplates_Fishing
, loot_owner
, true, true);
875 void GameObject::getFishLootJunk(Loot
* fishloot
, Player
* loot_owner
)
879 uint32 zone
, subzone
;
880 uint32 defaultzone
= 1;
881 GetZoneAndAreaId(zone
, subzone
);
883 // if subzone loot exist use it
884 fishloot
->FillLoot(subzone
, LootTemplates_Fishing
, loot_owner
, true, true, LOOT_MODE_JUNK_FISH
);
885 if (fishloot
->empty()) //use this becase if zone or subzone has normal mask drop, then fishloot->FillLoot return true.
888 fishloot
->FillLoot(zone
, LootTemplates_Fishing
, loot_owner
, true, true, LOOT_MODE_JUNK_FISH
);
889 if (fishloot
->empty())
890 //use zone 1 as default
891 fishloot
->FillLoot(defaultzone
, LootTemplates_Fishing
, loot_owner
, true, true, LOOT_MODE_JUNK_FISH
);
895 void GameObject::SaveToDB()
897 // this should only be used when the gameobject has already been loaded
898 // preferably after adding to map, because mapid may not be valid otherwise
899 GameObjectData
const* data
= sObjectMgr
->GetGOData(m_spawnId
);
902 TC_LOG_ERROR("misc", "GameObject::SaveToDB failed, cannot get gameobject data!");
906 SaveToDB(GetMapId(), data
->spawnMask
);
909 void GameObject::SaveToDB(uint32 mapid
, uint64 spawnMask
)
911 const GameObjectTemplate
* goI
= GetGOInfo();
917 m_spawnId
= sObjectMgr
->GenerateGameObjectSpawnId();
919 // update in loaded data (changing data only in this place)
920 GameObjectData
& data
= sObjectMgr
->NewGOData(m_spawnId
);
922 // data->guid = guid must not be updated at save
923 data
.id
= GetEntry();
925 data
.posX
= GetPositionX();
926 data
.posY
= GetPositionY();
927 data
.posZ
= GetPositionZ();
928 data
.orientation
= GetOrientation();
929 data
.rotation
= m_worldRotation
;
930 data
.spawntimesecs
= m_spawnedByDefault
? m_respawnDelayTime
: -(int32
)m_respawnDelayTime
;
931 data
.animprogress
= GetGoAnimProgress();
932 data
.go_state
= GetGoState();
933 data
.spawnMask
= spawnMask
;
934 data
.artKit
= GetGoArtKit();
936 data
.phaseId
= GetDBPhase() > 0 ? GetDBPhase() : data
.phaseId
;
937 data
.phaseGroup
= GetDBPhase() < 0 ? -GetDBPhase() : data
.phaseGroup
;
940 SQLTransaction trans
= WorldDatabase
.BeginTransaction();
944 PreparedStatement
* stmt
= WorldDatabase
.GetPreparedStatement(WORLD_DEL_GAMEOBJECT
);
945 stmt
->setUInt64(0, m_spawnId
);
948 stmt
= WorldDatabase
.GetPreparedStatement(WORLD_INS_GAMEOBJECT
);
949 stmt
->setUInt64(index
++, m_spawnId
);
950 stmt
->setUInt32(index
++, GetEntry());
951 stmt
->setUInt16(index
++, uint16(mapid
));
952 stmt
->setUInt64(index
++, spawnMask
);
953 stmt
->setUInt32(index
++, data
.phaseId
);
954 stmt
->setUInt32(index
++, data
.phaseGroup
);
955 stmt
->setFloat(index
++, GetPositionX());
956 stmt
->setFloat(index
++, GetPositionY());
957 stmt
->setFloat(index
++, GetPositionZ());
958 stmt
->setFloat(index
++, GetOrientation());
959 stmt
->setFloat(index
++, m_worldRotation
.x
);
960 stmt
->setFloat(index
++, m_worldRotation
.y
);
961 stmt
->setFloat(index
++, m_worldRotation
.z
);
962 stmt
->setFloat(index
++, m_worldRotation
.w
);
963 stmt
->setInt32(index
++, int32(m_respawnDelayTime
));
964 stmt
->setUInt8(index
++, GetGoAnimProgress());
965 stmt
->setUInt8(index
++, uint8(GetGoState()));
968 WorldDatabase
.CommitTransaction(trans
);
971 bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId
, Map
* map
, bool addToMap
)
973 GameObjectData
const* data
= sObjectMgr
->GetGOData(spawnId
);
976 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: " UI64FMTD
") not found in table `gameobject`, can't load. ", spawnId
);
980 uint32 entry
= data
->id
;
981 //uint32 map_id = data->mapid; // already used before call
982 Position
pos(data
->posX
, data
->posY
, data
->posZ
, data
->orientation
);
984 uint32 animprogress
= data
->animprogress
;
985 GOState go_state
= data
->go_state
;
986 uint32 artKit
= data
->artKit
;
989 if (!Create(entry
, map
, pos
, data
->rotation
, animprogress
, go_state
, artKit
))
992 PhasingHandler::InitDbPhaseShift(GetPhaseShift(), data
->phaseUseFlags
, data
->phaseId
, data
->phaseGroup
);
993 PhasingHandler::InitDbVisibleMapId(GetPhaseShift(), data
->terrainSwapMap
);
995 if (data
->spawntimesecs
>= 0)
997 m_spawnedByDefault
= true;
999 if (!GetGOInfo()->GetDespawnPossibility() && !GetGOInfo()->IsDespawnAtAction())
1001 SetFlag(GAMEOBJECT_FLAGS
, GO_FLAG_NODESPAWN
);
1002 m_respawnDelayTime
= 0;
1007 m_respawnDelayTime
= data
->spawntimesecs
;
1008 m_respawnTime
= GetMap()->GetGORespawnTime(m_spawnId
);
1011 if (m_respawnTime
&& m_respawnTime
<= time(nullptr))
1014 GetMap()->RemoveGORespawnTime(m_spawnId
);
1020 m_spawnedByDefault
= false;
1021 m_respawnDelayTime
= -data
->spawntimesecs
;
1027 if (addToMap
&& !GetMap()->AddToMap(this))
1033 void GameObject::DeleteFromDB()
1035 GetMap()->RemoveGORespawnTime(m_spawnId
);
1036 sObjectMgr
->DeleteGOData(m_spawnId
);
1038 PreparedStatement
* stmt
= WorldDatabase
.GetPreparedStatement(WORLD_DEL_GAMEOBJECT
);
1040 stmt
->setUInt64(0, m_spawnId
);
1042 WorldDatabase
.Execute(stmt
);
1044 stmt
= WorldDatabase
.GetPreparedStatement(WORLD_DEL_EVENT_GAMEOBJECT
);
1046 stmt
->setUInt64(0, m_spawnId
);
1048 WorldDatabase
.Execute(stmt
);
1051 /*********************************************************/
1052 /*** QUEST SYSTEM ***/
1053 /*********************************************************/
1054 bool GameObject::hasQuest(uint32 quest_id
) const
1056 QuestRelationBounds qr
= sObjectMgr
->GetGOQuestRelationBounds(GetEntry());
1057 for (QuestRelations::const_iterator itr
= qr
.first
; itr
!= qr
.second
; ++itr
)
1059 if (itr
->second
== quest_id
)
1065 bool GameObject::hasInvolvedQuest(uint32 quest_id
) const
1067 QuestRelationBounds qir
= sObjectMgr
->GetGOQuestInvolvedRelationBounds(GetEntry());
1068 for (QuestRelations::const_iterator itr
= qir
.first
; itr
!= qir
.second
; ++itr
)
1070 if (itr
->second
== quest_id
)
1076 bool GameObject::IsTransport() const
1078 // If something is marked as a transport, don't transmit an out of range packet for it.
1079 GameObjectTemplate
const* gInfo
= GetGOInfo();
1083 return gInfo
->type
== GAMEOBJECT_TYPE_TRANSPORT
|| gInfo
->type
== GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT
;
1086 // is Dynamic transport = non-stop Transport
1087 bool GameObject::IsDynTransport() const
1089 // If something is marked as a transport, don't transmit an out of range packet for it.
1090 GameObjectTemplate
const* gInfo
= GetGOInfo();
1094 return gInfo
->type
== GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT
|| (gInfo
->type
== GAMEOBJECT_TYPE_TRANSPORT
&& m_goValue
.Transport
.StopFrames
->empty());
1097 bool GameObject::IsDestructibleBuilding() const
1099 GameObjectTemplate
const* gInfo
= GetGOInfo();
1103 return gInfo
->type
== GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
;
1106 Unit
* GameObject::GetOwner() const
1108 return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
1111 void GameObject::SaveRespawnTime()
1113 if (m_goData
&& m_goData
->dbData
&& m_respawnTime
> time(NULL
) && m_spawnedByDefault
)
1114 GetMap()->SaveGORespawnTime(m_spawnId
, m_respawnTime
);
1117 bool GameObject::IsNeverVisibleFor(WorldObject
const* seer
) const
1119 if (WorldObject::IsNeverVisibleFor(seer
))
1122 if (GetGoType() == GAMEOBJECT_TYPE_SPELL_FOCUS
&& GetGOInfo()->spellFocus
.serverOnly
== 1)
1125 if (!GetUInt32Value(GAMEOBJECT_DISPLAYID
))
1131 bool GameObject::IsAlwaysVisibleFor(WorldObject
const* seer
) const
1133 if (WorldObject::IsAlwaysVisibleFor(seer
))
1136 if (IsTransport() || IsDestructibleBuilding())
1142 // Always seen by owner and friendly units
1143 if (!GetOwnerGUID().IsEmpty())
1145 if (seer
->GetGUID() == GetOwnerGUID())
1148 Unit
* owner
= GetOwner();
1149 if (Unit
const* unitSeer
= seer
->ToUnit())
1150 if (owner
&& owner
->IsFriendlyTo(unitSeer
))
1157 bool GameObject::IsInvisibleDueToDespawn() const
1159 if (WorldObject::IsInvisibleDueToDespawn())
1169 uint8
GameObject::GetLevelForTarget(WorldObject
const* target
) const
1171 if (Unit
* owner
= GetOwner())
1172 return owner
->GetLevelForTarget(target
);
1177 void GameObject::Respawn()
1179 if (m_spawnedByDefault
&& m_respawnTime
> 0)
1181 m_respawnTime
= time(NULL
);
1182 GetMap()->RemoveGORespawnTime(m_spawnId
);
1186 bool GameObject::ActivateToQuest(Player
* target
) const
1188 if (target
->HasQuestForGO(GetEntry()))
1191 if (!sObjectMgr
->IsGameObjectForQuests(GetEntry()))
1194 switch (GetGoType())
1196 case GAMEOBJECT_TYPE_QUESTGIVER
:
1198 GameObject
* go
= const_cast<GameObject
*>(this);
1199 QuestGiverStatus questStatus
= target
->GetQuestDialogStatus(go
);
1200 if (questStatus
> DIALOG_STATUS_UNAVAILABLE
)
1204 case GAMEOBJECT_TYPE_CHEST
:
1206 // scan GO chest with loot including quest items
1207 if (LootTemplates_Gameobject
.HaveQuestLootForPlayer(GetGOInfo()->GetLootId(), target
))
1209 if (Battleground
const* bg
= target
->GetBattleground())
1210 return bg
->CanActivateGO(GetEntry(), target
->GetTeam());
1215 case GAMEOBJECT_TYPE_GENERIC
:
1217 if (target
->GetQuestStatus(GetGOInfo()->generic
.questID
) == QUEST_STATUS_INCOMPLETE
)
1221 case GAMEOBJECT_TYPE_GOOBER
:
1223 if (target
->GetQuestStatus(GetGOInfo()->goober
.questID
) == QUEST_STATUS_INCOMPLETE
)
1234 void GameObject::TriggeringLinkedGameObject(uint32 trapEntry
, Unit
* target
)
1236 GameObjectTemplate
const* trapInfo
= sObjectMgr
->GetGameObjectTemplate(trapEntry
);
1237 if (!trapInfo
|| trapInfo
->type
!= GAMEOBJECT_TYPE_TRAP
)
1240 SpellInfo
const* trapSpell
= sSpellMgr
->GetSpellInfo(trapInfo
->trap
.spell
);
1241 if (!trapSpell
) // checked at load already
1244 if (GameObject
* trapGO
= GetLinkedTrap())
1245 trapGO
->CastSpell(target
, trapSpell
->Id
);
1248 GameObject
* GameObject::LookupFishingHoleAround(float range
)
1250 GameObject
* ok
= nullptr;
1251 Trinity::NearestGameObjectFishingHole
u_check(*this, range
);
1252 Trinity::GameObjectSearcher
<Trinity::NearestGameObjectFishingHole
> checker(this, ok
, u_check
);
1253 Cell::VisitGridObjects(this, checker
, range
);
1257 void GameObject::ResetDoorOrButton()
1259 if (m_lootState
== GO_READY
|| m_lootState
== GO_JUST_DEACTIVATED
)
1262 RemoveFlag(GAMEOBJECT_FLAGS
, GO_FLAG_IN_USE
);
1263 SetGoState(m_prevGoState
);
1265 SetLootState(GO_JUST_DEACTIVATED
);
1269 void GameObject::UseDoorOrButton(uint32 time_to_restore
, bool alternative
/* = false */, Unit
* user
/*=nullptr*/)
1271 if (m_lootState
!= GO_READY
)
1274 if (!time_to_restore
)
1275 time_to_restore
= GetGOInfo()->GetAutoCloseTime();
1277 SwitchDoorOrButton(true, alternative
);
1278 SetLootState(GO_ACTIVATED
, user
);
1280 m_cooldownTime
= time_to_restore
? (time(NULL
) + time_to_restore
) : 0;
1283 void GameObject::SetGoArtKit(uint8 kit
)
1285 SetByteValue(GAMEOBJECT_BYTES_1
, 2, kit
);
1286 GameObjectData
* data
= const_cast<GameObjectData
*>(sObjectMgr
->GetGOData(m_spawnId
));
1291 void GameObject::SetGoArtKit(uint8 artkit
, GameObject
* go
, ObjectGuid::LowType lowguid
)
1293 const GameObjectData
* data
= nullptr;
1296 go
->SetGoArtKit(artkit
);
1297 data
= go
->GetGOData();
1300 data
= sObjectMgr
->GetGOData(lowguid
);
1303 const_cast<GameObjectData
*>(data
)->artKit
= artkit
;
1306 void GameObject::SwitchDoorOrButton(bool activate
, bool alternative
/* = false */)
1309 SetFlag(GAMEOBJECT_FLAGS
, GO_FLAG_IN_USE
);
1311 RemoveFlag(GAMEOBJECT_FLAGS
, GO_FLAG_IN_USE
);
1313 if (GetGoState() == GO_STATE_READY
) //if closed -> open
1314 SetGoState(alternative
? GO_STATE_ACTIVE_ALTERNATIVE
: GO_STATE_ACTIVE
);
1315 else //if open -> close
1316 SetGoState(GO_STATE_READY
);
1319 void GameObject::Use(Unit
* user
)
1321 // by default spell caster is user
1322 Unit
* spellCaster
= user
;
1324 bool triggered
= false;
1326 if (Player
* playerUser
= user
->ToPlayer())
1328 if (sScriptMgr
->OnGossipHello(playerUser
, this))
1331 if (AI()->GossipHello(playerUser
, true))
1335 // If cooldown data present in template
1336 if (uint32 cooldown
= GetGOInfo()->GetCooldown())
1338 if (m_cooldownTime
> sWorld
->GetGameTime())
1341 m_cooldownTime
= sWorld
->GetGameTime() + cooldown
;
1344 switch (GetGoType())
1346 case GAMEOBJECT_TYPE_DOOR
: //0
1347 case GAMEOBJECT_TYPE_BUTTON
: //1
1348 //doors/buttons never really despawn, only reset to default state/flags
1349 UseDoorOrButton(0, false, user
);
1351 case GAMEOBJECT_TYPE_QUESTGIVER
: //2
1353 if (user
->GetTypeId() != TYPEID_PLAYER
)
1356 Player
* player
= user
->ToPlayer();
1358 player
->PrepareGossipMenu(this, GetGOInfo()->questgiver
.gossipID
, true);
1359 player
->SendPreparedGossip(this);
1362 case GAMEOBJECT_TYPE_TRAP
: //6
1364 GameObjectTemplate
const* goInfo
= GetGOInfo();
1365 if (goInfo
->trap
.spell
)
1366 CastSpell(user
, goInfo
->trap
.spell
);
1368 m_cooldownTime
= time(NULL
) + (goInfo
->trap
.cooldown
? goInfo
->trap
.cooldown
: uint32(4)); // template or 4 seconds
1370 if (goInfo
->trap
.charges
== 1) // Deactivate after trigger
1371 SetLootState(GO_JUST_DEACTIVATED
);
1375 //Sitting: Wooden bench, chairs enzz
1376 case GAMEOBJECT_TYPE_CHAIR
: //7
1378 GameObjectTemplate
const* info
= GetGOInfo();
1382 if (user
->GetTypeId() != TYPEID_PLAYER
)
1385 if (ChairListSlots
.empty()) // this is called once at first chair use to make list of available slots
1387 if (info
->chair
.chairslots
> 0) // sometimes chairs in DB have error in fields and we dont know number of slots
1388 for (uint32 i
= 0; i
< info
->chair
.chairslots
; ++i
)
1389 ChairListSlots
[i
].Clear(); // Last user of current slot set to 0 (none sit here yet)
1391 ChairListSlots
[0].Clear(); // error in DB, make one default slot
1394 Player
* player
= user
->ToPlayer();
1396 // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
1398 float lowestDist
= DEFAULT_VISIBILITY_DISTANCE
;
1400 uint32 nearest_slot
= 0;
1401 float x_lowest
= GetPositionX();
1402 float y_lowest
= GetPositionY();
1404 // the object orientation + 1/2 pi
1405 // every slot will be on that straight line
1406 float orthogonalOrientation
= GetOrientation() + float(M_PI
) * 0.5f
;
1407 // find nearest slot
1408 bool found_free_slot
= false;
1409 for (ChairSlotAndUser::iterator itr
= ChairListSlots
.begin(); itr
!= ChairListSlots
.end(); ++itr
)
1411 // the distance between this slot and the center of the go - imagine a 1D space
1412 float relativeDistance
= (info
->size
*itr
->first
) - (info
->size
*(info
->chair
.chairslots
- 1) / 2.0f
);
1414 float x_i
= GetPositionX() + relativeDistance
* std::cos(orthogonalOrientation
);
1415 float y_i
= GetPositionY() + relativeDistance
* std::sin(orthogonalOrientation
);
1417 if (!itr
->second
.IsEmpty())
1419 if (Player
* ChairUser
= ObjectAccessor::FindPlayer(itr
->second
))
1421 if (ChairUser
->IsSitState() && ChairUser
->GetStandState() != UNIT_STAND_STATE_SIT
&& ChairUser
->GetExactDist2d(x_i
, y_i
) < 0.1f
)
1422 continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->GetStandState() != UNIT_STAND_STATE_SIT check is required.
1424 itr
->second
.Clear(); // This seat is unoccupied.
1427 itr
->second
.Clear(); // The seat may of had an occupant, but they're offline.
1430 found_free_slot
= true;
1432 // calculate the distance between the player and this slot
1433 float thisDistance
= player
->GetDistance2d(x_i
, y_i
);
1435 if (thisDistance
<= lowestDist
)
1437 nearest_slot
= itr
->first
;
1438 lowestDist
= thisDistance
;
1444 if (found_free_slot
)
1446 ChairSlotAndUser::iterator itr
= ChairListSlots
.find(nearest_slot
);
1447 if (itr
!= ChairListSlots
.end())
1449 itr
->second
= player
->GetGUID(); //this slot in now used by player
1450 player
->TeleportTo(GetMapId(), x_lowest
, y_lowest
, GetPositionZ(), GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT
| TELE_TO_NOT_LEAVE_COMBAT
| TELE_TO_NOT_UNSUMMON_PET
);
1451 player
->SetStandState(UnitStandStateType(UNIT_STAND_STATE_SIT_LOW_CHAIR
+ info
->chair
.chairheight
));
1458 //big gun, its a spell/aura
1459 case GAMEOBJECT_TYPE_GOOBER
: //10
1461 GameObjectTemplate
const* info
= GetGOInfo();
1463 if (Player
* player
= user
->ToPlayer())
1465 if (info
->goober
.pageID
) // show page...
1467 WorldPackets::GameObject::PageText data
;
1468 data
.GameObjectGUID
= GetGUID();
1469 player
->SendDirectMessage(data
.Write());
1471 else if (info
->goober
.gossipID
)
1473 player
->PrepareGossipMenu(this, info
->goober
.gossipID
);
1474 player
->SendPreparedGossip(this);
1477 if (info
->goober
.eventID
)
1479 TC_LOG_DEBUG("maps.script", "Goober ScriptStart id %u for GO entry %u (GUID " UI64FMTD
").", info
->goober
.eventID
, GetEntry(), GetSpawnId());
1480 GetMap()->ScriptsStart(sEventScripts
, info
->goober
.eventID
, player
, this);
1481 EventInform(info
->goober
.eventID
, user
);
1484 // possible quest objective for active quests
1485 if (info
->goober
.questID
&& sObjectMgr
->GetQuestTemplate(info
->goober
.questID
))
1487 //Quest require to be active for GO using
1488 if (player
->GetQuestStatus(info
->goober
.questID
) != QUEST_STATUS_INCOMPLETE
)
1492 if (Group
* group
= player
->GetGroup())
1494 for (GroupReference
const* itr
= group
->GetFirstMember(); itr
!= nullptr; itr
= itr
->next())
1495 if (Player
* member
= itr
->GetSource())
1496 if (member
->IsAtGroupRewardDistance(this))
1497 member
->KillCreditGO(info
->entry
, GetGUID());
1500 player
->KillCreditGO(info
->entry
, GetGUID());
1503 if (uint32 trapEntry
= info
->goober
.linkedTrap
)
1504 TriggeringLinkedGameObject(trapEntry
, user
);
1506 SetFlag(GAMEOBJECT_FLAGS
, GO_FLAG_IN_USE
);
1507 SetLootState(GO_ACTIVATED
, user
);
1509 // this appear to be ok, however others exist in addition to this that should have custom (ex: 190510, 188692, 187389)
1510 if (info
->goober
.customAnim
)
1511 SendCustomAnim(GetGoAnimProgress());
1513 SetGoState(GO_STATE_ACTIVE
);
1515 m_cooldownTime
= time(NULL
) + info
->GetAutoCloseTime();
1517 // cast this spell later if provided
1518 spellId
= info
->goober
.spell
;
1519 spellCaster
= nullptr;
1523 case GAMEOBJECT_TYPE_CAMERA
: //13
1525 GameObjectTemplate
const* info
= GetGOInfo();
1529 if (user
->GetTypeId() != TYPEID_PLAYER
)
1532 Player
* player
= user
->ToPlayer();
1534 if (info
->camera
.camera
)
1535 player
->SendCinematicStart(info
->camera
.camera
);
1537 if (info
->camera
.eventID
)
1539 GetMap()->ScriptsStart(sEventScripts
, info
->camera
.eventID
, player
, this);
1540 EventInform(info
->camera
.eventID
, user
);
1546 case GAMEOBJECT_TYPE_FISHINGNODE
: //17
1548 Player
* player
= user
->ToPlayer();
1552 if (player
->GetGUID() != GetOwnerGUID())
1555 switch (getLootState())
1557 case GO_READY
: // ready for loot
1559 uint32 zone
, subzone
;
1560 GetZoneAndAreaId(zone
, subzone
);
1562 int32 zone_skill
= sObjectMgr
->GetFishingBaseSkillLevel(subzone
);
1564 zone_skill
= sObjectMgr
->GetFishingBaseSkillLevel(zone
);
1566 //provide error, no fishable zone or area should be 0
1568 TC_LOG_ERROR("sql.sql", "Fishable areaId %u are not properly defined in `skill_fishing_base_level`.", subzone
);
1570 int32 skill
= player
->GetSkillValue(SKILL_FISHING
);
1573 if (skill
< zone_skill
)
1575 chance
= int32(pow((double)skill
/zone_skill
, 2) * 100);
1582 int32 roll
= irand(1, 100);
1584 TC_LOG_DEBUG("misc", "Fishing check (skill: %i zone min skill: %i chance %i roll: %i", skill
, zone_skill
, chance
, roll
);
1586 player
->UpdateFishingSkill();
1588 /// @todo find reasonable value for fishing hole search
1589 GameObject
* fishingPool
= LookupFishingHoleAround(20.0f
+ CONTACT_DISTANCE
);
1591 // If fishing skill is high enough, or if fishing on a pool, send correct loot.
1592 // Fishing pools have no skill requirement as of patch 3.3.0 (undocumented change).
1593 if (chance
>= roll
|| fishingPool
)
1595 /// @todo I do not understand this hack. Need some explanation.
1596 // prevent removing GO at spell cancel
1598 SetOwnerGUID(player
->GetGUID());
1599 SetSpellId(0); // prevent removing unintended auras at Unit::RemoveGameObject
1603 fishingPool
->Use(player
);
1604 SetLootState(GO_JUST_DEACTIVATED
);
1607 player
->SendLoot(GetGUID(), LOOT_FISHING
);
1609 else // If fishing skill is too low, send junk loot.
1610 player
->SendLoot(GetGUID(), LOOT_FISHING_JUNK
);
1613 case GO_JUST_DEACTIVATED
: // nothing to do, will be deleted at next update
1617 SetLootState(GO_JUST_DEACTIVATED
);
1618 player
->SendDirectMessage(WorldPackets::GameObject::FishNotHooked().Write());
1623 player
->FinishSpell(CURRENT_CHANNELED_SPELL
);
1627 case GAMEOBJECT_TYPE_RITUAL
: //18
1629 if (user
->GetTypeId() != TYPEID_PLAYER
)
1632 Player
* player
= user
->ToPlayer();
1634 Unit
* owner
= GetOwner();
1636 GameObjectTemplate
const* info
= GetGOInfo();
1638 Player
* m_ritualOwner
= nullptr;
1639 if (!m_ritualOwnerGUID
.IsEmpty())
1640 m_ritualOwner
= ObjectAccessor::FindPlayer(m_ritualOwnerGUID
);
1642 // ritual owner is set for GO's without owner (not summoned)
1643 if (!m_ritualOwner
&& !owner
)
1645 m_ritualOwnerGUID
= player
->GetGUID();
1646 m_ritualOwner
= player
;
1651 if (owner
->GetTypeId() != TYPEID_PLAYER
)
1654 // accept only use by player from same group as owner, excluding owner itself (unique use already added in spell effect)
1655 if (player
== owner
->ToPlayer() || (info
->ritual
.castersGrouped
&& !player
->IsInSameRaidWith(owner
->ToPlayer())))
1658 // expect owner to already be channeling, so if not...
1659 if (!owner
->GetCurrentSpell(CURRENT_CHANNELED_SPELL
))
1662 // in case summoning ritual caster is GO creator
1663 spellCaster
= owner
;
1667 if (player
!= m_ritualOwner
&& (info
->ritual
.castersGrouped
&& !player
->IsInSameRaidWith(m_ritualOwner
)))
1670 spellCaster
= player
;
1673 AddUniqueUse(player
);
1675 if (info
->ritual
.animSpell
)
1677 player
->CastSpell(player
, info
->ritual
.animSpell
, true);
1679 // for this case, summoningRitual.spellId is always triggered
1683 // full amount unique participants including original summoner
1684 if (GetUniqueUseCount() == info
->ritual
.casters
)
1687 spellCaster
= m_ritualOwner
;
1689 spellId
= info
->ritual
.spell
;
1691 if (spellId
== 62330) // GO store nonexistent spell, replace by expected
1693 // spell have reagent and mana cost but it not expected use its
1694 // it triggered spell in fact cast at currently channeled GO
1699 // Cast casterTargetSpell at a random GO user
1700 // on the current DB there is only one gameobject that uses this (Ritual of Doom)
1701 // and its required target number is 1 (outter for loop will run once)
1702 if (info
->ritual
.casterTargetSpell
&& info
->ritual
.casterTargetSpell
!= 1) // No idea why this field is a bool in some cases
1703 for (uint32 i
= 0; i
< info
->ritual
.casterTargetSpellTargets
; i
++)
1704 // m_unique_users can contain only player GUIDs
1705 if (Player
* target
= ObjectAccessor::GetPlayer(*this, Trinity::Containers::SelectRandomContainerElement(m_unique_users
)))
1706 spellCaster
->CastSpell(target
, info
->ritual
.casterTargetSpell
, true);
1708 // finish owners spell
1710 owner
->FinishSpell(CURRENT_CHANNELED_SPELL
);
1712 // can be deleted now, if
1713 if (!info
->ritual
.ritualPersistent
)
1714 SetLootState(GO_JUST_DEACTIVATED
);
1717 // reset ritual for this GO
1718 m_ritualOwnerGUID
.Clear();
1719 m_unique_users
.clear();
1726 // go to end function to spell casting
1729 case GAMEOBJECT_TYPE_SPELLCASTER
: //22
1731 GameObjectTemplate
const* info
= GetGOInfo();
1735 if (info
->spellCaster
.partyOnly
)
1737 Unit
* caster
= GetOwner();
1738 if (!caster
|| caster
->GetTypeId() != TYPEID_PLAYER
)
1741 if (user
->GetTypeId() != TYPEID_PLAYER
|| !user
->ToPlayer()->IsInSameRaidWith(caster
->ToPlayer()))
1745 user
->RemoveAurasByType(SPELL_AURA_MOUNTED
);
1746 spellId
= info
->spellCaster
.spell
;
1751 case GAMEOBJECT_TYPE_MEETINGSTONE
: //23
1753 GameObjectTemplate
const* info
= GetGOInfo();
1755 if (user
->GetTypeId() != TYPEID_PLAYER
)
1758 Player
* player
= user
->ToPlayer();
1760 Player
* targetPlayer
= ObjectAccessor::FindPlayer(player
->GetTarget());
1762 // accept only use by player from same raid as caster, except caster itself
1763 if (!targetPlayer
|| targetPlayer
== player
|| !targetPlayer
->IsInSameRaidWith(player
))
1766 //required lvl checks!
1767 uint8 level
= player
->getLevel();
1768 if (level
< info
->meetingStone
.minLevel
)
1770 level
= targetPlayer
->getLevel();
1771 if (level
< info
->meetingStone
.minLevel
)
1774 if (info
->entry
== 194097)
1775 spellId
= 61994; // Ritual of Summoning
1777 spellId
= 59782; // Summoning Stone Effect
1782 case GAMEOBJECT_TYPE_FLAGSTAND
: // 24
1784 if (user
->GetTypeId() != TYPEID_PLAYER
)
1787 Player
* player
= user
->ToPlayer();
1789 if (player
->CanUseBattlegroundObject(this))
1791 // in battleground check
1792 Battleground
* bg
= player
->GetBattleground();
1796 if (player
->GetVehicle())
1799 player
->RemoveAurasByType(SPELL_AURA_MOD_STEALTH
);
1800 player
->RemoveAurasByType(SPELL_AURA_MOD_INVISIBILITY
);
1808 bg
->EventPlayerClickedOnFlag(player
, this);
1809 return; //we don;t need to delete flag ... it is despawned!
1814 case GAMEOBJECT_TYPE_FISHINGHOLE
: // 25
1816 if (user
->GetTypeId() != TYPEID_PLAYER
)
1819 Player
* player
= user
->ToPlayer();
1821 player
->SendLoot(GetGUID(), LOOT_FISHINGHOLE
);
1822 player
->UpdateCriteria(CRITERIA_TYPE_FISH_IN_GAMEOBJECT
, GetGOInfo()->entry
);
1826 case GAMEOBJECT_TYPE_FLAGDROP
: // 26
1828 if (user
->GetTypeId() != TYPEID_PLAYER
)
1831 Player
* player
= user
->ToPlayer();
1833 if (player
->CanUseBattlegroundObject(this))
1835 // in battleground check
1836 Battleground
* bg
= player
->GetBattleground();
1840 if (player
->GetVehicle())
1843 player
->RemoveAurasByType(SPELL_AURA_MOD_STEALTH
);
1844 player
->RemoveAurasByType(SPELL_AURA_MOD_INVISIBILITY
);
1847 // 179785 - Silverwing Flag
1848 // 179786 - Warsong Flag
1850 // 184142 - Netherstorm Flag
1851 GameObjectTemplate
const* info
= GetGOInfo();
1854 switch (info
->entry
)
1856 case 179785: // Silverwing Flag
1857 case 179786: // Warsong Flag
1858 if (bg
->GetTypeID(true) == BATTLEGROUND_WS
)
1859 bg
->EventPlayerClickedOnFlag(player
, this);
1861 case 184142: // Netherstorm Flag
1862 if (bg
->GetTypeID(true) == BATTLEGROUND_EY
)
1863 bg
->EventPlayerClickedOnFlag(player
, this);
1867 //this cause to call return, all flags must be deleted here!!
1873 case GAMEOBJECT_TYPE_BARBER_CHAIR
: //32
1875 GameObjectTemplate
const* info
= GetGOInfo();
1879 if (user
->GetTypeId() != TYPEID_PLAYER
)
1882 Player
* player
= user
->ToPlayer();
1884 WorldPackets::Misc::EnableBarberShop packet
;
1885 player
->SendDirectMessage(packet
.Write());
1887 // fallback, will always work
1888 player
->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT
| TELE_TO_NOT_LEAVE_COMBAT
| TELE_TO_NOT_UNSUMMON_PET
);
1890 player
->SetStandState(UnitStandStateType(UNIT_STAND_STATE_SIT_LOW_CHAIR
+ info
->barberChair
.chairheight
), info
->barberChair
.SitAnimKit
);
1893 case GAMEOBJECT_TYPE_ARTIFACT_FORGE
:
1895 GameObjectTemplate
const* info
= GetGOInfo();
1899 if (user
->GetTypeId() != TYPEID_PLAYER
)
1902 Player
* player
= user
->ToPlayer();
1903 if (PlayerConditionEntry
const* playerCondition
= sPlayerConditionStore
.LookupEntry(info
->artifactForge
.conditionID1
))
1904 if (!sConditionMgr
->IsPlayerMeetingCondition(player
, playerCondition
))
1907 Aura
const* artifactAura
= player
->GetAura(ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE
);
1908 Item
const* item
= artifactAura
? player
->GetItemByGuid(artifactAura
->GetCastItemGUID()) : nullptr;
1911 player
->SendDirectMessage(WorldPackets::Misc::DisplayGameError(GameError::ERR_MUST_EQUIP_ARTIFACT
).Write());
1915 WorldPackets::Artifact::ArtifactForgeOpened artifactForgeOpened
;
1916 artifactForgeOpened
.ArtifactGUID
= item
->GetGUID();
1917 artifactForgeOpened
.ForgeGUID
= GetGUID();
1918 player
->SendDirectMessage(artifactForgeOpened
.Write());
1921 case GAMEOBJECT_TYPE_UI_LINK
:
1923 Player
* player
= user
->ToPlayer();
1927 WorldPackets::GameObject::GameObjectUIAction gameObjectUIAction
;
1928 gameObjectUIAction
.ObjectGUID
= GetGUID();
1929 gameObjectUIAction
.UILink
= GetGOInfo()->UILink
.UILinkType
;
1930 player
->SendDirectMessage(gameObjectUIAction
.Write());
1934 if (GetGoType() >= MAX_GAMEOBJECT_TYPE
)
1935 TC_LOG_ERROR("misc", "GameObject::Use(): unit (type: %u, %s, name: %s) tries to use object (%s, name: %s) of unknown type (%u)",
1936 user
->GetTypeId(), user
->GetGUID().ToString().c_str(), user
->GetName().c_str(), GetGUID().ToString().c_str(), GetGOInfo()->name
.c_str(), GetGoType());
1943 SpellInfo
const* spellInfo
= sSpellMgr
->GetSpellInfo(spellId
);
1946 if (user
->GetTypeId() != TYPEID_PLAYER
|| !sOutdoorPvPMgr
->HandleCustomSpell(user
->ToPlayer(), spellId
, this))
1947 TC_LOG_ERROR("misc", "WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u)", spellId
, GetEntry(), GetGoType());
1949 TC_LOG_DEBUG("outdoorpvp", "WORLD: %u non-dbc spell was handled by OutdoorPvP", spellId
);
1953 if (Player
* player
= user
->ToPlayer())
1954 sOutdoorPvPMgr
->HandleCustomSpell(player
, spellId
, this);
1957 spellCaster
->CastSpell(user
, spellInfo
, triggered
);
1959 CastSpell(user
, spellId
);
1962 void GameObject::CastSpell(Unit
* target
, uint32 spellId
, bool triggered
/* = true*/)
1964 CastSpell(target
, spellId
, triggered
? TRIGGERED_FULL_MASK
: TRIGGERED_NONE
);
1967 void GameObject::CastSpell(Unit
* target
, uint32 spellId
, TriggerCastFlags triggered
)
1969 SpellInfo
const* spellInfo
= sSpellMgr
->GetSpellInfo(spellId
);
1974 for (SpellEffectInfo
const* effect
: spellInfo
->GetEffectsForDifficulty(GetMap()->GetDifficultyID()))
1976 if (effect
&& effect
->TargetA
.GetTarget() == TARGET_UNIT_CASTER
)
1986 target
->CastSpell(target
, spellInfo
, triggered
);
1990 //summon world trigger
1991 Creature
* trigger
= SummonTrigger(GetPositionX(), GetPositionY(), GetPositionZ(), 0, spellInfo
->CalcCastTime() + 100);
1995 // remove immunity flags, to allow spell to target anything
1996 trigger
->RemoveFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_IMMUNE_TO_NPC
| UNIT_FLAG_IMMUNE_TO_PC
);
1998 if (Unit
* owner
= GetOwner())
2000 trigger
->setFaction(owner
->getFaction());
2001 if (owner
->HasFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_PVP_ATTACKABLE
))
2002 trigger
->SetFlag(UNIT_FIELD_FLAGS
, UNIT_FLAG_PVP_ATTACKABLE
);
2003 // copy pvp state flags from owner
2004 trigger
->SetByteValue(UNIT_FIELD_BYTES_2
, UNIT_BYTES_2_OFFSET_PVP_FLAG
, owner
->GetByteValue(UNIT_FIELD_BYTES_2
, UNIT_BYTES_2_OFFSET_PVP_FLAG
));
2005 // needed for GO casts for proper target validation checks
2006 trigger
->SetOwnerGUID(owner
->GetGUID());
2007 trigger
->CastSpell(target
? target
: trigger
, spellInfo
, triggered
, nullptr, nullptr, owner
->GetGUID());
2011 trigger
->setFaction(spellInfo
->IsPositive() ? 35 : 14);
2012 // Set owner guid for target if no owner available - needed by trigger auras
2013 // - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell())
2014 trigger
->CastSpell(target
? target
: trigger
, spellInfo
, triggered
, nullptr, nullptr, target
? target
->GetGUID() : ObjectGuid::Empty
);
2018 void GameObject::SendCustomAnim(uint32 anim
)
2020 WorldPackets::GameObject::GameObjectCustomAnim customAnim
;
2021 customAnim
.ObjectGUID
= GetGUID();
2022 customAnim
.CustomAnim
= anim
;
2023 SendMessageToSet(customAnim
.Write(), true);
2026 bool GameObject::IsInRange(float x
, float y
, float z
, float radius
) const
2028 GameObjectDisplayInfoEntry
const* info
= sGameObjectDisplayInfoStore
.LookupEntry(m_goInfo
->displayId
);
2030 return IsWithinDist3d(x
, y
, z
, radius
);
2032 float sinA
= std::sin(GetOrientation());
2033 float cosA
= std::cos(GetOrientation());
2034 float dx
= x
- GetPositionX();
2035 float dy
= y
- GetPositionY();
2036 float dz
= z
- GetPositionZ();
2037 float dist
= std::sqrt(dx
*dx
+ dy
*dy
);
2038 //! Check if the distance between the 2 objects is 0, can happen if both objects are on the same position.
2039 //! The code below this check wont crash if dist is 0 because 0/0 in float operations is valid, and returns infinite
2040 if (G3D::fuzzyEq(dist
, 0.0f
))
2043 float sinB
= dx
/ dist
;
2044 float cosB
= dy
/ dist
;
2045 dx
= dist
* (cosA
* cosB
+ sinA
* sinB
);
2046 dy
= dist
* (cosA
* sinB
- sinA
* cosB
);
2047 return dx
< info
->GeoBoxMax
.X
+ radius
&& dx
> info
->GeoBoxMin
.X
- radius
2048 && dy
< info
->GeoBoxMax
.Y
+ radius
&& dy
> info
->GeoBoxMin
.Y
- radius
2049 && dz
< info
->GeoBoxMax
.Z
+ radius
&& dz
> info
->GeoBoxMin
.Z
- radius
;
2052 void GameObject::EventInform(uint32 eventId
, WorldObject
* invoker
/*= nullptr*/)
2058 AI()->EventInform(eventId
);
2060 if (GetZoneScript())
2061 GetZoneScript()->ProcessEvent(this, eventId
);
2063 if (BattlegroundMap
* bgMap
= GetMap()->ToBattlegroundMap())
2065 bgMap
->GetBG()->ProcessEvent(this, eventId
, invoker
);
2068 uint32
GameObject::GetScriptId() const
2070 if (GameObjectData
const* gameObjectData
= GetGOData())
2071 return gameObjectData
->ScriptId
;
2073 return GetGOInfo()->ScriptId
;
2076 // overwrite WorldObject function for proper name localization
2077 std::string
const & GameObject::GetNameForLocaleIdx(LocaleConstant loc_idx
) const
2079 if (loc_idx
!= DEFAULT_LOCALE
)
2081 uint8 uloc_idx
= uint8(loc_idx
);
2082 if (GameObjectLocale
const* cl
= sObjectMgr
->GetGameObjectLocale(GetEntry()))
2083 if (cl
->Name
.size() > uloc_idx
&& !cl
->Name
[uloc_idx
].empty())
2084 return cl
->Name
[uloc_idx
];
2090 void GameObject::UpdatePackedRotation()
2092 static const int32 PACK_YZ
= 1 << 20;
2093 static const int32 PACK_X
= PACK_YZ
<< 1;
2095 static const int32 PACK_YZ_MASK
= (PACK_YZ
<< 1) - 1;
2096 static const int32 PACK_X_MASK
= (PACK_X
<< 1) - 1;
2098 int8 w_sign
= (m_worldRotation
.w
>= 0.f
? 1 : -1);
2099 int64 x
= int32(m_worldRotation
.x
* PACK_X
) * w_sign
& PACK_X_MASK
;
2100 int64 y
= int32(m_worldRotation
.y
* PACK_YZ
) * w_sign
& PACK_YZ_MASK
;
2101 int64 z
= int32(m_worldRotation
.z
* PACK_YZ
) * w_sign
& PACK_YZ_MASK
;
2102 m_packedRotation
= z
| (y
<< 21) | (x
<< 42);
2105 void GameObject::SetWorldRotation(float qx
, float qy
, float qz
, float qw
)
2107 G3D::Quat
rotation(qx
, qy
, qz
, qw
);
2109 m_worldRotation
.x
= rotation
.x
;
2110 m_worldRotation
.y
= rotation
.y
;
2111 m_worldRotation
.z
= rotation
.z
;
2112 m_worldRotation
.w
= rotation
.w
;
2113 UpdatePackedRotation();
2116 void GameObject::SetParentRotation(QuaternionData
const& rotation
)
2118 SetFloatValue(GAMEOBJECT_PARENTROTATION
+ 0, rotation
.x
);
2119 SetFloatValue(GAMEOBJECT_PARENTROTATION
+ 1, rotation
.y
);
2120 SetFloatValue(GAMEOBJECT_PARENTROTATION
+ 2, rotation
.z
);
2121 SetFloatValue(GAMEOBJECT_PARENTROTATION
+ 3, rotation
.w
);
2124 void GameObject::SetWorldRotationAngles(float z_rot
, float y_rot
, float x_rot
)
2126 G3D::Quat
quat(G3D::Matrix3::fromEulerAnglesZYX(z_rot
, y_rot
, x_rot
));
2127 SetWorldRotation(quat
.x
, quat
.y
, quat
.z
, quat
.w
);
2130 void GameObject::ModifyHealth(int32 change
, Unit
* attackerOrHealer
/*= nullptr*/, uint32 spellId
/*= 0*/)
2132 if (!m_goValue
.Building
.MaxHealth
|| !change
)
2135 // prevent double destructions of the same object
2136 if (change
< 0 && !m_goValue
.Building
.Health
)
2139 if (int32(m_goValue
.Building
.Health
) + change
<= 0)
2140 m_goValue
.Building
.Health
= 0;
2141 else if (int32(m_goValue
.Building
.Health
) + change
>= int32(m_goValue
.Building
.MaxHealth
))
2142 m_goValue
.Building
.Health
= m_goValue
.Building
.MaxHealth
;
2144 m_goValue
.Building
.Health
+= change
;
2146 // Set the health bar, value = 255 * healthPct;
2147 SetGoAnimProgress(m_goValue
.Building
.Health
* 255 / m_goValue
.Building
.MaxHealth
);
2149 Player
* player
= attackerOrHealer
? attackerOrHealer
->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr;
2151 // dealing damage, send packet
2154 WorldPackets::GameObject::DestructibleBuildingDamage packet
;
2155 packet
.Caster
= attackerOrHealer
->GetGUID(); // todo: this can be a GameObject
2156 packet
.Target
= GetGUID();
2157 packet
.Damage
= -change
;
2158 packet
.Owner
= player
->GetGUID();
2159 packet
.SpellID
= spellId
;
2160 player
->SendDirectMessage(packet
.Write());
2163 GameObjectDestructibleState newState
= GetDestructibleState();
2165 if (!m_goValue
.Building
.Health
)
2166 newState
= GO_DESTRUCTIBLE_DESTROYED
;
2167 else if (m_goValue
.Building
.Health
<= 10000/*GetGOInfo()->destructibleBuilding.damagedNumHits*/) // TODO: Get health somewhere
2168 newState
= GO_DESTRUCTIBLE_DAMAGED
;
2169 else if (m_goValue
.Building
.Health
== m_goValue
.Building
.MaxHealth
)
2170 newState
= GO_DESTRUCTIBLE_INTACT
;
2172 if (newState
== GetDestructibleState())
2175 /// @todo: pass attackerOrHealer instead of player
2176 SetDestructibleState(newState
, player
, false);
2179 void GameObject::SetDestructibleState(GameObjectDestructibleState state
, Player
* eventInvoker
/*= nullptr*/, bool setHealth
/*= false*/)
2181 // the user calling this must know he is already operating on destructible gameobject
2182 ASSERT(GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
);
2186 case GO_DESTRUCTIBLE_INTACT
:
2187 RemoveFlag(GAMEOBJECT_FLAGS
, GO_FLAG_DAMAGED
| GO_FLAG_DESTROYED
);
2188 SetDisplayId(m_goInfo
->displayId
);
2191 m_goValue
.Building
.Health
= m_goValue
.Building
.MaxHealth
;
2192 SetGoAnimProgress(255);
2194 EnableCollision(true);
2196 case GO_DESTRUCTIBLE_DAMAGED
:
2198 EventInform(m_goInfo
->destructibleBuilding
.DamagedEvent
, eventInvoker
);
2199 sScriptMgr
->OnGameObjectDamaged(this, eventInvoker
);
2201 RemoveFlag(GAMEOBJECT_FLAGS
, GO_FLAG_DESTROYED
);
2202 SetFlag(GAMEOBJECT_FLAGS
, GO_FLAG_DAMAGED
);
2204 uint32 modelId
= m_goInfo
->displayId
;
2205 if (DestructibleModelDataEntry
const* modelData
= sDestructibleModelDataStore
.LookupEntry(m_goInfo
->destructibleBuilding
.DestructibleModelRec
))
2206 if (modelData
->State1Wmo
)
2207 modelId
= modelData
->State1Wmo
;
2208 SetDisplayId(modelId
);
2212 m_goValue
.Building
.Health
= 10000/*m_goInfo->destructibleBuilding.damagedNumHits*/;
2213 uint32 maxHealth
= m_goValue
.Building
.MaxHealth
;
2214 // in this case current health is 0 anyway so just prevent crashing here
2217 SetGoAnimProgress(m_goValue
.Building
.Health
* 255 / maxHealth
);
2221 case GO_DESTRUCTIBLE_DESTROYED
:
2223 sScriptMgr
->OnGameObjectDestroyed(this, eventInvoker
);
2224 EventInform(m_goInfo
->destructibleBuilding
.DestroyedEvent
, eventInvoker
);
2226 if (Battleground
* bg
= eventInvoker
->GetBattleground())
2227 bg
->DestroyGate(eventInvoker
, this);
2229 RemoveFlag(GAMEOBJECT_FLAGS
, GO_FLAG_DAMAGED
);
2230 SetFlag(GAMEOBJECT_FLAGS
, GO_FLAG_DESTROYED
);
2232 uint32 modelId
= m_goInfo
->displayId
;
2233 if (DestructibleModelDataEntry
const* modelData
= sDestructibleModelDataStore
.LookupEntry(m_goInfo
->destructibleBuilding
.DestructibleModelRec
))
2234 if (modelData
->State2Wmo
)
2235 modelId
= modelData
->State2Wmo
;
2236 SetDisplayId(modelId
);
2240 m_goValue
.Building
.Health
= 0;
2241 SetGoAnimProgress(0);
2243 EnableCollision(false);
2246 case GO_DESTRUCTIBLE_REBUILDING
:
2248 EventInform(m_goInfo
->destructibleBuilding
.RebuildingEvent
, eventInvoker
);
2249 RemoveFlag(GAMEOBJECT_FLAGS
, GO_FLAG_DAMAGED
| GO_FLAG_DESTROYED
);
2251 uint32 modelId
= m_goInfo
->displayId
;
2252 if (DestructibleModelDataEntry
const* modelData
= sDestructibleModelDataStore
.LookupEntry(m_goInfo
->destructibleBuilding
.DestructibleModelRec
))
2253 if (modelData
->State3Wmo
)
2254 modelId
= modelData
->State3Wmo
;
2255 SetDisplayId(modelId
);
2257 // restores to full health
2260 m_goValue
.Building
.Health
= m_goValue
.Building
.MaxHealth
;
2261 SetGoAnimProgress(255);
2263 EnableCollision(true);
2269 void GameObject::SetLootState(LootState state
, Unit
* unit
)
2271 m_lootState
= state
;
2273 m_lootStateUnitGUID
= unit
->GetGUID();
2275 m_lootStateUnitGUID
.Clear();
2277 AI()->OnStateChanged(state
, unit
);
2278 sScriptMgr
->OnGameObjectLootStateChanged(this, state
, unit
);
2280 if (GetGoType() == GAMEOBJECT_TYPE_DOOR
) // only set collision for doors on SetGoState
2285 bool collision
= false;
2286 // Use the current go state
2287 if ((GetGoState() != GO_STATE_READY
&& (state
== GO_ACTIVATED
|| state
== GO_JUST_DEACTIVATED
)) || state
== GO_READY
)
2288 collision
= !collision
;
2290 EnableCollision(collision
);
2294 void GameObject::SetGoState(GOState state
)
2296 SetByteValue(GAMEOBJECT_BYTES_1
, 0, state
);
2297 sScriptMgr
->OnGameObjectStateChanged(this, state
);
2298 if (m_model
&& !IsTransport())
2303 // startOpen determines whether we are going to add or remove the LoS on activation
2304 bool collision
= false;
2305 if (state
== GO_STATE_READY
)
2306 collision
= !collision
;
2308 EnableCollision(collision
);
2312 uint32
GameObject::GetTransportPeriod() const
2314 ASSERT(GetGOInfo()->type
== GAMEOBJECT_TYPE_TRANSPORT
);
2315 if (m_goValue
.Transport
.AnimationInfo
)
2316 return m_goValue
.Transport
.AnimationInfo
->TotalTime
;
2321 void GameObject::SetTransportState(GOState state
, uint32 stopFrame
/*= 0*/)
2323 if (GetGoState() == state
)
2326 ASSERT(GetGOInfo()->type
== GAMEOBJECT_TYPE_TRANSPORT
);
2327 ASSERT(state
>= GO_STATE_TRANSPORT_ACTIVE
);
2328 if (state
== GO_STATE_TRANSPORT_ACTIVE
)
2330 m_goValue
.Transport
.StateUpdateTimer
= 0;
2331 m_goValue
.Transport
.PathProgress
= getMSTime();
2332 if (GetGoState() >= GO_STATE_TRANSPORT_STOPPED
)
2333 m_goValue
.Transport
.PathProgress
+= m_goValue
.Transport
.StopFrames
->at(GetGoState() - GO_STATE_TRANSPORT_STOPPED
);
2334 SetGoState(GO_STATE_TRANSPORT_ACTIVE
);
2338 ASSERT(state
< GOState(GO_STATE_TRANSPORT_STOPPED
+ MAX_GO_STATE_TRANSPORT_STOP_FRAMES
));
2339 ASSERT(stopFrame
< m_goValue
.Transport
.StopFrames
->size());
2340 m_goValue
.Transport
.PathProgress
= getMSTime() + m_goValue
.Transport
.StopFrames
->at(stopFrame
);
2341 SetGoState(GOState(GO_STATE_TRANSPORT_STOPPED
+ stopFrame
));
2345 void GameObject::SetDisplayId(uint32 displayid
)
2347 SetUInt32Value(GAMEOBJECT_DISPLAYID
, displayid
);
2351 uint8
GameObject::GetNameSetId() const
2353 switch (GetGoType())
2355 case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
:
2356 if (DestructibleModelDataEntry
const* modelData
= sDestructibleModelDataStore
.LookupEntry(m_goInfo
->destructibleBuilding
.DestructibleModelRec
))
2358 switch (GetDestructibleState())
2360 case GO_DESTRUCTIBLE_INTACT
:
2361 return modelData
->State0NameSet
;
2362 case GO_DESTRUCTIBLE_DAMAGED
:
2363 return modelData
->State1NameSet
;
2364 case GO_DESTRUCTIBLE_DESTROYED
:
2365 return modelData
->State2NameSet
;
2366 case GO_DESTRUCTIBLE_REBUILDING
:
2367 return modelData
->State3NameSet
;
2373 case GAMEOBJECT_TYPE_GARRISON_BUILDING
:
2374 case GAMEOBJECT_TYPE_GARRISON_PLOT
:
2375 case GAMEOBJECT_TYPE_PHASEABLE_MO
:
2376 return GetByteValue(GAMEOBJECT_FLAGS
, 1) & 0xF;
2384 void GameObject::EnableCollision(bool enable
)
2389 /*if (enable && !GetMap()->ContainsGameObjectModel(*m_model))
2390 GetMap()->InsertGameObjectModel(*m_model);*/
2392 m_model
->enableCollision(enable
);
2395 void GameObject::UpdateModel()
2400 if (GetMap()->ContainsGameObjectModel(*m_model
))
2401 GetMap()->RemoveGameObjectModel(*m_model
);
2403 m_model
= CreateModel();
2405 GetMap()->InsertGameObjectModel(*m_model
);
2408 Player
* GameObject::GetLootRecipient() const
2410 if (!m_lootRecipient
)
2412 return ObjectAccessor::FindConnectedPlayer(m_lootRecipient
);
2415 Group
* GameObject::GetLootRecipientGroup() const
2417 if (!m_lootRecipientGroup
)
2419 return sGroupMgr
->GetGroupByGUID(m_lootRecipientGroup
);
2422 void GameObject::SetLootRecipient(Unit
* unit
, Group
* group
)
2424 // set the player whose group should receive the right
2425 // to loot the creature after it dies
2426 // should be set to nullptr after the loot disappears
2430 m_lootRecipient
.Clear();
2431 m_lootRecipientGroup
= group
? group
->GetGUID() : ObjectGuid::Empty
;
2435 if (unit
->GetTypeId() != TYPEID_PLAYER
&& !unit
->IsVehicle())
2438 Player
* player
= unit
->GetCharmerOrOwnerPlayerOrPlayerItself();
2439 if (!player
) // normal creature, no player involved
2442 m_lootRecipient
= player
->GetGUID();
2444 // either get the group from the passed parameter or from unit's one
2446 m_lootRecipientGroup
= group
->GetGUID();
2447 else if (Group
* unitGroup
= player
->GetGroup())
2448 m_lootRecipientGroup
= unitGroup
->GetGUID();
2451 bool GameObject::IsLootAllowedFor(Player
const* player
) const
2453 if (!m_lootRecipient
&& !m_lootRecipientGroup
)
2456 if (player
->GetGUID() == m_lootRecipient
)
2459 Group
const* playerGroup
= player
->GetGroup();
2460 if (!playerGroup
|| playerGroup
!= GetLootRecipientGroup()) // if we dont have a group we arent the recipient
2461 return false; // if go doesnt have group bound it means it was solo killed by someone else
2466 GameObject
* GameObject::GetLinkedTrap()
2468 return ObjectAccessor::GetGameObject(*this, m_linkedTrap
);
2471 void GameObject::BuildValuesUpdate(uint8 updateType
, ByteBuffer
* data
, Player
* target
) const
2476 bool isStoppableTransport
= GetGoType() == GAMEOBJECT_TYPE_TRANSPORT
&& !m_goValue
.Transport
.StopFrames
->empty();
2477 bool forcedFlags
= GetGoType() == GAMEOBJECT_TYPE_CHEST
&& GetGOInfo()->chest
.usegrouplootrules
&& HasLootRecipient();
2478 bool targetIsGM
= target
->IsGameMaster();
2480 std::size_t blockCount
= UpdateMask::GetBlockCount(m_valuesCount
);
2482 uint32
* flags
= GameObjectUpdateFieldFlags
;
2483 uint32 visibleFlag
= UF_FLAG_PUBLIC
;
2484 if (GetOwnerGUID() == target
->GetGUID())
2485 visibleFlag
|= UF_FLAG_OWNER
;
2487 *data
<< uint8(blockCount
);
2488 std::size_t maskPos
= data
->wpos();
2489 data
->resize(data
->size() + blockCount
* sizeof(UpdateMask::BlockType
));
2491 for (uint16 index
= 0; index
< m_valuesCount
; ++index
)
2493 if (_fieldNotifyFlags
& flags
[index
] ||
2494 ((updateType
== UPDATETYPE_VALUES
? _changesMask
[index
] : m_uint32Values
[index
]) && (flags
[index
] & visibleFlag
)) ||
2495 (index
== GAMEOBJECT_FLAGS
&& forcedFlags
))
2497 UpdateMask::SetUpdateBit(data
->contents() + maskPos
, index
);
2499 if (index
== OBJECT_DYNAMIC_FLAGS
)
2501 uint16 dynFlags
= 0;
2502 int16 pathProgress
= -1;
2503 switch (GetGoType())
2505 case GAMEOBJECT_TYPE_QUESTGIVER
:
2506 if (ActivateToQuest(target
))
2507 dynFlags
|= GO_DYNFLAG_LO_ACTIVATE
;
2509 case GAMEOBJECT_TYPE_CHEST
:
2510 case GAMEOBJECT_TYPE_GOOBER
:
2511 if (ActivateToQuest(target
))
2512 dynFlags
|= GO_DYNFLAG_LO_ACTIVATE
| GO_DYNFLAG_LO_SPARKLE
;
2513 else if (targetIsGM
)
2514 dynFlags
|= GO_DYNFLAG_LO_ACTIVATE
;
2516 case GAMEOBJECT_TYPE_GENERIC
:
2517 if (ActivateToQuest(target
))
2518 dynFlags
|= GO_DYNFLAG_LO_SPARKLE
;
2520 case GAMEOBJECT_TYPE_TRANSPORT
:
2521 case GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT
:
2523 if (uint32 transportPeriod
= GetTransportPeriod())
2525 float timer
= float(m_goValue
.Transport
.PathProgress
% transportPeriod
);
2526 pathProgress
= int16(timer
/ float(transportPeriod
) * 65535.0f
);
2534 *data
<< uint16(dynFlags
);
2535 *data
<< int16(pathProgress
);
2537 else if (index
== GAMEOBJECT_FLAGS
)
2539 uint32 goFlags
= m_uint32Values
[GAMEOBJECT_FLAGS
];
2540 if (GetGoType() == GAMEOBJECT_TYPE_CHEST
)
2541 if (GetGOInfo()->chest
.usegrouplootrules
&& !IsLootAllowedFor(target
))
2542 goFlags
|= GO_FLAG_LOCKED
| GO_FLAG_NOT_SELECTABLE
;
2546 else if (index
== GAMEOBJECT_LEVEL
)
2548 if (isStoppableTransport
)
2549 *data
<< uint32(m_goValue
.Transport
.PathProgress
);
2551 *data
<< m_uint32Values
[index
];
2553 else if (index
== GAMEOBJECT_BYTES_1
)
2555 uint32 bytes1
= m_uint32Values
[index
];
2556 if (isStoppableTransport
&& GetGoState() == GO_STATE_TRANSPORT_ACTIVE
)
2558 if ((m_goValue
.Transport
.StateUpdateTimer
/ 20000) & 1)
2560 bytes1
&= 0xFFFFFF00;
2561 bytes1
|= GO_STATE_TRANSPORT_STOPPED
;
2568 *data
<< m_uint32Values
[index
]; // other cases
2573 void GameObject::GetRespawnPosition(float &x
, float &y
, float &z
, float* ori
/* = nullptr*/) const
2577 if (GameObjectData
const* data
= sObjectMgr
->GetGOData(GetSpawnId()))
2583 *ori
= data
->orientation
;
2592 *ori
= GetOrientation();
2595 float GameObject::GetInteractionDistance() const
2597 switch (GetGoType())
2599 /// @todo find out how the client calculates the maximal usage distance to spellless working
2600 // gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary choosen number
2601 case GAMEOBJECT_TYPE_GUILD_BANK
:
2602 case GAMEOBJECT_TYPE_MAILBOX
:
2604 case GAMEOBJECT_TYPE_FISHINGHOLE
:
2605 case GAMEOBJECT_TYPE_FISHINGNODE
:
2606 return 20.0f
+ CONTACT_DISTANCE
; // max spell range
2608 return INTERACTION_DISTANCE
;
2612 void GameObject::UpdateModelPosition()
2617 if (GetMap()->ContainsGameObjectModel(*m_model
))
2619 GetMap()->RemoveGameObjectModel(*m_model
);
2620 m_model
->UpdatePosition();
2621 GetMap()->InsertGameObjectModel(*m_model
);
2625 void GameObject::SetAnimKitId(uint16 animKitId
, bool oneshot
)
2627 if (_animKitId
== animKitId
)
2630 if (animKitId
&& !sAnimKitStore
.LookupEntry(animKitId
))
2634 _animKitId
= animKitId
;
2638 WorldPackets::GameObject::GameObjectActivateAnimKit activateAnimKit
;
2639 activateAnimKit
.ObjectGUID
= GetGUID();
2640 activateAnimKit
.AnimKitID
= animKitId
;
2641 activateAnimKit
.Maintain
= !oneshot
;
2642 SendMessageToSet(activateAnimKit
.Write(), true);
2645 class GameObjectModelOwnerImpl
: public GameObjectModelOwnerBase
2648 explicit GameObjectModelOwnerImpl(GameObject
const* owner
) : _owner(owner
) { }
2649 virtual ~GameObjectModelOwnerImpl() = default;
2651 bool IsSpawned() const override
{ return _owner
->isSpawned(); }
2652 uint32
GetDisplayId() const override
{ return _owner
->GetDisplayId(); }
2653 uint8
GetNameSetId() const override
{ return _owner
->GetNameSetId(); }
2654 bool IsInPhase(PhaseShift
const& phaseShift
) const override
{ return _owner
->GetPhaseShift().CanSee(phaseShift
); }
2655 G3D::Vector3
GetPosition() const override
{ return G3D::Vector3(_owner
->GetPositionX(), _owner
->GetPositionY(), _owner
->GetPositionZ()); }
2656 float GetOrientation() const override
{ return _owner
->GetOrientation(); }
2657 float GetScale() const override
{ return _owner
->GetObjectScale(); }
2658 void DebugVisualizeCorner(G3D::Vector3
const& corner
) const override
{ _owner
->SummonCreature(1, corner
.x
, corner
.y
, corner
.z
, 0, TEMPSUMMON_MANUAL_DESPAWN
); }
2661 GameObject
const* _owner
;
2664 GameObjectModel
* GameObject::CreateModel()
2666 return GameObjectModel::Create(Trinity::make_unique
<GameObjectModelOwnerImpl
>(this), sWorld
->GetDataPath());