TrinityCore
Loading...
Searching...
No Matches
GameObject.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "GameObject.h"
19#include "ArtifactPackets.h"
20#include "AzeriteItem.h"
21#include "AzeritePackets.h"
22#include "Battleground.h"
23#include "BattlegroundPackets.h"
24#include "CellImpl.h"
25#include "Containers.h"
26#include "CreatureAISelector.h"
27#include "DB2Stores.h"
28#include "DatabaseEnv.h"
29#include "G3DPosition.hpp"
30#include "GameEventSender.h"
31#include "GameObjectAI.h"
32#include "GameObjectModel.h"
33#include "GameObjectPackets.h"
34#include "GameTime.h"
35#include "GossipDef.h"
36#include "GridNotifiersImpl.h"
37#include "Group.h"
38#include "Item.h"
39#include "ItemBonusMgr.h"
40#include "Log.h"
41#include "Loot.h"
42#include "LootMgr.h"
43#include "Map.h"
44#include "MapManager.h"
45#include "MapUtils.h"
46#include "MiscPackets.h"
47#include "ObjectAccessor.h"
48#include "ObjectMgr.h"
49#include "OutdoorPvPMgr.h"
50#include "PhasingHandler.h"
51#include "PoolMgr.h"
52#include "QueryPackets.h"
53#include "SpellAuras.h"
54#include "SpellMgr.h"
55#include "Transport.h"
56#include "Util.h"
57#include "Vignette.h"
58#include "World.h"
59#include <G3D/Box.h>
60#include <G3D/CoordinateFrame.h>
61#include <G3D/Quat.h>
62#include <sstream>
63
65{
66 QueryData = std::make_unique<WorldPacket[]>(TOTAL_LOCALES);
67
68 for (uint8 loc = LOCALE_enUS; loc < TOTAL_LOCALES; ++loc)
69 {
70 if (!sWorld->getBoolConfig(CONFIG_LOAD_LOCALES) && loc != DEFAULT_LOCALE)
71 continue;
72
73 QueryData[loc] = BuildQueryData(static_cast<LocaleConstant>(loc));
74 }
75}
76
78{
80
81 queryTemp.GameObjectID = entry;
82
83 queryTemp.Allow = true;
85
86 stats.Type = type;
87 stats.DisplayID = displayId;
88
89 stats.Name[0] = name;
90 stats.IconName = IconName;
92 stats.UnkString = unk1;
93
94 if (loc != LOCALE_enUS)
95 if (GameObjectLocale const* gameObjectLocale = sObjectMgr->GetGameObjectLocale(entry))
96 {
97 ObjectMgr::GetLocaleString(gameObjectLocale->Name, loc, stats.Name[0]);
98 ObjectMgr::GetLocaleString(gameObjectLocale->CastBarCaption, loc, stats.CastBarCaption);
99 ObjectMgr::GetLocaleString(gameObjectLocale->Unk1, loc, stats.UnkString);
100 }
101
102 stats.Size = size;
103
104 if (std::vector<uint32> const* items = sObjectMgr->GetGameObjectQuestItemList(entry))
105 for (int32 item : *items)
106 stats.QuestItems.push_back(item);
107
108 memcpy(stats.Data.data(), raw.data, MAX_GAMEOBJECT_DATA * sizeof(int32));
111
112 queryTemp.Write();
113 queryTemp.ShrinkToFit();
114 return queryTemp.Move();
115}
116
118{
119 return fabs(x * x + y * y + z * z + w * w - 1.0f) < 1e-5f;
120}
121
122void QuaternionData::toEulerAnglesZYX(float& Z, float& Y, float& X) const
123{
124 G3D::Matrix3(G3D::Quat(x, y, z, w)).toEulerAnglesZYX(Z, Y, X);
125}
126
128{
129 G3D::Quat quat(G3D::Matrix3::fromEulerAnglesZYX(Z, Y, X));
130 return QuaternionData(quat.x, quat.y, quat.z, quat.w);
131}
132
134
136{
137//11 GAMEOBJECT_TYPE_TRANSPORT
139{
140public:
141 static constexpr Milliseconds PositionUpdateInterval = 50ms;
142
143 explicit Transport(GameObject& owner) : GameObjectTypeBase(owner), _animationInfo(sTransportMgr->GetTransportAnimInfo(owner.GetGOInfo()->entry)),
146 {
147 GameObjectTemplate const* goInfo = _owner.GetGOInfo();
148 if (goInfo->transport.Timeto2ndfloor > 0)
149 {
150 _stopFrames.push_back(goInfo->transport.Timeto2ndfloor);
151 if (goInfo->transport.Timeto3rdfloor > 0)
152 {
153 _stopFrames.push_back(goInfo->transport.Timeto3rdfloor);
154 if (goInfo->transport.Timeto4thfloor > 0)
155 {
156 _stopFrames.push_back(goInfo->transport.Timeto4thfloor);
157 if (goInfo->transport.Timeto5thfloor > 0)
158 {
159 _stopFrames.push_back(goInfo->transport.Timeto5thfloor);
160 if (goInfo->transport.Timeto6thfloor > 0)
161 {
162 _stopFrames.push_back(goInfo->transport.Timeto6thfloor);
163 if (goInfo->transport.Timeto7thfloor > 0)
164 {
165 _stopFrames.push_back(goInfo->transport.Timeto7thfloor);
166 if (goInfo->transport.Timeto8thfloor > 0)
167 {
168 _stopFrames.push_back(goInfo->transport.Timeto8thfloor);
169 if (goInfo->transport.Timeto9thfloor > 0)
170 {
171 _stopFrames.push_back(goInfo->transport.Timeto9thfloor);
172 if (goInfo->transport.Timeto10thfloor > 0)
173 _stopFrames.push_back(goInfo->transport.Timeto10thfloor);
174 }
175 }
176 }
177 }
178 }
179 }
180 }
181 }
182
183 if (!_stopFrames.empty())
184 {
185 _pathProgress = 0;
187 }
188
190 }
191
192 void Update(uint32 diff) override
193 {
194 if (!_animationInfo)
195 return;
196
199 return;
200
202
204 uint32 period = GetTransportPeriod();
205 uint32 newProgress = 0;
206 if (_stopFrames.empty())
207 newProgress = now % period;
208 else
209 {
210 int32 stopTargetTime = 0;
212 stopTargetTime = 0;
213 else
215
216 if (now < uint32(*_owner.m_gameObjectData->Level))
217 {
218 int32 timeToStop = _owner.m_gameObjectData->Level - _stateChangeTime;
219 float stopSourcePathPct = float(_stateChangeProgress) / float(period);
220 float stopTargetPathPct = float(stopTargetTime) / float(period);
221 float timeSinceStopProgressPct = float(now - _stateChangeTime) / float(timeToStop);
222
223 float progressPct;
225 {
227 stopTargetPathPct = 1.0f;
228
229 float pathPctBetweenStops = stopTargetPathPct - stopSourcePathPct;
230 if (pathPctBetweenStops < 0.0f)
231 pathPctBetweenStops += 1.0f;
232
233 progressPct = pathPctBetweenStops * timeSinceStopProgressPct + stopSourcePathPct;
234 if (progressPct > 1.0f)
235 progressPct = progressPct - 1.0f;
236 }
237 else
238 {
239 float pathPctBetweenStops = stopSourcePathPct - stopTargetPathPct;
240 if (pathPctBetweenStops < 0.0f)
241 pathPctBetweenStops += 1.0f;
242
243 progressPct = stopSourcePathPct - pathPctBetweenStops * timeSinceStopProgressPct;
244 if (progressPct < 0.0f)
245 progressPct += 1.0f;
246 }
247
248 newProgress = uint32(float(period) * progressPct) % period;
249 }
250 else
251 newProgress = stopTargetTime;
252
253 if (int32(newProgress) == stopTargetTime && newProgress != _pathProgress)
254 {
255 uint32 eventId = [&]()
256 {
258 {
259 case 0:
261 case 1:
263 case 2:
265 case 3:
267 case 4:
269 case 5:
271 case 6:
273 case 7:
275 case 8:
277 case 9:
279 default:
280 return 0u;
281 }
282 }();
283 if (eventId)
284 GameEvents::Trigger(eventId, &_owner, &_owner);
285
287 {
288 GOState currentState = _owner.GetGoState();
289 GOState newState;
290 if (currentState == GO_STATE_TRANSPORT_ACTIVE)
292 else if (currentState - GO_STATE_TRANSPORT_ACTIVE == int32(_stopFrames.size()))
293 newState = GOState(currentState - 1);
295 newState = GOState(currentState - 1);
296 else
297 newState = GOState(currentState + 1);
298
299 _owner.SetGoState(newState);
300 }
301 }
302 }
303
304 if (_pathProgress == newProgress)
305 return;
306
307 _pathProgress = newProgress;
308
309 TransportAnimationEntry const* oldAnimation = _animationInfo->GetPrevAnimNode(newProgress);
310 TransportAnimationEntry const* newAnimation = _animationInfo->GetNextAnimNode(newProgress);
311 if (oldAnimation && newAnimation)
312 {
313 G3D::Matrix3 pathRotation = G3D::Quat(_owner.m_gameObjectData->ParentRotation->x, _owner.m_gameObjectData->ParentRotation->y,
314 _owner.m_gameObjectData->ParentRotation->z, _owner.m_gameObjectData->ParentRotation->w).toRotationMatrix();
315
316 G3D::Vector3 prev(oldAnimation->Pos.X, oldAnimation->Pos.Y, oldAnimation->Pos.Z);
317 G3D::Vector3 next(newAnimation->Pos.X, newAnimation->Pos.Y, newAnimation->Pos.Z);
318
319 G3D::Vector3 dst = next;
320 if (prev != next)
321 {
322 float animProgress = float(newProgress - oldAnimation->TimeIndex) / float(newAnimation->TimeIndex - oldAnimation->TimeIndex);
323
324 dst = prev.lerp(next, animProgress);
325 }
326
327 dst = dst * pathRotation;
328 dst += PositionToVector3(_owner.GetStationaryPosition());
329
330 _owner.GetMap()->GameObjectRelocation(&_owner, dst.x, dst.y, dst.z, _owner.GetOrientation());
331 }
332
333 TransportRotationEntry const* oldRotation = _animationInfo->GetPrevAnimRotation(newProgress);
334 TransportRotationEntry const* newRotation = _animationInfo->GetNextAnimRotation(newProgress);
335 if (oldRotation && newRotation)
336 {
337 G3D::Quat prev(oldRotation->Rot[0], oldRotation->Rot[1], oldRotation->Rot[2], oldRotation->Rot[3]);
338 G3D::Quat next(newRotation->Rot[0], newRotation->Rot[1], newRotation->Rot[2], newRotation->Rot[3]);
339
340 G3D::Quat rotation = next;
341
342 if (prev != next)
343 {
344 float animProgress = float(newProgress - oldRotation->TimeIndex) / float(newRotation->TimeIndex - oldRotation->TimeIndex);
345
346 rotation = prev.slerp(next, animProgress);
347 }
348
349 _owner.SetLocalRotation(rotation.x, rotation.y, rotation.z, rotation.w);
351 }
352
353 // update progress marker for client
354 _owner.SetPathProgressForClient(float(_pathProgress) / float(period));
355 }
356
357 void OnStateChanged(GOState oldState, GOState newState) override
358 {
360
361 // transports without stop frames just keep animating in state 24
362 if (_stopFrames.empty())
363 {
364 if (newState != GO_STATE_TRANSPORT_ACTIVE)
366 return;
367 }
368
369 int32 stopPathProgress = 0;
370
371 if (newState != GO_STATE_TRANSPORT_ACTIVE)
372 {
374 uint32 stopFrame = newState - GO_STATE_TRANSPORT_STOPPED;
375 ASSERT(stopFrame < _stopFrames.size());
376 stopPathProgress = _stopFrames[stopFrame];
377 }
378
381 uint32 timeToStop = std::abs(int32(_pathProgress) - stopPathProgress);
384
385 if (oldState == GO_STATE_ACTIVE || oldState == newState)
386 {
387 // initialization
388 if (int32(_pathProgress) > stopPathProgress)
390 else
392
393 return;
394 }
395
396 int32 pauseTimesCount = _stopFrames.size();
397 int32 newToOldStateDelta = newState - oldState;
398 if (newToOldStateDelta < 0)
399 newToOldStateDelta += pauseTimesCount + 1;
400
401 int32 oldToNewStateDelta = oldState - newState;
402 if (oldToNewStateDelta < 0)
403 oldToNewStateDelta += pauseTimesCount + 1;
404
405 // this additional check is neccessary because client doesn't check dynamic flags on progress update
406 // instead it multiplies progress from dynamicflags field by -1 and then compares that against 0
407 // when calculating path progress while we simply check the flag if (!_owner.HasDynamicFlag(GO_DYNFLAG_LO_INVERTED_MOVEMENT))
408 bool isAtStartOfPath = _stateChangeProgress == 0;
409
410 if (oldToNewStateDelta < newToOldStateDelta && !isAtStartOfPath)
412 else
414 }
415
416 void OnRelocated() override
417 {
419 }
420
422 {
423 for (WorldObject* passenger : _passengers)
424 UpdatePassengerPosition(_owner.GetMap(), passenger, _owner.GetPositionWithOffset(passenger->m_movementInfo.transport.pos), true);
425 }
426
428 {
429 if (_animationInfo)
431
432 return 1;
433 }
434
435 std::span<uint32 const> GetPauseTimes() const
436 {
437 return _stopFrames;
438 }
439
440 ObjectGuid GetTransportGUID() const override { return _owner.GetGUID(); }
441
442 float GetTransportOrientation() const override { return _owner.GetOrientation(); }
443
444 void AddPassenger(WorldObject* passenger, Position const& offset) override
445 {
446 if (!_owner.IsInWorld())
447 return;
448
449 if (_passengers.insert(passenger).second)
450 {
451 passenger->SetTransport(this);
453 passenger->m_movementInfo.transport.pos = offset;
454 TC_LOG_DEBUG("entities.transport", "Object {} boarded transport {}.", passenger->GetName(), _owner.GetName());
455 }
456 }
457
459 {
460 if (_passengers.erase(passenger) > 0)
461 {
462 passenger->SetTransport(nullptr);
463 passenger->m_movementInfo.transport.Reset();
464 TC_LOG_DEBUG("entities.transport", "Object {} removed from transport {}.", passenger->GetName(), _owner.GetName());
465
466 if (Player* plr = passenger->ToPlayer())
467 plr->SetFallInformation(0, plr->GetPositionZ());
468 }
469
470 return this;
471 }
472
473 Position GetPositionWithOffset(Position const& offset) const override
474 {
475 return _owner.GetPositionWithOffset(offset);
476 }
477
478 Position GetPositionOffsetTo(Position const& endPos) const override
479 {
480 return _owner.GetPositionOffsetTo(endPos);
481 }
482
483 int32 GetMapIdForSpawning() const override
484 {
486 }
487
492
493private:
498 std::vector<uint32> _stopFrames;
501 std::unordered_set<WorldObject*> _passengers;
502};
503
507
509{
510 if (Transport* transport = dynamic_cast<Transport*>(&type))
511 transport->SetAutoCycleBetweenStopFrames(_on);
512}
513
515{
516public:
518
519 void SetState(FlagState newState, Player* player)
520 {
521 if (_state == newState)
522 return;
523
524 FlagState oldState = _state;
525 _state = newState;
526
527 if (player && newState == FlagState::Taken)
528 _carrierGUID = player->GetGUID();
529 else
531
532 if (newState == FlagState::Taken && oldState == FlagState::InBase)
534 else if (newState == FlagState::InBase || newState == FlagState::Respawning)
536
538
539 if (newState == FlagState::Respawning)
541 else
542 _respawnTime = 0;
543
544 if (ZoneScript* zoneScript = _owner.GetZoneScript())
545 zoneScript->OnFlagStateChange(&_owner, oldState, _state, player);
546 }
547
548 void Update([[maybe_unused]] uint32 diff) override
549 {
551 SetState(FlagState::InBase, nullptr);
552 }
553
554 bool IsNeverVisibleFor([[maybe_unused]] WorldObject const* seer, [[maybe_unused]] bool allowServersideObjects) const override
555 {
556 return _state != FlagState::InBase;
557 }
558
559 FlagState GetState() const { return _state; }
560 ObjectGuid const& GetCarrierGUID() const { return _carrierGUID; }
561 time_t GetTakenFromBaseTime() const { return _takenFromBaseTime; }
562
563private:
568};
569
570SetNewFlagState::SetNewFlagState(FlagState state, Player* player) : _state(state), _player(player)
571{
572}
573
575{
576 if (NewFlag* newFlag = dynamic_cast<NewFlag*>(&type))
577 newFlag->SetState(_state, _player);
578}
579
581{
582public:
583 explicit ControlZone(GameObject& owner) : GameObjectTypeBase(owner), _value(static_cast<float>(owner.GetGOInfo()->controlZone.startingValue))
584 {
585 if (owner.GetMap()->Instanceable())
586 _heartbeatRate = 1s;
587 else if (owner.GetGOInfo()->controlZone.FrequentHeartbeat)
588 _heartbeatRate = 2500ms;
589 else
590 _heartbeatRate = 5s;
591
594 _contestedTriggered = false;
595 }
596
597 void Update(uint32 diff) override
598 {
600 return;
601
604 {
607 }
608 }
609
611 {
612 if (_value < GetMaxHordeValue())
613 return TEAM_HORDE;
614
616 return TEAM_ALLIANCE;
617
618 return TEAM_NEUTRAL;
619 }
620
622
623 void ActivateObject(GameObjectActions action, int32 /*param*/, WorldObject* /*spellCaster*/, uint32 /*spellId*/, int32 /*effectIndex*/) override
624 {
625 switch (action)
626 {
628 for (ObjectGuid const& guid : _insidePlayers)
629 if (Player* player = ObjectAccessor::GetPlayer(_owner, guid))
630 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldState1, 0);
631
632 _insidePlayers.clear();
633 break;
634 default:
635 break;
636 }
637 }
638
639 void SetValue(float value)
640 {
641 _value = RoundToInterval<float>(value, 0.0f, 100.0f);
642 }
643
645 {
646 // update player list inside control zone
647 std::vector<Player*> targetList;
648 SearchTargets(targetList);
649
650 TeamId oldControllingTeam = GetControllingTeam();
651 float pointsGained = CalculatePointsPerSecond(targetList) * _heartbeatRate.count() / 1000.0f;
652 if (pointsGained == 0)
653 return;
654
655 int32 oldRoundedValue = static_cast<int32>(_value);
656 SetValue(_value + pointsGained);
657 int32 roundedValue = static_cast<int32>(_value);
658 if (oldRoundedValue == roundedValue)
659 return;
660
661 TeamId newControllingTeam = GetControllingTeam();
662 TeamId assaultingTeam = pointsGained > 0 ? TEAM_ALLIANCE : TEAM_HORDE;
663
664 if (oldControllingTeam != newControllingTeam)
665 _contestedTriggered = false;
666
667 if (oldControllingTeam != TEAM_ALLIANCE && newControllingTeam == TEAM_ALLIANCE)
669 else if (oldControllingTeam != TEAM_HORDE && newControllingTeam == TEAM_HORDE)
671 else if (oldControllingTeam == TEAM_HORDE && newControllingTeam == TEAM_NEUTRAL)
673 else if (oldControllingTeam == TEAM_ALLIANCE && newControllingTeam == TEAM_NEUTRAL)
675
676 if (roundedValue == 100 && newControllingTeam == TEAM_ALLIANCE && assaultingTeam == TEAM_ALLIANCE)
678 else if (roundedValue == 0 && newControllingTeam == TEAM_HORDE && assaultingTeam == TEAM_HORDE)
680
681 if (oldRoundedValue == 100 && assaultingTeam == TEAM_HORDE && !_contestedTriggered)
682 {
684 _contestedTriggered = true;
685 }
686 else if (oldRoundedValue == 0 && assaultingTeam == TEAM_ALLIANCE && !_contestedTriggered)
687 {
689 _contestedTriggered = true;
690 }
691
692 for (Player* player : targetList)
693 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldstate2, roundedValue);
694 }
695
696 void SearchTargets(std::vector<Player*>& targetList)
697 {
701 HandleUnitEnterExit(targetList);
702 }
703
704 float CalculatePointsPerSecond(std::vector<Player*> const& targetList) const
705 {
706 int32 hordePlayers = 0;
707 int32 alliancePlayers = 0;
708
709 for (Player const* player : targetList)
710 {
711 if (!player->IsOutdoorPvPActive())
712 continue;
713
714 if (player->GetTeamId() == TEAM_HORDE)
715 hordePlayers++;
716 else
717 alliancePlayers++;
718 }
719
720 int8 factionCoefficient = 0; // alliance superiority = 1; horde superiority = -1
721
722 if (alliancePlayers > hordePlayers)
723 factionCoefficient = 1;
724 else if (hordePlayers > alliancePlayers)
725 factionCoefficient = -1;
726
727 float const timeNeeded = CalculateTimeNeeded(hordePlayers, alliancePlayers);
728 if (timeNeeded == 0.0f)
729 return 0.0f;
730
731 return 100.0f / timeNeeded * static_cast<float>(factionCoefficient);
732 }
733
734 float CalculateTimeNeeded(int32 hordePlayers, int32 alliancePlayers) const
735 {
736 uint32 const uncontestedTime = _owner.GetGOInfo()->controlZone.UncontestedTime;
737 uint32 const delta = std::abs(alliancePlayers - hordePlayers);
738 uint32 const minSuperiority = _owner.GetGOInfo()->controlZone.minSuperiority;
739
740 if (delta < minSuperiority)
741 return 0.0f;
742
743 // return the uncontested time if controlzone is not contested
744 if (uncontestedTime && (hordePlayers == 0 || alliancePlayers == 0))
745 return static_cast<float>(uncontestedTime);
746
747 uint32 const minTime = _owner.GetGOInfo()->controlZone.minTime;
748 uint32 const maxTime = _owner.GetGOInfo()->controlZone.maxTime;
749 uint32 const maxSuperiority = _owner.GetGOInfo()->controlZone.maxSuperiority;
750
751 float const slope = static_cast<float>(minTime - maxTime) / static_cast<float>(std::max<uint32>(maxSuperiority - minSuperiority, 1));
752 float const intercept = static_cast<float>(maxTime) - slope * static_cast<float>(minSuperiority);
753 return slope * static_cast<float>(delta) + intercept;
754 }
755
756 void HandleUnitEnterExit(std::vector<Player*> const& newTargetList)
757 {
758 GuidUnorderedSet exitPlayers(std::move(_insidePlayers));
759
760 std::vector<Player*> enteringPlayers;
761
762 for (Player* unit : newTargetList)
763 {
764 if (exitPlayers.erase(unit->GetGUID()) == 0) // erase(key_type) returns number of elements erased
765 enteringPlayers.push_back(unit);
766
767 _insidePlayers.insert(unit->GetGUID());
768 }
769
770 for (Player* player : enteringPlayers)
771 {
772 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldState1, 1);
773 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldstate2, static_cast<int32>(_value));
775 }
776
777 for (ObjectGuid const& exitPlayerGuid : exitPlayers)
778 {
779 if (Player* player = ObjectAccessor::GetPlayer(_owner, exitPlayerGuid))
780 {
781 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldState1, 0);
782 }
783 }
784 }
785
786 float GetMaxHordeValue() const
787 {
788 // ex: if neutralPercent is 40; then 0 - 30 is Horde Controlled
789 return 50.0f - _owner.GetGOInfo()->controlZone.neutralPercent / 2.0f;
790 }
791
793 {
794 // ex: if neutralPercent is 40; then 70 - 100 is Alliance Controlled
795 return 50.0f + _owner.GetGOInfo()->controlZone.neutralPercent / 2.0f;
796 }
797
798 void TriggerEvent(uint32 eventId) const
799 {
800 if (eventId <= 0)
801 return;
802
804 GameEvents::Trigger(eventId, &_owner, nullptr);
805 }
806
811
812private:
817 float _value;
819};
820
822{
823}
824
826{
827 if (ControlZone* controlZone = dynamic_cast<ControlZone*>(&type))
828 {
829 uint32 value = controlZone->GetStartingValue();
830 if (_value.has_value())
831 value = *_value;
832
833 controlZone->SetValue(value);
834 }
835}
836}
837
839 m_goValue(), m_stringIds(), m_AI(nullptr), m_respawnCompatibilityMode(false), _animKitId(0), _worldEffectID(0)
840{
842
844 m_updateFlag.Rotation = true;
845
847
848 m_respawnTime = 0;
849 m_respawnDelayTime = 300;
850 m_despawnDelay = 0;
852 m_restockTime = 0;
854 m_spawnedByDefault = true;
855 m_usetimes = 0;
856 m_spellId = 0;
857 m_cooldownTime = 0;
859 m_goInfo = nullptr;
860 m_goData = nullptr;
862 m_goTemplateAddon = nullptr;
863
864 m_spawnId = UI64LIT(0);
865
866 ResetLootMode(); // restore default loot mode
867 m_stationaryPosition.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
868}
869
871{
872 delete m_AI;
873}
874
876{
877 delete m_AI;
878 m_AI = nullptr;
879}
880
882{
883 AIM_Destroy();
884
886
887 if (!m_AI)
888 return false;
889
891 return true;
892}
893
894std::string const& GameObject::GetAIName() const
895{
896 return sObjectMgr->GetGameObjectTemplate(GetEntry())->AIName;
897}
898
899void GameObject::CleanupsBeforeDelete(bool finalCleanup)
900{
901 SetVignette(0);
902
904
906}
907
909{
910 ObjectGuid ownerGUID = GetOwnerGUID();
911 if (!ownerGUID)
912 return;
913
914 if (Unit* owner = ObjectAccessor::GetUnit(*this, ownerGUID))
915 {
916 owner->RemoveGameObject(this, false);
918 return;
919 }
920
921 // This happens when a mage portal is despawned after the caster changes map (for example using the portal)
922 TC_LOG_DEBUG("misc", "Removed GameObject ({} Entry: {} SpellId: {} LinkedGO: {}) that just lost any reference to the owner ({}) GO list",
923 GetGUID().ToString(), GetGOInfo()->entry, m_spellId, GetGOInfo()->GetLinkedGameObjectEntry(), ownerGUID.ToString());
925}
926
928{
930 if (!IsInWorld())
931 {
932 if (m_zoneScript)
934
936 if (m_spawnId)
937 GetMap()->GetGameObjectBySpawnIdStore().insert(std::make_pair(m_spawnId, this));
938
939 // The state can be changed after GameObject::Create but before GameObject::AddToWorld
940 bool toggledState = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : (GetGoState() == GO_STATE_READY || IsTransport());
941 if (m_model)
942 {
943 if (Transport* trans = ToTransport())
944 trans->SetDelayedAddModelToMap();
945 else
947 }
948
949 EnableCollision(toggledState);
951 }
952}
953
955{
957 if (IsInWorld())
958 {
959 if (m_zoneScript)
961
963 if (m_model)
964 if (GetMap()->ContainsGameObjectModel(*m_model))
966
967 // If linked trap exists, despawn it
968 if (GameObject* linkedTrap = GetLinkedTrap())
969 linkedTrap->DespawnOrUnsummon();
970
972
973 if (m_spawnId)
974 Trinity::Containers::MultimapErasePair(GetMap()->GetGameObjectBySpawnIdStore(), m_spawnId, this);
976 }
977}
978
979bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit, bool dynamic, ObjectGuid::LowType spawnid)
980{
981 ASSERT(map);
982 SetMap(map);
983
984 Relocate(pos);
986 if (!IsPositionValid())
987 {
988 TC_LOG_ERROR("misc", "Gameobject (Spawn id: {} Entry: {}) not created. Suggested coordinates isn't valid (X: {} Y: {})", GetSpawnId(), entry, pos.GetPositionX(), pos.GetPositionY());
989 return false;
990 }
991
992 // Set if this object can handle dynamic spawns
993 if (!dynamic)
995
997
999 if (m_zoneScript)
1000 {
1001 entry = m_zoneScript->GetGameObjectEntry(m_spawnId, entry);
1002 if (!entry)
1003 return false;
1004 }
1005
1006 GameObjectTemplate const* goInfo = sObjectMgr->GetGameObjectTemplate(entry);
1007 if (!goInfo)
1008 {
1009 TC_LOG_ERROR("sql.sql", "Gameobject (Spawn id: {} Entry: {}) not created: non-existing entry in `gameobject_template`. Map: {} (X: {} Y: {} Z: {})", GetSpawnId(), entry, map->GetId(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
1010 return false;
1011 }
1012
1014 {
1015 TC_LOG_ERROR("sql.sql", "Gameobject (Spawn id: {} Entry: {}) not created: gameobject type GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT cannot be manually created.", GetSpawnId(), entry);
1016 return false;
1017 }
1018
1019 ObjectGuid guid;
1020 if (goInfo->type != GAMEOBJECT_TYPE_TRANSPORT)
1021 guid = ObjectGuid::Create<HighGuid::GameObject>(map->GetId(), goInfo->entry, map->GenerateLowGuid<HighGuid::GameObject>());
1022 else
1023 {
1024 guid = ObjectGuid::Create<HighGuid::Transport>(map->GenerateLowGuid<HighGuid::Transport>());
1025 m_updateFlag.ServerTime = true;
1026 }
1027
1028 _Create(guid);
1029
1030 m_goInfo = goInfo;
1031 m_goTemplateAddon = sObjectMgr->GetGameObjectTemplateAddon(entry);
1032
1033 if (goInfo->type >= MAX_GAMEOBJECT_TYPE)
1034 {
1035 TC_LOG_ERROR("sql.sql", "Gameobject ({} Spawn id: {} Entry: {}) not created: non-existing GO type '{}' in `gameobject_template`. It will crash client if created.", guid.ToString(), GetSpawnId(), entry, goInfo->type);
1036 return false;
1037 }
1038
1039 SetLocalRotation(rotation.x, rotation.y, rotation.z, rotation.w);
1040 GameObjectAddon const* gameObjectAddon = sObjectMgr->GetGameObjectAddon(GetSpawnId());
1041
1042 // For most of gameobjects is (0, 0, 0, 1) quaternion, there are only some transports with not standard rotation
1043 QuaternionData parentRotation;
1044 if (gameObjectAddon)
1045 parentRotation = gameObjectAddon->ParentRotation;
1046
1047 SetParentRotation(parentRotation);
1048
1049 SetObjectScale(goInfo->size);
1050
1051 if (GameObjectOverride const* goOverride = GetGameObjectOverride())
1052 {
1053 SetFaction(goOverride->Faction);
1054 ReplaceAllFlags(GameObjectFlags(goOverride->Flags));
1055 }
1056
1058 {
1060 {
1061 m_updateFlag.GameObject = true;
1063 }
1064
1066 {
1067 m_updateFlag.AnimKit = true;
1069 }
1070 }
1071
1072 SetEntry(goInfo->entry);
1073
1074 // set name for logs usage, doesn't affect anything ingame
1075 SetName(goInfo->name);
1076
1077 SetDisplayId(goInfo->displayId);
1078
1079 // GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3
1080 SetGoType(GameobjectTypes(goInfo->type));
1081 CreateModel();
1082 m_prevGoState = goState;
1083 SetGoState(goState);
1084 SetGoArtKit(artKit);
1085
1086 switch (goInfo->type)
1087 {
1089 SetGoAnimProgress(animProgress);
1090 m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishingHole.minRestock, GetGOInfo()->fishingHole.maxRestock);
1091 break;
1093 {
1094 m_goValue.Building.DestructibleHitpoint = sObjectMgr->GetDestructibleHitpoint(GetGOInfo()->destructibleBuilding.HealthRec);
1096 SetGoAnimProgress(255);
1097
1098 // yes, even after the updatefield rewrite this garbage hack is still in client
1099 QuaternionData reinterpretId;
1100 memcpy(&reinterpretId.x, &m_goInfo->destructibleBuilding.DestructibleModelRec, sizeof(float));
1102 break;
1103 }
1105 {
1106 m_updateFlag.GameObject = true;
1107 m_goTypeImpl = std::make_unique<GameObjectType::Transport>(*this);
1108 if (goInfo->transport.startOpen)
1110 else
1112
1113 SetGoAnimProgress(animProgress);
1114 setActive(true);
1115 break;
1116 }
1118 SetLevel(0);
1119 SetGoAnimProgress(255);
1120 break;
1122 if (GetGOInfo()->trap.stealthed)
1123 {
1126 }
1127
1128 if (GetGOInfo()->trap.stealthAffected)
1129 {
1132 }
1133 break;
1135 m_goTypeImpl = std::make_unique<GameObjectType::ControlZone>(*this);
1136 setActive(true);
1137 break;
1139 m_goTypeImpl = std::make_unique<GameObjectType::NewFlag>(*this);
1140 if (map->Instanceable())
1141 setActive(true);
1142 break;
1144 if (map->Instanceable())
1145 setActive(true);
1146 break;
1150
1151 if (GetGOInfo()->phaseableMO.DoodadSetA)
1153
1156 break;
1163 if (map->Instanceable())
1164 setActive(true);
1165 break;
1166 default:
1167 SetGoAnimProgress(animProgress);
1168 break;
1169 }
1170
1171 if (gameObjectAddon)
1172 {
1173 if (gameObjectAddon->InvisibilityValue)
1174 {
1175 m_invisibility.AddFlag(gameObjectAddon->invisibilityType);
1176 m_invisibility.AddValue(gameObjectAddon->invisibilityType, gameObjectAddon->InvisibilityValue);
1177 }
1178
1179 if (gameObjectAddon->WorldEffectID)
1180 {
1181 m_updateFlag.GameObject = true;
1182 SetWorldEffectID(gameObjectAddon->WorldEffectID);
1183 }
1184
1185 if (gameObjectAddon->AIAnimKitID)
1186 {
1187 m_updateFlag.AnimKit = true;
1188 _animKitId = gameObjectAddon->AIAnimKitID;
1189 }
1190 }
1191
1192 if (uint32 vignetteId = GetGOInfo()->GetSpawnVignette())
1193 SetVignette(vignetteId);
1194
1196
1198
1200
1201 if (spawnid)
1202 m_spawnId = spawnid;
1203
1204 if (uint32 linkedEntry = GetGOInfo()->GetLinkedGameObjectEntry())
1205 {
1206 if (GameObject* linkedGo = GameObject::CreateGameObject(linkedEntry, map, pos, rotation, 255, GO_STATE_READY))
1207 {
1208 SetLinkedTrap(linkedGo);
1209 if (!map->AddToMap(linkedGo))
1210 delete linkedGo;
1211 }
1212 }
1213
1214 // Check if GameObject is Infinite
1215 if (goInfo->IsInfiniteGameObject())
1217
1218 // Check if GameObject is Gigantic
1219 if (goInfo->IsGiganticGameObject())
1221
1222 // Check if GameObject is Large
1223 if (goInfo->IsLargeGameObject())
1225
1226 return true;
1227}
1228
1229GameObject* GameObject::CreateGameObject(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit /*= 0*/)
1230{
1231 GameObjectTemplate const* goInfo = sObjectMgr->GetGameObjectTemplate(entry);
1232 if (!goInfo)
1233 return nullptr;
1234
1235 GameObject* go = new GameObject();
1236 if (!go->Create(entry, map, pos, rotation, animProgress, goState, artKit, false, 0))
1237 {
1238 delete go;
1239 return nullptr;
1240 }
1241
1242 return go;
1243}
1244
1246{
1247 GameObject* go = new GameObject();
1248 if (!go->LoadFromDB(spawnId, map, addToMap))
1249 {
1250 delete go;
1251 return nullptr;
1252 }
1253
1254 return go;
1255}
1256
1258{
1259 WorldObject::Update(diff);
1260
1261 if (AI())
1262 AI()->UpdateAI(diff);
1263 else if (!AIM_Initialize())
1264 TC_LOG_ERROR("misc", "Could not initialize GameObjectAI");
1265
1266 if (m_despawnDelay)
1267 {
1268 if (m_despawnDelay > diff)
1269 m_despawnDelay -= diff;
1270 else
1271 {
1272 m_despawnDelay = 0;
1274 }
1275 }
1276
1277 if (m_goTypeImpl)
1278 m_goTypeImpl->Update(diff);
1279
1280 if (m_perPlayerState)
1281 {
1282 for (auto itr = m_perPlayerState->begin(); itr != m_perPlayerState->end(); )
1283 {
1284 if (itr->second.ValidUntil > GameTime::GetSystemTime())
1285 {
1286 ++itr;
1287 continue;
1288 }
1289
1290 Player* seer = ObjectAccessor::GetPlayer(*this, itr->first);
1291 bool needsStateUpdate = itr->second.State != GetGoState();
1292 bool despawned = itr->second.Despawned;
1293
1294 itr = m_perPlayerState->erase(itr);
1295
1296 if (seer)
1297 {
1298 if (despawned)
1299 {
1300 seer->UpdateVisibilityOf(this);
1301 }
1302 else if (needsStateUpdate)
1303 {
1304 UF::ObjectData::Base objMask;
1307
1308 UpdateData udata(GetMapId());
1309 BuildValuesUpdateForPlayerWithMask(&udata, objMask.GetChangesMask(), goMask.GetChangesMask(), seer, false);
1310 WorldPacket packet;
1311 udata.BuildPacket(&packet);
1312 seer->SendDirectMessage(&packet);
1313 }
1314 }
1315 }
1316 }
1317
1318 switch (m_lootState)
1319 {
1320 case GO_NOT_READY:
1321 {
1322 switch (GetGoType())
1323 {
1325 {
1326 // Arming Time for GAMEOBJECT_TYPE_TRAP (6)
1327 GameObjectTemplate const* goInfo = GetGOInfo();
1328 // Bombs
1329 if (goInfo->trap.charges == 2)
1330 // Hardcoded tooltip value
1332 else if (Unit* owner = GetOwner())
1333 if (owner->IsInCombat())
1335
1337 break;
1338 }
1340 {
1341 // fishing code (bobber ready)
1343 {
1344 // splash bobber (bobber ready now)
1345 Unit* caster = GetOwner();
1346 if (caster && caster->GetTypeId() == TYPEID_PLAYER)
1347 SendCustomAnim(0);
1348
1349 m_lootState = GO_READY; // can be successfully open with some chance
1350 }
1351 return;
1352 }
1355 return;
1356 // If there is no restock timer, or if the restock timer passed, the chest becomes ready to loot
1357 m_restockTime = 0;
1359 ClearLoot();
1361 break;
1362 default:
1363 m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY
1364 break;
1365 }
1366 [[fallthrough]];
1367 }
1368 case GO_READY:
1369 {
1371 {
1372 if (m_respawnTime > 0) // timer on
1373 {
1374 time_t now = GameTime::GetGameTime();
1375 if (m_respawnTime <= now) // timer expired
1376 {
1377 ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::GameObject>(GetMapId(), GetEntry(), m_spawnId);
1378 time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
1379 if (linkedRespawntime) // Can't respawn, the master is dead
1380 {
1381 ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
1382 if (targetGuid == dbtableHighGuid) // if linking self, never respawn
1384 else
1385 m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
1387 return;
1388 }
1389
1390 m_respawnTime = 0;
1391 m_SkillupList.clear();
1392 m_usetimes = 0;
1393
1394 switch (GetGoType())
1395 {
1396 case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
1397 {
1398 Unit* caster = GetOwner();
1399 if (caster && caster->GetTypeId() == TYPEID_PLAYER)
1400 {
1401 caster->ToPlayer()->RemoveGameObject(this, false);
1402
1404 }
1405 // can be delete
1407 return;
1408 }
1411 // 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)
1412 if (GetGoState() != GO_STATE_READY)
1414 break;
1416 // Initialize a new max fish count on respawn
1417 m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishingHole.minRestock, GetGOInfo()->fishingHole.maxRestock);
1418 break;
1419 default:
1420 break;
1421 }
1422
1423 // Despawn timer
1424 if (!m_spawnedByDefault)
1425 {
1426 // Can be despawned or destroyed
1428 return;
1429 }
1430
1431 // Call AI Reset (required for example in SmartAI to clear one time events)
1432 if (AI())
1433 AI()->Reset();
1434
1435 // Respawn timer
1437 if (poolid)
1438 sPoolMgr->UpdatePool<GameObject>(GetMap()->GetPoolData(), poolid, GetSpawnId());
1439 else
1440 GetMap()->AddToMap(this);
1441 }
1442 }
1443 }
1444
1445 // Set respawn timer
1448
1449 if (isSpawned())
1450 {
1451 GameObjectTemplate const* goInfo = GetGOInfo();
1452 if (goInfo->type == GAMEOBJECT_TYPE_TRAP)
1453 {
1455 break;
1456
1457 // Type 2 (bomb) does not need to be triggered by a unit and despawns after casting its spell.
1458 if (goInfo->trap.charges == 2)
1459 {
1461 break;
1462 }
1463
1464 // Type 0 despawns after being triggered, type 1 does not.
1466 float radius = goInfo->trap.radius / 2.f; // this division seems to date back to when the field was called diameter, don't think it is still relevant.
1467 if (!radius)
1468 break;
1469
1470 // Pointer to appropriate target if found any
1471 Unit* target = nullptr;
1472
1473 if (GetOwner() || goInfo->trap.Checkallunits)
1474 {
1475 // Hunter trap: Search units which are unfriendly to the trap's owner
1478 Cell::VisitAllObjects(this, searcher, radius);
1479 }
1480 else
1481 {
1482 // Environmental trap: Any player
1483 Player* player = nullptr;
1484 Trinity::AnyUnitInObjectRangeCheck checker(this, radius);
1485 Trinity::PlayerSearcher searcher(this, player, checker);
1486 Cell::VisitWorldObjects(this, searcher, radius);
1487 target = player;
1488 }
1489
1490 if (target)
1491 SetLootState(GO_ACTIVATED, target);
1492
1493 }
1494 else if (goInfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT)
1495 {
1498 if (hordeCapturing || allianceCapturing)
1499 {
1501 {
1503 if (hordeCapturing)
1504 {
1506 if (BattlegroundMap* map = GetMap()->ToBattlegroundMap())
1507 {
1508 if (Battleground* bg = map->GetBG())
1509 {
1510 if (goInfo->capturePoint.CaptureEventHorde)
1512 bg->SendBroadcastText(goInfo->capturePoint.CaptureBroadcastHorde, CHAT_MSG_BG_SYSTEM_HORDE);
1513 }
1514 }
1515 }
1516 else
1517 {
1519 if (BattlegroundMap* map = GetMap()->ToBattlegroundMap())
1520 {
1521 if (Battleground* bg = map->GetBG())
1522 {
1526 }
1527 }
1528 }
1529
1532 }
1533 else
1535 }
1536 }
1537 else if (uint32 max_charges = goInfo->GetCharges())
1538 {
1539 if (m_usetimes >= max_charges)
1540 {
1541 m_usetimes = 0;
1542 SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
1543 }
1544 }
1545 }
1546
1547 break;
1548 }
1549 case GO_ACTIVATED:
1550 {
1551 switch (GetGoType())
1552 {
1557 break;
1560 {
1562
1564 m_cooldownTime = 0;
1565 }
1566 break;
1568 if (m_loot)
1569 {
1570 m_loot->Update();
1571
1572 // Non-consumable chest was partially looted and restock time passed, restock all loot now
1573 if (!GetGOInfo()->IsDespawnAtAction() && m_restockTime && GameTime::GetGameTime() >= m_restockTime)
1574 {
1575 m_restockTime = 0;
1577 ClearLoot();
1579 }
1580 }
1581
1582 for (auto&& [playerOwner, loot] : m_personalLoot)
1583 loot->Update();
1584 break;
1586 {
1587 GameObjectTemplate const* goInfo = GetGOInfo();
1588 if (goInfo->trap.charges == 2 && goInfo->trap.spell)
1589 {
1591 CastSpell(nullptr, goInfo->trap.spell);
1593 }
1594 else if (Unit* target = ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID))
1595 {
1596 // Some traps do not have a spell but should be triggered
1597 CastSpellExtraArgs args;
1599 if (goInfo->trap.spell)
1600 CastSpell(target, goInfo->trap.spell, args);
1601
1602 // Template value or 4 seconds
1604
1605 if (goInfo->trap.charges == 1)
1607 else if (!goInfo->trap.charges)
1609 }
1610 break;
1611 }
1612 default:
1613 break;
1614 }
1615 break;
1616 }
1618 {
1619 // If nearby linked trap exists, despawn it
1620 if (GameObject* linkedTrap = GetLinkedTrap())
1621 linkedTrap->DespawnOrUnsummon();
1622
1623 //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
1625 {
1626 uint32 spellId = GetGOInfo()->goober.spell;
1627
1628 if (spellId)
1629 {
1630 for (GuidSet::const_iterator it = m_unique_users.begin(); it != m_unique_users.end(); ++it)
1631 // m_unique_users can contain only player GUIDs
1632 if (Player* owner = ObjectAccessor::GetPlayer(*this, *it))
1633 owner->CastSpell(owner, spellId, false);
1634
1635 m_unique_users.clear();
1636 m_usetimes = 0;
1637 }
1638
1640
1641 //any return here in case battleground traps
1642 if (GameObjectOverride const* goOverride = GetGameObjectOverride())
1643 if (goOverride->Flags & GO_FLAG_NODESPAWN)
1644 return;
1645 }
1646
1647 ClearLoot();
1648
1649 // Do not delete chests or goobers that are not consumed on loot, while still allowing them to despawn when they expire if summoned
1650 bool isSummonedAndExpired = (GetOwner() || GetSpellId()) && m_respawnTime == 0;
1651 if ((GetGoType() == GAMEOBJECT_TYPE_CHEST || GetGoType() == GAMEOBJECT_TYPE_GOOBER) && !GetGOInfo()->IsDespawnAtAction() && !isSummonedAndExpired)
1652 {
1654 {
1655 // Start restock timer when the chest is fully looted
1659 }
1660 else
1663 return;
1664 }
1665 else if (!GetOwnerGUID().IsEmpty() || GetSpellId())
1666 {
1667 SetRespawnTime(0);
1668
1670 {
1671 if (GameObject* go = GetMap()->GetGameObject(GetOwnerGUID()))
1672 go->HandleCustomTypeCommand(GameObjectType::SetNewFlagState(FlagState::InBase, nullptr));
1673 }
1674
1675 Delete();
1676 return;
1677 }
1678
1680
1681 //burning flags in some battlegrounds, if you find better condition, just add it
1682 if (GetGOInfo()->IsDespawnAtAction() || GetGoAnimProgress() > 0)
1683 {
1685 //reset flags
1686 if (GameObjectOverride const* goOverride = GetGameObjectOverride())
1687 ReplaceAllFlags(GameObjectFlags(goOverride->Flags));
1688 }
1689
1690 if (!m_respawnDelayTime)
1691 return;
1692
1693 if (!m_spawnedByDefault)
1694 {
1695 m_respawnTime = 0;
1696
1697 if (m_spawnId)
1699 else
1700 Delete();
1701
1702 return;
1703 }
1704
1705 uint32 respawnDelay = m_respawnDelayTime;
1706 if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
1707 GetMap()->ApplyDynamicModeRespawnScaling(this, this->m_spawnId, respawnDelay, scalingMode);
1708 m_respawnTime = GameTime::GetGameTime() + respawnDelay;
1709
1710 // if option not set then object will be saved at grid unload
1711 // Otherwise just save respawn time to map object memory
1713
1716 else
1718
1719 break;
1720 }
1721 }
1722}
1723
1725{
1726 if (m_spawnId)
1727 {
1728 if (GameObjectOverride const* goOverride = sObjectMgr->GetGameObjectOverride(m_spawnId))
1729 return goOverride;
1730 }
1731
1732 return m_goTemplateAddon;
1733}
1734
1736{
1737 // Do not refresh despawned GO from spellcast (GO's from spellcast are destroyed after despawn)
1739 return;
1740
1741 if (isSpawned())
1742 GetMap()->AddToMap(this);
1743}
1744
1746{
1747 AddUse();
1748 m_unique_users.insert(player->GetGUID());
1749}
1750
1752{
1753 if (delay > 0ms)
1754 {
1755 if (!m_despawnDelay || m_despawnDelay > delay.count())
1756 {
1757 m_despawnDelay = delay.count();
1758 m_despawnRespawnTime = forceRespawnTime;
1759 }
1760 }
1761 else
1762 {
1763 if (m_goData)
1764 {
1765 uint32 const respawnDelay = (forceRespawnTime > 0s) ? forceRespawnTime.count() : m_goData->spawntimesecs;
1766 SaveRespawnTime(respawnDelay);
1767 }
1768 Delete();
1769 }
1770}
1771
1773{
1774 PerPlayerState& perPlayerState = GetOrCreatePerPlayerStates()[seer->GetGUID()];
1775 perPlayerState.ValidUntil = GameTime::GetSystemTime() + respawnTime;
1776 perPlayerState.Despawned = true;
1777 seer->UpdateVisibilityOf(this);
1778}
1779
1781{
1784
1786 {
1788 SendMessageToSet(packet.Write(), true);
1789 }
1790
1792
1795
1796 if (GameObjectOverride const* goOverride = GetGameObjectOverride())
1797 ReplaceAllFlags(GameObjectFlags(goOverride->Flags));
1798
1800 if (m_respawnCompatibilityMode && poolid)
1801 sPoolMgr->UpdatePool<GameObject>(GetMap()->GetPoolData(), poolid, GetSpawnId());
1802 else
1804}
1805
1812
1814{
1815 uint32 defaultzone = 1;
1816
1817 Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING, nullptr);
1818
1819 uint32 areaId = GetAreaId();
1820 ItemContext itemContext = ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), lootOwner);
1821 while (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId))
1822 {
1823 fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_DEFAULT, itemContext);
1824 if (!fishLoot->isLooted())
1825 break;
1826
1827 areaId = areaEntry->ParentAreaID;
1828 }
1829
1830 if (fishLoot->isLooted())
1831 fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_DEFAULT, itemContext);
1832
1833 return fishLoot;
1834}
1835
1837{
1838 uint32 defaultzone = 1;
1839
1840 Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING_JUNK, nullptr);
1841
1842 uint32 areaId = GetAreaId();
1843 ItemContext itemContext = ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), lootOwner);
1844 while (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId))
1845 {
1846 fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH, itemContext);
1847 if (!fishLoot->isLooted())
1848 break;
1849
1850 areaId = areaEntry->ParentAreaID;
1851 }
1852
1853 if (fishLoot->isLooted())
1854 fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH, itemContext);
1855
1856 return fishLoot;
1857}
1858
1860{
1861 // this should only be used when the gameobject has already been loaded
1862 // preferably after adding to map, because mapid may not be valid otherwise
1863 GameObjectData const* data = sObjectMgr->GetGameObjectData(m_spawnId);
1864 if (!data)
1865 {
1866 TC_LOG_ERROR("misc", "GameObject::SaveToDB failed, cannot get gameobject data!");
1867 return;
1868 }
1869
1870 uint32 mapId = GetMapId();
1871 if (TransportBase* transport = GetTransport())
1872 if (transport->GetMapIdForSpawning() >= 0)
1873 mapId = transport->GetMapIdForSpawning();
1874
1875 SaveToDB(mapId, data->spawnDifficulties);
1876}
1877
1878void GameObject::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDifficulties)
1879{
1880 GameObjectTemplate const* goI = GetGOInfo();
1881 if (!goI)
1882 return;
1883
1884 if (!m_spawnId)
1885 m_spawnId = sObjectMgr->GenerateGameObjectSpawnId();
1886
1887 // update in loaded data (changing data only in this place)
1888 GameObjectData& data = sObjectMgr->NewOrExistGameObjectData(m_spawnId);
1889
1890 if (!data.spawnId)
1891 data.spawnId = m_spawnId;
1892 ASSERT(data.spawnId == m_spawnId);
1893 data.id = GetEntry();
1894 data.mapId = GetMapId();
1895 data.spawnPoint.Relocate(this);
1899 data.goState = GetGoState();
1900 data.spawnDifficulties = spawnDifficulties;
1901 data.artKit = GetGoArtKit();
1902 if (!data.spawnGroupData)
1903 data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
1904
1905 data.phaseId = GetDBPhase() > 0 ? GetDBPhase() : data.phaseId;
1906 data.phaseGroup = GetDBPhase() < 0 ? -GetDBPhase() : data.phaseGroup;
1907
1908 // Update in DB
1909 WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
1910
1911 uint8 index = 0;
1912
1914 stmt->setUInt64(0, m_spawnId);
1915 trans->Append(stmt);
1916
1917 stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAMEOBJECT);
1918 stmt->setUInt64(index++, m_spawnId);
1919 stmt->setUInt32(index++, GetEntry());
1920 stmt->setUInt16(index++, uint16(mapid));
1921 stmt->setString(index++, [&data]() -> std::string
1922 {
1923 std::ostringstream os;
1924 if (!data.spawnDifficulties.empty())
1925 {
1926 auto itr = data.spawnDifficulties.begin();
1927 os << int32(*itr++);
1928
1929 for (; itr != data.spawnDifficulties.end(); ++itr)
1930 os << ',' << int32(*itr);
1931 }
1932
1933 return std::move(os).str();
1934 }());
1935 stmt->setUInt8(index++, data.phaseUseFlags);
1936 stmt->setUInt32(index++, data.phaseId);
1937 stmt->setUInt32(index++, data.phaseGroup);
1938 stmt->setInt32(index++, data.terrainSwapMap);
1939 stmt->setFloat(index++, GetPositionX());
1940 stmt->setFloat(index++, GetPositionY());
1941 stmt->setFloat(index++, GetPositionZ());
1942 stmt->setFloat(index++, GetOrientation());
1943 stmt->setFloat(index++, m_localRotation.x);
1944 stmt->setFloat(index++, m_localRotation.y);
1945 stmt->setFloat(index++, m_localRotation.z);
1946 stmt->setFloat(index++, m_localRotation.w);
1947 stmt->setInt32(index++, int32(m_respawnDelayTime));
1948 stmt->setUInt8(index++, GetGoAnimProgress());
1949 stmt->setUInt8(index++, uint8(GetGoState()));
1950 stmt->setString(index++, sObjectMgr->GetScriptName(data.scriptId));
1951 if (std::string_view stringId = GetStringId(StringIdType::Spawn); !stringId.empty())
1952 stmt->setString(index++, stringId);
1953 else
1954 stmt->setNull(index++);
1955
1956 trans->Append(stmt);
1957
1958 WorldDatabase.CommitTransaction(trans);
1959}
1960
1961bool GameObject::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool)
1962{
1963 GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
1964 if (!data)
1965 {
1966 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {}) not found in table `gameobject`, can't load. ", spawnId);
1967 return false;
1968 }
1969
1970 uint32 entry = data->id;
1971 //uint32 map_id = data->mapid; // already used before call
1972
1973 uint32 animprogress = data->animprogress;
1974 GOState go_state = data->goState;
1975 uint32 artKit = data->artKit;
1976
1977 m_spawnId = spawnId;
1979 if (!Create(entry, map, data->spawnPoint, data->rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode, spawnId))
1980 return false;
1981
1984
1985 // Set StateWorldEffectsQuestObjectiveID if there is only one linked objective for this gameobject
1986 if (data && data->spawnTrackingQuestObjectives.size() == 1)
1988
1989 if (data->spawntimesecs >= 0)
1990 {
1991 m_spawnedByDefault = true;
1992
1993 if (!GetGOInfo()->GetDespawnPossibility() && !GetGOInfo()->IsDespawnAtAction())
1994 {
1997 m_respawnTime = 0;
1998 }
1999 else
2000 {
2003
2004 // ready to respawn
2006 {
2007 m_respawnTime = 0;
2009 }
2010 }
2011 }
2012 else
2013 {
2015 {
2016 TC_LOG_WARN("sql.sql", "GameObject {} (SpawnID {}) is not spawned by default, but tries to use a non-hack spawn system. This will not work. Defaulting to compatibility mode.", entry, spawnId);
2018 }
2019
2020 m_spawnedByDefault = false;
2022 m_respawnTime = 0;
2023 }
2024
2025 m_goData = data;
2026
2028
2029 if (addToMap && !GetMap()->AddToMap(this))
2030 return false;
2031
2032 return true;
2033}
2034
2036{
2037 GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
2038 if (!data)
2039 return false;
2040
2041 CharacterDatabaseTransaction charTrans = CharacterDatabase.BeginTransaction();
2042
2043 sMapMgr->DoForAllMapsWithMapId(data->mapId,
2044 [spawnId, charTrans](Map* map) -> void
2045 {
2046 // despawn all active objects, and remove their respawns
2047 std::vector<GameObject*> toUnload;
2048 for (auto const& pair : Trinity::Containers::MapEqualRange(map->GetGameObjectBySpawnIdStore(), spawnId))
2049 toUnload.push_back(pair.second);
2050 for (GameObject* obj : toUnload)
2051 map->AddObjectToRemoveList(obj);
2052 map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, spawnId, charTrans);
2053 }
2054 );
2055
2056 // delete data from memory
2057 sObjectMgr->DeleteGameObjectData(spawnId);
2058
2059 CharacterDatabase.CommitTransaction(charTrans);
2060
2061 WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
2062
2063 // ... and the database
2065 stmt->setUInt64(0, spawnId);
2066 trans->Append(stmt);
2067
2068 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER);
2070 stmt->setUInt64(1, spawnId);
2071 trans->Append(stmt);
2072
2073 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_EVENT_GAMEOBJECT);
2074 stmt->setUInt64(0, spawnId);
2075 trans->Append(stmt);
2076
2077 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN);
2078 stmt->setUInt64(0, spawnId);
2080 trans->Append(stmt);
2081
2082 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN);
2083 stmt->setUInt64(0, spawnId);
2085 trans->Append(stmt);
2086
2087 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN_MASTER);
2088 stmt->setUInt64(0, spawnId);
2090 trans->Append(stmt);
2091
2092 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN_MASTER);
2093 stmt->setUInt64(0, spawnId);
2095 trans->Append(stmt);
2096
2097 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT_ADDON);
2098 stmt->setUInt64(0, spawnId);
2099 trans->Append(stmt);
2100
2101 WorldDatabase.CommitTransaction(trans);
2102
2103 return true;
2104}
2105
2106/*********************************************************/
2107/*** QUEST SYSTEM ***/
2108/*********************************************************/
2109bool GameObject::hasQuest(uint32 quest_id) const
2110{
2111 return sObjectMgr->GetGOQuestRelations(GetEntry()).HasQuest(quest_id);
2112}
2113
2115{
2116 return sObjectMgr->GetGOQuestInvolvedRelations(GetEntry()).HasQuest(quest_id);
2117}
2118
2120{
2121 // If something is marked as a transport, don't transmit an out of range packet for it.
2122 GameObjectTemplate const* gInfo = GetGOInfo();
2123 if (!gInfo)
2124 return false;
2125
2127}
2128
2130{
2131 GameObjectTemplate const* gInfo = GetGOInfo();
2132 if (!gInfo)
2133 return false;
2134
2136}
2137
2139{
2140 if (m_goData && (forceDelay || m_respawnTime > GameTime::GetGameTime()) && m_spawnedByDefault)
2141 {
2143 {
2144 RespawnInfo ri;
2146 ri.spawnId = m_spawnId;
2149 return;
2150 }
2151
2152 uint32 thisRespawnTime = forceDelay ? GameTime::GetGameTime() + forceDelay : m_respawnTime;
2154 }
2155}
2156
2157bool GameObject::IsNeverVisibleFor(WorldObject const* seer, bool allowServersideObjects) const
2158{
2160 return true;
2161
2162 if (GetGOInfo()->GetServerOnly() && !allowServersideObjects)
2163 return true;
2164
2165 if (!GetDisplayId() && GetGOInfo()->IsDisplayMandatory())
2166 return true;
2167
2168 if (m_goTypeImpl)
2169 return m_goTypeImpl->IsNeverVisibleFor(seer, allowServersideObjects);
2170
2171 return false;
2172}
2173
2175{
2177 return true;
2178
2180 return true;
2181
2182 if (!seer)
2183 return false;
2184
2185 // Always seen by owner and friendly units
2186 if (!GetOwnerGUID().IsEmpty())
2187 {
2188 if (seer->GetGUID() == GetOwnerGUID())
2189 return true;
2190
2191 Unit* owner = GetOwner();
2192 if (Unit const* unitSeer = seer->ToUnit())
2193 if (owner && owner->IsFriendlyTo(unitSeer))
2194 return true;
2195 }
2196
2197 return false;
2198}
2199
2201{
2203 return true;
2204
2205 // Despawned
2206 if (!isSpawned())
2207 return true;
2208
2209 if (m_perPlayerState)
2211 if (state->Despawned)
2212 return true;
2213
2214 return false;
2215}
2216
2218{
2219 if (Unit* owner = GetOwner())
2220 return owner->GetLevelForTarget(target);
2221
2223 {
2224 if (Player const* player = target->ToPlayer())
2225 if (Optional<ContentTuningLevels> userLevels = sDB2Manager.GetContentTuningData(GetGOInfo()->ContentTuningId, player->m_playerData->CtrOptions->ConditionalFlags))
2226 return uint8(std::clamp<int16>(player->GetLevel(), userLevels->MinLevel, userLevels->MaxLevel));
2227
2228 if (Unit const* targetUnit = target->ToUnit())
2229 return targetUnit->GetLevel();
2230 }
2231
2232 return 1;
2233}
2234
2236{
2237 time_t now = GameTime::GetGameTime();
2238 if (m_respawnTime > now)
2239 return m_respawnTime;
2240 else
2241 return now;
2242}
2243
2245{
2246 m_respawnTime = respawn > 0 ? GameTime::GetGameTime() + respawn : 0;
2247 m_respawnDelayTime = respawn > 0 ? respawn : 0;
2248 if (respawn && !m_spawnedByDefault)
2250}
2251
2260
2262{
2263 if (GetGOInfo()->GetQuestID())
2264 return true;
2265
2266 if (GetGoType() != GAMEOBJECT_TYPE_AURA_GENERATOR && GetGOInfo()->GetConditionID1())
2267 return true;
2268
2269 if (sObjectMgr->IsGameObjectForQuests(GetEntry()))
2270 return true;
2271
2272 return false;
2273}
2274
2276{
2277 if (!MeetsInteractCondition(target))
2278 return false;
2279
2280 if (!ActivateToQuest(target))
2281 return false;
2282
2283 return true;
2284}
2285
2286bool GameObject::ActivateToQuest(Player const* target) const
2287{
2288 if (target->HasQuestForGO(GetEntry()))
2289 return true;
2290
2291 if (!sObjectMgr->IsGameObjectForQuests(GetEntry()))
2292 return true;
2293
2294 switch (GetGoType())
2295 {
2297 {
2299 return true;
2300 break;
2301 }
2303 {
2304 // Chests become inactive while not ready to be looted
2305 if (getLootState() == GO_NOT_READY)
2306 return false;
2307
2308 // scan GO chest with loot including quest items
2313 {
2314 return true;
2315 }
2316 break;
2317 }
2319 {
2320 if (target->GetQuestStatus(GetGOInfo()->generic.questID) == QUEST_STATUS_INCOMPLETE)
2321 return true;
2322 break;
2323 }
2325 {
2326 if (target->GetQuestStatus(GetGOInfo()->spellFocus.questID) == QUEST_STATUS_INCOMPLETE)
2327 return true;
2328 break;
2329 }
2331 {
2332 if (target->GetQuestStatus(GetGOInfo()->goober.questID) == QUEST_STATUS_INCOMPLETE)
2333 return true;
2334 break;
2335 }
2337 {
2339 return true;
2340 break;
2341 }
2342 default:
2343 break;
2344 }
2345
2346 return false;
2347}
2348
2350{
2351 GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(trapEntry);
2352 if (!trapInfo || trapInfo->type != GAMEOBJECT_TYPE_TRAP)
2353 return;
2354
2355 SpellInfo const* trapSpell = sSpellMgr->GetSpellInfo(trapInfo->trap.spell, GetMap()->GetDifficultyID());
2356 if (!trapSpell) // checked at load already
2357 return;
2358
2359 if (GameObject* trapGO = GetLinkedTrap())
2360 trapGO->CastSpell(target, trapSpell->Id);
2361}
2362
2364{
2365 GameObject* ok = nullptr;
2366 Trinity::NearestGameObjectFishingHole u_check(*this, range);
2368 Cell::VisitGridObjects(this, checker, range);
2369 return ok;
2370}
2371
2383
2384void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */, Unit* user /*=nullptr*/)
2385{
2386 if (m_lootState != GO_READY)
2387 return;
2388
2389 if (!time_to_restore)
2390 time_to_restore = GetGOInfo()->GetAutoCloseTime();
2391
2392 SwitchDoorOrButton(true, alternative);
2394
2395 m_cooldownTime = time_to_restore ? (GameTime::GetGameTimeMS() + time_to_restore) : 0;
2396}
2397
2398void GameObject::ActivateObject(GameObjectActions action, int32 param, WorldObject* spellCaster /*= nullptr*/, uint32 spellId /*= 0*/, int32 effectIndex /*= -1*/)
2399{
2400 Unit* unitCaster = spellCaster ? spellCaster->ToUnit() : nullptr;
2401
2402 switch (action)
2403 {
2405 TC_LOG_FATAL("spell", "Spell {} has action type NONE in effect {}", spellId, effectIndex);
2406 break;
2412 break;
2413 case GameObjectActions::Disturb: // What's the difference with Open?
2414 if (unitCaster)
2415 Use(unitCaster);
2416 break;
2419 break;
2422 break;
2424 if (unitCaster)
2425 Use(unitCaster);
2426 break;
2428 if (unitCaster)
2429 UseDoorOrButton(0, false, unitCaster);
2431 break;
2434 break;
2436 // No use cases, implementation unknown
2437 break;
2439 if (unitCaster)
2440 UseDoorOrButton(0, true, unitCaster);
2441 break;
2444 break;
2446 // No use cases, implementation unknown
2447 break;
2450 break;
2453 break;
2456 break;
2460 break;
2466 {
2467 GameObjectTemplateAddon const* templateAddon = GetTemplateAddon();
2468
2469 uint32 artKitIndex = action != GameObjectActions::UseArtKit4 ? uint32(action) - uint32(GameObjectActions::UseArtKit0) : 4;
2470
2471 uint32 artKitValue = 0;
2472 if (templateAddon != nullptr)
2473 artKitValue = templateAddon->ArtKits[artKitIndex];
2474
2475 if (artKitValue == 0)
2476 TC_LOG_ERROR("sql.sql", "GameObject {} hit by spell {} needs `artkit{}` in `gameobject_template_addon`", GetEntry(), spellId, artKitIndex);
2477 else
2478 SetGoArtKit(artKitValue);
2479
2480 break;
2481 }
2494 SetGoState(GOState(action));
2495 else
2496 TC_LOG_ERROR("spell", "Spell {} targeted non-transport gameobject for transport only action \"Go to Floor\" {} in effect {}", spellId, int32(action), effectIndex);
2497 break;
2499 SetAnimKitId(param, false);
2500 break;
2502 if (unitCaster)
2503 UseDoorOrButton(0, false, unitCaster);
2504 SetAnimKitId(param, false);
2505 break;
2508 SetAnimKitId(param, false);
2509 break;
2511 SetAnimKitId(param, true);
2512 break;
2514 SetAnimKitId(0, false);
2515 break;
2517 if (unitCaster)
2518 UseDoorOrButton(0, false, unitCaster);
2519 SetAnimKitId(0, false);
2520 break;
2523 SetAnimKitId(0, false);
2524 break;
2526 SetSpellVisualId(param, Object::GetGUID(spellCaster));
2527 break;
2530 break;
2531 default:
2532 TC_LOG_ERROR("spell", "Spell {} has unhandled action {} in effect {}", spellId, int32(action), effectIndex);
2533 break;
2534 }
2535
2536 // Apply side effects of type
2537 if (m_goTypeImpl)
2538 m_goTypeImpl->ActivateObject(action, param, spellCaster, spellId, effectIndex);
2539}
2540
2542{
2544 GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGameObjectData(m_spawnId));
2545 if (data)
2546 data->artKit = kit;
2547}
2548
2550{
2551 GameObjectData const* data = nullptr;
2552 if (go)
2553 {
2554 go->SetGoArtKit(artkit);
2555 data = go->GetGameObjectData();
2556 }
2557 else if (lowguid)
2558 data = sObjectMgr->GetGameObjectData(lowguid);
2559
2560 if (data)
2561 const_cast<GameObjectData*>(data)->artKit = artkit;
2562}
2563
2564void GameObject::SwitchDoorOrButton(bool activate, bool alternative /* = false */)
2565{
2566 if (activate)
2568 else
2570
2571 if (GetGoState() == GO_STATE_READY) //if closed -> open
2573 else //if open -> close
2575}
2576
2577void GameObject::Use(Unit* user, bool ignoreCastInProgress /*= false*/)
2578{
2579 // by default spell caster is user
2580 Unit* spellCaster = user;
2581 uint32 spellId = 0;
2582 CastSpellExtraArgs spellArgs;
2583 if (ignoreCastInProgress)
2585
2586 if (Player* playerUser = user->ToPlayer())
2587 {
2588 if (m_goInfo->GetNoDamageImmune() && playerUser->HasUnitFlag(UNIT_FLAG_IMMUNE))
2589 return;
2590
2591 if (!m_goInfo->IsUsableMounted())
2592 playerUser->RemoveAurasByType(SPELL_AURA_MOUNTED);
2593
2594 playerUser->PlayerTalkClass->ClearMenus();
2595 if (AI()->OnGossipHello(playerUser))
2596 return;
2597 }
2598
2599 // If cooldown data present in template
2600 if (uint32 cooldown = GetGOInfo()->GetCooldown())
2601 {
2603 return;
2604
2606 }
2607
2608 switch (GetGoType())
2609 {
2610 case GAMEOBJECT_TYPE_DOOR: //0
2611 case GAMEOBJECT_TYPE_BUTTON: //1
2612 //doors/buttons never really despawn, only reset to default state/flags
2613 UseDoorOrButton(0, false, user);
2614 return;
2616 {
2617 if (user->GetTypeId() != TYPEID_PLAYER)
2618 return;
2619
2620 Player* player = user->ToPlayer();
2621
2622 player->PrepareGossipMenu(this, GetGOInfo()->questgiver.gossipID, true);
2623 player->SendPreparedGossip(this);
2624 return;
2625 }
2626 case GAMEOBJECT_TYPE_CHEST: //3
2627 {
2628 Player* player = user->ToPlayer();
2629 if (!player)
2630 return;
2631
2632 GameObjectTemplate const* info = GetGOInfo();
2633 if (!m_loot && info->GetLootId())
2634 {
2635 if (info->GetLootId())
2636 {
2637 Group const* group = player->GetGroup();
2638 bool groupRules = group && info->IsUsingGroupLootRules();
2639
2640 Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_CHEST, groupRules ? group : nullptr);
2641 m_loot.reset(loot);
2642
2644 loot->FillLoot(info->GetLootId(), LootTemplates_Gameobject, player, !groupRules, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
2645
2646 if (GetLootMode() > 0)
2647 if (GameObjectTemplateAddon const* addon = GetTemplateAddon())
2648 loot->generateMoneyLoot(addon->Mingold, addon->Maxgold);
2649 }
2650
2651 if (info->chest.triggeredEvent)
2652 GameEvents::Trigger(info->chest.triggeredEvent, player, this);
2653
2654 // triggering linked GO
2655 if (uint32 trapEntry = info->chest.linkedTrap)
2656 TriggeringLinkedGameObject(trapEntry, player);
2657 }
2658 else if (!m_personalLoot.count(player->GetGUID()))
2659 {
2660 if (info->chest.chestPersonalLoot)
2661 {
2663 if (info->chest.DungeonEncounter)
2664 {
2665 std::vector<Player*> tappers;
2666 for (ObjectGuid tapperGuid : GetTapList())
2667 if (Player* tapper = ObjectAccessor::GetPlayer(*this, tapperGuid))
2668 tappers.push_back(tapper);
2669
2670 if (tappers.empty())
2671 tappers.push_back(player);
2672
2674 LootTemplates_Gameobject, LOOT_CHEST, this, addon ? addon->Mingold : 0, addon ? addon->Maxgold : 0,
2675 GetLootMode(), GetMap()->GetMapDifficulty(), tappers);
2676 }
2677 else
2678 {
2679 Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_CHEST, nullptr);
2680 m_personalLoot[player->GetGUID()].reset(loot);
2681
2683 loot->FillLoot(info->chest.chestPersonalLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
2684
2685 if (GetLootMode() > 0 && addon)
2686 loot->generateMoneyLoot(addon->Mingold, addon->Maxgold);
2687 }
2688 }
2689 }
2690
2691 if (!m_unique_users.count(player->GetGUID()) && !info->GetLootId())
2692 {
2693 if (info->chest.chestPushLoot)
2694 {
2695 Loot pushLoot(GetMap(), GetGUID(), LOOT_CHEST, nullptr);
2696 pushLoot.FillLoot(info->chest.chestPushLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
2697 pushLoot.AutoStore(player, NULL_BAG, NULL_SLOT);
2698 }
2699
2700 if (info->chest.triggeredEvent)
2701 GameEvents::Trigger(info->chest.triggeredEvent, player, this);
2702
2703 // triggering linked GO
2704 if (uint32 trapEntry = info->chest.linkedTrap)
2705 TriggeringLinkedGameObject(trapEntry, player);
2706
2707 // Cast spell before sending loot
2708 if (spellCaster && info->chest.spell)
2709 spellCaster->CastSpell(nullptr, info->chest.spell, spellArgs);
2710
2711 AddUniqueUse(player);
2712 }
2713
2714 if (getLootState() != GO_ACTIVATED)
2715 SetLootState(GO_ACTIVATED, player);
2716
2717 // Send loot
2718 if (Loot* loot = GetLootForPlayer(player))
2719 player->SendLoot(*loot);
2720 break;
2721 }
2722 case GAMEOBJECT_TYPE_TRAP: //6
2723 {
2724 GameObjectTemplate const* goInfo = GetGOInfo();
2725 if (goInfo->trap.spell)
2726 CastSpell(user, goInfo->trap.spell);
2727
2728 m_cooldownTime = GameTime::GetGameTimeMS() + (goInfo->trap.cooldown ? goInfo->trap.cooldown : uint32(4)) * IN_MILLISECONDS; // template or 4 seconds
2729
2730 if (goInfo->trap.charges == 1) // Deactivate after trigger
2732
2733 return;
2734 }
2735 //Sitting: Wooden bench, chairs enzz
2736 case GAMEOBJECT_TYPE_CHAIR: //7
2737 {
2738 GameObjectTemplate const* info = GetGOInfo();
2739 if (ChairListSlots.empty()) // this is called once at first chair use to make list of available slots
2740 {
2741 if (info->chair.chairslots > 0) // sometimes chairs in DB have error in fields and we dont know number of slots
2742 for (uint32 i = 0; i < info->chair.chairslots; ++i)
2743 ChairListSlots[i].Clear(); // Last user of current slot set to 0 (none sit here yet)
2744 else
2745 ChairListSlots[0].Clear(); // error in DB, make one default slot
2746 }
2747
2748 // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
2749
2750 float lowestDist = DEFAULT_VISIBILITY_DISTANCE;
2751
2752 uint32 nearest_slot = 0;
2753 float x_lowest = GetPositionX();
2754 float y_lowest = GetPositionY();
2755
2756 // the object orientation + 1/2 pi
2757 // every slot will be on that straight line
2758 float orthogonalOrientation = GetOrientation() + float(M_PI) * 0.5f;
2759 // find nearest slot
2760 bool found_free_slot = false;
2761 for (auto& [slot, sittingUnit] : ChairListSlots)
2762 {
2763 // the distance between this slot and the center of the go - imagine a 1D space
2764 float relativeDistance = (info->size * slot) - (info->size * (info->chair.chairslots - 1) / 2.0f);
2765
2766 float x_i = GetPositionX() + relativeDistance * std::cos(orthogonalOrientation);
2767 float y_i = GetPositionY() + relativeDistance * std::sin(orthogonalOrientation);
2768
2769 if (!sittingUnit.IsEmpty())
2770 {
2771 if (Unit* chairUser = ObjectAccessor::GetUnit(*this, sittingUnit))
2772 {
2773 if (chairUser->IsSitState() && chairUser->GetStandState() != UNIT_STAND_STATE_SIT && chairUser->GetExactDist2d(x_i, y_i) < 0.1f)
2774 continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->GetStandState() != UNIT_STAND_STATE_SIT check is required.
2775
2776 sittingUnit.Clear(); // This seat is unoccupied.
2777 }
2778 else
2779 sittingUnit.Clear(); // The seat may of had an occupant, but they're offline.
2780 }
2781
2782 found_free_slot = true;
2783
2784 // calculate the distance between the player and this slot
2785 float thisDistance = user->GetDistance2d(x_i, y_i);
2786
2787 if (thisDistance <= lowestDist)
2788 {
2789 nearest_slot = slot;
2790 lowestDist = thisDistance;
2791 x_lowest = x_i;
2792 y_lowest = y_i;
2793 }
2794 }
2795
2796 if (found_free_slot)
2797 {
2798 auto itr = ChairListSlots.find(nearest_slot);
2799 if (itr != ChairListSlots.end())
2800 {
2801 itr->second = user->GetGUID(); //this slot in now used by player
2802 user->NearTeleportTo(x_lowest, y_lowest, GetPositionZ(), GetOrientation());
2804 if (info->chair.triggeredEvent)
2805 GameEvents::Trigger(info->chair.triggeredEvent, user, this);
2806 return;
2807 }
2808 }
2809
2810 return;
2811 }
2813 // triggering linked GO
2814 if (uint32 trapEntry = GetGOInfo()->spellFocus.linkedTrap)
2815 TriggeringLinkedGameObject(trapEntry, user);
2816 break;
2817 //big gun, its a spell/aura
2818 case GAMEOBJECT_TYPE_GOOBER: //10
2819 {
2820 GameObjectTemplate const* info = GetGOInfo();
2821 Player* player = user->ToPlayer();
2822
2823 if (player)
2824 {
2825 if (info->goober.pageID) // show page...
2826 {
2828 data.GameObjectGUID = GetGUID();
2829 player->SendDirectMessage(data.Write());
2830 }
2831 else if (info->goober.gossipID)
2832 {
2833 player->PrepareGossipMenu(this, info->goober.gossipID);
2834 player->SendPreparedGossip(this);
2835 }
2836
2837 if (info->goober.eventID)
2838 {
2839 TC_LOG_DEBUG("maps.script", "Goober ScriptStart id {} for GO entry {} (GUID {}).", info->goober.eventID, GetEntry(), GetSpawnId());
2840 GameEvents::Trigger(info->goober.eventID, player, this);
2841 }
2842
2843 // possible quest objective for active quests
2844 if (info->goober.questID && sObjectMgr->GetQuestTemplate(info->goober.questID))
2845 {
2846 //Quest require to be active for GO using
2848 break;
2849 }
2850
2851 if (Group* group = player->GetGroup())
2852 {
2853 for (GroupReference const& itr : group->GetMembers())
2854 if (itr.GetSource()->IsAtGroupRewardDistance(this))
2855 itr.GetSource()->KillCreditGO(info->entry, GetGUID());
2856 }
2857 else
2858 player->KillCreditGO(info->entry, GetGUID());
2859 }
2860
2861 if (uint32 trapEntry = info->goober.linkedTrap)
2862 TriggeringLinkedGameObject(trapEntry, user);
2863
2864 if (info->goober.AllowMultiInteract && player)
2865 {
2866 if (info->IsDespawnAtAction())
2868 else
2870 }
2871 else
2872 {
2875
2876 // this appear to be ok, however others exist in addition to this that should have custom (ex: 190510, 188692, 187389)
2877 if (info->goober.customAnim)
2879 else
2881
2883 }
2884
2885 // cast this spell later if provided
2886 spellId = info->goober.spell;
2887 if (!info->goober.playerCast)
2888 spellCaster = nullptr;
2889
2890 break;
2891 }
2892 case GAMEOBJECT_TYPE_CAMERA: //13
2893 {
2894 GameObjectTemplate const* info = GetGOInfo();
2895 if (!info)
2896 return;
2897
2898 if (user->GetTypeId() != TYPEID_PLAYER)
2899 return;
2900
2901 Player* player = user->ToPlayer();
2902
2903 if (info->camera.camera)
2904 player->SendCinematicStart(info->camera.camera);
2905
2906 if (info->camera.eventID)
2907 GameEvents::Trigger(info->camera.eventID, player, this);
2908
2909 return;
2910 }
2911 //fishing bobber
2913 {
2914 Player* player = user->ToPlayer();
2915 if (!player)
2916 return;
2917
2918 if (player->GetGUID() != GetOwnerGUID())
2919 return;
2920
2921 switch (getLootState())
2922 {
2923 case GO_READY: // ready for loot
2924 {
2925 SetLootState(GO_ACTIVATED, player);
2926
2929
2930 SendUpdateToPlayer(player);
2931
2932 AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(GetAreaId());
2933 if (!areaEntry)
2934 {
2935 TC_LOG_ERROR("entities.gameobject", "Gameobject '{}' ({}) spawned in unknown area (x: {} y: {} z: {} map: {})",
2937 break;
2938 }
2939
2940 // Update the correct fishing skill according to the area's ContentTuning
2941 ContentTuningEntry const* areaContentTuning = DB2Manager::GetContentTuningForArea(areaEntry);
2942 if (!areaContentTuning)
2943 break;
2944
2945 player->UpdateFishingSkill(areaContentTuning->ExpansionID);
2946
2947 // Send loot
2948 int32 areaFishingLevel = sObjectMgr->GetFishingBaseSkillLevel(areaEntry);
2949
2950 uint32 playerFishingSkill = player->GetProfessionSkillForExp(SKILL_FISHING, areaContentTuning->ExpansionID);
2951 int32 playerFishingLevel = player->GetSkillValue(playerFishingSkill);
2952
2953 int32 roll = irand(1, 100);
2954 int32 chance = 100;
2955 if (playerFishingLevel < areaFishingLevel)
2956 {
2957 chance = int32(pow((double)playerFishingLevel / areaFishingLevel, 2) * 100);
2958 if (chance < 1)
2959 chance = 1;
2960 }
2961
2962 TC_LOG_DEBUG("misc", "Fishing check (skill {} level: {} area skill level: {} chance {} roll: {}", playerFishingSkill, playerFishingLevel, areaFishingLevel, chance, roll);
2963
2965 GameObject* fishingPool = LookupFishingHoleAround(20.0f + CONTACT_DISTANCE);
2966
2967 // If fishing skill is high enough, or if fishing on a pool, send correct loot.
2968 // Fishing pools have no skill requirement as of patch 3.3.0 (undocumented change).
2969 if (chance >= roll || fishingPool)
2970 {
2972 // prevent removing GO at spell cancel
2974 SetOwnerGUID(player->GetGUID());
2975 SetSpellId(0); // prevent removing unintended auras at Unit::RemoveGameObject
2976
2977 if (fishingPool)
2978 {
2979 fishingPool->Use(player, ignoreCastInProgress);
2981 }
2982 else
2983 {
2984 m_loot.reset(GetFishLoot(player));
2985 player->SendLoot(*m_loot);
2986 }
2987 }
2988 else // If fishing skill is too low, send junk loot.
2989 {
2990 m_loot.reset(GetFishLootJunk(player));
2991 player->SendLoot(*m_loot);
2992 }
2993 break;
2994 }
2995 case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update
2996 break;
2997 default:
2998 {
3001 break;
3002 }
3003 }
3004
3006 return;
3007 }
3008
3009 case GAMEOBJECT_TYPE_RITUAL: //18
3010 {
3011 if (user->GetTypeId() != TYPEID_PLAYER)
3012 return;
3013
3014 Player* player = user->ToPlayer();
3015
3016 Unit* owner = GetOwner();
3017
3018 GameObjectTemplate const* info = GetGOInfo();
3019
3020 Player* m_ritualOwner = nullptr;
3023
3024 // ritual owner is set for GO's without owner (not summoned)
3025 if (!m_ritualOwner && !owner)
3026 {
3027 m_ritualOwnerGUID = player->GetGUID();
3028 m_ritualOwner = player;
3029 }
3030
3031 if (owner)
3032 {
3033 if (owner->GetTypeId() != TYPEID_PLAYER)
3034 return;
3035
3036 // accept only use by player from same group as owner, excluding owner itself (unique use already added in spell effect)
3037 if (player == owner->ToPlayer() || (info->ritual.castersGrouped && !player->IsInSameRaidWith(owner->ToPlayer())))
3038 return;
3039
3040 // expect owner to already be channeling, so if not...
3042 return;
3043
3044 // in case summoning ritual caster is GO creator
3045 spellCaster = owner;
3046 }
3047 else
3048 {
3049 if (player != m_ritualOwner && (info->ritual.castersGrouped && !player->IsInSameRaidWith(m_ritualOwner)))
3050 return;
3051
3052 spellCaster = player;
3053 }
3054
3055 AddUniqueUse(player);
3056
3057 if (info->ritual.animSpell)
3058 {
3059 player->CastSpell(player, info->ritual.animSpell, true);
3060
3061 // for this case, summoningRitual.spellId is always triggered
3063 }
3064
3065 // full amount unique participants including original summoner
3066 if (GetUniqueUseCount() == info->ritual.casters)
3067 {
3068 if (m_ritualOwner)
3069 spellCaster = m_ritualOwner;
3070
3071 spellId = info->ritual.spell;
3072
3073 if (spellId == 62330) // GO store nonexistent spell, replace by expected
3074 {
3075 // spell have reagent and mana cost but it not expected use its
3076 // it triggered spell in fact cast at currently channeled GO
3077 spellId = 61993;
3079 }
3080
3081 // Cast casterTargetSpell at a random GO user
3082 // on the current DB there is only one gameobject that uses this (Ritual of Doom)
3083 // and its required target number is 1 (outter for loop will run once)
3084 if (info->ritual.casterTargetSpell && info->ritual.casterTargetSpell != 1) // No idea why this field is a bool in some cases
3085 for (uint32 i = 0; i < info->ritual.casterTargetSpellTargets; i++)
3086 // m_unique_users can contain only player GUIDs
3088 spellCaster->CastSpell(target, info->ritual.casterTargetSpell, true);
3089
3090 // finish owners spell
3091 if (owner)
3093
3094 // can be deleted now, if
3095 if (!info->ritual.ritualPersistent)
3097 else
3098 {
3099 // reset ritual for this GO
3101 m_unique_users.clear();
3102 m_usetimes = 0;
3103 }
3104 }
3105 else
3106 return;
3107
3108 // go to end function to spell casting
3109 break;
3110 }
3112 {
3113 GameObjectTemplate const* info = GetGOInfo();
3114 if (!info)
3115 return;
3116
3117 if (info->spellCaster.partyOnly)
3118 {
3119 Unit* caster = GetOwner();
3120 if (!caster || caster->GetTypeId() != TYPEID_PLAYER)
3121 return;
3122
3123 if (user->GetTypeId() != TYPEID_PLAYER || !user->ToPlayer()->IsInSameRaidWith(caster->ToPlayer()))
3124 return;
3125 }
3126
3128 spellId = info->spellCaster.spell;
3129
3130 AddUse();
3131 break;
3132 }
3134 {
3135 GameObjectTemplate const* info = GetGOInfo();
3136
3137 if (user->GetTypeId() != TYPEID_PLAYER)
3138 return;
3139
3140 Player* player = user->ToPlayer();
3141
3142 Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetTarget());
3143
3144 // accept only use by player from same raid as caster, except caster itself
3145 if (!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameRaidWith(player))
3146 return;
3147
3148 //required lvl checks!
3149 if (Optional<ContentTuningLevels> userLevels = sDB2Manager.GetContentTuningData(info->ContentTuningId, player->m_playerData->CtrOptions->ConditionalFlags))
3150 if (player->GetLevel() < userLevels->MaxLevel)
3151 return;
3152
3153 if (Optional<ContentTuningLevels> targetLevels = sDB2Manager.GetContentTuningData(info->ContentTuningId, targetPlayer->m_playerData->CtrOptions->ConditionalFlags))
3154 if (targetPlayer->GetLevel() < targetLevels->MaxLevel)
3155 return;
3156
3157 if (info->entry == 194097)
3158 spellId = 61994; // Ritual of Summoning
3159 else
3160 spellId = 59782; // Summoning Stone Effect
3161
3162 break;
3163 }
3164
3165 case GAMEOBJECT_TYPE_FLAGSTAND: // 24
3166 {
3167 if (user->GetTypeId() != TYPEID_PLAYER)
3168 return;
3169
3170 Player* player = user->ToPlayer();
3171
3172 if (player->CanUseBattlegroundObject(this))
3173 {
3174 if (player->GetVehicle())
3175 return;
3176
3178 return;
3179
3180 if (!MeetsInteractCondition(player))
3181 return;
3182
3185 spellId = GetGOInfo()->flagStand.pickupSpell;
3186 spellCaster = nullptr;
3187 }
3188 break;
3189 }
3190
3191 case GAMEOBJECT_TYPE_FISHINGHOLE: // 25
3192 {
3193 if (user->GetTypeId() != TYPEID_PLAYER)
3194 return;
3195
3196 Player* player = user->ToPlayer();
3197
3198 Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_FISHINGHOLE, nullptr);
3199 loot->FillLoot(GetGOInfo()->GetLootId(), LootTemplates_Gameobject, player, true, false, LOOT_MODE_DEFAULT, ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
3200 m_personalLoot[player->GetGUID()].reset(loot);
3201
3202 player->SendLoot(*loot);
3204 return;
3205 }
3206
3207 case GAMEOBJECT_TYPE_FLAGDROP: // 26
3208 {
3209 if (user->GetTypeId() != TYPEID_PLAYER)
3210 return;
3211
3212 Player* player = user->ToPlayer();
3213
3214 if (player->CanUseBattlegroundObject(this))
3215 {
3216 if (player->GetVehicle())
3217 return;
3218
3221 // BG flag dropped
3222 // WS:
3223 // 179785 - Silverwing Flag
3224 // 179786 - Warsong Flag
3225 // EotS:
3226 // 184142 - Netherstorm Flag
3227 GameObjectTemplate const* info = GetGOInfo();
3228 if (info->flagDrop.eventID)
3229 GameEvents::Trigger(info->flagDrop.eventID, player, this);
3230 //this cause to call return, all flags must be deleted here!!
3231 spellId = 0;
3232 Delete();
3233 }
3234 break;
3235 }
3237 {
3238 GameObjectTemplate const* info = GetGOInfo();
3239 if (!info)
3240 return;
3241
3242 if (user->GetTypeId() != TYPEID_PLAYER)
3243 return;
3244
3245 Player* player = user->ToPlayer();
3246
3247 WorldPackets::Misc::EnableBarberShop enableBarberShop;
3249 player->SendDirectMessage(enableBarberShop.Write());
3250
3251 // fallback, will always work
3253
3255 return;
3256 }
3258 {
3259 GameObjectTemplate const* info = GetGOInfo();
3260 if (!info)
3261 return;
3262
3263 Player* player = user->ToPlayer();
3264 if (!player)
3265 return;
3266
3267 if (!player->CanUseBattlegroundObject(this))
3268 return;
3269
3270 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(m_goTypeImpl.get());
3271 if (!newFlag)
3272 return;
3273
3274 if (newFlag->GetState() != FlagState::InBase)
3275 return;
3276
3277 spellId = info->newflag.pickupSpell;
3278 spellCaster = nullptr;
3279 break;
3280 }
3282 {
3283 GameObjectTemplate const* info = GetGOInfo();
3284 if (!info)
3285 return;
3286
3287 if (user->GetTypeId() != TYPEID_PLAYER)
3288 return;
3289
3290 if (!user->IsAlive())
3291 return;
3292
3293 if (GameObject* owner = GetMap()->GetGameObject(GetOwnerGUID()))
3294 {
3295 if (owner->GetGoType() == GAMEOBJECT_TYPE_NEW_FLAG)
3296 {
3297 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(owner->m_goTypeImpl.get());
3298 if (!newFlag)
3299 return;
3300
3301 if (newFlag->GetState() != FlagState::Dropped)
3302 return;
3303
3304 // friendly with enemy flag means you're taking it
3305 bool defenderInteract = !owner->IsFriendlyTo(user);
3306 if (defenderInteract && owner->GetGOInfo()->newflag.ReturnonDefenderInteract)
3307 {
3308 Delete();
3309 owner->HandleCustomTypeCommand(GameObjectType::SetNewFlagState(FlagState::InBase, user->ToPlayer()));
3310 return;
3311 }
3312 else
3313 {
3314 // we let the owner cast the spell for now
3315 // so that caster guid is set correctly
3316 SpellCastResult result = owner->CastSpell(user, owner->GetGOInfo()->newflag.pickupSpell, CastSpellExtraArgs(TRIGGERED_FULL_MASK));
3317 if (result == SPELL_CAST_OK)
3318 {
3319 Delete();
3320 owner->HandleCustomTypeCommand(GameObjectType::SetNewFlagState(FlagState::Taken, user->ToPlayer()));
3321 return;
3322 }
3323 }
3324 }
3325 }
3326
3327 Delete();
3328 return;
3329 }
3331 {
3332 Player* player = user->ToPlayer();
3333 if (!player)
3334 return;
3335
3336 AssaultCapturePoint(player);
3337 return;
3338 }
3340 {
3341 GameObjectTemplate const* info = GetGOInfo();
3342 if (!info)
3343 return;
3344
3345 if (user->GetTypeId() != TYPEID_PLAYER)
3346 return;
3347
3348 Player* player = user->ToPlayer();
3349
3350 if (!MeetsInteractCondition(player))
3351 return;
3352
3353 switch (info->itemForge.ForgeType)
3354 {
3355 case 0: // Artifact Forge
3356 case 1: // Relic Forge
3357 {
3359 Item const* item = artifactAura ? player->GetItemByGuid(artifactAura->GetCastItemGUID()) : nullptr;
3360 if (!item)
3361 {
3363 return;
3364 }
3365
3367 openArtifactForge.ArtifactGUID = item->GetGUID();
3368 openArtifactForge.ForgeGUID = GetGUID();
3369 player->SendDirectMessage(openArtifactForge.Write());
3370 break;
3371 }
3372 case 2: // Heart Forge
3373 {
3375 if (!item)
3376 return;
3377
3379 openHeartForge.ObjectGUID = GetGUID();
3381 player->SendDirectMessage(openHeartForge.Write());
3382 break;
3383 }
3384 default:
3385 break;
3386 }
3387 return;
3388 }
3390 {
3391 Player* player = user->ToPlayer();
3392 if (!player)
3393 return;
3394
3396 gameObjectUILink.ObjectGUID = GetGUID();
3397 switch (GetGOInfo()->UILink.UILinkType)
3398 {
3399 case 0:
3401 break;
3402 case 1:
3404 break;
3405 case 2:
3407 break;
3408 case 3:
3410 break;
3411 default:
3412 break;
3413 }
3414 player->SendDirectMessage(gameObjectUILink.Write());
3415 return;
3416 }
3418 {
3419 Player* player = user->ToPlayer();
3420 if (!player)
3421 return;
3422
3423 GameObjectTemplate const* info = GetGOInfo();
3424 if (!m_personalLoot.count(player->GetGUID()))
3425 {
3426 if (info->gatheringNode.chestLoot)
3427 {
3428 Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_CHEST, nullptr);
3429 m_personalLoot[player->GetGUID()].reset(loot);
3430
3431 loot->FillLoot(info->gatheringNode.chestLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
3432 }
3433
3434 if (info->gatheringNode.triggeredEvent)
3436
3437 // triggering linked GO
3438 if (uint32 trapEntry = info->gatheringNode.linkedTrap)
3439 TriggeringLinkedGameObject(trapEntry, player);
3440
3441 if (info->gatheringNode.xpDifficulty && info->gatheringNode.xpDifficulty < 10)
3442 if (QuestXPEntry const* questXp = sQuestXPStore.LookupEntry(player->GetLevel()))
3443 if (uint32 xp = Quest::RoundXPValue(questXp->Difficulty[info->gatheringNode.xpDifficulty]))
3444 player->GiveXP(xp, nullptr);
3445
3446 spellId = info->gatheringNode.spell;
3447 }
3448
3449 if (m_personalLoot.size() >= info->gatheringNode.MaxNumberofLoots)
3450 {
3453 }
3454
3455 if (getLootState() != GO_ACTIVATED)
3456 {
3457 SetLootState(GO_ACTIVATED, player);
3460 }
3461
3462 // Send loot
3463 if (Loot* loot = GetLootForPlayer(player))
3464 player->SendLoot(*loot);
3465 break;
3466 }
3467 default:
3469 TC_LOG_ERROR("misc", "GameObject::Use(): unit ({}, name: {}) tries to use object ({}, name: {}) of unknown type ({})",
3470 user->GetGUID().ToString(), user->GetName(), GetGUID().ToString(), GetGOInfo()->name, GetGoType());
3471 break;
3472 }
3473
3474 if (m_vignette)
3475 {
3476 if (Player* player = user->ToPlayer())
3477 {
3478 if (Quest const* reward = sObjectMgr->GetQuestTemplate(m_vignette->Data->RewardQuestID))
3479 if (!player->GetQuestRewardStatus(m_vignette->Data->RewardQuestID))
3480 player->RewardQuest(reward, LootItemType::Item, 0, this, false);
3481
3482 if (m_vignette->Data->VisibleTrackingQuestID)
3483 player->SetRewardedQuest(m_vignette->Data->VisibleTrackingQuestID);
3484 }
3485
3486 // only unregister it from visibility (need to keep vignette for other gameobject users in case its usable by multiple players
3487 // to flag their quest completion
3488 if (GetGOInfo()->ClearObjectVignetteonOpening())
3490 }
3491
3492 if (!spellId)
3493 return;
3494
3495 if (!sSpellMgr->GetSpellInfo(spellId, GetMap()->GetDifficultyID()))
3496 {
3497 if (user->GetTypeId() != TYPEID_PLAYER || !sOutdoorPvPMgr->HandleCustomSpell(user->ToPlayer(), spellId, this))
3498 TC_LOG_ERROR("misc", "WORLD: unknown spell id {} at use action for gameobject (Entry: {} GoType: {})", spellId, GetEntry(), GetGoType());
3499 else
3500 TC_LOG_DEBUG("outdoorpvp", "WORLD: {} non-dbc spell was handled by OutdoorPvP", spellId);
3501 return;
3502 }
3503
3504 if (Player* player = user->ToPlayer())
3505 sOutdoorPvPMgr->HandleCustomSpell(player, spellId, this);
3506
3507 if (spellCaster)
3508 spellCaster->CastSpell(user, spellId, spellArgs);
3509 else
3510 {
3511 SpellCastResult castResult = CastSpell(user, spellId, spellArgs);
3512 if (castResult == SPELL_FAILED_SUCCESS)
3513 {
3514 switch (GetGoType())
3515 {
3518 break;
3521 if (ZoneScript* zonescript = GetZoneScript())
3522 zonescript->OnFlagTaken(this, Object::ToPlayer(user));
3523
3524 Delete();
3525 break;
3526 default:
3527 break;
3528 }
3529 }
3530 }
3531}
3532
3534{
3536 customAnim.ObjectGUID = GetGUID();
3537 customAnim.CustomAnim = anim;
3538 SendMessageToSet(customAnim.Write(), true);
3539}
3540
3541bool GameObject::IsInRange(float x, float y, float z, float radius) const
3542{
3544 if (!info)
3545 return IsWithinDist3d(x, y, z, radius);
3546
3547 float sinA = std::sin(GetOrientation());
3548 float cosA = std::cos(GetOrientation());
3549 float dx = x - GetPositionX();
3550 float dy = y - GetPositionY();
3551 float dz = z - GetPositionZ();
3552 float dist = std::sqrt(dx*dx + dy*dy);
3555 if (G3D::fuzzyEq(dist, 0.0f))
3556 return true;
3557
3558 float sinB = dx / dist;
3559 float cosB = dy / dist;
3560 dx = dist * (cosA * cosB + sinA * sinB);
3561 dy = dist * (cosA * sinB - sinA * cosB);
3562 return dx < info->GeoBoxMax.X + radius && dx > info->GeoBoxMin.X - radius
3563 && dy < info->GeoBoxMax.Y + radius && dy > info->GeoBoxMin.Y - radius
3564 && dz < info->GeoBoxMax.Z + radius && dz > info->GeoBoxMin.Z - radius;
3565}
3566
3568{
3570 if (uint32 scriptId = gameObjectData->scriptId)
3571 return scriptId;
3572
3573 return GetGOInfo()->ScriptId;
3574}
3575
3577{
3578 // copy references to stringIds from template and spawn
3579 m_stringIds = parent->m_stringIds;
3580
3581 // then copy script stringId, not just its reference
3582 SetScriptStringId(std::string(parent->GetStringId(StringIdType::Script)));
3583}
3584
3585bool GameObject::HasStringId(std::string_view id) const
3586{
3587 return std::ranges::any_of(m_stringIds, [id](std::string const* stringId) { return stringId && *stringId == id; });
3588}
3589
3591{
3592 if (!id.empty())
3593 {
3594 m_scriptStringId.emplace(std::move(id));
3596 }
3597 else
3598 {
3599 m_scriptStringId.reset();
3601 }
3602}
3603
3605{
3606 if (!player)
3607 return nullptr;
3608
3609 if (GameObjectData const* data = GetGameObjectData())
3610 {
3611 if (data->spawnTrackingData && !data->spawnTrackingQuestObjectives.empty())
3612 {
3613 SpawnTrackingState state = player->GetSpawnTrackingStateByObjectives(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectives);
3614 return &data->spawnTrackingStates[AsUnderlyingType(state)];
3615 }
3616 }
3617
3618 return nullptr;
3619}
3620
3621// overwrite WorldObject function for proper name localization
3623{
3624 if (locale != DEFAULT_LOCALE)
3625 if (GameObjectLocale const* cl = sObjectMgr->GetGameObjectLocale(GetEntry()))
3626 if (cl->Name.size() > locale && !cl->Name[locale].empty())
3627 return cl->Name[locale];
3628
3629 return GetName();
3630}
3631
3632bool GameObject::HasLabel(int32 gameobjectLabel) const
3633{
3634 return advstd::ranges::contains(GetLabels(), gameobjectLabel);
3635}
3636
3637std::span<int32 const> GameObject::GetLabels() const
3638{
3639 return sDB2Manager.GetGameObjectLabels(GetEntry());
3640}
3641
3643{
3644 static const int32 PACK_YZ = 1 << 20;
3645 static const int32 PACK_X = PACK_YZ << 1;
3646
3647 static const int32 PACK_YZ_MASK = (PACK_YZ << 1) - 1;
3648 static const int32 PACK_X_MASK = (PACK_X << 1) - 1;
3649
3650 int8 w_sign = (m_localRotation.w >= 0.f ? 1 : -1);
3651 int64 x = int32(m_localRotation.x * PACK_X) * w_sign & PACK_X_MASK;
3652 int64 y = int32(m_localRotation.y * PACK_YZ) * w_sign & PACK_YZ_MASK;
3653 int64 z = int32(m_localRotation.z * PACK_YZ) * w_sign & PACK_YZ_MASK;
3654 m_packedRotation = z | (y << 21) | (x << 42);
3655}
3656
3657void GameObject::SetLocalRotation(float qx, float qy, float qz, float qw)
3658{
3659 G3D::Quat rotation(qx, qy, qz, qw);
3660 rotation.unitize();
3661 m_localRotation.x = rotation.x;
3662 m_localRotation.y = rotation.y;
3663 m_localRotation.z = rotation.z;
3664 m_localRotation.w = rotation.w;
3666}
3667
3672
3673void GameObject::SetLocalRotationAngles(float z_rot, float y_rot, float x_rot)
3674{
3675 G3D::Quat quat(G3D::Matrix3::fromEulerAnglesZYX(z_rot, y_rot, x_rot));
3676 SetLocalRotation(quat.x, quat.y, quat.z, quat.w);
3677}
3678
3680{
3681 QuaternionData localRotation = GetLocalRotation();
3682 if (Transport* transport = dynamic_cast<Transport*>(GetTransport()))
3683 {
3684 QuaternionData worldRotation = transport->GetWorldRotation();
3685
3686 G3D::Quat worldRotationQuat(worldRotation.x, worldRotation.y, worldRotation.z, worldRotation.w);
3687 G3D::Quat localRotationQuat(localRotation.x, localRotation.y, localRotation.z, localRotation.w);
3688
3689 G3D::Quat resultRotation = localRotationQuat * worldRotationQuat;
3690
3691 return QuaternionData(resultRotation.x, resultRotation.y, resultRotation.z, resultRotation.w);
3692 }
3693 return localRotation;
3694}
3695
3696void GameObject::ModifyHealth(int32 change, WorldObject* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/)
3697{
3698 if (!m_goValue.Building.DestructibleHitpoint || !change)
3699 return;
3700
3701 // prevent double destructions of the same object
3702 if (change < 0 && !m_goValue.Building.Health)
3703 return;
3704
3705 if (int32(m_goValue.Building.Health) + change <= 0)
3709 else
3710 m_goValue.Building.Health += change;
3711
3712 // Set the health bar, value = 255 * healthPct;
3714
3715 // dealing damage, send packet
3716 if (Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr)
3717 {
3719 packet.Caster = attackerOrHealer->GetGUID(); // todo: this can be a GameObject
3720 packet.Target = GetGUID();
3721 packet.Damage = -change;
3722 packet.Owner = player->GetGUID();
3723 packet.SpellID = spellId;
3724 player->SendDirectMessage(packet.Write());
3725 }
3726
3727 if (change < 0 && GetGOInfo()->destructibleBuilding.DamageEvent)
3728 GameEvents::Trigger(GetGOInfo()->destructibleBuilding.DamageEvent, attackerOrHealer, this);
3729
3731
3733 newState = GO_DESTRUCTIBLE_DESTROYED;
3735 newState = GO_DESTRUCTIBLE_DAMAGED;
3737 newState = GO_DESTRUCTIBLE_INTACT;
3738
3739 if (newState == GetDestructibleState())
3740 return;
3741
3742 SetDestructibleState(newState, attackerOrHealer, false);
3743}
3744
3745void GameObject::SetDestructibleState(GameObjectDestructibleState state, WorldObject* attackerOrHealer /*= nullptr*/, bool setHealth /*= false*/)
3746{
3747 // the user calling this must know he is already operating on destructible gameobject
3749
3750 switch (state)
3751 {
3755 if (setHealth && m_goValue.Building.DestructibleHitpoint)
3756 {
3758 SetGoAnimProgress(255);
3759 }
3760 break;
3762 {
3763 if (GetGOInfo()->destructibleBuilding.DamagedEvent && attackerOrHealer)
3764 GameEvents::Trigger(GetGOInfo()->destructibleBuilding.DamagedEvent, attackerOrHealer, this);
3765 AI()->Damaged(attackerOrHealer, m_goInfo->destructibleBuilding.DamagedEvent);
3766
3769
3770 uint32 modelId = m_goInfo->displayId;
3772 if (modelData->State1Wmo)
3773 modelId = modelData->State1Wmo;
3774 SetDisplayId(modelId);
3775
3776 if (setHealth && m_goValue.Building.DestructibleHitpoint)
3777 {
3780 // in this case current health is 0 anyway so just prevent crashing here
3781 if (!maxHealth)
3782 maxHealth = 1;
3783 SetGoAnimProgress(m_goValue.Building.Health * 255 / maxHealth);
3784 }
3785 break;
3786 }
3788 {
3789 if (GetGOInfo()->destructibleBuilding.DestroyedEvent && attackerOrHealer)
3790 GameEvents::Trigger(GetGOInfo()->destructibleBuilding.DestroyedEvent, attackerOrHealer, this);
3792
3795
3796 uint32 modelId = m_goInfo->displayId;
3798 if (modelData->State2Wmo)
3799 modelId = modelData->State2Wmo;
3800 SetDisplayId(modelId);
3801
3802 if (setHealth)
3803 {
3806 }
3807 break;
3808 }
3810 {
3811 if (GetGOInfo()->destructibleBuilding.RebuildingEvent && attackerOrHealer)
3812 GameEvents::Trigger(GetGOInfo()->destructibleBuilding.RebuildingEvent, attackerOrHealer, this);
3814
3815 uint32 modelId = m_goInfo->displayId;
3817 if (modelData->State3Wmo)
3818 modelId = modelData->State3Wmo;
3819 SetDisplayId(modelId);
3820
3821 // restores to full health
3822 if (setHealth && m_goValue.Building.DestructibleHitpoint)
3823 {
3825 SetGoAnimProgress(255);
3826 }
3827 break;
3828 }
3829 }
3830}
3831
3833{
3834 m_lootState = state;
3835 if (unit)
3836 m_lootStateUnitGUID = unit->GetGUID();
3837 else
3839
3840 AI()->OnLootStateChanged(state, unit);
3841
3842 // Start restock timer if the chest is partially looted or not looted at all
3843 if (GetGoType() == GAMEOBJECT_TYPE_CHEST && state == GO_ACTIVATED && GetGOInfo()->chest.chestRestockTime > 0 && m_restockTime == 0 && m_loot && m_loot->IsChanged())
3845
3846 if (GetGoType() == GAMEOBJECT_TYPE_DOOR) // only set collision for doors on SetGoState
3847 return;
3848
3849 if (m_model)
3850 {
3851 bool collision = false;
3852 // Use the current go state
3853 if ((GetGoState() != GO_STATE_READY && (state == GO_ACTIVATED || state == GO_JUST_DEACTIVATED)) || state == GO_READY)
3854 collision = !collision;
3855
3856 EnableCollision(collision);
3857 }
3858}
3859
3861{
3862 // Unlink loot objects from this GameObject before destroying to avoid accessing freed memory from Loot destructor
3863 std::unique_ptr<Loot> loot(std::move(m_loot));
3864 std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> personalLoot(std::move(m_personalLoot));
3865
3866 loot.reset();
3867 personalLoot.clear();
3868 m_unique_users.clear();
3869 m_usetimes = 0;
3870}
3871
3873{
3874 if (m_loot && !m_loot->isLooted())
3875 return false;
3876
3877 for (auto const& [_, loot] : m_personalLoot)
3878 if (!loot->isLooted())
3879 return false;
3880
3881 return true;
3882}
3883
3885{
3886 switch (GetGoType())
3887 {
3889 {
3890 GameObjectTemplate const* goInfo = GetGOInfo();
3891 if (!goInfo->IsDespawnAtAction() && goInfo->chest.chestPersonalLoot)
3892 {
3894 ? Seconds(goInfo->chest.chestRestockTime)
3895 : Seconds(m_respawnDelayTime)); // not hiding this object permanently to prevent infinite growth of m_perPlayerState
3896 // while also maintaining some sort of cheater protection (not getting rid of entries on logout)
3897 }
3898 break;
3899 }
3901 {
3903
3904 UF::ObjectData::Base objMask;
3907
3908 UpdateData udata(GetMapId());
3909 BuildValuesUpdateForPlayerWithMask(&udata, objMask.GetChangesMask(), goMask.GetChangesMask(), looter, false);
3910 WorldPacket packet;
3911 udata.BuildPacket(&packet);
3912 looter->SendDirectMessage(&packet);
3913 break;
3914 }
3915 default:
3916 break;
3917 }
3918}
3919
3921{
3922 GOState oldState = GetGoState();
3924 if (AI())
3925 AI()->OnStateChanged(state);
3926
3927 if (m_goTypeImpl)
3928 m_goTypeImpl->OnStateChanged(oldState, state);
3929
3930 if (m_model && !IsTransport())
3931 {
3932 if (!IsInWorld())
3933 return;
3934
3935 // startOpen determines whether we are going to add or remove the LoS on activation
3936 bool collision = false;
3937 if (state == GO_STATE_READY)
3938 collision = !collision;
3939
3940 EnableCollision(collision);
3941 }
3942}
3943
3945{
3946 if (m_perPlayerState)
3948 if (state->State)
3949 return *state->State;
3950
3951 return GetGoState();
3952}
3953
3954void GameObject::SetGoStateFor(GOState state, Player const* viewer)
3955{
3956 PerPlayerState& perPlayerState = GetOrCreatePerPlayerStates()[viewer->GetGUID()];
3958 perPlayerState.State = state;
3959
3961 setStateLocal.ObjectGUID = GetGUID();
3962 setStateLocal.State = state;
3963 viewer->SendDirectMessage(setStateLocal.Write());
3964}
3965
3971
3973{
3974 switch (GetGoType())
3975 {
3978 {
3979 switch (GetDestructibleState())
3980 {
3982 return modelData->State0NameSet;
3984 return modelData->State1NameSet;
3986 return modelData->State2NameSet;
3988 return modelData->State3NameSet;
3989 default:
3990 break;
3991 }
3992 }
3993 break;
3997 return ((*m_gameObjectData->Flags) >> 8) & 0xF;
3998 default:
3999 break;
4000 }
4001
4002 return 0;
4003}
4004
4006{
4007 if (!m_model)
4008 return;
4009
4010 /*if (enable && !GetMap()->ContainsGameObjectModel(*m_model))
4011 GetMap()->InsertGameObjectModel(*m_model);*/
4012
4013 m_model->EnableCollision(enable);
4014}
4015
4017{
4018 if (!IsInWorld())
4019 return;
4020 bool modelCollisionEnabled;
4021 if (m_model)
4022 {
4023 modelCollisionEnabled = m_model->IsCollisionEnabled();
4024 if (GetMap()->ContainsGameObjectModel(*m_model))
4026 }
4027 else
4028 modelCollisionEnabled = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : (GetGoState() == GO_STATE_READY || IsTransport());
4029
4031 std::unique_ptr<GameObjectModel> oldModel = std::exchange(m_model, nullptr);
4032
4033 CreateModel();
4034 if (m_model)
4035 {
4037 if (modelCollisionEnabled)
4038 m_model->EnableCollision(modelCollisionEnabled);
4039 }
4040
4041 switch (GetGoType())
4042 {
4043 // Only update navmesh when display id changes and not on spawn
4044 // default state of destructible buildings is intended to be baked in the mesh produced by mmaps_generator
4049 if (m_model)
4051 else if (oldModel)
4053 break;
4054 default:
4055 break;
4056 }
4057}
4058
4059bool GameObject::IsLootAllowedFor(Player const* player) const
4060{
4061 if (Loot const* loot = GetLootForPlayer(player)) // check only if loot was already generated
4062 {
4063 if (loot->isLooted()) // nothing to loot or everything looted.
4064 return false;
4065 if (!loot->HasAllowedLooter(GetGUID()) || (!loot->hasItemForAll() && !loot->hasItemFor(player))) // no loot in chest for this player
4066 return false;
4067 }
4068
4069 if (HasLootRecipient())
4070 return m_tapList.find(player->GetGUID()) != m_tapList.end();
4071
4072 return true;
4073}
4074
4076{
4077 if (m_personalLoot.empty())
4078 return m_loot.get();
4079
4081 return loot;
4082
4083 return nullptr;
4084}
4085
4090
4092{
4093 m_objectData->WriteCreate(flags, data, target, this);
4094 m_gameObjectData->WriteCreate(flags, data, target, this);
4095}
4096
4098{
4100
4102 m_objectData->WriteUpdate(flags, data, target, this);
4103
4105 m_gameObjectData->WriteUpdate(flags, data, target, this);
4106}
4107
4109 UF::GameObjectData::Mask const& requestedGameObjectMask, Player const* target, bool ignoreNestedChangesMask) const
4110{
4113 if (requestedObjectMask.IsAnySet())
4114 valuesMask.Set(TYPEID_OBJECT);
4115
4116 if (requestedGameObjectMask.IsAnySet())
4117 valuesMask.Set(TYPEID_GAMEOBJECT);
4118
4119 ByteBuffer& buffer = PrepareValuesUpdateBuffer(data);
4120 std::size_t sizePos = buffer.wpos();
4121 buffer << uint32(0);
4123 buffer << uint32(valuesMask.GetBlock(0));
4124
4125 if (valuesMask[TYPEID_OBJECT])
4126 m_objectData->WriteUpdate(requestedObjectMask, buffer, target, this, ignoreNestedChangesMask);
4127
4128 if (valuesMask[TYPEID_GAMEOBJECT])
4129 m_gameObjectData->WriteUpdate(requestedGameObjectMask, buffer, target, this, ignoreNestedChangesMask);
4130
4131 buffer.put<uint32>(sizePos, buffer.wpos() - sizePos - 4);
4132
4133 data->AddUpdateBlock();
4134}
4135
4137{
4138 UpdateData udata(Owner->GetMapId());
4139 WorldPacket packet;
4140
4142
4143 udata.BuildPacket(&packet);
4144 player->SendDirectMessage(&packet);
4145}
4146
4152
4153std::span<uint32 const> GameObject::GetPauseTimes() const
4154{
4155 std::span<uint32 const> result;
4156 if (GameObjectType::Transport const* transport = dynamic_cast<GameObjectType::Transport const*>(m_goTypeImpl.get()))
4157 result = transport->GetPauseTimes();
4158
4159 return result;
4160}
4161
4163{
4164 m_transportPathProgress = progress;
4165}
4166
4168{
4169 if (m_goData)
4170 return m_goData->spawnPoint;
4171
4172 return GetPosition();
4173}
4174
4176{
4177 switch (GetGoType())
4178 {
4180 return static_cast<GameObjectType::Transport const*>(m_goTypeImpl.get());
4182 return static_cast<Transport const*>(this);
4183 default:
4184 break;
4185 }
4186
4187 return nullptr;
4188}
4189
4191{
4194 if (m_goTypeImpl)
4195 m_goTypeImpl->OnRelocated();
4196
4197 // TODO: on heartbeat
4198 if (m_vignette)
4200
4202}
4203
4205{
4206 if (GetGOInfo()->GetInteractRadiusOverride())
4207 return float(GetGOInfo()->GetInteractRadiusOverride()) / 100.0f;
4208
4209 switch (GetGoType())
4210 {
4212 return 0.0f;
4218 return 5.5555553f;
4220 return 10.0f;
4223 return 3.0f;
4225 return 100.0f;
4227 return 20.0f + CONTACT_DISTANCE; // max spell range
4233 return 5.0f;
4234 // Following values are not blizzlike
4237 // Successful mailbox interaction is rather critical to the client, failing it will start a minute-long cooldown until the next mail query may be executed.
4238 // And since movement info update is not sent with mailbox interaction query, server may find the player outside of interaction range. Thus we increase it.
4239 return 10.0f; // 5.0f is blizzlike
4240 default:
4241 return INTERACTION_DISTANCE;
4242 }
4243}
4244
4246{
4247 if (!m_model)
4248 return;
4249
4250 if (GetMap()->ContainsGameObjectModel(*m_model))
4251 {
4253 m_model->UpdatePosition();
4255 }
4256}
4257
4258void GameObject::SetAnimKitId(uint16 animKitId, bool oneshot)
4259{
4260 if (_animKitId == animKitId)
4261 return;
4262
4263 if (animKitId && !sAnimKitStore.LookupEntry(animKitId))
4264 return;
4265
4266 if (!oneshot)
4267 _animKitId = animKitId;
4268 else
4269 _animKitId = 0;
4270
4272
4274 activateAnimKit.ObjectGUID = GetGUID();
4275 activateAnimKit.AnimKitID = animKitId;
4276 activateAnimKit.Maintain = !oneshot;
4277 SendMessageToSet(activateAnimKit.Write(), true);
4278}
4279
4281{
4282 if (m_vignette)
4283 {
4284 if (m_vignette->Data->ID == vignetteId)
4285 return;
4286
4288 m_vignette = nullptr;
4289 }
4290
4291 if (VignetteEntry const* vignette = sVignetteStore.LookupEntry(vignetteId))
4292 m_vignette = Vignettes::Create(vignette, this);
4293}
4294
4295void GameObject::SetSpellVisualId(int32 spellVisualId, ObjectGuid activatorGuid)
4296{
4298
4300 packet.ObjectGUID = GetGUID();
4301 packet.ActivatorGUID = activatorGuid;
4302 packet.SpellVisualID = spellVisualId;
4303 SendMessageToSet(packet.Write(), true);
4304}
4305
4307{
4308 if (!CanInteractWithCapturePoint(player))
4309 return;
4310
4311 if (GameObjectAI* ai = AI())
4312 if (ai->OnCapturePointAssaulted(player))
4313 return;
4314
4315 // only supported in battlegrounds
4316 Battleground* battleground = nullptr;
4317 if (BattlegroundMap* map = GetMap()->ToBattlegroundMap())
4318 if (Battleground* bg = map->GetBG())
4319 battleground = bg;
4320
4321 if (!battleground)
4322 return;
4323
4324 // Cancel current timer
4326
4327 if (player->GetBGTeam() == HORDE)
4328 {
4330 {
4331 // defended. capture instantly.
4333 battleground->SendBroadcastText(GetGOInfo()->capturePoint.DefendedBroadcastHorde, CHAT_MSG_BG_SYSTEM_HORDE, player);
4335 if (GetGOInfo()->capturePoint.DefendedEventHorde)
4336 GameEvents::Trigger(GetGOInfo()->capturePoint.DefendedEventHorde, player, this);
4337 return;
4338 }
4339
4341 {
4346 battleground->SendBroadcastText(GetGOInfo()->capturePoint.AssaultBroadcastHorde, CHAT_MSG_BG_SYSTEM_HORDE, player);
4348 if (GetGOInfo()->capturePoint.ContestedEventHorde)
4349 GameEvents::Trigger(GetGOInfo()->capturePoint.ContestedEventHorde, player, this);
4351 break;
4352 default:
4353 break;
4354 }
4355 }
4356 else
4357 {
4359 {
4360 // defended. capture instantly.
4364 if (GetGOInfo()->capturePoint.DefendedEventAlliance)
4365 GameEvents::Trigger(GetGOInfo()->capturePoint.DefendedEventAlliance, player, this);
4366 return;
4367 }
4368
4370 {
4377 if (GetGOInfo()->capturePoint.ContestedEventAlliance)
4378 GameEvents::Trigger(GetGOInfo()->capturePoint.ContestedEventAlliance, player, this);
4380 break;
4381 default:
4382 break;
4383 }
4384 }
4385}
4386
4388{
4390 return;
4391
4392 if (GameObjectAI* ai = AI())
4393 if (ai->OnCapturePointUpdated(m_goValue.CapturePoint.State))
4394 return;
4395
4396 uint32 spellVisualId = 0;
4397 uint32 customAnim = 0;
4398
4400 {
4402 spellVisualId = GetGOInfo()->capturePoint.SpellVisual1;
4403 break;
4405 customAnim = 1;
4406 spellVisualId = GetGOInfo()->capturePoint.SpellVisual2;
4407 break;
4409 customAnim = 2;
4410 spellVisualId = GetGOInfo()->capturePoint.SpellVisual3;
4411 break;
4413 customAnim = 3;
4414 spellVisualId = GetGOInfo()->capturePoint.SpellVisual4;
4415 break;
4417 customAnim = 4;
4418 spellVisualId = GetGOInfo()->capturePoint.SpellVisual5;
4419 break;
4420 default:
4421 break;
4422 }
4423
4424 if (customAnim != 0)
4425 SendCustomAnim(customAnim);
4426
4427 SetSpellVisualId(spellVisualId);
4429
4430 if (BattlegroundMap* map = GetMap()->ToBattlegroundMap())
4431 {
4432 if (Battleground* bg = map->GetBG())
4433 {
4436 packet.CapturePointInfo.Pos = GetPosition();
4437 packet.CapturePointInfo.Guid = GetGUID();
4440 bg->SendPacketToAll(packet.Write());
4441 bg->UpdateWorldState(GetGOInfo()->capturePoint.worldState1, AsUnderlyingType(m_goValue.CapturePoint.State));
4442 }
4443 }
4444
4446}
4447
4466
4468{
4470 return FlagState(0);
4471
4472 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(m_goTypeImpl.get());
4473 if (!newFlag)
4474 return FlagState(0);
4475
4476 return newFlag->GetState();
4477}
4478
4480{
4482 return ObjectGuid::Empty;
4483
4484 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(m_goTypeImpl.get());
4485 if (!newFlag)
4486 return ObjectGuid::Empty;
4487
4488 return newFlag->GetCarrierGUID();
4489}
4490
4492{
4494 return time_t(0);
4495
4496 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(m_goTypeImpl.get());
4497 if (!newFlag)
4498 return time_t(0);
4499
4500 return newFlag->GetTakenFromBaseTime();
4501}
4502
4504{
4505 if (GameObjectType::ControlZone const* controlZone = dynamic_cast<GameObjectType::ControlZone const*>(m_goTypeImpl.get()))
4506 return controlZone->GetInsidePlayers();
4507
4508 return nullptr;
4509}
4510
4515
4516std::unordered_map<ObjectGuid, GameObject::PerPlayerState>& GameObject::GetOrCreatePerPlayerStates()
4517{
4518 if (!m_perPlayerState)
4519 m_perPlayerState = std::make_unique<std::unordered_map<ObjectGuid, PerPlayerState>>();
4520
4521 return *m_perPlayerState;
4522}
4523
4525{
4526public:
4527 explicit GameObjectModelOwnerImpl(GameObject* owner) : _owner(owner) { }
4528 virtual ~GameObjectModelOwnerImpl() = default;
4529
4530 bool IsSpawned() const override { return _owner->isSpawned(); }
4531 uint32 GetDisplayId() const override { return _owner->GetDisplayId(); }
4532 uint8 GetNameSetId() const override { return _owner->GetNameSetId(); }
4533 bool IsInPhase(PhaseShift const& phaseShift) const override { return _owner->GetPhaseShift().CanSee(phaseShift); }
4534 G3D::Vector3 GetPosition() const override { return G3D::Vector3(_owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ()); }
4535 G3D::Quat GetRotation() const override { return G3D::Quat(_owner->GetLocalRotation().x, _owner->GetLocalRotation().y, _owner->GetLocalRotation().z, _owner->GetLocalRotation().w); }
4536 int64 GetPackedRotation() const override { return _owner->GetPackedLocalRotation(); }
4537 float GetScale() const override { return _owner->GetObjectScale(); }
4538 void DebugVisualizeCorner(G3D::Vector3 const& corner) const override { _owner->SummonCreature(1, corner.x, corner.y, corner.z, 0, TEMPSUMMON_MANUAL_DESPAWN); }
4539
4540private:
4542};
4543
4549
4551{
4552 if (m_goTypeImpl)
4553 command.Execute(*m_goTypeImpl);
4554}
4555
4557{
4559 return TEAM_NEUTRAL;
4560
4561 GameObjectType::ControlZone const* controlZone = dynamic_cast<GameObjectType::ControlZone const*>(m_goTypeImpl.get());
4562 if (!controlZone)
4563 return TEAM_NEUTRAL;
4564
4565 return controlZone->GetControllingTeam();
4566}
4567
4569{
4570 m_model = GameObjectModel::Create(std::make_unique<GameObjectModelOwnerImpl>(this), sWorld->GetDataPath());
4571 if (m_model)
4572 {
4573 if (m_model->IsMapObject())
4575
4576 switch (GetGoType())
4577 {
4579 m_model->DisableLosBlocking(GetGOInfo()->door.NotLOSBlocking);
4580 break;
4585 m_model->IncludeInNavMesh(true);
4586 break;
4587 default:
4588 break;
4589 }
4590 }
4591}
4592
4593std::string GameObject::GetDebugInfo() const
4594{
4595 std::stringstream sstr;
4596 sstr << WorldObject::GetDebugInfo() << "\n"
4597 << "SpawnId: " << GetSpawnId() << " GoState: " << std::to_string(GetGoState()) << " ScriptId: " << GetScriptId() << " AIName: " << GetAIName();
4598 return sstr.str();
4599}
4600
4601bool GameObject::IsAtInteractDistance(Player const* player, SpellInfo const* spell) const
4602{
4603 if (spell || (spell = GetSpellForLock(player)))
4604 {
4605 float maxRange = spell->GetMaxRange(spell->IsPositive());
4606
4608 return IsInDist(player, maxRange);
4609
4610 if (sGameObjectDisplayInfoStore.LookupEntry(GetGOInfo()->displayId))
4611 return IsAtInteractDistance(*player, maxRange);
4612 }
4613
4615}
4616
4617bool GameObject::IsAtInteractDistance(Position const& pos, float radius) const
4618{
4619 if (GameObjectDisplayInfoEntry const* displayInfo = sGameObjectDisplayInfoStore.LookupEntry(GetGOInfo()->displayId))
4620 {
4621 float scale = GetObjectScale();
4622
4623 float minX = displayInfo->GeoBoxMin.X * scale - radius;
4624 float minY = displayInfo->GeoBoxMin.Y * scale - radius;
4625 float minZ = displayInfo->GeoBoxMin.Z * scale - radius;
4626 float maxX = displayInfo->GeoBoxMax.X * scale + radius;
4627 float maxY = displayInfo->GeoBoxMax.Y * scale + radius;
4628 float maxZ = displayInfo->GeoBoxMax.Z * scale + radius;
4629
4630 QuaternionData worldRotation = GetWorldRotation();
4631 G3D::Quat worldRotationQuat(worldRotation.x, worldRotation.y, worldRotation.z, worldRotation.w);
4632
4633 return G3D::CoordinateFrame { { worldRotationQuat }, { GetPositionX(), GetPositionY(), GetPositionZ() } }
4634 .toWorldSpace(G3D::Box { { minX, minY, minZ }, { maxX, maxY, maxZ } })
4635 .contains({ pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ() });
4636 }
4637
4638 return GetExactDist(&pos) <= radius;
4639}
4640
4641bool GameObject::IsWithinDistInMap(Player const* player) const
4642{
4643 return IsInMap(player) && InSamePhase(player) && IsAtInteractDistance(player);
4644}
4645
4647{
4648 if (!player)
4649 return nullptr;
4650
4651 uint32 lockId = GetGOInfo()->GetLockId();
4652 if (!lockId)
4653 return nullptr;
4654
4655 LockEntry const* lock = sLockStore.LookupEntry(lockId);
4656 if (!lock)
4657 return nullptr;
4658
4659 for (uint8 i = 0; i < MAX_LOCK_CASE; ++i)
4660 {
4661 if (!lock->Type[i])
4662 continue;
4663
4664 if (lock->Type[i] == LOCK_KEY_SPELL)
4665 if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(lock->Index[i], GetMap()->GetDifficultyID()))
4666 return spell;
4667
4668 if (lock->Type[i] != LOCK_KEY_SKILL)
4669 break;
4670
4671 for (auto&& playerSpell : player->GetSpellMap())
4672 if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(playerSpell.first, GetMap()->GetDifficultyID()))
4673 for (auto&& effect : spell->GetEffects())
4674 if (effect.Effect == SPELL_EFFECT_OPEN_LOCK && effect.MiscValue == lock->Index[i])
4675 if (effect.CalcValueAsInt(player) >= int32(lock->Skill[i]))
4676 return spell;
4677 }
4678
4679 return nullptr;
4680}
constexpr uint32 ITEM_ID_HEART_OF_AZEROTH
Definition AzeriteItem.h:23
LocaleConstant
Definition Common.h:51
@ TOTAL_LOCALES
Definition Common.h:65
@ LOCALE_enUS
Definition Common.h:52
#define DEFAULT_LOCALE
Definition Common.h:69
@ IN_MILLISECONDS
Definition Common.h:38
@ MINUTE
Definition Common.h:32
@ WEEK
Definition Common.h:35
#define M_PI
Definition Common.h:118
DB2Storage< GameObjectDisplayInfoEntry > sGameObjectDisplayInfoStore("GameObjectDisplayInfo.db2", &GameobjectDisplayInfoLoadInfo::Instance)
DB2Storage< AnimKitEntry > sAnimKitStore("AnimKit.db2", &AnimKitLoadInfo::Instance)
DB2Storage< DestructibleModelDataEntry > sDestructibleModelDataStore("DestructibleModelData.db2", &DestructibleModelDataLoadInfo::Instance)
DB2Storage< LockEntry > sLockStore("Lock.db2", &LockLoadInfo::Instance)
DB2Storage< VignetteEntry > sVignetteStore("Vignette.db2", &VignetteLoadInfo::Instance)
DB2Storage< QuestXPEntry > sQuestXPStore("QuestXP.db2", &QuestXpLoadInfo::Instance)
DB2Storage< AreaTableEntry > sAreaTableStore("AreaTable.db2", &AreaTableLoadInfo::Instance)
#define sDB2Manager
Definition DB2Stores.h:569
#define MAX_LOCK_CASE
ItemContext
Definition DBCEnums.h:1315
@ CatchFishInFishingHole
SQLTransaction< WorldDatabaseConnection > WorldDatabaseTransaction
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
uint8_t uint8
Definition Define.h:156
int64_t int64
Definition Define.h:149
int8_t int8
Definition Define.h:152
int32_t int32
Definition Define.h:150
#define UI64LIT(N)
Definition Define.h:139
uint16_t uint16
Definition Define.h:155
uint32_t uint32
Definition Define.h:154
uint16 flags
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition Duration.h:28
#define ASSERT
Definition Errors.h:80
GameObjectActions
LootState
Definition GameObject.h:155
@ GO_ACTIVATED
Definition GameObject.h:158
@ GO_READY
Definition GameObject.h:157
@ GO_NOT_READY
Definition GameObject.h:156
@ GO_JUST_DEACTIVATED
Definition GameObject.h:159
FlagState
Definition GameObject.h:46
#define FISHING_BOBBER_READY_TIME
Definition GameObject.h:163
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_FATAL(filterType__, message__,...)
Definition Log.h:193
#define TC_LOG_WARN(filterType__, message__,...)
Definition Log.h:187
LootStore LootTemplates_Gameobject("gameobject_loot_template", "gameobject entry", true)
std::unordered_map< ObjectGuid, std::unique_ptr< Loot > > GenerateDungeonEncounterPersonalLoot(uint32 dungeonEncounterId, uint32 lootId, LootStore const &store, LootType type, WorldObject const *lootOwner, uint32 minMoney, uint32 maxMoney, uint16 lootMode, MapDifficultyEntry const *mapDifficulty, std::vector< Player * > const &tappers)
Definition LootMgr.cpp:1037
LootStore LootTemplates_Fishing("fishing_loot_template", "area id", true)
@ LOOT_FISHING_JUNK
Definition Loot.h:114
@ LOOT_CHEST
Definition Loot.h:109
@ LOOT_FISHING
Definition Loot.h:104
@ LOOT_FISHINGHOLE
Definition Loot.h:112
#define sMapMgr
Definition MapManager.h:186
#define DEFAULT_VISIBILITY_DISTANCE
@ TEMPSUMMON_MANUAL_DESPAWN
#define INTERACTION_DISTANCE
#define CONTACT_DISTANCE
std::unordered_set< ObjectGuid > GuidUnorderedSet
Definition ObjectGuid.h:435
@ TYPEID_OBJECT
Definition ObjectGuid.h:38
@ TYPEID_GAMEOBJECT
Definition ObjectGuid.h:46
@ TYPEID_PLAYER
Definition ObjectGuid.h:44
#define sObjectMgr
Definition ObjectMgr.h:1885
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
#define sOutdoorPvPMgr
@ TELE_TO_NOT_LEAVE_COMBAT
Definition Player.h:925
@ TELE_TO_NOT_UNSUMMON_PET
Definition Player.h:926
@ TELE_TO_NOT_LEAVE_TRANSPORT
Definition Player.h:924
#define sPoolMgr
Definition PoolMgr.h:179
constexpr QuestGiverStatus QuestGiverStatusFutureMask
Definition QuestDef.h:210
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:150
int32 irand(int32 min, int32 max)
Definition Random.cpp:35
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:42
@ STEALTH_TRAP
#define MAX_GO_STATE_TRANSPORT_STOP_FRAMES
GameobjectTypes
@ GAMEOBJECT_TYPE_GUILD_BANK
@ GAMEOBJECT_TYPE_MINI_GAME
@ GAMEOBJECT_TYPE_CAMERA
@ GAMEOBJECT_TYPE_CAPTURE_POINT
@ GAMEOBJECT_TYPE_BUTTON
@ GAMEOBJECT_TYPE_MAP_OBJECT
@ GAMEOBJECT_TYPE_SPELL_FOCUS
@ GAMEOBJECT_TYPE_TRANSPORT
@ GAMEOBJECT_TYPE_AURA_GENERATOR
@ GAMEOBJECT_TYPE_MEETINGSTONE
@ GAMEOBJECT_TYPE_TRAP
@ GAMEOBJECT_TYPE_GENERIC
@ GAMEOBJECT_TYPE_CHEST
@ GAMEOBJECT_TYPE_FISHINGHOLE
@ GAMEOBJECT_TYPE_GARRISON_PLOT
@ GAMEOBJECT_TYPE_TRAPDOOR
@ GAMEOBJECT_TYPE_CONTROL_ZONE
@ GAMEOBJECT_TYPE_NEW_FLAG_DROP
@ GAMEOBJECT_TYPE_FLAGDROP
@ GAMEOBJECT_TYPE_QUESTGIVER
@ GAMEOBJECT_TYPE_MAILBOX
@ GAMEOBJECT_TYPE_SPELLCASTER
@ GAMEOBJECT_TYPE_FLAGSTAND
@ GAMEOBJECT_TYPE_CHAIR
@ GAMEOBJECT_TYPE_TEXT
@ GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY
@ GAMEOBJECT_TYPE_AREADAMAGE
@ GAMEOBJECT_TYPE_SIEGEABLE_MO
@ GAMEOBJECT_TYPE_ITEM_FORGE
@ GAMEOBJECT_TYPE_PHASEABLE_MO
@ GAMEOBJECT_TYPE_NEW_FLAG
@ GAMEOBJECT_TYPE_GOOBER
@ GAMEOBJECT_TYPE_FISHINGNODE
@ GAMEOBJECT_TYPE_BARBER_CHAIR
@ GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT
@ GAMEOBJECT_TYPE_DOOR
@ GAMEOBJECT_TYPE_GARRISON_BUILDING
@ GAMEOBJECT_TYPE_RITUAL
@ GAMEOBJECT_TYPE_UI_LINK
@ GAMEOBJECT_TYPE_GATHERING_NODE
@ GAMEOBJECT_TYPE_BINDER
@ GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
@ INVISIBILITY_TRAP
#define MAX_GAMEOBJECT_TYPE
@ TEAM_NEUTRAL
@ TEAM_ALLIANCE
@ TEAM_HORDE
@ LOCK_KEY_SKILL
@ LOCK_KEY_SPELL
@ SPELL_EFFECT_OPEN_LOCK
@ ERR_MUST_EQUIP_ARTIFACT
@ GO_DYNFLAG_LO_INVERTED_MOVEMENT
@ GO_DYNFLAG_LO_NO_INTERACT
#define MAX_GAMEOBJECT_DATA
GameObjectDestructibleState
@ GO_DESTRUCTIBLE_DESTROYED
@ GO_DESTRUCTIBLE_REBUILDING
@ GO_DESTRUCTIBLE_INTACT
@ GO_DESTRUCTIBLE_DAMAGED
@ HORDE
@ LOOT_MODE_DEFAULT
@ LOOT_MODE_JUNK_FISH
SpellCastResult
@ SPELL_FAILED_SUCCESS
@ SPELL_CAST_OK
GameObjectFlags
@ GO_FLAG_NODESPAWN
@ GO_FLAG_NOT_SELECTABLE
@ GO_FLAG_IN_MULTI_USE
@ GO_FLAG_MAP_OBJECT
@ GO_FLAG_IN_USE
@ GO_FLAG_DESTROYED
@ GO_FLAG_LOCKED
@ GO_FLAG_DAMAGED
@ CHAT_MSG_BG_SYSTEM_ALLIANCE
@ CHAT_MSG_BG_SYSTEM_HORDE
GOState
@ GO_STATE_TRANSPORT_STOPPED
@ GO_STATE_TRANSPORT_ACTIVE
@ GO_STATE_READY
@ GO_STATE_DESTROYED
@ GO_STATE_ACTIVE
@ SKILL_FISHING
@ SPAWNGROUP_FLAG_COMPATIBILITY_MODE
Definition SpawnData.h:57
@ LINKED_RESPAWN_CREATURE_TO_GO
Definition SpawnData.h:154
@ LINKED_RESPAWN_GO_TO_GO
Definition SpawnData.h:155
@ LINKED_RESPAWN_GO_TO_CREATURE
Definition SpawnData.h:156
@ SPAWN_TYPE_GAMEOBJECT
Definition SpawnData.h:37
SpawnTrackingState
Definition SpawnData.h:94
@ SPELL_AURA_MOD_INVISIBILITY
@ SPELL_AURA_MOUNTED
@ SPELL_AURA_MOD_STEALTH
@ TRIGGERED_FULL_MASK
Used when doing CastSpell with triggered == true.
@ TRIGGERED_IGNORE_CAST_IN_PROGRESS
Will not check if a current cast is in progress.
#define sSpellMgr
Definition SpellMgr.h:812
#define sTransportMgr
UnitStandStateType
Definition UnitDefines.h:41
@ UNIT_STAND_STATE_SIT_LOW_CHAIR
Definition UnitDefines.h:46
@ UNIT_STAND_STATE_SIT
Definition UnitDefines.h:43
@ UNIT_FLAG_IMMUNE
@ CURRENT_CHANNELED_SPELL
Definition Unit.h:599
@ NULL_BAG
Definition Unit.h:63
@ NULL_SLOT
Definition Unit.h:64
#define ARTIFACTS_ALL_WEAPONS_GENERAL_WEAPON_EQUIPPED_PASSIVE
Definition Unit.h:38
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:565
@ WORLD_DEL_EVENT_GAMEOBJECT
@ WORLD_DEL_SPAWNGROUP_MEMBER
@ WORLD_DEL_GAMEOBJECT
@ WORLD_DEL_LINKED_RESPAWN
@ WORLD_INS_GAMEOBJECT
@ WORLD_DEL_GAMEOBJECT_ADDON
@ WORLD_DEL_LINKED_RESPAWN_MASTER
ObjectGuid GetCastItemGUID() const
Definition SpellAuras.h:188
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
void SetUpdateFieldValue(UF::UpdateFieldPrivateSetter< T > setter, typename UF::UpdateFieldPrivateSetter< T >::value_type value)
Definition BaseEntity.h:221
WowCS::EntityFragmentsHolder m_entityFragments
Definition BaseEntity.h:353
UF::UpdateFieldHolder m_values
Definition BaseEntity.h:205
bool IsInWorld() const
Definition BaseEntity.h:158
void _Create(ObjectGuid const &guid)
Definition BaseEntity.h:218
CreateObjectBits m_updateFlag
Definition BaseEntity.h:352
void AddToObjectUpdateIfNeeded()
UF::DynamicUpdateFieldSetter< T >::insert_result AddDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter< T > setter)
Definition BaseEntity.h:242
ByteBuffer & PrepareValuesUpdateBuffer(UpdateData *data) const
TypeID m_objectTypeId
Definition BaseEntity.h:351
TypeID GetTypeId() const
Definition BaseEntity.h:166
virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const *target) const
void SendUpdateToPlayer(Player *player) const
void SendBroadcastText(uint32 id, ChatMsg msgType, WorldObject const *target=nullptr)
size_t wpos() const
Definition ByteBuffer.h:461
void put(std::size_t pos, T value)
Definition ByteBuffer.h:260
static bool IsPlayerMeetingCondition(Player const *player, uint32 conditionId)
static ContentTuningEntry const * GetContentTuningForArea(AreaTableEntry const *areaEntry)
void AddFlag(FLAG_TYPE flag)
Definition Object.h:209
void AddValue(FLAG_TYPE flag, T_VALUES value)
Definition Object.h:214
virtual void Damaged(WorldObject *, uint32)
virtual void OnStateChanged(uint32)
virtual void InitializeAI()
virtual void UpdateAI(uint32)
virtual void Destroyed(WorldObject *, uint32)
virtual void Reset()
virtual void OnLootStateChanged(uint32, Unit *)
int64 GetPackedRotation() const override
float GetScale() const override
uint32 GetDisplayId() const override
bool IsInPhase(PhaseShift const &phaseShift) const override
uint8 GetNameSetId() const override
G3D::Vector3 GetPosition() const override
virtual ~GameObjectModelOwnerImpl()=default
G3D::Quat GetRotation() const override
GameObjectModelOwnerImpl(GameObject *owner)
bool IsSpawned() const override
void DebugVisualizeCorner(G3D::Vector3 const &corner) const override
static std::unique_ptr< GameObjectModel > Create(std::unique_ptr< GameObjectModelOwnerBase > modelOwner, std::string const &dataPath)
virtual void Execute(GameObjectTypeBase &type) const =0
GameObject & _owner
Definition GameObject.h:82
void SetValue(float value)
uint32 GetStartingValue() const
TeamId GetControllingTeam() const
void Update(uint32 diff) override
ControlZone(GameObject &owner)
GuidUnorderedSet const * GetInsidePlayers() const
float CalculatePointsPerSecond(std::vector< Player * > const &targetList) const
void SearchTargets(std::vector< Player * > &targetList)
float CalculateTimeNeeded(int32 hordePlayers, int32 alliancePlayers) const
GuidUnorderedSet _insidePlayers
void ActivateObject(GameObjectActions action, int32, WorldObject *, uint32, int32) override
void HandleUnitEnterExit(std::vector< Player * > const &newTargetList)
float GetMinAllianceValue() const
void TriggerEvent(uint32 eventId) const
time_t GetTakenFromBaseTime() const
ObjectGuid const & GetCarrierGUID() const
bool IsNeverVisibleFor(WorldObject const *seer, bool allowServersideObjects) const override
void Update(uint32 diff) override
FlagState GetState() const
NewFlag(GameObject &owner)
void SetState(FlagState newState, Player *player)
SetControlZoneValue(Optional< uint32 > value={ })
void Execute(GameObjectTypeBase &type) const override
SetNewFlagState(FlagState state, Player *player)
void Execute(GameObjectTypeBase &type) const override
void Execute(GameObjectTypeBase &type) const override
TransportBase * RemovePassenger(WorldObject *passenger) override
std::unordered_set< WorldObject * > _passengers
float GetTransportOrientation() const override
std::vector< uint32 > _stopFrames
void OnStateChanged(GOState oldState, GOState newState) override
Transport(GameObject &owner)
TransportAnimation const * _animationInfo
uint32 GetTransportPeriod() const
void OnRelocated() override
Position GetPositionWithOffset(Position const &offset) const override
This method transforms supplied transport offsets into global coordinates.
Position GetPositionOffsetTo(Position const &endPos) const override
This method transforms supplied global coordinates into local offsets.
void AddPassenger(WorldObject *passenger, Position const &offset) override
void Update(uint32 diff) override
void SetAutoCycleBetweenStopFrames(bool on)
int32 GetMapIdForSpawning() const override
std::span< uint32 const > GetPauseTimes() const
static constexpr Milliseconds PositionUpdateInterval
ObjectGuid GetTransportGUID() const override
void BuildValuesCreate(UF::UpdateFieldFlag flags, ByteBuffer &data, Player const *target) const override
void SetLocalRotationAngles(float z_rot, float y_rot, float x_rot)
void UpdateCapturePoint()
bool HasStringId(std::string_view id) const
void SetGoState(GOState state)
ObjectGuid GetOwnerGUID() const override
Definition GameObject.h:245
bool IsWithinDistInMap(Player const *player) const
void UseDoorOrButton(uint32 time_to_restore=0, bool alternative=false, Unit *user=nullptr)
GameObjectValue m_goValue
Definition GameObject.h:491
SpawnTrackingStateData const * GetSpawnTrackingStateDataForPlayer(Player const *player) const override
GOState GetGoStateFor(ObjectGuid const &viewer) const
GameObjectTemplate const * GetGOInfo() const
Definition GameObject.h:203
void UpdateDynamicFlagsForNearbyPlayers()
void CreateModel()
GOState GetGoState() const
Definition GameObject.h:284
bool MeetsInteractCondition(Player const *user) const
time_t m_respawnTime
Definition GameObject.h:465
bool IsInvisibleDueToDespawn(WorldObject const *seer) const override
void ClearLoot()
std::string GetDebugInfo() const override
time_t m_cooldownTime
Definition GameObject.h:473
std::unordered_map< ObjectGuid, PerPlayerState > & GetOrCreatePerPlayerStates()
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool=true)
void AddUse()
Definition GameObject.h:324
void RemoveFlag(GameObjectFlags flags)
Definition GameObject.h:278
uint32 GetGoArtKit() const
Definition GameObject.h:288
bool hasInvolvedQuest(uint32 quest_id) const override
ObjectGuid const & GetFlagCarrierGUID() const
bool HasFlag(GameObjectFlags flags) const
Definition GameObject.h:276
void SetSpellVisualId(int32 spellVisualId, ObjectGuid activatorGuid=ObjectGuid::Empty)
GameObjectData const * m_goData
Definition GameObject.h:489
LootState getLootState() const
Definition GameObject.h:302
uint32 GetSpellId() const
Definition GameObject.h:252
uint8 GetGoAnimProgress() const
Definition GameObject.h:290
int64 m_packedRotation
Definition GameObject.h:495
Position m_stationaryPosition
Definition GameObject.h:497
void InheritStringIds(GameObject const *parent)
bool IsFullyLooted() const
QuaternionData const & GetLocalRotation() const
Definition GameObject.h:218
time_t m_restockTime
Definition GameObject.h:472
uint32 GetUniqueUseCount() const
Definition GameObject.h:327
void ReplaceAllFlags(GameObjectFlags flags)
Definition GameObject.h:279
void RemoveFromWorld() override
void EnableCollision(bool enable)
std::unordered_map< ObjectGuid, std::unique_ptr< Loot > > m_personalLoot
Definition GameObject.h:332
void SetGoAnimProgress(uint8 animprogress)
Definition GameObject.h:291
ObjectGuid m_lootStateUnitGUID
Definition GameObject.h:470
static GameObject * CreateGameObject(uint32 entry, Map *map, Position const &pos, QuaternionData const &rotation, uint32 animProgress, GOState goState, uint32 artKit=0)
uint8 GetLevelForTarget(WorldObject const *target) const override
bool IsAtInteractDistance(Position const &pos, float radius) const
bool isSpawned() const
Definition GameObject.h:259
void SetOwnerGUID(ObjectGuid owner)
Definition GameObject.h:235
void SetVignette(uint32 vignetteId)
void DespawnOrUnsummon(Milliseconds delay=0ms, Seconds forceRespawnTime=0s)
void SetDestructibleState(GameObjectDestructibleState state, WorldObject *attackerOrHealer=nullptr, bool setHealth=false)
ChairSlotAndUser ChairListSlots
Definition GameObject.h:484
void HandleCustomTypeCommand(GameObjectTypeBase::CustomCommand const &command) const
void SetScriptStringId(std::string id)
time_t GetFlagTakenFromBaseTime() const
bool ActivateToQuest(Player const *target) const
void OnLootRelease(Player *looter)
void SetFlag(GameObjectFlags flags)
Definition GameObject.h:277
Loot * GetFishLootJunk(Player *lootOwner)
uint32 m_spellId
Definition GameObject.h:464
bool IsAlwaysVisibleFor(WorldObject const *seer) const override
std::array< std::string const *, 3 > m_stringIds
Definition GameObject.h:492
void SetWorldEffectID(uint32 worldEffectID)
Definition GameObject.h:431
bool HasLootRecipient() const
Definition GameObject.h:337
void SetLootState(LootState s, Unit *unit=nullptr)
static GameObject * CreateGameObjectFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap=true)
uint32 m_respawnDelayTime
Definition GameObject.h:466
SpellInfo const * GetSpellForLock(Player const *player) const
GameObjectAI * AI() const
Definition GameObject.h:384
void ResetLootMode()
Definition GameObject.h:311
GuidSet m_unique_users
Definition GameObject.h:480
GameObject * GetLinkedTrap()
GuidUnorderedSet const * GetInsidePlayers() const
std::unique_ptr< Vignettes::VignetteData > m_vignette
Definition GameObject.h:522
bool IsInRange(float x, float y, float z, float radius) const
void SetRespawnTime(int32 respawn)
void TriggeringLinkedGameObject(uint32 trapEntry, Unit *target)
void SetRespawnCompatibilityMode(bool mode=true)
Definition GameObject.h:379
GameobjectTypes GetGoType() const
Definition GameObject.h:282
void SendGameObjectDespawn()
void SetGoType(GameobjectTypes type)
Definition GameObject.h:283
uint32 m_despawnDelay
Definition GameObject.h:467
Seconds m_despawnRespawnTime
Definition GameObject.h:468
QuaternionData m_localRotation
Definition GameObject.h:496
void SetLinkedTrap(GameObject *linkedTrap)
Definition GameObject.h:341
TeamId GetControllingTeam() const
void SetLocalRotation(float qx, float qy, float qz, float qw)
std::string GetNameForLocaleIdx(LocaleConstant locale) const override
GameObjectData const * GetGameObjectData() const
Definition GameObject.h:206
bool m_respawnCompatibilityMode
Definition GameObject.h:517
GameObjectTemplateAddon const * m_goTemplateAddon
Definition GameObject.h:488
ObjectGuid::LowType GetSpawnId() const
Definition GameObject.h:212
std::span< uint32 const > GetPauseTimes() const
void Use(Unit *user, bool ignoreCastInProgress=false)
Position const & GetStationaryPosition() const override
Definition GameObject.h:409
bool AIM_Initialize()
void SaveRespawnTime(uint32 forceDelay=0)
float GetInteractionDistance() const
void RemoveFromOwner()
std::span< int32 const > GetLabels() const
Loot * GetFishLoot(Player *lootOwner)
void SendCustomAnim(uint32 anim)
time_t GetRespawnTimeEx() const
bool hasQuest(uint32 quest_id) const override
FlagState GetFlagState() const
GuidUnorderedSet m_tapList
Definition GameObject.h:499
GameObjectAI * m_AI
Definition GameObject.h:516
uint32 GetDisplayId() const
Definition GameObject.h:394
void ResetDoorOrButton()
std::unique_ptr< Loot > m_loot
Definition GameObject.h:331
void SetLevel(uint32 level)
Definition GameObject.h:281
Optional< float > m_transportPathProgress
Definition GameObject.h:520
uint32 GetScriptId() const
bool IsNeverVisibleFor(WorldObject const *seer, bool allowServersideObjects=false) const override
uint8 GetNameSetId() const
bool CanInteractWithCapturePoint(Player const *target) const
Optional< std::string > m_scriptStringId
Definition GameObject.h:493
void UpdateModelPosition()
void AfterRelocation()
void AIM_Destroy()
void SetPathProgressForClient(float progress)
void Update(uint32 diff) override
GameObjectTemplateAddon const * GetTemplateAddon() const
Definition GameObject.h:204
void ModifyHealth(int32 change, WorldObject *attackerOrHealer=nullptr, uint32 spellId=0)
void ClearValuesChangesMask() override
void DespawnForPlayer(Player *seer, Seconds respawnTime)
GameObject * LookupFishingHoleAround(float range)
uint16 _animKitId
Definition GameObject.h:518
void SetDisplayId(uint32 displayid)
GameObjectTemplate const * m_goInfo
Definition GameObject.h:487
GuidUnorderedSet const & GetTapList() const
Definition GameObject.h:334
ObjectGuid::LowType m_spawnId
For new or temporary gameobjects is 0 for saved it is lowguid.
Definition GameObject.h:486
bool HasLabel(int32 gameobjectLabel) const
bool CanActivateForPlayer(Player const *target) const
void BuildValuesUpdateForPlayerWithMask(UpdateData *data, UF::ObjectData::Mask const &requestedObjectMask, UF::GameObjectData::Mask const &requestedGameObjectMask, Player const *target, bool ignoreNestedChangesMask) const
std::string_view GetStringId(StringIdType type) const
Definition GameObject.h:389
void UpdateModel()
void ActivateObject(GameObjectActions action, int32 param, WorldObject *spellCaster=nullptr, uint32 spellId=0, int32 effectIndex=-1)
ObjectGuid m_linkedTrap
Definition GameObject.h:502
void SwitchDoorOrButton(bool activate, bool alternative=false)
void CleanupsBeforeDelete(bool finalCleanup=true) override
bool HasConditionalInteraction() const
bool IsDestructibleBuilding() const
void SaveToDB()
Loot * GetLootForPlayer(Player const *) const override
uint16 GetLootMode() const
Definition GameObject.h:306
TransportBase * ToTransportBase()
Definition GameObject.h:403
void SetGoStateFor(GOState state, Player const *viewer)
QuaternionData GetWorldRotation() const
void AssaultCapturePoint(Player *player)
void SetAnimKitId(uint16 animKitId, bool oneshot)
static bool DeleteFromDB(ObjectGuid::LowType spawnId)
void UpdatePackedRotation()
Transport * ToTransport()
Definition GameObject.h:406
bool m_spawnedByDefault
Definition GameObject.h:471
void SetGoArtKit(uint32 artkit)
void AddToWorld() override
bool Create(uint32 entry, Map *map, Position const &pos, QuaternionData const &rotation, uint32 animProgress, GOState goState, uint32 artKit, bool dynamic, ObjectGuid::LowType spawnid)
GameObjectDestructibleState GetDestructibleState() const
Definition GameObject.h:369
std::unique_ptr< std::unordered_map< ObjectGuid, PerPlayerState > > m_perPlayerState
Definition GameObject.h:531
bool IsLootAllowedFor(Player const *player) const
LootState m_lootState
Definition GameObject.h:469
void AddUniqueUse(Player *player)
std::string const & GetAIName() const
GameObjectOverride const * GetGameObjectOverride() const
UF::UpdateField< UF::GameObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_GAMEOBJECT > m_gameObjectData
Definition GameObject.h:457
void SetParentRotation(QuaternionData const &rotation)
Position GetRespawnPosition() const
std::unique_ptr< GameObjectTypeBase > m_goTypeImpl
Definition GameObject.h:490
ObjectGuid m_ritualOwnerGUID
Definition GameObject.h:479
std::unique_ptr< GameObjectModel > m_model
Definition GameObject.h:400
uint32 m_usetimes
Definition GameObject.h:481
void SetSpellId(uint32 id)
Definition GameObject.h:247
GOState m_prevGoState
Definition GameObject.h:475
bool IsTransport() const
void SetFaction(uint32 faction) override
Definition GameObject.h:398
void BuildValuesUpdate(UF::UpdateFieldFlag flags, ByteBuffer &data, Player const *target) const override
GuidSet m_SkillupList
Definition GameObject.h:477
Definition Group.h:205
Definition Item.h:179
bool HaveQuestLootForPlayer(uint32 loot_id, Player const *player) const
Definition LootMgr.cpp:198
Definition Map.h:225
void GameObjectRelocation(GameObject *go, float x, float y, float z, float orientation, bool respawnRelocationOnFail=true)
Definition Map.cpp:1082
void UpdateSpawnGroupConditions()
Definition Map.cpp:2506
MapStoredObjectTypesContainer & GetObjectsStore()
Definition Map.h:458
MapDifficultyEntry const * GetMapDifficulty() const
Definition Map.cpp:3252
void RemoveGameObjectModel(GameObjectModel const &model)
Definition Map.h:498
bool AddToMap(T *)
Definition Map.cpp:517
time_t GetLinkedRespawnTime(ObjectGuid guid) const
Definition Map.cpp:3705
void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans=nullptr, bool alwaysDeleteFromDB=false)
Definition Map.h:727
void ApplyDynamicModeRespawnScaling(WorldObject const *obj, ObjectGuid::LowType spawnId, uint32 &respawnDelay, uint32 mode) const
Definition Map.cpp:2277
ObjectGuid::LowType GenerateLowGuid()
Definition Map.h:558
void RequestRebuildNavMeshOnGameObjectModelChange(GameObjectModel const &model, PhaseShift const &phaseShift)
Definition Map.cpp:1775
time_t GetGORespawnTime(ObjectGuid::LowType spawnId) const
Definition Map.h:525
void SaveRespawnInfoDB(RespawnInfo const &info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition Map.cpp:3647
void SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 gridId, CharacterDatabaseTransaction dbTrans=nullptr, bool startup=false)
Definition Map.cpp:3614
GameObjectBySpawnIdContainer & GetGameObjectBySpawnIdStore()
Definition Map.h:465
void InsertGameObjectModel(GameObjectModel const &model)
Definition Map.h:499
bool Instanceable() const
Definition Map.cpp:3262
uint32 GetId() const
Definition Map.cpp:3257
SpawnedPoolData & GetPoolData()
Definition Map.h:755
void Respawn(RespawnInfo *info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition Map.cpp:2060
static ObjectGuid const Empty
Definition ObjectGuid.h:314
bool IsEmpty() const
Definition ObjectGuid.h:362
std::string ToString() const
uint64 LowType
Definition ObjectGuid.h:321
void Clear()
Definition ObjectGuid.h:329
static std::string_view GetLocaleString(std::vector< std::string > const &data, LocaleConstant locale)
Definition ObjectMgr.h:1611
void SetDynamicFlag(uint32 flag)
Definition Object.h:97
float GetObjectScale() const
Definition Object.h:92
Player * ToPlayer()
Definition Object.h:126
bool HasDynamicFlag(uint32 flag) const
Definition Object.h:96
uint32 GetEntry() const
Definition Object.h:89
void RemoveDynamicFlag(uint32 flag)
Definition Object.h:98
void BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer &data, EnumFlag< UF::UpdateFieldFlag > flags) const
Definition Object.cpp:113
void SetEntry(uint32 entry)
Definition Object.h:90
virtual void ClearValuesChangesMask()
Definition Object.cpp:130
virtual void SetObjectScale(float scale)
Definition Object.h:93
UF::UpdateField< UF::ObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_OBJECT > m_objectData
Definition Object.h:161
Unit * ToUnit()
Definition Object.h:116
static void InitDbVisibleMapId(PhaseShift &phaseShift, int32 visibleMapId)
static void InitDbPhaseShift(PhaseShift &phaseShift, uint8 phaseUseFlags, uint16 phaseId, uint32 phaseGroupId)
UF::UpdateField< UF::PlayerData, int32(WowCS::EntityFragment::CGObject), TYPEID_PLAYER > m_playerData
Definition Player.h:3061
bool IsInSameRaidWith(Player const *p) const
Definition Player.cpp:2149
void SendDirectMessage(WorldPacket const *data) const
Definition Player.cpp:6283
uint16 GetSkillValue(uint32 skill) const
Definition Player.cpp:6010
bool CanUseBattlegroundObject(GameObject *gameobject) const
Definition Player.cpp:26976
void GiveXP(uint32 xp, Unit *victim, float group_rate=1.0f)
Definition Player.cpp:2199
Item * GetItemByEntry(uint32 entry, ItemSearchLocation where=ItemSearchLocation::Default) const
Definition Player.cpp:12475
void SendPreparedGossip(WorldObject *source)
Definition Player.cpp:14028
QuestGiverStatus GetQuestDialogStatus(Object const *questGiver) const
Definition Player.cpp:16129
void UpdateVisibilityOf(WorldObject *target)
Definition Player.cpp:24652
PlayerSpellMap const & GetSpellMap() const
Definition Player.h:2070
void SendLoot(Loot &loot, bool aeLooting=false)
Definition Player.cpp:9167
void UpdateCriteria(CriteriaType type, uint64 miscValue1=0, uint64 miscValue2=0, uint64 miscValue3=0, WorldObject *ref=nullptr)
Definition Player.cpp:27588
bool HasQuestForGO(int32 goId) const
Definition Player.cpp:25928
bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, TeleportToOptions options=TELE_TO_NONE, Optional< uint32 > instanceId={}, uint32 teleportSpellId=0)
Definition Player.cpp:1226
void SendCinematicStart(uint32 CinematicSequenceId) const
Definition Player.cpp:6288
uint32 GetProfessionSkillForExp(uint32 skill, int32 expansion) const
Definition Player.cpp:5963
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition Player.cpp:15962
void KillCreditGO(uint32 entry, ObjectGuid guid=ObjectGuid::Empty)
Definition Player.cpp:16703
Group * GetGroup(Optional< uint8 > partyIndex)
Definition Player.h:2796
void PrepareGossipMenu(WorldObject *source, uint32 menuId, bool showQuests=false)
Definition Player.cpp:13927
bool UpdateFishingSkill(int32 expansion)
Definition Player.cpp:5592
Item * GetItemByGuid(ObjectGuid guid) const
Definition Player.cpp:9614
SpawnTrackingState GetSpawnTrackingStateByObjectives(uint32 spawnTrackingId, std::vector< uint32 > const &questObjectives) const
Definition Player.cpp:17559
Team GetBGTeam() const
Definition Player.cpp:24358
void setUInt16(uint8 index, uint16 value)
void setString(uint8 index, std::string &&value)
void setUInt32(uint8 index, uint32 value)
void setFloat(uint8 index, float value)
void setUInt64(uint8 index, uint64 value)
void setInt32(uint8 index, int32 value)
void setUInt8(uint8 index, uint8 value)
static uint32 RoundXPValue(uint32 xp)
Definition QuestDef.cpp:848
float GetMaxRange(bool positive=false, WorldObject const *caster=nullptr, Spell *spell=nullptr) const
uint32 const Id
Definition SpellInfo.h:328
bool IsPositive() const
void UpdatePassengerPosition(Map *map, WorldObject *passenger, Position const &position, bool setHomePosition)
Definition Transport.cpp:36
Mask const & GetChangesMask() const
void MarkChanged(UpdateField< T, BlockBit, Bit >(Derived::*))
MutableFieldReference< T, false > ModifyValue(UpdateField< T, BlockBit, Bit >(Derived::*field))
void ClearChangesMask(UpdateField< T, BlockBit, Bit >(Derived::*field))
bool HasChanged(uint32 index) const
Definition BaseEntity.h:83
uint32 GetChangedObjectTypeMask() const
Definition BaseEntity.h:81
Definition Unit.h:635
Vehicle * GetVehicle() const
Definition Unit.h:1784
void RemoveAurasByType(AuraType auraType, std::function< bool(AuraApplication const *)> const &check, AuraRemoveMode removeMode=AURA_REMOVE_BY_DEFAULT)
Definition Unit.cpp:3955
void RemoveGameObject(GameObject *gameObj, bool del)
Definition Unit.cpp:5378
void SetStandState(UnitStandStateType state, uint32 animKitID=0)
Definition Unit.cpp:10731
void FinishSpell(CurrentSpellTypes spellType, SpellCastResult result=SPELL_CAST_OK)
Definition Unit.cpp:3189
bool IsAlive() const
Definition Unit.h:1185
Aura * GetAura(uint32 spellId, ObjectGuid casterGUID=ObjectGuid::Empty, ObjectGuid itemCasterGUID=ObjectGuid::Empty, uint32 reqEffMask=0) const
Definition Unit.cpp:4700
void NearTeleportTo(TeleportLocation const &target, bool casting=false)
Definition Unit.cpp:12958
ObjectGuid GetTarget() const
Definition Unit.h:1831
uint8 GetLevel() const
Definition Unit.h:757
Spell * GetCurrentSpell(CurrentSpellTypes spellType) const
Definition Unit.h:1466
bool BuildPacket(WorldPacket *packet)
void AddUpdateBlock()
Definition UpdateData.h:46
constexpr uint32 GetBlock(uint32 index) const
Definition UpdateMask.h:59
constexpr void Set(uint32 index)
Definition UpdateMask.h:91
constexpr uint32 GetMapId() const
Definition Position.h:216
bool InSamePhase(PhaseShift const &phaseShift) const
Definition Object.h:314
virtual bool IsInvisibleDueToDespawn(WorldObject const *seer) const
Definition Object.h:607
bool IsWithinDist3d(float x, float y, float z, float dist) const
Definition Object.cpp:476
virtual void SendMessageToSet(WorldPacket const *data, bool self) const
Definition Object.cpp:1094
Map * GetMap() const
Definition Object.h:411
void AddToWorld() override
Definition Object.cpp:365
virtual bool IsAlwaysVisibleFor(WorldObject const *seer) const
Definition Object.h:606
void RemoveFromWorld() override
Definition Object.cpp:371
virtual void UpdateObjectVisibilityOnDestroy()
Definition Object.h:511
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition Object.cpp:2217
std::string GetDebugInfo() const override
Definition Object.cpp:3136
uint32 LastUsedScriptID
Definition Object.h:534
PhaseShift & GetPhaseShift()
Definition Object.h:310
Unit * GetOwner() const
Definition Object.cpp:1598
FlaggedValuesArray32< int32, uint64, InvisibilityType, TOTAL_INVISIBILITY_TYPES > m_invisibility
Definition Object.h:403
ZoneScript * GetZoneScript() const
Definition Object.h:417
void SetZoneScript()
Definition Object.cpp:1384
TransportBase * GetTransport() const
Definition Object.h:537
void setActive(bool isActiveObject)
Definition Object.cpp:276
float GetDistance2d(WorldObject const *obj) const
Definition Object.cpp:450
ZoneScript * m_zoneScript
Definition Object.h:584
std::string const & GetName() const
Definition Object.h:342
virtual void SetMap(Map *map)
Definition Object.cpp:1144
void AddObjectToRemoveList()
Definition Object.cpp:1174
void UpdatePositionData()
Definition Object.cpp:346
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition Object.cpp:1621
void SetName(std::string newname)
Definition Object.h:343
FlaggedValuesArray32< int32, uint32, StealthType, TOTAL_STEALTH_TYPES > m_stealth
Definition Object.h:400
uint32 GetAreaId() const
Definition Object.h:333
void SetVisibilityDistanceOverride(VisibilityDistanceType type)
Definition Object.cpp:299
MovementInfo m_movementInfo
Definition Object.h:548
virtual bool IsNeverVisibleFor(WorldObject const *seer, bool allowServersideObjects=false) const
Definition Object.h:605
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2186
bool IsInMap(WorldObject const *obj) const
Definition Object.cpp:469
virtual void Update(uint32 diff)
Definition Object.cpp:245
int32 GetDBPhase() const
Definition Object.h:327
virtual void UpdateObjectVisibility(bool forced=true)
Definition Object.cpp:3047
virtual void CleanupsBeforeDelete(bool finalCleanup=true)
Definition Object.cpp:335
void SetTransport(TransportBase *t)
Definition Object.cpp:3100
WorldPacket const * Write() override
WorldPacket const * Write() override
WorldPacket const * Write() override
WorldPacket && Move()
Definition Packet.h:54
virtual uint32 GetGameObjectEntry(ObjectGuid::LowType, uint32 entry)
Definition ZoneScript.h:74
virtual void OnGameObjectRemove(GameObject *)
Definition ZoneScript.h:80
virtual void OnGameObjectCreate(GameObject *)
Definition ZoneScript.h:79
#define sWorld
Definition World.h:916
@ CONFIG_RESPAWN_DYNAMICMODE
Definition World.h:419
@ CONFIG_LOAD_LOCALES
Definition World.h:200
static constexpr ObjectData gameObjectData[]
GameObjectAI * SelectGameObjectAI(GameObject *go)
TC_GAME_API void Trigger(uint32 gameEventId, WorldObject *source, WorldObject *target)
SystemTimePoint GetSystemTime()
Current chrono system_clock time point.
Definition GameTime.cpp:62
time_t GetGameTime()
Definition GameTime.cpp:52
uint32 GetGameTimeMS()
Definition GameTime.cpp:57
ItemContext GetContextForPlayer(MapDifficultyEntry const *mapDifficulty, Player const *player)
TC_GAME_API Unit * GetUnit(WorldObject const &, ObjectGuid const &guid)
TC_GAME_API GameObject * GetGameObject(WorldObject const &u, ObjectGuid const &guid)
TC_GAME_API Player * FindPlayer(ObjectGuid const &)
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)
auto SelectRandomContainerElement(C const &container) -> std::add_const_t< decltype(*std::ranges::begin(container))> &
Definition Containers.h:110
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:37
void MultimapErasePair(M &multimap, typename M::key_type const &key, typename M::mapped_type const &value)
Definition MapUtils.h:57
GridCoord ComputeGridCoord(float x, float y)
UpdateFieldFlag
Definition UpdateField.h:37
std::unique_ptr< VignetteData > Create(VignetteEntry const *vignetteData, WorldObject const *owner)
Definition Vignette.cpp:81
void Update(VignetteData &vignette, WorldObject const *owner)
Definition Vignette.cpp:101
void Remove(VignetteData &vignette, WorldObject const *owner)
Definition Vignette.cpp:113
struct advstd::ranges::Contains contains
TriggerCastFlags TriggerFlags
CastSpellExtraArgs & SetOriginalCaster(ObjectGuid const &guid)
static void VisitAllObjects(WorldObject const *obj, T &visitor, float radius, bool dont_load=true)
Definition CellImpl.h:203
static void VisitGridObjects(WorldObject const *obj, T &visitor, float radius, bool dont_load=true)
Definition CellImpl.h:179
static void VisitWorldObjects(WorldObject const *obj, T &visitor, float radius, bool dont_load=true)
Definition CellImpl.h:191
uint32 GetMaxHealth() const
QuaternionData ParentRotation
InvisibilityType invisibilityType
QuaternionData rotation
std::array< uint32, 5 > ArtKits
uint32 GetAutoCloseTime() const
struct GameObjectTemplate::@197::@231 barberChair
struct GameObjectTemplate::@197::@264 raw
bool IsLargeGameObject() const
bool IsUsableMounted() const
uint32 GetNoDamageImmune() const
struct GameObjectTemplate::@197::@232 destructibleBuilding
bool IsGiganticGameObject() const
WorldPacket BuildQueryData(LocaleConstant loc) const
struct GameObjectTemplate::@197::@223 flagStand
struct GameObjectTemplate::@197::@206 chair
struct GameObjectTemplate::@197::@209 goober
struct GameObjectTemplate::@197::@241 capturePoint
bool IsInfiniteGameObject() const
bool IsDespawnAtAction() const
struct GameObjectTemplate::@197::@249 gatheringNode
uint32 GetCharges() const
struct GameObjectTemplate::@197::@228 controlZone
struct GameObjectTemplate::@197::@205 trap
struct GameObjectTemplate::@197::@246 itemForge
struct GameObjectTemplate::@197::@225 flagDrop
struct GameObjectTemplate::@197::@235 newflag
struct GameObjectTemplate::@197::@221 spellCaster
std::string castBarCaption
std::unique_ptr< WorldPacket[]> QueryData
bool IsUsingGroupLootRules() const
struct GameObjectTemplate::@197::@242 phaseableMO
struct GameObjectTemplate::@197::@202 chest
uint32 GetLockId() const
struct GameObjectTemplate::@197::@217 ritual
struct GameObjectTemplate::@197::@210 transport
uint32 GetConditionID1() const
uint32 GetLootId() const
SystemTimePoint ValidUntil
Definition GameObject.h:526
Optional< GOState > State
Definition GameObject.h:527
void operator()(Player const *player) const
std::array< uint8, MAX_LOCK_CASE > Type
std::array< uint16, MAX_LOCK_CASE > Skill
std::array< int32, MAX_LOCK_CASE > Index
Definition Loot.h:286
bool AutoStore(Player *player, uint8 bag, uint8 slot, bool broadcast=false, bool createdByPlayer=false)
Definition Loot.cpp:976
bool isLooted() const
Definition Loot.h:312
void generateMoneyLoot(uint32 minAmount, uint32 maxAmount)
Definition Loot.cpp:845
void SetDungeonEncounterId(uint32 dungeonEncounterId)
Definition Loot.h:310
bool FillLoot(uint32 lootId, LootStore const &store, Player *lootOwner, bool personal, bool noEmptyError=false, uint16 lootMode=LOOT_MODE_DEFAULT, ItemContext context=ItemContext::NONE)
Definition Loot.cpp:859
struct MovementInfo::TransportInfo transport
constexpr float GetPositionX() const
Definition Position.h:87
constexpr float GetPositionY() const
Definition Position.h:88
std::string ToString() const
Definition Position.cpp:202
float GetExactDist(float x, float y, float z) const
Definition Position.h:129
Position GetPositionWithOffset(Position const &offset) const
Definition Position.cpp:61
Position GetPositionOffsetTo(Position const &endPos) const
Definition Position.cpp:47
bool IsPositionValid() const
Definition Position.cpp:42
constexpr void Relocate(float x, float y)
Definition Position.h:74
constexpr Position GetPosition() const
Definition Position.h:95
constexpr bool IsInDist(float x, float y, float z, float dist) const
Definition Position.h:155
constexpr float GetOrientation() const
Definition Position.h:90
constexpr float GetPositionZ() const
Definition Position.h:89
void toEulerAnglesZYX(float &Z, float &Y, float &X) const
bool isUnit() const
static QuaternionData fromEulerAnglesZYX(float Z, float Y, float X)
ObjectGuid::LowType spawnId
Definition Map.h:172
time_t respawnTime
Definition Map.h:174
SpawnObjectType type
Definition Map.h:171
uint32 scriptId
Definition SpawnData.h:144
uint8 phaseUseFlags
Definition SpawnData.h:137
uint32 id
Definition SpawnData.h:135
uint32 phaseId
Definition SpawnData.h:138
Position spawnPoint
Definition SpawnData.h:136
int32 spawntimesecs
Definition SpawnData.h:142
int32 terrainSwapMap
Definition SpawnData.h:140
uint32 phaseGroup
Definition SpawnData.h:139
uint32 poolId
Definition SpawnData.h:141
std::string StringId
Definition SpawnData.h:145
std::vector< Difficulty > spawnDifficulties
Definition SpawnData.h:143
SpawnGroupFlags flags
Definition SpawnData.h:72
SpawnGroupTemplateData const * spawnGroupData
Definition SpawnData.h:124
uint64 spawnId
Definition SpawnData.h:121
std::vector< uint32 > spawnTrackingQuestObjectives
Definition SpawnData.h:126
void Update(int32 diff)
Definition Timer.h:121
bool Passed() const
Definition Timer.h:131
void Reset(int32 expiry)
Definition Timer.h:136
TransportRotationEntry const * GetNextAnimRotation(uint32 time) const
TransportAnimationEntry const * GetNextAnimNode(uint32 time) const
TransportAnimationEntry const * GetPrevAnimNode(uint32 time) const
TransportRotationEntry const * GetPrevAnimRotation(uint32 time) const
std::array< float, 4 > Rot
bool Insert(ValueType< ObjectType > object)
bool Remove(ValueType< ObjectType > object)
UpdateField< int32, 0, 4 > DisplayID
UpdateField< QuaternionData, 0, 13 > ParentRotation
UpdateField< uint32, 0, 18 > ArtKit
UpdateField< uint32, 0, 5 > SpellVisualID
UpdateField< uint32, 0, 9 > StateWorldEffectsQuestObjectiveID
DynamicUpdateField< int32, 0, 2 > EnableDoodadSets
UpdateField< int8, 0, 15 > State
UpdateField< uint32, 0, 2 > DynamicFlags
std::array< uint32, MAX_GAMEOBJECT_DATA > Data
void Add(EntityFragment fragment, bool update, void const *data=nullptr)
TeamId LastTeamCapture
Definition GameObject.h:144
struct GameObjectValue::@196 CapturePoint
struct GameObjectValue::@193 FishingHole
uint32 AssaultTimer
Definition GameObject.h:146
struct GameObjectValue::@195 Building
WorldPackets::Battleground::BattlegroundCapturePointState State
Definition GameObject.h:145
::DestructibleHitpoint const * DestructibleHitpoint
Definition GameObject.h:139