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::Quat(x, y, z, w).toYPRAngles(Z, Y, X);
125}
126
128{
129 G3D::Quat quat = G3D::Quat::fromYPRAngles(Z, Y, X);
130 return QuaternionData(quat.x, quat.y, quat.z, quat.w);
131}
132
134{
135 G3D::Quat quat = G3D::Quat::fromAxisAngleRotation(G3D::Vector3::unitZ(), orientation);
136 return QuaternionData(quat.x, quat.y, quat.z, quat.w);
137}
138
140
142{
143//11 GAMEOBJECT_TYPE_TRANSPORT
145{
146public:
147 static constexpr Milliseconds PositionUpdateInterval = 50ms;
148
149 explicit Transport(GameObject& owner) : GameObjectTypeBase(owner), _animationInfo(sTransportMgr->GetTransportAnimInfo(owner.GetGOInfo()->entry)),
152 {
153 GameObjectTemplate const* goInfo = _owner.GetGOInfo();
154 if (goInfo->transport.Timeto2ndfloor > 0)
155 {
156 _stopFrames.push_back(goInfo->transport.Timeto2ndfloor);
157 if (goInfo->transport.Timeto3rdfloor > 0)
158 {
159 _stopFrames.push_back(goInfo->transport.Timeto3rdfloor);
160 if (goInfo->transport.Timeto4thfloor > 0)
161 {
162 _stopFrames.push_back(goInfo->transport.Timeto4thfloor);
163 if (goInfo->transport.Timeto5thfloor > 0)
164 {
165 _stopFrames.push_back(goInfo->transport.Timeto5thfloor);
166 if (goInfo->transport.Timeto6thfloor > 0)
167 {
168 _stopFrames.push_back(goInfo->transport.Timeto6thfloor);
169 if (goInfo->transport.Timeto7thfloor > 0)
170 {
171 _stopFrames.push_back(goInfo->transport.Timeto7thfloor);
172 if (goInfo->transport.Timeto8thfloor > 0)
173 {
174 _stopFrames.push_back(goInfo->transport.Timeto8thfloor);
175 if (goInfo->transport.Timeto9thfloor > 0)
176 {
177 _stopFrames.push_back(goInfo->transport.Timeto9thfloor);
178 if (goInfo->transport.Timeto10thfloor > 0)
179 _stopFrames.push_back(goInfo->transport.Timeto10thfloor);
180 }
181 }
182 }
183 }
184 }
185 }
186 }
187 }
188
189 if (!_stopFrames.empty())
190 {
191 _pathProgress = 0;
193 }
194
196 }
197
198 void Update(uint32 diff) override
199 {
200 if (!_animationInfo)
201 return;
202
205 return;
206
208
210 uint32 period = GetTransportPeriod();
211 uint32 newProgress = 0;
212 if (_stopFrames.empty())
213 newProgress = now % period;
214 else
215 {
216 int32 stopTargetTime = 0;
218 stopTargetTime = 0;
219 else
221
222 if (now < uint32(*_owner.m_gameObjectData->Level))
223 {
224 int32 timeToStop = _owner.m_gameObjectData->Level - _stateChangeTime;
225 float stopSourcePathPct = float(_stateChangeProgress) / float(period);
226 float stopTargetPathPct = float(stopTargetTime) / float(period);
227 float timeSinceStopProgressPct = float(now - _stateChangeTime) / float(timeToStop);
228
229 float progressPct;
231 {
233 stopTargetPathPct = 1.0f;
234
235 float pathPctBetweenStops = stopTargetPathPct - stopSourcePathPct;
236 if (pathPctBetweenStops < 0.0f)
237 pathPctBetweenStops += 1.0f;
238
239 progressPct = pathPctBetweenStops * timeSinceStopProgressPct + stopSourcePathPct;
240 if (progressPct > 1.0f)
241 progressPct = progressPct - 1.0f;
242 }
243 else
244 {
245 float pathPctBetweenStops = stopSourcePathPct - stopTargetPathPct;
246 if (pathPctBetweenStops < 0.0f)
247 pathPctBetweenStops += 1.0f;
248
249 progressPct = stopSourcePathPct - pathPctBetweenStops * timeSinceStopProgressPct;
250 if (progressPct < 0.0f)
251 progressPct += 1.0f;
252 }
253
254 newProgress = uint32(float(period) * progressPct) % period;
255 }
256 else
257 newProgress = stopTargetTime;
258
259 if (int32(newProgress) == stopTargetTime && newProgress != _pathProgress)
260 {
261 uint32 eventId = [&]()
262 {
264 {
265 case 0:
267 case 1:
269 case 2:
271 case 3:
273 case 4:
275 case 5:
277 case 6:
279 case 7:
281 case 8:
283 case 9:
285 default:
286 return 0u;
287 }
288 }();
289 if (eventId)
290 GameEvents::Trigger(eventId, &_owner, &_owner);
291
293 {
294 GOState currentState = _owner.GetGoState();
295 GOState newState;
296 if (currentState == GO_STATE_TRANSPORT_ACTIVE)
298 else if (currentState - GO_STATE_TRANSPORT_ACTIVE == int32(_stopFrames.size()))
299 newState = GOState(currentState - 1);
301 newState = GOState(currentState - 1);
302 else
303 newState = GOState(currentState + 1);
304
305 _owner.SetGoState(newState);
306 }
307 }
308 }
309
310 if (_pathProgress == newProgress)
311 return;
312
313 _pathProgress = newProgress;
314
315 TransportAnimationEntry const* oldAnimation = _animationInfo->GetPrevAnimNode(newProgress);
316 TransportAnimationEntry const* newAnimation = _animationInfo->GetNextAnimNode(newProgress);
317 if (oldAnimation && newAnimation)
318 {
319 G3D::Matrix3 pathRotation = G3D::Quat(_owner.m_gameObjectData->ParentRotation->x, _owner.m_gameObjectData->ParentRotation->y,
320 _owner.m_gameObjectData->ParentRotation->z, _owner.m_gameObjectData->ParentRotation->w).toRotationMatrix();
321
322 G3D::Vector3 prev(oldAnimation->Pos.X, oldAnimation->Pos.Y, oldAnimation->Pos.Z);
323 G3D::Vector3 next(newAnimation->Pos.X, newAnimation->Pos.Y, newAnimation->Pos.Z);
324
325 G3D::Vector3 dst = next;
326 if (prev != next)
327 {
328 float animProgress = float(newProgress - oldAnimation->TimeIndex) / float(newAnimation->TimeIndex - oldAnimation->TimeIndex);
329
330 dst = prev.lerp(next, animProgress);
331 }
332
333 dst = dst * pathRotation;
334 dst += PositionToVector3(_owner.GetStationaryPosition());
335
336 _owner.GetMap()->GameObjectRelocation(&_owner, dst.x, dst.y, dst.z, _owner.GetOrientation());
337 }
338
339 TransportRotationEntry const* oldRotation = _animationInfo->GetPrevAnimRotation(newProgress);
340 TransportRotationEntry const* newRotation = _animationInfo->GetNextAnimRotation(newProgress);
341 if (oldRotation && newRotation)
342 {
343 G3D::Quat prev(oldRotation->Rot[0], oldRotation->Rot[1], oldRotation->Rot[2], oldRotation->Rot[3]);
344 G3D::Quat next(newRotation->Rot[0], newRotation->Rot[1], newRotation->Rot[2], newRotation->Rot[3]);
345
346 G3D::Quat rotation = next;
347
348 if (prev != next)
349 {
350 float animProgress = float(newProgress - oldRotation->TimeIndex) / float(newRotation->TimeIndex - oldRotation->TimeIndex);
351
352 rotation = prev.slerp(next, animProgress);
353 }
354
355 _owner.SetLocalRotation(rotation.x, rotation.y, rotation.z, rotation.w);
357 }
358
359 // update progress marker for client
360 _owner.SetPathProgressForClient(float(_pathProgress) / float(period));
361 }
362
363 void OnStateChanged(GOState oldState, GOState newState) override
364 {
366
367 // transports without stop frames just keep animating in state 24
368 if (_stopFrames.empty())
369 {
370 if (newState != GO_STATE_TRANSPORT_ACTIVE)
372 return;
373 }
374
375 int32 stopPathProgress = 0;
376
377 if (newState != GO_STATE_TRANSPORT_ACTIVE)
378 {
380 uint32 stopFrame = newState - GO_STATE_TRANSPORT_STOPPED;
381 ASSERT(stopFrame < _stopFrames.size());
382 stopPathProgress = _stopFrames[stopFrame];
383 }
384
387 uint32 timeToStop = std::abs(int32(_pathProgress) - stopPathProgress);
390
391 if (oldState == GO_STATE_ACTIVE || oldState == newState)
392 {
393 // initialization
394 if (int32(_pathProgress) > stopPathProgress)
396 else
398
399 return;
400 }
401
402 int32 pauseTimesCount = _stopFrames.size();
403 int32 newToOldStateDelta = newState - oldState;
404 if (newToOldStateDelta < 0)
405 newToOldStateDelta += pauseTimesCount + 1;
406
407 int32 oldToNewStateDelta = oldState - newState;
408 if (oldToNewStateDelta < 0)
409 oldToNewStateDelta += pauseTimesCount + 1;
410
411 // this additional check is neccessary because client doesn't check dynamic flags on progress update
412 // instead it multiplies progress from dynamicflags field by -1 and then compares that against 0
413 // when calculating path progress while we simply check the flag if (!_owner.HasDynamicFlag(GO_DYNFLAG_LO_INVERTED_MOVEMENT))
414 bool isAtStartOfPath = _stateChangeProgress == 0;
415
416 if (oldToNewStateDelta < newToOldStateDelta && !isAtStartOfPath)
418 else
420 }
421
422 void OnRelocated() override
423 {
425 }
426
428 {
429 for (WorldObject* passenger : _passengers)
430 UpdatePassengerPosition(_owner.GetMap(), passenger, _owner.GetPositionWithOffset(passenger->m_movementInfo.transport.pos), true);
431 }
432
434 {
435 if (_animationInfo)
437
438 return 1;
439 }
440
441 std::span<uint32 const> GetPauseTimes() const
442 {
443 return _stopFrames;
444 }
445
446 ObjectGuid GetTransportGUID() const override { return _owner.GetGUID(); }
447
448 float GetTransportOrientation() const override { return _owner.GetOrientation(); }
449
450 void AddPassenger(WorldObject* passenger, Position const& offset) override
451 {
452 if (!_owner.IsInWorld())
453 return;
454
455 if (_passengers.insert(passenger).second)
456 {
457 passenger->SetTransport(this);
459 passenger->m_movementInfo.transport.pos = offset;
460 TC_LOG_DEBUG("entities.transport", "Object {} boarded transport {}.", passenger->GetName(), _owner.GetName());
461 }
462 }
463
465 {
466 if (_passengers.erase(passenger) > 0)
467 {
468 passenger->SetTransport(nullptr);
469 passenger->m_movementInfo.transport.Reset();
470 TC_LOG_DEBUG("entities.transport", "Object {} removed from transport {}.", passenger->GetName(), _owner.GetName());
471
472 if (Player* plr = passenger->ToPlayer())
473 plr->SetFallInformation(0, plr->GetPositionZ());
474 }
475
476 return this;
477 }
478
479 Position GetPositionWithOffset(Position const& offset) const override
480 {
481 return _owner.GetPositionWithOffset(offset);
482 }
483
484 Position GetPositionOffsetTo(Position const& endPos) const override
485 {
486 return _owner.GetPositionOffsetTo(endPos);
487 }
488
489 int32 GetMapIdForSpawning() const override
490 {
492 }
493
498
499private:
504 std::vector<uint32> _stopFrames;
507 std::unordered_set<WorldObject*> _passengers;
508};
509
513
515{
516 if (Transport* transport = dynamic_cast<Transport*>(&type))
517 transport->SetAutoCycleBetweenStopFrames(_on);
518}
519
521{
522public:
524
525 void SetState(FlagState newState, Player* player)
526 {
527 if (_state == newState)
528 return;
529
530 FlagState oldState = _state;
531 _state = newState;
532
533 if (player && newState == FlagState::Taken)
534 _carrierGUID = player->GetGUID();
535 else
537
538 if (newState == FlagState::Taken && oldState == FlagState::InBase)
540 else if (newState == FlagState::InBase || newState == FlagState::Respawning)
542
544
545 if (newState == FlagState::Respawning)
547 else
548 _respawnTime = 0;
549
550 if (ZoneScript* zoneScript = _owner.GetZoneScript())
551 zoneScript->OnFlagStateChange(&_owner, oldState, _state, player);
552 }
553
554 void Update([[maybe_unused]] uint32 diff) override
555 {
557 SetState(FlagState::InBase, nullptr);
558 }
559
560 bool IsNeverVisibleFor([[maybe_unused]] WorldObject const* seer, [[maybe_unused]] bool allowServersideObjects) const override
561 {
562 return _state != FlagState::InBase;
563 }
564
565 FlagState GetState() const { return _state; }
566 ObjectGuid const& GetCarrierGUID() const { return _carrierGUID; }
567 time_t GetTakenFromBaseTime() const { return _takenFromBaseTime; }
568
569private:
574};
575
576SetNewFlagState::SetNewFlagState(FlagState state, Player* player) : _state(state), _player(player)
577{
578}
579
581{
582 if (NewFlag* newFlag = dynamic_cast<NewFlag*>(&type))
583 newFlag->SetState(_state, _player);
584}
585
587{
588public:
589 explicit ControlZone(GameObject& owner) : GameObjectTypeBase(owner), _value(static_cast<float>(owner.GetGOInfo()->controlZone.startingValue))
590 {
591 if (owner.GetMap()->Instanceable())
592 _heartbeatRate = 1s;
593 else if (owner.GetGOInfo()->controlZone.FrequentHeartbeat)
594 _heartbeatRate = 2500ms;
595 else
596 _heartbeatRate = 5s;
597
600 _contestedTriggered = false;
601 }
602
603 void Update(uint32 diff) override
604 {
606 return;
607
610 {
613 }
614 }
615
617 {
618 if (_value < GetMaxHordeValue())
619 return TEAM_HORDE;
620
622 return TEAM_ALLIANCE;
623
624 return TEAM_NEUTRAL;
625 }
626
628
629 void ActivateObject(GameObjectActions action, int32 /*param*/, WorldObject* /*spellCaster*/, uint32 /*spellId*/, int32 /*effectIndex*/) override
630 {
631 switch (action)
632 {
634 for (ObjectGuid const& guid : _insidePlayers)
635 if (Player* player = ObjectAccessor::GetPlayer(_owner, guid))
636 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldState1, 0);
637
638 _insidePlayers.clear();
639 break;
640 default:
641 break;
642 }
643 }
644
645 void SetValue(float value)
646 {
647 _value = RoundToInterval<float>(value, 0.0f, 100.0f);
648 }
649
651 {
652 // update player list inside control zone
653 std::vector<Player*> targetList;
654 SearchTargets(targetList);
655
656 TeamId oldControllingTeam = GetControllingTeam();
657 float pointsGained = CalculatePointsPerSecond(targetList) * _heartbeatRate.count() / 1000.0f;
658 if (pointsGained == 0)
659 return;
660
661 int32 oldRoundedValue = static_cast<int32>(_value);
662 SetValue(_value + pointsGained);
663 int32 roundedValue = static_cast<int32>(_value);
664 if (oldRoundedValue == roundedValue)
665 return;
666
667 TeamId newControllingTeam = GetControllingTeam();
668 TeamId assaultingTeam = pointsGained > 0 ? TEAM_ALLIANCE : TEAM_HORDE;
669
670 if (oldControllingTeam != newControllingTeam)
671 _contestedTriggered = false;
672
673 if (oldControllingTeam != TEAM_ALLIANCE && newControllingTeam == TEAM_ALLIANCE)
675 else if (oldControllingTeam != TEAM_HORDE && newControllingTeam == TEAM_HORDE)
677 else if (oldControllingTeam == TEAM_HORDE && newControllingTeam == TEAM_NEUTRAL)
679 else if (oldControllingTeam == TEAM_ALLIANCE && newControllingTeam == TEAM_NEUTRAL)
681
682 if (roundedValue == 100 && newControllingTeam == TEAM_ALLIANCE && assaultingTeam == TEAM_ALLIANCE)
684 else if (roundedValue == 0 && newControllingTeam == TEAM_HORDE && assaultingTeam == TEAM_HORDE)
686
687 if (oldRoundedValue == 100 && assaultingTeam == TEAM_HORDE && !_contestedTriggered)
688 {
690 _contestedTriggered = true;
691 }
692 else if (oldRoundedValue == 0 && assaultingTeam == TEAM_ALLIANCE && !_contestedTriggered)
693 {
695 _contestedTriggered = true;
696 }
697
698 for (Player* player : targetList)
699 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldstate2, roundedValue);
700 }
701
702 void SearchTargets(std::vector<Player*>& targetList)
703 {
707 HandleUnitEnterExit(targetList);
708 }
709
710 float CalculatePointsPerSecond(std::vector<Player*> const& targetList) const
711 {
712 int32 hordePlayers = 0;
713 int32 alliancePlayers = 0;
714
715 for (Player const* player : targetList)
716 {
717 if (!player->IsOutdoorPvPActive())
718 continue;
719
720 if (player->GetTeamId() == TEAM_HORDE)
721 hordePlayers++;
722 else
723 alliancePlayers++;
724 }
725
726 int8 factionCoefficient = 0; // alliance superiority = 1; horde superiority = -1
727
728 if (alliancePlayers > hordePlayers)
729 factionCoefficient = 1;
730 else if (hordePlayers > alliancePlayers)
731 factionCoefficient = -1;
732
733 float const timeNeeded = CalculateTimeNeeded(hordePlayers, alliancePlayers);
734 if (timeNeeded == 0.0f)
735 return 0.0f;
736
737 return 100.0f / timeNeeded * static_cast<float>(factionCoefficient);
738 }
739
740 float CalculateTimeNeeded(int32 hordePlayers, int32 alliancePlayers) const
741 {
742 uint32 const uncontestedTime = _owner.GetGOInfo()->controlZone.UncontestedTime;
743 uint32 const delta = std::abs(alliancePlayers - hordePlayers);
744 uint32 const minSuperiority = _owner.GetGOInfo()->controlZone.minSuperiority;
745
746 if (delta < minSuperiority)
747 return 0.0f;
748
749 // return the uncontested time if controlzone is not contested
750 if (uncontestedTime && (hordePlayers == 0 || alliancePlayers == 0))
751 return static_cast<float>(uncontestedTime);
752
753 uint32 const minTime = _owner.GetGOInfo()->controlZone.minTime;
754 uint32 const maxTime = _owner.GetGOInfo()->controlZone.maxTime;
755 uint32 const maxSuperiority = _owner.GetGOInfo()->controlZone.maxSuperiority;
756
757 float const slope = static_cast<float>(minTime - maxTime) / static_cast<float>(std::max<uint32>(maxSuperiority - minSuperiority, 1));
758 float const intercept = static_cast<float>(maxTime) - slope * static_cast<float>(minSuperiority);
759 return slope * static_cast<float>(delta) + intercept;
760 }
761
762 void HandleUnitEnterExit(std::vector<Player*> const& newTargetList)
763 {
764 GuidUnorderedSet exitPlayers(std::move(_insidePlayers));
765
766 std::vector<Player*> enteringPlayers;
767
768 for (Player* unit : newTargetList)
769 {
770 if (exitPlayers.erase(unit->GetGUID()) == 0) // erase(key_type) returns number of elements erased
771 enteringPlayers.push_back(unit);
772
773 _insidePlayers.insert(unit->GetGUID());
774 }
775
776 for (Player* player : enteringPlayers)
777 {
778 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldState1, 1);
779 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldstate2, static_cast<int32>(_value));
781 }
782
783 for (ObjectGuid const& exitPlayerGuid : exitPlayers)
784 {
785 if (Player* player = ObjectAccessor::GetPlayer(_owner, exitPlayerGuid))
786 {
787 player->SendUpdateWorldState(_owner.GetGOInfo()->controlZone.worldState1, 0);
788 }
789 }
790 }
791
792 float GetMaxHordeValue() const
793 {
794 // ex: if neutralPercent is 40; then 0 - 30 is Horde Controlled
795 return 50.0f - _owner.GetGOInfo()->controlZone.neutralPercent / 2.0f;
796 }
797
799 {
800 // ex: if neutralPercent is 40; then 70 - 100 is Alliance Controlled
801 return 50.0f + _owner.GetGOInfo()->controlZone.neutralPercent / 2.0f;
802 }
803
804 void TriggerEvent(uint32 eventId) const
805 {
806 if (eventId <= 0)
807 return;
808
810 GameEvents::Trigger(eventId, &_owner, nullptr);
811 }
812
817
818private:
823 float _value;
825};
826
828{
829}
830
832{
833 if (ControlZone* controlZone = dynamic_cast<ControlZone*>(&type))
834 {
835 uint32 value = controlZone->GetStartingValue();
836 if (_value.has_value())
837 value = *_value;
838
839 controlZone->SetValue(value);
840 }
841}
842}
843
845 m_goValue(), m_stringIds(), m_AI(nullptr), m_respawnCompatibilityMode(false), _animKitId(0), _worldEffectID(0)
846{
848
850 m_updateFlag.Rotation = true;
851
853
854 m_respawnTime = 0;
855 m_respawnDelayTime = 300;
856 m_despawnDelay = 0;
858 m_restockTime = 0;
860 m_spawnedByDefault = true;
861 m_usetimes = 0;
862 m_spellId = 0;
863 m_cooldownTime = 0;
865 m_goInfo = nullptr;
866 m_goData = nullptr;
868 m_goTemplateAddon = nullptr;
869
870 m_spawnId = UI64LIT(0);
871
872 ResetLootMode(); // restore default loot mode
873 m_stationaryPosition.Relocate(0.0f, 0.0f, 0.0f, 0.0f);
874}
875
877{
878 delete m_AI;
879}
880
882{
883 delete m_AI;
884 m_AI = nullptr;
885}
886
888{
889 AIM_Destroy();
890
892
893 if (!m_AI)
894 return false;
895
897 return true;
898}
899
900std::string const& GameObject::GetAIName() const
901{
902 return sObjectMgr->GetGameObjectTemplate(GetEntry())->AIName;
903}
904
905void GameObject::CleanupsBeforeDelete(bool finalCleanup)
906{
907 SetVignette(0);
908
910
912}
913
915{
916 ObjectGuid ownerGUID = GetOwnerGUID();
917 if (!ownerGUID)
918 return;
919
920 if (Unit* owner = ObjectAccessor::GetUnit(*this, ownerGUID))
921 {
922 owner->RemoveGameObject(this, false);
924 return;
925 }
926
927 // This happens when a mage portal is despawned after the caster changes map (for example using the portal)
928 TC_LOG_DEBUG("misc", "Removed GameObject ({} Entry: {} SpellId: {} LinkedGO: {}) that just lost any reference to the owner ({}) GO list",
929 GetGUID().ToString(), GetGOInfo()->entry, m_spellId, GetGOInfo()->GetLinkedGameObjectEntry(), ownerGUID.ToString());
931}
932
934{
936 if (!IsInWorld())
937 {
938 if (m_zoneScript)
940
942 if (m_spawnId)
943 GetMap()->GetGameObjectBySpawnIdStore().insert(std::make_pair(m_spawnId, this));
944
945 // The state can be changed after GameObject::Create but before GameObject::AddToWorld
946 bool toggledState = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : (GetGoState() == GO_STATE_READY || IsTransport());
947 if (m_model)
948 {
949 if (Transport* trans = ToTransport())
950 trans->SetDelayedAddModelToMap();
951 else
953 }
954
955 EnableCollision(toggledState);
957 }
958}
959
961{
963 if (IsInWorld())
964 {
965 if (m_zoneScript)
967
969 if (m_model)
970 if (GetMap()->ContainsGameObjectModel(*m_model))
972
973 // If linked trap exists, despawn it
974 if (GameObject* linkedTrap = GetLinkedTrap())
975 linkedTrap->DespawnOrUnsummon();
976
978
979 if (m_spawnId)
980 Trinity::Containers::MultimapErasePair(GetMap()->GetGameObjectBySpawnIdStore(), m_spawnId, this);
982 }
983}
984
985bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit, bool dynamic, ObjectGuid::LowType spawnid)
986{
987 ASSERT(map);
988 SetMap(map);
989
990 Relocate(pos);
992 if (!IsPositionValid())
993 {
994 TC_LOG_ERROR("misc", "Gameobject (Spawn id: {} Entry: {}) not created. Suggested coordinates isn't valid (X: {} Y: {})", GetSpawnId(), entry, pos.GetPositionX(), pos.GetPositionY());
995 return false;
996 }
997
998 // Set if this object can handle dynamic spawns
999 if (!dynamic)
1001
1003
1004 SetZoneScript();
1005 if (m_zoneScript)
1006 {
1007 entry = m_zoneScript->GetGameObjectEntry(m_spawnId, entry);
1008 if (!entry)
1009 return false;
1010 }
1011
1012 GameObjectTemplate const* goInfo = sObjectMgr->GetGameObjectTemplate(entry);
1013 if (!goInfo)
1014 {
1015 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());
1016 return false;
1017 }
1018
1020 {
1021 TC_LOG_ERROR("sql.sql", "Gameobject (Spawn id: {} Entry: {}) not created: gameobject type GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT cannot be manually created.", GetSpawnId(), entry);
1022 return false;
1023 }
1024
1025 ObjectGuid guid;
1026 if (goInfo->type != GAMEOBJECT_TYPE_TRANSPORT)
1027 guid = ObjectGuid::Create<HighGuid::GameObject>(map->GetId(), goInfo->entry, map->GenerateLowGuid<HighGuid::GameObject>());
1028 else
1029 {
1030 guid = ObjectGuid::Create<HighGuid::Transport>(map->GenerateLowGuid<HighGuid::Transport>());
1031 m_updateFlag.ServerTime = true;
1032 }
1033
1034 _Create(guid);
1035
1036 m_goInfo = goInfo;
1037 m_goTemplateAddon = sObjectMgr->GetGameObjectTemplateAddon(entry);
1038
1039 if (goInfo->type >= MAX_GAMEOBJECT_TYPE)
1040 {
1041 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);
1042 return false;
1043 }
1044
1045 SetLocalRotation(rotation.x, rotation.y, rotation.z, rotation.w);
1046 GameObjectAddon const* gameObjectAddon = sObjectMgr->GetGameObjectAddon(GetSpawnId());
1047
1048 // For most of gameobjects is (0, 0, 0, 1) quaternion, there are only some transports with not standard rotation
1049 QuaternionData parentRotation;
1050 if (gameObjectAddon)
1051 parentRotation = gameObjectAddon->ParentRotation;
1052
1053 SetParentRotation(parentRotation);
1054
1055 SetObjectScale(goInfo->size);
1056
1057 if (GameObjectOverride const* goOverride = GetGameObjectOverride())
1058 {
1059 SetFaction(goOverride->Faction);
1060 ReplaceAllFlags(GameObjectFlags(goOverride->Flags));
1061 }
1062
1064 {
1066 {
1067 m_updateFlag.GameObject = true;
1069 }
1070
1072 {
1073 m_updateFlag.AnimKit = true;
1075 }
1076 }
1077
1078 SetEntry(goInfo->entry);
1079
1080 // set name for logs usage, doesn't affect anything ingame
1081 SetName(goInfo->name);
1082
1083 SetDisplayId(goInfo->displayId);
1084
1085 // GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3
1086 SetGoType(GameobjectTypes(goInfo->type));
1087 CreateModel();
1088 m_prevGoState = goState;
1089 SetGoState(goState);
1090 SetGoArtKit(artKit);
1091
1092 switch (goInfo->type)
1093 {
1095 SetGoAnimProgress(animProgress);
1096 m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishingHole.minRestock, std::max(GetGOInfo()->fishingHole.minRestock, GetGOInfo()->fishingHole.maxRestock));
1097 break;
1099 {
1100 m_goValue.Building.DestructibleHitpoint = sObjectMgr->GetDestructibleHitpoint(GetGOInfo()->destructibleBuilding.HealthRec);
1102 SetGoAnimProgress(255);
1103
1104 // yes, even after the updatefield rewrite this garbage hack is still in client
1105 QuaternionData reinterpretId;
1106 memcpy(&reinterpretId.x, &m_goInfo->destructibleBuilding.DestructibleModelRec, sizeof(float));
1108 break;
1109 }
1111 {
1112 m_updateFlag.GameObject = true;
1113 m_goTypeImpl = std::make_unique<GameObjectType::Transport>(*this);
1114 if (goInfo->transport.startOpen)
1116 else
1118
1119 SetGoAnimProgress(animProgress);
1120 setActive(true);
1121 break;
1122 }
1124 SetLevel(0);
1125 SetGoAnimProgress(255);
1126 break;
1128 if (GetGOInfo()->trap.stealthed)
1129 {
1132 }
1133
1134 if (GetGOInfo()->trap.stealthAffected)
1135 {
1138 }
1139
1141 if (SpellInfo const* trapSpell = sSpellMgr->GetSpellInfo(goInfo->trap.spell, map->GetDifficultyID()))
1142 {
1143 // positive spells may require enemy targets
1144 if (trapSpell->IsPositive())
1145 {
1146 bool targetsAlly = false;
1147 bool targetsEnemy = false;
1148 auto isAllyTarget = [](SpellImplicitTargetInfo const& targetInfo)
1149 {
1150 return targetInfo.GetObjectType() == TARGET_OBJECT_TYPE_UNIT && targetInfo.GetCheckType() == TARGET_CHECK_ALLY;
1151 };
1152 auto isEnemyTarget = [](SpellImplicitTargetInfo const& targetInfo)
1153 {
1154 return targetInfo.GetObjectType() == TARGET_OBJECT_TYPE_UNIT && targetInfo.GetCheckType() == TARGET_CHECK_ENEMY;
1155 };
1156 for (SpellEffectInfo const& spellEffectInfo : trapSpell->GetEffects())
1157 {
1158 if (!spellEffectInfo.IsEffect())
1159 continue;
1160
1161 targetsAlly = targetsAlly || isAllyTarget(spellEffectInfo.TargetA) || isAllyTarget(spellEffectInfo.TargetB);
1162 targetsEnemy = targetsEnemy || isEnemyTarget(spellEffectInfo.TargetA) || isEnemyTarget(spellEffectInfo.TargetB);
1163 }
1164 if (targetsAlly)
1166 }
1167 }
1168 break;
1170 m_goTypeImpl = std::make_unique<GameObjectType::ControlZone>(*this);
1171 setActive(true);
1172 break;
1174 m_goTypeImpl = std::make_unique<GameObjectType::NewFlag>(*this);
1175 if (map->Instanceable())
1176 setActive(true);
1177 break;
1179 if (map->Instanceable())
1180 setActive(true);
1181 break;
1185
1186 if (GetGOInfo()->phaseableMO.DoodadSetA)
1188
1191 break;
1198 if (map->Instanceable())
1199 setActive(true);
1200 break;
1201 default:
1202 SetGoAnimProgress(animProgress);
1203 break;
1204 }
1205
1206 if (gameObjectAddon)
1207 {
1208 if (gameObjectAddon->InvisibilityValue)
1209 {
1210 m_invisibility.AddFlag(gameObjectAddon->invisibilityType);
1211 m_invisibility.AddValue(gameObjectAddon->invisibilityType, gameObjectAddon->InvisibilityValue);
1212 }
1213
1214 if (gameObjectAddon->WorldEffectID)
1215 {
1216 m_updateFlag.GameObject = true;
1217 SetWorldEffectID(gameObjectAddon->WorldEffectID);
1218 }
1219
1220 if (gameObjectAddon->AIAnimKitID)
1221 {
1222 m_updateFlag.AnimKit = true;
1223 _animKitId = gameObjectAddon->AIAnimKitID;
1224 }
1225 }
1226
1227 if (uint32 vignetteId = GetGOInfo()->GetSpawnVignette())
1228 SetVignette(vignetteId);
1229
1231
1233
1235
1236 if (spawnid)
1237 m_spawnId = spawnid;
1238
1239 if (uint32 linkedEntry = GetGOInfo()->GetLinkedGameObjectEntry())
1240 {
1241 if (GameObject* linkedGo = GameObject::CreateGameObject(linkedEntry, map, pos, rotation, 255, GO_STATE_READY))
1242 {
1243 SetLinkedTrap(linkedGo);
1244 if (!map->AddToMap(linkedGo))
1245 delete linkedGo;
1246 }
1247 }
1248
1249 // Check if GameObject is Infinite
1250 if (goInfo->IsInfiniteGameObject())
1252
1253 // Check if GameObject is Gigantic
1254 if (goInfo->IsGiganticGameObject())
1256
1257 // Check if GameObject is Large
1258 if (goInfo->IsLargeGameObject())
1260
1261 return true;
1262}
1263
1264GameObject* GameObject::CreateGameObject(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit /*= 0*/)
1265{
1266 GameObjectTemplate const* goInfo = sObjectMgr->GetGameObjectTemplate(entry);
1267 if (!goInfo)
1268 return nullptr;
1269
1270 GameObject* go = new GameObject();
1271 if (!go->Create(entry, map, pos, rotation, animProgress, goState, artKit, false, 0))
1272 {
1273 delete go;
1274 return nullptr;
1275 }
1276
1277 return go;
1278}
1279
1281{
1282 GameObject* go = new GameObject();
1283 if (!go->LoadFromDB(spawnId, map, addToMap))
1284 {
1285 delete go;
1286 return nullptr;
1287 }
1288
1289 return go;
1290}
1291
1293{
1294 WorldObject::Update(diff);
1295
1296 if (AI())
1297 AI()->UpdateAI(diff);
1298 else if (!AIM_Initialize())
1299 TC_LOG_ERROR("misc", "Could not initialize GameObjectAI");
1300
1301 if (m_despawnDelay)
1302 {
1303 if (m_despawnDelay > diff)
1304 m_despawnDelay -= diff;
1305 else
1306 {
1307 m_despawnDelay = 0;
1309 }
1310 }
1311
1312 if (m_goTypeImpl)
1313 m_goTypeImpl->Update(diff);
1314
1315 if (m_perPlayerState)
1316 {
1317 for (auto itr = m_perPlayerState->begin(); itr != m_perPlayerState->end(); )
1318 {
1319 if (itr->second.ValidUntil > GameTime::GetSystemTime())
1320 {
1321 ++itr;
1322 continue;
1323 }
1324
1325 Player* seer = ObjectAccessor::GetPlayer(*this, itr->first);
1326 bool needsStateUpdate = itr->second.State != GetGoState();
1327 bool despawned = itr->second.Despawned;
1328
1329 itr = m_perPlayerState->erase(itr);
1330
1331 if (seer)
1332 {
1333 if (despawned)
1334 {
1335 seer->UpdateVisibilityOf(this);
1336 }
1337 else if (needsStateUpdate)
1338 {
1339 UF::ObjectData::Base objMask;
1342
1343 UpdateData udata(GetMapId());
1344 BuildValuesUpdateForPlayerWithMask(&udata, objMask.GetChangesMask(), goMask.GetChangesMask(), seer, false);
1345 WorldPacket packet;
1346 udata.BuildPacket(&packet);
1347 seer->SendDirectMessage(&packet);
1348 }
1349 }
1350 }
1351 }
1352
1353 switch (m_lootState)
1354 {
1355 case GO_NOT_READY:
1356 {
1357 switch (GetGoType())
1358 {
1360 {
1361 // Arming Time for GAMEOBJECT_TYPE_TRAP (6)
1362 GameObjectTemplate const* goInfo = GetGOInfo();
1363 // Bombs
1364 if (goInfo->trap.charges == 2)
1365 // Hardcoded tooltip value
1367 else if (Unit* owner = GetOwner())
1368 if (owner->IsInCombat())
1370
1372 break;
1373 }
1375 {
1376 // fishing code (bobber ready)
1378 {
1379 // splash bobber (bobber ready now)
1380 Unit* caster = GetOwner();
1381 if (caster && caster->GetTypeId() == TYPEID_PLAYER)
1382 SendCustomAnim(0);
1383
1384 m_lootState = GO_READY; // can be successfully open with some chance
1385 }
1386 return;
1387 }
1390 return;
1391 // If there is no restock timer, or if the restock timer passed, the chest becomes ready to loot
1392 m_restockTime = 0;
1394 ClearLoot();
1396 break;
1397 default:
1398 m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY
1399 break;
1400 }
1401 [[fallthrough]];
1402 }
1403 case GO_READY:
1404 {
1406 {
1407 if (m_respawnTime > 0) // timer on
1408 {
1409 time_t now = GameTime::GetGameTime();
1410 if (m_respawnTime <= now) // timer expired
1411 {
1412 ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::GameObject>(GetMapId(), GetEntry(), m_spawnId);
1413 time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
1414 if (linkedRespawntime) // Can't respawn, the master is dead
1415 {
1416 ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
1417 if (targetGuid == dbtableHighGuid) // if linking self, never respawn
1419 else
1420 m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
1422 return;
1423 }
1424
1425 m_respawnTime = 0;
1426 m_SkillupList.clear();
1427 m_usetimes = 0;
1428
1429 switch (GetGoType())
1430 {
1431 case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
1432 {
1433 Unit* caster = GetOwner();
1434 if (caster && caster->GetTypeId() == TYPEID_PLAYER)
1435 {
1436 caster->ToPlayer()->RemoveGameObject(this, false);
1437
1439 }
1440 // can be delete
1442 return;
1443 }
1446 // 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)
1447 if (GetGoState() != GO_STATE_READY)
1449 break;
1451 // Initialize a new max fish count on respawn
1452 m_goValue.FishingHole.MaxOpens = urand(GetGOInfo()->fishingHole.minRestock, std::max(GetGOInfo()->fishingHole.minRestock, GetGOInfo()->fishingHole.maxRestock));
1453 break;
1454 default:
1455 break;
1456 }
1457
1458 // Despawn timer
1459 if (!m_spawnedByDefault)
1460 {
1461 // Can be despawned or destroyed
1463 return;
1464 }
1465
1466 // Call AI Reset (required for example in SmartAI to clear one time events)
1467 if (AI())
1468 AI()->Reset();
1469
1470 // Respawn timer
1472 if (poolid)
1473 sPoolMgr->UpdatePool<GameObject>(GetMap()->GetPoolData(), poolid, GetSpawnId());
1474 else
1475 GetMap()->AddToMap(this);
1476 }
1477 }
1478 }
1479
1480 // Set respawn timer
1483
1484 if (isSpawned())
1485 {
1486 GameObjectTemplate const* goInfo = GetGOInfo();
1487 if (goInfo->type == GAMEOBJECT_TYPE_TRAP)
1488 {
1490 break;
1491
1492 // Type 2 (bomb) does not need to be triggered by a unit and despawns after casting its spell.
1493 if (goInfo->trap.charges == 2)
1494 {
1496 break;
1497 }
1498
1499 // Type 0 despawns after being triggered, type 1 does not.
1501 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.
1502 if (!radius)
1503 break;
1504
1505 // Pointer to appropriate target if found any
1506 Unit* target = nullptr;
1507
1508 if (GetOwner() || goInfo->trap.Checkallunits)
1509 {
1510 // summoned traps: Search targets fit to trap spell data
1511 if (SpellInfo const* trapSpell = sSpellMgr->GetSpellInfo(goInfo->trap.spell, GetMap()->GetDifficultyID()))
1512 {
1513 WorldObject* worldObjectTarget = nullptr;
1516 Cell::VisitAllObjects(this, searcher, radius);
1517 target = Object::ToUnit(worldObjectTarget);
1518 }
1519 else
1520 {
1523 Cell::VisitAllObjects(this, searcher, radius);
1524 }
1525 }
1526 else
1527 {
1528 // Environmental trap: Any player
1529 Player* player = nullptr;
1530 Trinity::AnyUnitInObjectRangeCheck checker(this, radius);
1531 Trinity::PlayerSearcher searcher(this, player, checker);
1532 Cell::VisitWorldObjects(this, searcher, radius);
1533 target = player;
1534 }
1535
1536 if (target)
1537 SetLootState(GO_ACTIVATED, target);
1538
1539 }
1540 else if (goInfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT)
1541 {
1544 if (hordeCapturing || allianceCapturing)
1545 {
1547 {
1549 if (hordeCapturing)
1550 {
1552 if (BattlegroundMap* map = GetMap()->ToBattlegroundMap())
1553 {
1554 if (Battleground* bg = map->GetBG())
1555 {
1556 if (goInfo->capturePoint.CaptureEventHorde)
1558 bg->SendBroadcastText(goInfo->capturePoint.CaptureBroadcastHorde, CHAT_MSG_BG_SYSTEM_HORDE);
1559 }
1560 }
1561 }
1562 else
1563 {
1565 if (BattlegroundMap* map = GetMap()->ToBattlegroundMap())
1566 {
1567 if (Battleground* bg = map->GetBG())
1568 {
1572 }
1573 }
1574 }
1575
1578 }
1579 else
1581 }
1582 }
1583 else if (uint32 max_charges = goInfo->GetCharges())
1584 {
1585 if (m_usetimes >= max_charges)
1586 {
1587 m_usetimes = 0;
1588 SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
1589 }
1590 }
1591 }
1592
1593 break;
1594 }
1595 case GO_ACTIVATED:
1596 {
1597 switch (GetGoType())
1598 {
1603 break;
1606 {
1608
1610 m_cooldownTime = 0;
1611 }
1612 break;
1614 if (m_loot)
1615 {
1616 m_loot->Update();
1617
1618 // Non-consumable chest was partially looted and restock time passed, restock all loot now
1619 if (!GetGOInfo()->IsDespawnAtAction() && m_restockTime && GameTime::GetGameTime() >= m_restockTime)
1620 {
1621 m_restockTime = 0;
1623 ClearLoot();
1625 }
1626 }
1627
1628 for (auto&& [playerOwner, loot] : m_personalLoot)
1629 loot->Update();
1630 break;
1632 {
1633 GameObjectTemplate const* goInfo = GetGOInfo();
1634 if (goInfo->trap.charges == 2 && goInfo->trap.spell)
1635 {
1637 CastSpell(nullptr, goInfo->trap.spell);
1639 }
1640 else if (Unit* target = ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID))
1641 {
1642 // Some traps do not have a spell but should be triggered
1643 CastSpellExtraArgs args;
1645 if (goInfo->trap.spell)
1646 CastSpell(target, goInfo->trap.spell, args);
1647
1648 // Template value or 4 seconds
1650
1651 if (goInfo->trap.charges == 1)
1653 else if (!goInfo->trap.charges)
1655 }
1656 break;
1657 }
1658 default:
1659 break;
1660 }
1661 break;
1662 }
1664 {
1665 // If nearby linked trap exists, despawn it
1666 if (GameObject* linkedTrap = GetLinkedTrap())
1667 linkedTrap->DespawnOrUnsummon();
1668
1669 //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
1671 {
1672 uint32 spellId = GetGOInfo()->goober.spell;
1673
1674 if (spellId)
1675 {
1676 for (GuidSet::const_iterator it = m_unique_users.begin(); it != m_unique_users.end(); ++it)
1677 // m_unique_users can contain only player GUIDs
1678 if (Player* owner = ObjectAccessor::GetPlayer(*this, *it))
1679 owner->CastSpell(owner, spellId, false);
1680
1681 m_unique_users.clear();
1682 m_usetimes = 0;
1683 }
1684
1686
1687 //any return here in case battleground traps
1688 if (GameObjectOverride const* goOverride = GetGameObjectOverride())
1689 if (goOverride->Flags & GO_FLAG_NODESPAWN)
1690 return;
1691 }
1692
1693 ClearLoot();
1694
1695 // Do not delete chests or goobers that are not consumed on loot, while still allowing them to despawn when they expire if summoned
1696 bool isSummonedAndExpired = (GetOwner() || GetSpellId()) && m_respawnTime == 0;
1697 if ((GetGoType() == GAMEOBJECT_TYPE_CHEST || GetGoType() == GAMEOBJECT_TYPE_GOOBER) && !GetGOInfo()->IsDespawnAtAction() && !isSummonedAndExpired)
1698 {
1700 {
1701 // Start restock timer when the chest is fully looted
1705 }
1706 else
1709 return;
1710 }
1711 else if (!GetOwnerGUID().IsEmpty() || GetSpellId())
1712 {
1713 SetRespawnTime(0);
1714
1716 {
1717 if (GameObject* go = GetMap()->GetGameObject(GetOwnerGUID()))
1718 go->HandleCustomTypeCommand(GameObjectType::SetNewFlagState(FlagState::InBase, nullptr));
1719 }
1720
1721 Delete();
1722 return;
1723 }
1724
1726
1727 //burning flags in some battlegrounds, if you find better condition, just add it
1728 if (GetGOInfo()->IsDespawnAtAction() || GetGoAnimProgress() > 0)
1729 {
1731 //reset flags
1732 if (GameObjectOverride const* goOverride = GetGameObjectOverride())
1733 ReplaceAllFlags(GameObjectFlags(goOverride->Flags));
1734 }
1735
1736 if (!m_respawnDelayTime)
1737 return;
1738
1739 if (!m_spawnedByDefault)
1740 {
1741 m_respawnTime = 0;
1742
1743 if (m_spawnId)
1745 else
1746 Delete();
1747
1748 return;
1749 }
1750
1751 uint32 respawnDelay = m_respawnDelayTime;
1752 if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
1753 GetMap()->ApplyDynamicModeRespawnScaling(this, this->m_spawnId, respawnDelay, scalingMode);
1754 m_respawnTime = GameTime::GetGameTime() + respawnDelay;
1755
1756 // if option not set then object will be saved at grid unload
1757 // Otherwise just save respawn time to map object memory
1759
1762 else
1764
1765 break;
1766 }
1767 }
1768}
1769
1771{
1772 if (m_spawnId)
1773 {
1774 if (GameObjectOverride const* goOverride = sObjectMgr->GetGameObjectOverride(m_spawnId))
1775 return goOverride;
1776 }
1777
1778 return m_goTemplateAddon;
1779}
1780
1782{
1783 // Do not refresh despawned GO from spellcast (GO's from spellcast are destroyed after despawn)
1785 return;
1786
1787 if (isSpawned())
1788 GetMap()->AddToMap(this);
1789}
1790
1792{
1793 AddUse();
1794 m_unique_users.insert(player->GetGUID());
1795}
1796
1798{
1799 if (delay > 0ms)
1800 {
1801 if (!m_despawnDelay || m_despawnDelay > delay.count())
1802 {
1803 m_despawnDelay = delay.count();
1804 m_despawnRespawnTime = forceRespawnTime;
1805 }
1806 }
1807 else
1808 {
1809 if (m_goData)
1810 {
1811 uint32 const respawnDelay = (forceRespawnTime > 0s) ? forceRespawnTime.count() : m_goData->spawntimesecs;
1812 SaveRespawnTime(respawnDelay);
1813 }
1814 Delete();
1815 }
1816}
1817
1819{
1820 PerPlayerState& perPlayerState = GetOrCreatePerPlayerStates()[seer->GetGUID()];
1821 perPlayerState.ValidUntil = GameTime::GetSystemTime() + respawnTime;
1822 perPlayerState.Despawned = true;
1823 seer->UpdateVisibilityOf(this);
1824}
1825
1827{
1830
1832 {
1834 SendMessageToSet(packet.Write(), true);
1835 }
1836
1838
1841
1842 if (GameObjectOverride const* goOverride = GetGameObjectOverride())
1843 ReplaceAllFlags(GameObjectFlags(goOverride->Flags));
1844
1846 if (m_respawnCompatibilityMode && poolid)
1847 sPoolMgr->UpdatePool<GameObject>(GetMap()->GetPoolData(), poolid, GetSpawnId());
1848 else
1850}
1851
1858
1860{
1861 uint32 defaultzone = 1;
1862
1863 Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING, nullptr);
1864
1865 uint32 areaId = GetAreaId();
1866 ItemContext itemContext = ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), lootOwner);
1867 while (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId))
1868 {
1869 fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_DEFAULT, itemContext);
1870 if (!fishLoot->isLooted())
1871 break;
1872
1873 areaId = areaEntry->ParentAreaID;
1874 }
1875
1876 if (fishLoot->isLooted())
1877 fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_DEFAULT, itemContext);
1878
1879 return fishLoot;
1880}
1881
1883{
1884 uint32 defaultzone = 1;
1885
1886 Loot* fishLoot = new Loot(GetMap(), GetGUID(), LOOT_FISHING_JUNK, nullptr);
1887
1888 uint32 areaId = GetAreaId();
1889 ItemContext itemContext = ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), lootOwner);
1890 while (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId))
1891 {
1892 fishLoot->FillLoot(areaId, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH, itemContext);
1893 if (!fishLoot->isLooted())
1894 break;
1895
1896 areaId = areaEntry->ParentAreaID;
1897 }
1898
1899 if (fishLoot->isLooted())
1900 fishLoot->FillLoot(defaultzone, LootTemplates_Fishing, lootOwner, true, true, LOOT_MODE_JUNK_FISH, itemContext);
1901
1902 return fishLoot;
1903}
1904
1906{
1907 // this should only be used when the gameobject has already been loaded
1908 // preferably after adding to map, because mapid may not be valid otherwise
1909 GameObjectData const* data = sObjectMgr->GetGameObjectData(m_spawnId);
1910 if (!data)
1911 {
1912 TC_LOG_ERROR("misc", "GameObject::SaveToDB failed, cannot get gameobject data!");
1913 return;
1914 }
1915
1916 uint32 mapId = GetMapId();
1917 if (TransportBase* transport = GetTransport())
1918 if (transport->GetMapIdForSpawning() >= 0)
1919 mapId = transport->GetMapIdForSpawning();
1920
1921 SaveToDB(mapId, data->spawnDifficulties);
1922}
1923
1924void GameObject::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDifficulties)
1925{
1926 GameObjectTemplate const* goI = GetGOInfo();
1927 if (!goI)
1928 return;
1929
1930 if (!m_spawnId)
1931 m_spawnId = sObjectMgr->GenerateGameObjectSpawnId();
1932
1933 // update in loaded data (changing data only in this place)
1934 GameObjectData& data = sObjectMgr->NewOrExistGameObjectData(m_spawnId);
1935
1936 if (!data.spawnId)
1937 data.spawnId = m_spawnId;
1938 ASSERT(data.spawnId == m_spawnId);
1939 data.id = GetEntry();
1940 data.mapId = GetMapId();
1941 data.spawnPoint.Relocate(this);
1945 data.goState = GetGoState();
1946 data.spawnDifficulties = spawnDifficulties;
1947 data.artKit = GetGoArtKit();
1948 if (!data.spawnGroupData)
1949 data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
1950
1951 data.phaseId = GetDBPhase() > 0 ? GetDBPhase() : data.phaseId;
1952 data.phaseGroup = GetDBPhase() < 0 ? -GetDBPhase() : data.phaseGroup;
1953
1954 // Update in DB
1955 WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
1956
1957 uint8 index = 0;
1958
1960 stmt->setUInt64(0, m_spawnId);
1961 trans->Append(stmt);
1962
1963 stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAMEOBJECT);
1964 stmt->setUInt64(index++, m_spawnId);
1965 stmt->setUInt32(index++, GetEntry());
1966 stmt->setUInt16(index++, uint16(mapid));
1967 stmt->setString(index++, [&data]() -> std::string
1968 {
1969 std::ostringstream os;
1970 if (!data.spawnDifficulties.empty())
1971 {
1972 auto itr = data.spawnDifficulties.begin();
1973 os << int32(*itr++);
1974
1975 for (; itr != data.spawnDifficulties.end(); ++itr)
1976 os << ',' << int32(*itr);
1977 }
1978
1979 return std::move(os).str();
1980 }());
1981 stmt->setUInt8(index++, data.phaseUseFlags);
1982 stmt->setUInt32(index++, data.phaseId);
1983 stmt->setUInt32(index++, data.phaseGroup);
1984 stmt->setInt32(index++, data.terrainSwapMap);
1985 stmt->setFloat(index++, GetPositionX());
1986 stmt->setFloat(index++, GetPositionY());
1987 stmt->setFloat(index++, GetPositionZ());
1988 stmt->setFloat(index++, GetOrientation());
1989 stmt->setFloat(index++, m_localRotation.x);
1990 stmt->setFloat(index++, m_localRotation.y);
1991 stmt->setFloat(index++, m_localRotation.z);
1992 stmt->setFloat(index++, m_localRotation.w);
1993 stmt->setInt32(index++, int32(m_respawnDelayTime));
1994 stmt->setUInt8(index++, GetGoAnimProgress());
1995 stmt->setUInt8(index++, uint8(GetGoState()));
1996 stmt->setString(index++, sObjectMgr->GetScriptName(data.scriptId));
1997 if (std::string_view stringId = GetStringId(StringIdType::Spawn); !stringId.empty())
1998 stmt->setString(index++, stringId);
1999 else
2000 stmt->setNull(index++);
2001
2002 trans->Append(stmt);
2003
2004 WorldDatabase.CommitTransaction(trans);
2005}
2006
2007bool GameObject::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool)
2008{
2009 GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
2010 if (!data)
2011 {
2012 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {}) not found in table `gameobject`, can't load. ", spawnId);
2013 return false;
2014 }
2015
2016 uint32 entry = data->id;
2017 //uint32 map_id = data->mapid; // already used before call
2018
2019 uint32 animprogress = data->animprogress;
2020 GOState go_state = data->goState;
2021 uint32 artKit = data->artKit;
2022
2023 m_spawnId = spawnId;
2025 if (!Create(entry, map, data->spawnPoint, data->rotation, animprogress, go_state, artKit, !m_respawnCompatibilityMode, spawnId))
2026 return false;
2027
2030
2031 // Set StateWorldEffectsQuestObjectiveID if there is only one linked objective for this gameobject
2032 if (data && data->spawnTrackingQuestObjectives.size() == 1)
2034
2035 if (data->spawntimesecs >= 0)
2036 {
2037 m_spawnedByDefault = true;
2038
2039 if (!GetGOInfo()->GetDespawnPossibility() && !GetGOInfo()->IsDespawnAtAction())
2040 {
2043 m_respawnTime = 0;
2044 }
2045 else
2046 {
2049
2050 // ready to respawn
2052 {
2053 m_respawnTime = 0;
2055 }
2056 }
2057 }
2058 else
2059 {
2061 {
2062 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);
2064 }
2065
2066 m_spawnedByDefault = false;
2068 m_respawnTime = 0;
2069 }
2070
2071 m_goData = data;
2072
2074
2075 if (addToMap && !GetMap()->AddToMap(this))
2076 return false;
2077
2078 return true;
2079}
2080
2082{
2083 GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId);
2084 if (!data)
2085 return false;
2086
2087 CharacterDatabaseTransaction charTrans = CharacterDatabase.BeginTransaction();
2088
2089 sMapMgr->DoForAllMapsWithMapId(data->mapId,
2090 [spawnId, charTrans](Map* map) -> void
2091 {
2092 // despawn all active objects, and remove their respawns
2093 std::vector<GameObject*> toUnload;
2094 for (auto const& pair : Trinity::Containers::MapEqualRange(map->GetGameObjectBySpawnIdStore(), spawnId))
2095 toUnload.push_back(pair.second);
2096 for (GameObject* obj : toUnload)
2097 map->AddObjectToRemoveList(obj);
2098 map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, spawnId, charTrans);
2099 }
2100 );
2101
2102 // delete data from memory
2103 sObjectMgr->DeleteGameObjectData(spawnId);
2104
2105 CharacterDatabase.CommitTransaction(charTrans);
2106
2107 WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
2108
2109 // ... and the database
2111 stmt->setUInt64(0, spawnId);
2112 trans->Append(stmt);
2113
2114 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER);
2116 stmt->setUInt64(1, spawnId);
2117 trans->Append(stmt);
2118
2119 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_EVENT_GAMEOBJECT);
2120 stmt->setUInt64(0, spawnId);
2121 trans->Append(stmt);
2122
2123 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN);
2124 stmt->setUInt64(0, spawnId);
2126 trans->Append(stmt);
2127
2128 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN);
2129 stmt->setUInt64(0, spawnId);
2131 trans->Append(stmt);
2132
2133 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN_MASTER);
2134 stmt->setUInt64(0, spawnId);
2136 trans->Append(stmt);
2137
2138 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN_MASTER);
2139 stmt->setUInt64(0, spawnId);
2141 trans->Append(stmt);
2142
2143 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT_ADDON);
2144 stmt->setUInt64(0, spawnId);
2145 trans->Append(stmt);
2146
2147 WorldDatabase.CommitTransaction(trans);
2148
2149 return true;
2150}
2151
2152/*********************************************************/
2153/*** QUEST SYSTEM ***/
2154/*********************************************************/
2155bool GameObject::hasQuest(uint32 quest_id) const
2156{
2157 return sObjectMgr->GetGOQuestRelations(GetEntry()).HasQuest(quest_id);
2158}
2159
2161{
2162 return sObjectMgr->GetGOQuestInvolvedRelations(GetEntry()).HasQuest(quest_id);
2163}
2164
2166{
2167 // If something is marked as a transport, don't transmit an out of range packet for it.
2168 GameObjectTemplate const* gInfo = GetGOInfo();
2169 if (!gInfo)
2170 return false;
2171
2173}
2174
2176{
2177 GameObjectTemplate const* gInfo = GetGOInfo();
2178 if (!gInfo)
2179 return false;
2180
2182}
2183
2185{
2186 if (m_goData && (forceDelay || m_respawnTime > GameTime::GetGameTime()) && m_spawnedByDefault)
2187 {
2189 {
2190 RespawnInfo ri;
2192 ri.spawnId = m_spawnId;
2195 return;
2196 }
2197
2198 uint32 thisRespawnTime = forceDelay ? GameTime::GetGameTime() + forceDelay : m_respawnTime;
2200 }
2201}
2202
2203bool GameObject::IsNeverVisibleFor(WorldObject const* seer, bool allowServersideObjects) const
2204{
2205 if (WorldObject::IsNeverVisibleFor(seer, allowServersideObjects))
2206 return true;
2207
2208 if (GetGOInfo()->GetServerOnly() && !allowServersideObjects)
2209 return true;
2210
2211 if (!GetDisplayId() && GetGOInfo()->IsDisplayMandatory())
2212 return true;
2213
2214 if (m_goTypeImpl)
2215 return m_goTypeImpl->IsNeverVisibleFor(seer, allowServersideObjects);
2216
2217 return false;
2218}
2219
2221{
2223 return true;
2224
2226 return true;
2227
2228 if (!seer)
2229 return false;
2230
2231 // Always seen by owner and friendly units
2232 if (!GetOwnerGUID().IsEmpty())
2233 {
2234 if (seer->GetGUID() == GetOwnerGUID())
2235 return true;
2236
2237 Unit* owner = GetOwner();
2238 if (Unit const* unitSeer = seer->ToUnit())
2239 if (owner && owner->IsFriendlyTo(unitSeer))
2240 return true;
2241 }
2242
2243 return false;
2244}
2245
2247{
2249 return true;
2250
2251 // Despawned
2252 if (!isSpawned())
2253 return true;
2254
2255 if (m_perPlayerState)
2257 if (state->Despawned)
2258 return true;
2259
2260 return false;
2261}
2262
2264{
2265 if (Unit* owner = GetOwner())
2266 return owner->GetLevelForTarget(target);
2267
2269 {
2270 if (Player const* player = target->ToPlayer())
2271 if (Optional<ContentTuningLevels> userLevels = sDB2Manager.GetContentTuningData(GetGOInfo()->ContentTuningId, player->m_playerData->CtrOptions->ConditionalFlags))
2272 return uint8(std::clamp<int16>(player->GetLevel(), userLevels->MinLevel, userLevels->MaxLevel));
2273
2274 if (Unit const* targetUnit = target->ToUnit())
2275 return targetUnit->GetLevel();
2276 }
2277
2278 return 1;
2279}
2280
2282{
2283 time_t now = GameTime::GetGameTime();
2284 if (m_respawnTime > now)
2285 return m_respawnTime;
2286 else
2287 return now;
2288}
2289
2291{
2292 m_respawnTime = respawn > 0 ? GameTime::GetGameTime() + respawn : 0;
2293 m_respawnDelayTime = respawn > 0 ? respawn : 0;
2294 if (respawn && !m_spawnedByDefault)
2296
2299}
2300
2309
2311{
2312 if (GetGOInfo()->GetQuestID())
2313 return true;
2314
2315 if (GetGoType() != GAMEOBJECT_TYPE_AURA_GENERATOR && GetGOInfo()->GetConditionID1())
2316 return true;
2317
2318 if (sObjectMgr->IsGameObjectForQuests(GetEntry()))
2319 return true;
2320
2321 return false;
2322}
2323
2325{
2326 if (!MeetsInteractCondition(target))
2327 return false;
2328
2329 if (!ActivateToQuest(target))
2330 return false;
2331
2332 return true;
2333}
2334
2335bool GameObject::ActivateToQuest(Player const* target) const
2336{
2337 if (target->HasQuestForGO(GetEntry()))
2338 return true;
2339
2340 if (!sObjectMgr->IsGameObjectForQuests(GetEntry()))
2341 return true;
2342
2343 switch (GetGoType())
2344 {
2346 {
2348 return true;
2349 break;
2350 }
2352 {
2353 // Chests become inactive while not ready to be looted
2354 if (getLootState() == GO_NOT_READY)
2355 return false;
2356
2357 // scan GO chest with loot including quest items
2362 {
2363 return true;
2364 }
2365 break;
2366 }
2368 {
2369 if (target->GetQuestStatus(GetGOInfo()->generic.questID) == QUEST_STATUS_INCOMPLETE)
2370 return true;
2371 break;
2372 }
2374 {
2375 if (target->GetQuestStatus(GetGOInfo()->spellFocus.questID) == QUEST_STATUS_INCOMPLETE)
2376 return true;
2377 break;
2378 }
2380 {
2381 if (target->GetQuestStatus(GetGOInfo()->goober.questID) == QUEST_STATUS_INCOMPLETE)
2382 return true;
2383 break;
2384 }
2386 {
2388 return true;
2389 break;
2390 }
2391 default:
2392 break;
2393 }
2394
2395 return false;
2396}
2397
2399{
2400 GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(trapEntry);
2401 if (!trapInfo || trapInfo->type != GAMEOBJECT_TYPE_TRAP)
2402 return;
2403
2404 SpellInfo const* trapSpell = sSpellMgr->GetSpellInfo(trapInfo->trap.spell, GetMap()->GetDifficultyID());
2405 if (!trapSpell) // checked at load already
2406 return;
2407
2408 if (GameObject* trapGO = GetLinkedTrap())
2409 trapGO->CastSpell(target, trapSpell->Id);
2410}
2411
2413{
2414 GameObject* ok = nullptr;
2415 Trinity::NearestGameObjectFishingHole u_check(*this, range);
2417 Cell::VisitGridObjects(this, checker, range);
2418 return ok;
2419}
2420
2432
2433void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */, Unit* user /*=nullptr*/)
2434{
2435 if (m_lootState != GO_READY)
2436 return;
2437
2438 if (!time_to_restore)
2439 time_to_restore = GetGOInfo()->GetAutoCloseTime();
2440
2441 SwitchDoorOrButton(true, alternative);
2443
2444 m_cooldownTime = time_to_restore ? (GameTime::GetGameTimeMS() + time_to_restore) : 0;
2445}
2446
2447void GameObject::ActivateObject(GameObjectActions action, int32 param, WorldObject* spellCaster /*= nullptr*/, uint32 spellId /*= 0*/, int32 effectIndex /*= -1*/)
2448{
2449 Unit* unitCaster = spellCaster ? spellCaster->ToUnit() : nullptr;
2450
2451 switch (action)
2452 {
2454 TC_LOG_FATAL("spell", "Spell {} has action type NONE in effect {}", spellId, effectIndex);
2455 break;
2461 break;
2462 case GameObjectActions::Disturb: // What's the difference with Open?
2463 if (unitCaster)
2464 Use(unitCaster);
2465 break;
2468 break;
2471 break;
2473 if (unitCaster)
2474 Use(unitCaster);
2475 break;
2477 if (unitCaster)
2478 UseDoorOrButton(0, false, unitCaster);
2480 break;
2483 break;
2485 // No use cases, implementation unknown
2486 break;
2488 if (unitCaster)
2489 UseDoorOrButton(0, true, unitCaster);
2490 break;
2493 break;
2495 // No use cases, implementation unknown
2496 break;
2499 break;
2502 break;
2505 break;
2509 break;
2515 {
2516 GameObjectTemplateAddon const* templateAddon = GetTemplateAddon();
2517
2518 uint32 artKitIndex = action != GameObjectActions::UseArtKit4 ? uint32(action) - uint32(GameObjectActions::UseArtKit0) : 4;
2519
2520 uint32 artKitValue = 0;
2521 if (templateAddon != nullptr)
2522 artKitValue = templateAddon->ArtKits[artKitIndex];
2523
2524 if (artKitValue == 0)
2525 TC_LOG_ERROR("sql.sql", "GameObject {} hit by spell {} needs `artkit{}` in `gameobject_template_addon`", GetEntry(), spellId, artKitIndex);
2526 else
2527 SetGoArtKit(artKitValue);
2528
2529 break;
2530 }
2543 SetGoState(GOState(action));
2544 else
2545 TC_LOG_ERROR("spell", "Spell {} targeted non-transport gameobject for transport only action \"Go to Floor\" {} in effect {}", spellId, int32(action), effectIndex);
2546 break;
2548 SetAnimKitId(param, false);
2549 break;
2551 if (unitCaster)
2552 UseDoorOrButton(0, false, unitCaster);
2553 SetAnimKitId(param, false);
2554 break;
2557 SetAnimKitId(param, false);
2558 break;
2560 SetAnimKitId(param, true);
2561 break;
2563 SetAnimKitId(0, false);
2564 break;
2566 if (unitCaster)
2567 UseDoorOrButton(0, false, unitCaster);
2568 SetAnimKitId(0, false);
2569 break;
2572 SetAnimKitId(0, false);
2573 break;
2575 SetSpellVisualId(param, Object::GetGUID(spellCaster));
2576 break;
2579 break;
2580 default:
2581 TC_LOG_ERROR("spell", "Spell {} has unhandled action {} in effect {}", spellId, int32(action), effectIndex);
2582 break;
2583 }
2584
2585 // Apply side effects of type
2586 if (m_goTypeImpl)
2587 m_goTypeImpl->ActivateObject(action, param, spellCaster, spellId, effectIndex);
2588}
2589
2591{
2593 GameObjectData* data = const_cast<GameObjectData*>(sObjectMgr->GetGameObjectData(m_spawnId));
2594 if (data)
2595 data->artKit = kit;
2596}
2597
2599{
2600 GameObjectData const* data = nullptr;
2601 if (go)
2602 {
2603 go->SetGoArtKit(artkit);
2604 data = go->GetGameObjectData();
2605 }
2606 else if (lowguid)
2607 data = sObjectMgr->GetGameObjectData(lowguid);
2608
2609 if (data)
2610 const_cast<GameObjectData*>(data)->artKit = artkit;
2611}
2612
2613void GameObject::SwitchDoorOrButton(bool activate, bool alternative /* = false */)
2614{
2615 if (activate)
2617 else
2619
2620 if (GetGoState() == GO_STATE_READY) //if closed -> open
2622 else //if open -> close
2624}
2625
2626void GameObject::Use(Unit* user, bool ignoreCastInProgress /*= false*/)
2627{
2628 // by default spell caster is user
2629 Unit* spellCaster = user;
2630 uint32 spellId = 0;
2631 CastSpellExtraArgs spellArgs;
2632 if (ignoreCastInProgress)
2634 bool addUse = false;
2635
2636 if (Player* playerUser = user->ToPlayer())
2637 {
2638 if (m_goInfo->GetNoDamageImmune() && playerUser->HasUnitFlag(UNIT_FLAG_IMMUNE))
2639 return;
2640
2641 if (!m_goInfo->IsUsableMounted())
2642 playerUser->RemoveAurasByType(SPELL_AURA_MOUNTED);
2643
2644 playerUser->PlayerTalkClass->ClearMenus();
2645 if (AI()->OnGossipHello(playerUser))
2646 return;
2647 }
2648
2649 // If cooldown data present in template
2650 if (uint32 cooldown = GetGOInfo()->GetCooldown())
2651 {
2653 return;
2654
2656 }
2657
2658 switch (GetGoType())
2659 {
2660 case GAMEOBJECT_TYPE_DOOR: //0
2661 case GAMEOBJECT_TYPE_BUTTON: //1
2662 //doors/buttons never really despawn, only reset to default state/flags
2663 UseDoorOrButton(0, false, user);
2664 return;
2666 {
2667 if (user->GetTypeId() != TYPEID_PLAYER)
2668 return;
2669
2670 Player* player = user->ToPlayer();
2671
2672 player->PrepareGossipMenu(this, GetGOInfo()->questgiver.gossipID, true);
2673 player->SendPreparedGossip(this);
2674 return;
2675 }
2676 case GAMEOBJECT_TYPE_CHEST: //3
2677 {
2678 Player* player = user->ToPlayer();
2679 if (!player)
2680 return;
2681
2682 GameObjectTemplate const* info = GetGOInfo();
2683 if (!m_loot && info->GetLootId())
2684 {
2685 if (info->GetLootId())
2686 {
2687 Group const* group = player->GetGroup();
2688 bool groupRules = group && info->IsUsingGroupLootRules();
2689
2690 Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_CHEST, groupRules ? group : nullptr);
2691 m_loot.reset(loot);
2692
2694 loot->FillLoot(info->GetLootId(), LootTemplates_Gameobject, player, !groupRules, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
2695
2696 if (GetLootMode() > 0)
2697 if (GameObjectTemplateAddon const* addon = GetTemplateAddon())
2698 loot->generateMoneyLoot(addon->Mingold, addon->Maxgold);
2699 }
2700
2701 if (info->chest.triggeredEvent)
2702 GameEvents::Trigger(info->chest.triggeredEvent, player, this);
2703
2704 // triggering linked GO
2705 if (uint32 trapEntry = info->chest.linkedTrap)
2706 TriggeringLinkedGameObject(trapEntry, player);
2707 }
2708 else if (!m_personalLoot.count(player->GetGUID()))
2709 {
2710 if (info->chest.chestPersonalLoot)
2711 {
2713 if (info->chest.DungeonEncounter)
2714 {
2715 std::vector<Player*> tappers;
2716 for (ObjectGuid tapperGuid : GetTapList())
2717 if (Player* tapper = ObjectAccessor::GetPlayer(*this, tapperGuid))
2718 tappers.push_back(tapper);
2719
2720 if (tappers.empty())
2721 tappers.push_back(player);
2722
2724 LootTemplates_Gameobject, LOOT_CHEST, this, addon ? addon->Mingold : 0, addon ? addon->Maxgold : 0,
2725 GetLootMode(), GetMap()->GetMapDifficulty(), tappers);
2726 }
2727 else
2728 {
2729 Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_CHEST, nullptr);
2730 m_personalLoot[player->GetGUID()].reset(loot);
2731
2733 loot->FillLoot(info->chest.chestPersonalLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
2734
2735 if (GetLootMode() > 0 && addon)
2736 loot->generateMoneyLoot(addon->Mingold, addon->Maxgold);
2737 }
2738 }
2739 }
2740
2741 if (!m_unique_users.count(player->GetGUID()) && !info->GetLootId())
2742 {
2743 if (info->chest.chestPushLoot)
2744 {
2745 Loot pushLoot(GetMap(), GetGUID(), LOOT_CHEST, nullptr);
2746 pushLoot.FillLoot(info->chest.chestPushLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
2747 pushLoot.AutoStore(player, NULL_BAG, NULL_SLOT);
2748 }
2749
2750 if (info->chest.triggeredEvent)
2751 GameEvents::Trigger(info->chest.triggeredEvent, player, this);
2752
2753 // triggering linked GO
2754 if (uint32 trapEntry = info->chest.linkedTrap)
2755 TriggeringLinkedGameObject(trapEntry, player);
2756
2757 // Cast spell before sending loot
2758 if (spellCaster && info->chest.spell)
2759 spellCaster->CastSpell(nullptr, info->chest.spell, spellArgs);
2760
2761 AddUniqueUse(player);
2762 }
2763
2764 if (getLootState() != GO_ACTIVATED)
2765 SetLootState(GO_ACTIVATED, player);
2766
2767 // Send loot
2768 if (Loot* loot = GetLootForPlayer(player))
2769 player->SendLoot(*loot);
2770 break;
2771 }
2772 case GAMEOBJECT_TYPE_TRAP: //6
2773 {
2774 GameObjectTemplate const* goInfo = GetGOInfo();
2775 if (goInfo->trap.spell)
2776 CastSpell(user, goInfo->trap.spell);
2777
2778 m_cooldownTime = GameTime::GetGameTimeMS() + (goInfo->trap.cooldown ? goInfo->trap.cooldown : uint32(4)) * IN_MILLISECONDS; // template or 4 seconds
2779
2780 if (goInfo->trap.charges == 1) // Deactivate after trigger
2782
2783 return;
2784 }
2785 //Sitting: Wooden bench, chairs enzz
2786 case GAMEOBJECT_TYPE_CHAIR: //7
2787 {
2788 GameObjectTemplate const* info = GetGOInfo();
2789 if (ChairListSlots.empty()) // this is called once at first chair use to make list of available slots
2790 {
2791 if (info->chair.chairslots > 0) // sometimes chairs in DB have error in fields and we dont know number of slots
2792 for (uint32 i = 0; i < info->chair.chairslots; ++i)
2793 ChairListSlots[i].Clear(); // Last user of current slot set to 0 (none sit here yet)
2794 else
2795 ChairListSlots[0].Clear(); // error in DB, make one default slot
2796 }
2797
2798 // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
2799
2800 float lowestDist = DEFAULT_VISIBILITY_DISTANCE;
2801
2802 uint32 nearest_slot = 0;
2803 float x_lowest = GetPositionX();
2804 float y_lowest = GetPositionY();
2805
2806 // the object orientation + 1/2 pi
2807 // every slot will be on that straight line
2808 float orthogonalOrientation = GetOrientation() + float(M_PI) * 0.5f;
2809 // find nearest slot
2810 bool found_free_slot = false;
2811 for (auto& [slot, sittingUnit] : ChairListSlots)
2812 {
2813 // the distance between this slot and the center of the go - imagine a 1D space
2814 float relativeDistance = (info->size * slot) - (info->size * (info->chair.chairslots - 1) / 2.0f);
2815
2816 float x_i = GetPositionX() + relativeDistance * std::cos(orthogonalOrientation);
2817 float y_i = GetPositionY() + relativeDistance * std::sin(orthogonalOrientation);
2818
2819 if (!sittingUnit.IsEmpty())
2820 {
2821 if (Unit* chairUser = ObjectAccessor::GetUnit(*this, sittingUnit))
2822 {
2823 if (chairUser->IsSitState() && chairUser->GetStandState() != UNIT_STAND_STATE_SIT && chairUser->GetExactDist2d(x_i, y_i) < 0.1f)
2824 continue; // This seat is already occupied by ChairUser. NOTE: Not sure if the ChairUser->GetStandState() != UNIT_STAND_STATE_SIT check is required.
2825
2826 sittingUnit.Clear(); // This seat is unoccupied.
2827 }
2828 else
2829 sittingUnit.Clear(); // The seat may of had an occupant, but they're offline.
2830 }
2831
2832 found_free_slot = true;
2833
2834 // calculate the distance between the player and this slot
2835 float thisDistance = user->GetDistance2d(x_i, y_i);
2836
2837 if (thisDistance <= lowestDist)
2838 {
2839 nearest_slot = slot;
2840 lowestDist = thisDistance;
2841 x_lowest = x_i;
2842 y_lowest = y_i;
2843 }
2844 }
2845
2846 if (found_free_slot)
2847 {
2848 auto itr = ChairListSlots.find(nearest_slot);
2849 if (itr != ChairListSlots.end())
2850 {
2851 itr->second = user->GetGUID(); //this slot in now used by player
2852 user->NearTeleportTo(x_lowest, y_lowest, GetPositionZ(), GetOrientation());
2854 if (info->chair.triggeredEvent)
2855 GameEvents::Trigger(info->chair.triggeredEvent, user, this);
2856 return;
2857 }
2858 }
2859
2860 return;
2861 }
2863 // triggering linked GO
2864 if (uint32 trapEntry = GetGOInfo()->spellFocus.linkedTrap)
2865 TriggeringLinkedGameObject(trapEntry, user);
2866 break;
2867 //big gun, its a spell/aura
2868 case GAMEOBJECT_TYPE_GOOBER: //10
2869 {
2870 GameObjectTemplate const* info = GetGOInfo();
2871 Player* player = user->ToPlayer();
2872
2873 if (player)
2874 {
2875 if (info->goober.pageID) // show page...
2876 {
2878 data.GameObjectGUID = GetGUID();
2879 player->SendDirectMessage(data.Write());
2880 }
2881 else if (info->goober.gossipID)
2882 {
2883 player->PrepareGossipMenu(this, info->goober.gossipID);
2884 player->SendPreparedGossip(this);
2885 }
2886
2887 if (info->goober.eventID)
2888 {
2889 TC_LOG_DEBUG("maps.script", "Goober ScriptStart id {} for GO entry {} (GUID {}).", info->goober.eventID, GetEntry(), GetSpawnId());
2890 GameEvents::Trigger(info->goober.eventID, player, this);
2891 }
2892
2893 // possible quest objective for active quests
2894 if (info->goober.questID && sObjectMgr->GetQuestTemplate(info->goober.questID))
2895 {
2896 //Quest require to be active for GO using
2898 break;
2899 }
2900
2901 if (Group* group = player->GetGroup())
2902 {
2903 for (GroupReference const& itr : group->GetMembers())
2904 if (itr.GetSource()->IsAtGroupRewardDistance(this))
2905 itr.GetSource()->KillCreditGO(info->entry, GetGUID());
2906 }
2907 else
2908 player->KillCreditGO(info->entry, GetGUID());
2909 }
2910
2911 if (uint32 trapEntry = info->goober.linkedTrap)
2912 TriggeringLinkedGameObject(trapEntry, user);
2913
2914 if (info->goober.AllowMultiInteract && player)
2915 {
2916 if (info->IsDespawnAtAction())
2918 else
2920 }
2921 else
2922 {
2925
2926 // this appear to be ok, however others exist in addition to this that should have custom (ex: 190510, 188692, 187389)
2927 if (info->goober.customAnim)
2929 else
2931
2933 }
2934
2935 // cast this spell later if provided
2936 spellId = info->goober.spell;
2937 if (!info->goober.playerCast)
2938 spellCaster = nullptr;
2939
2940 break;
2941 }
2942 case GAMEOBJECT_TYPE_CAMERA: //13
2943 {
2944 GameObjectTemplate const* info = GetGOInfo();
2945 if (!info)
2946 return;
2947
2948 if (user->GetTypeId() != TYPEID_PLAYER)
2949 return;
2950
2951 Player* player = user->ToPlayer();
2952
2953 if (info->camera.camera)
2954 player->SendCinematicStart(info->camera.camera);
2955
2956 if (info->camera.eventID)
2957 GameEvents::Trigger(info->camera.eventID, player, this);
2958
2959 return;
2960 }
2961 //fishing bobber
2963 {
2964 Player* player = user->ToPlayer();
2965 if (!player)
2966 return;
2967
2968 if (player->GetGUID() != GetOwnerGUID())
2969 return;
2970
2971 switch (getLootState())
2972 {
2973 case GO_READY: // ready for loot
2974 {
2975 SetLootState(GO_ACTIVATED, player);
2976
2979
2980 SendUpdateToPlayer(player);
2981
2982 AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(GetAreaId());
2983 if (!areaEntry)
2984 {
2985 TC_LOG_ERROR("entities.gameobject", "Gameobject '{}' ({}) spawned in unknown area (x: {} y: {} z: {} map: {})",
2987 break;
2988 }
2989
2990 // Update the correct fishing skill according to the area's ContentTuning
2991 ContentTuningEntry const* areaContentTuning = DB2Manager::GetContentTuningForArea(areaEntry);
2992 if (!areaContentTuning)
2993 break;
2994
2995 player->UpdateFishingSkill(areaContentTuning->ExpansionID);
2996
2997 // Send loot
2998 int32 areaFishingLevel = sObjectMgr->GetFishingBaseSkillLevel(areaEntry);
2999
3000 uint32 playerFishingSkill = player->GetProfessionSkillForExp(SKILL_FISHING, areaContentTuning->ExpansionID);
3001 int32 playerFishingLevel = player->GetSkillValue(playerFishingSkill);
3002
3003 int32 roll = irand(1, 100);
3004 int32 chance = 100;
3005 if (playerFishingLevel < areaFishingLevel)
3006 {
3007 chance = int32(pow((double)playerFishingLevel / areaFishingLevel, 2) * 100);
3008 if (chance < 1)
3009 chance = 1;
3010 }
3011
3012 TC_LOG_DEBUG("misc", "Fishing check (skill {} level: {} area skill level: {} chance {} roll: {}", playerFishingSkill, playerFishingLevel, areaFishingLevel, chance, roll);
3013
3015 GameObject* fishingPool = LookupFishingHoleAround(20.0f + CONTACT_DISTANCE);
3016
3017 // If fishing skill is high enough, or if fishing on a pool, send correct loot.
3018 // Fishing pools have no skill requirement as of patch 3.3.0 (undocumented change).
3019 if (chance >= roll || fishingPool)
3020 {
3022 // prevent removing GO at spell cancel
3024 SetOwnerGUID(player->GetGUID());
3025 SetSpellId(0); // prevent removing unintended auras at Unit::RemoveGameObject
3026
3027 if (fishingPool)
3028 {
3029 fishingPool->Use(player, ignoreCastInProgress);
3031 }
3032 else
3033 {
3034 m_loot.reset(GetFishLoot(player));
3035 player->SendLoot(*m_loot);
3036 }
3037 }
3038 else // If fishing skill is too low, send junk loot.
3039 {
3040 m_loot.reset(GetFishLootJunk(player));
3041 player->SendLoot(*m_loot);
3042 }
3043 break;
3044 }
3045 case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update
3046 break;
3047 default:
3048 {
3051 break;
3052 }
3053 }
3054
3056 return;
3057 }
3058
3059 case GAMEOBJECT_TYPE_RITUAL: //18
3060 {
3061 if (user->GetTypeId() != TYPEID_PLAYER)
3062 return;
3063
3064 Player* player = user->ToPlayer();
3065
3066 Unit* owner = GetOwner();
3067
3068 GameObjectTemplate const* info = GetGOInfo();
3069
3070 Player* m_ritualOwner = nullptr;
3073
3074 // ritual owner is set for GO's without owner (not summoned)
3075 if (!m_ritualOwner && !owner)
3076 {
3077 m_ritualOwnerGUID = player->GetGUID();
3078 m_ritualOwner = player;
3079 }
3080
3081 if (owner)
3082 {
3083 if (owner->GetTypeId() != TYPEID_PLAYER)
3084 return;
3085
3086 // accept only use by player from same group as owner, excluding owner itself (unique use already added in spell effect)
3087 if (player == owner->ToPlayer() || (info->ritual.castersGrouped && !player->IsInSameRaidWith(owner->ToPlayer())))
3088 return;
3089
3090 // expect owner to already be channeling, so if not...
3092 return;
3093
3094 // in case summoning ritual caster is GO creator
3095 spellCaster = owner;
3096 }
3097 else
3098 {
3099 if (player != m_ritualOwner && (info->ritual.castersGrouped && !player->IsInSameRaidWith(m_ritualOwner)))
3100 return;
3101
3102 spellCaster = player;
3103 }
3104
3105 AddUniqueUse(player);
3106
3107 if (info->ritual.animSpell)
3108 {
3109 player->CastSpell(player, info->ritual.animSpell, true);
3110
3111 // for this case, summoningRitual.spellId is always triggered
3113 }
3114
3115 // full amount unique participants including original summoner
3116 if (GetUniqueUseCount() == info->ritual.casters)
3117 {
3118 if (m_ritualOwner)
3119 spellCaster = m_ritualOwner;
3120
3121 spellId = info->ritual.spell;
3122
3123 if (spellId == 62330) // GO store nonexistent spell, replace by expected
3124 {
3125 // spell have reagent and mana cost but it not expected use its
3126 // it triggered spell in fact cast at currently channeled GO
3127 spellId = 61993;
3129 }
3130
3131 // Cast casterTargetSpell at a random GO user
3132 // on the current DB there is only one gameobject that uses this (Ritual of Doom)
3133 // and its required target number is 1 (outter for loop will run once)
3134 if (info->ritual.casterTargetSpell && info->ritual.casterTargetSpell != 1) // No idea why this field is a bool in some cases
3135 for (uint32 i = 0; i < info->ritual.casterTargetSpellTargets; i++)
3136 // m_unique_users can contain only player GUIDs
3138 spellCaster->CastSpell(target, info->ritual.casterTargetSpell, true);
3139
3140 // finish owners spell
3141 if (owner)
3143
3144 // can be deleted now, if
3145 if (!info->ritual.ritualPersistent)
3147 else
3148 {
3149 // reset ritual for this GO
3151 m_unique_users.clear();
3152 m_usetimes = 0;
3153 }
3154 }
3155 else
3156 return;
3157
3158 // go to end function to spell casting
3159 break;
3160 }
3162 {
3163 GameObjectTemplate const* info = GetGOInfo();
3164 if (!info)
3165 return;
3166
3167 if (info->spellCaster.partyOnly)
3168 {
3169 ObjectGuid ownerGuid = GetOwnerGUID();
3170 if (ownerGuid.IsEmpty())
3171 return;
3172
3173 if (ownerGuid != user->GetGUID())
3174 {
3175 if (Unit* owner = ObjectAccessor::GetUnit(*this, ownerGuid))
3176 ownerGuid = owner->GetCharmerOrOwnerOrOwnGUID();
3177
3178 Player const* playerUser = user->GetCharmerOrOwnerPlayerOrPlayerItself();
3179 if (!playerUser)
3180 return;
3181
3182 Group const* group = playerUser->GetGroup();
3183 if (!group || !group->IsMember(ownerGuid))
3184 return;
3185 }
3186 }
3187
3189 spellId = info->spellCaster.spell;
3190
3191 addUse = true;
3192 break;
3193 }
3195 {
3196 GameObjectTemplate const* info = GetGOInfo();
3197
3198 if (user->GetTypeId() != TYPEID_PLAYER)
3199 return;
3200
3201 Player* player = user->ToPlayer();
3202
3203 Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetTarget());
3204
3205 // accept only use by player from same raid as caster, except caster itself
3206 if (!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameRaidWith(player))
3207 return;
3208
3209 //required lvl checks!
3210 if (Optional<ContentTuningLevels> userLevels = sDB2Manager.GetContentTuningData(info->ContentTuningId, player->m_playerData->CtrOptions->ConditionalFlags))
3211 if (player->GetLevel() < userLevels->MaxLevel)
3212 return;
3213
3214 if (Optional<ContentTuningLevels> targetLevels = sDB2Manager.GetContentTuningData(info->ContentTuningId, targetPlayer->m_playerData->CtrOptions->ConditionalFlags))
3215 if (targetPlayer->GetLevel() < targetLevels->MaxLevel)
3216 return;
3217
3218 if (info->entry == 194097)
3219 spellId = 61994; // Ritual of Summoning
3220 else
3221 spellId = 59782; // Summoning Stone Effect
3222
3223 break;
3224 }
3225
3226 case GAMEOBJECT_TYPE_FLAGSTAND: // 24
3227 {
3228 if (user->GetTypeId() != TYPEID_PLAYER)
3229 return;
3230
3231 Player* player = user->ToPlayer();
3232
3233 if (player->CanUseBattlegroundObject(this))
3234 {
3235 if (player->GetVehicle())
3236 return;
3237
3239 return;
3240
3241 if (!MeetsInteractCondition(player))
3242 return;
3243
3246 spellId = GetGOInfo()->flagStand.pickupSpell;
3247 spellCaster = nullptr;
3248 }
3249 break;
3250 }
3251
3252 case GAMEOBJECT_TYPE_FISHINGHOLE: // 25
3253 {
3254 if (user->GetTypeId() != TYPEID_PLAYER)
3255 return;
3256
3257 Player* player = user->ToPlayer();
3258
3259 Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_FISHINGHOLE, nullptr);
3260 loot->FillLoot(GetGOInfo()->GetLootId(), LootTemplates_Gameobject, player, true, false, LOOT_MODE_DEFAULT, ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
3261 m_personalLoot[player->GetGUID()].reset(loot);
3262
3263 player->SendLoot(*loot);
3265 return;
3266 }
3267
3268 case GAMEOBJECT_TYPE_FLAGDROP: // 26
3269 {
3270 if (user->GetTypeId() != TYPEID_PLAYER)
3271 return;
3272
3273 Player* player = user->ToPlayer();
3274
3275 if (player->CanUseBattlegroundObject(this))
3276 {
3277 if (player->GetVehicle())
3278 return;
3279
3282 // BG flag dropped
3283 // WS:
3284 // 179785 - Silverwing Flag
3285 // 179786 - Warsong Flag
3286 // EotS:
3287 // 184142 - Netherstorm Flag
3288 GameObjectTemplate const* info = GetGOInfo();
3289 if (info->flagDrop.eventID)
3290 GameEvents::Trigger(info->flagDrop.eventID, player, this);
3291 //this cause to call return, all flags must be deleted here!!
3292 spellId = 0;
3293 Delete();
3294 }
3295 break;
3296 }
3298 {
3299 GameObjectTemplate const* info = GetGOInfo();
3300 if (!info)
3301 return;
3302
3303 if (user->GetTypeId() != TYPEID_PLAYER)
3304 return;
3305
3306 Player* player = user->ToPlayer();
3307
3308 WorldPackets::Misc::EnableBarberShop enableBarberShop;
3310 player->SendDirectMessage(enableBarberShop.Write());
3311
3312 // fallback, will always work
3314
3316 return;
3317 }
3319 {
3320 GameObjectTemplate const* info = GetGOInfo();
3321 if (!info)
3322 return;
3323
3324 Player* player = user->ToPlayer();
3325 if (!player)
3326 return;
3327
3328 if (!player->CanUseBattlegroundObject(this))
3329 return;
3330
3331 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(m_goTypeImpl.get());
3332 if (!newFlag)
3333 return;
3334
3335 if (newFlag->GetState() != FlagState::InBase)
3336 return;
3337
3338 spellId = info->newflag.pickupSpell;
3339 spellCaster = nullptr;
3340 break;
3341 }
3343 {
3344 GameObjectTemplate const* info = GetGOInfo();
3345 if (!info)
3346 return;
3347
3348 if (user->GetTypeId() != TYPEID_PLAYER)
3349 return;
3350
3351 if (!user->IsAlive())
3352 return;
3353
3354 if (GameObject* owner = GetMap()->GetGameObject(GetOwnerGUID()))
3355 {
3356 if (owner->GetGoType() == GAMEOBJECT_TYPE_NEW_FLAG)
3357 {
3358 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(owner->m_goTypeImpl.get());
3359 if (!newFlag)
3360 return;
3361
3362 if (newFlag->GetState() != FlagState::Dropped)
3363 return;
3364
3365 // friendly with enemy flag means you're taking it
3366 bool defenderInteract = !owner->IsFriendlyTo(user);
3367 if (defenderInteract && owner->GetGOInfo()->newflag.ReturnonDefenderInteract)
3368 {
3369 Delete();
3370 owner->HandleCustomTypeCommand(GameObjectType::SetNewFlagState(FlagState::InBase, user->ToPlayer()));
3371 return;
3372 }
3373 else
3374 {
3375 // we let the owner cast the spell for now
3376 // so that caster guid is set correctly
3377 SpellCastResult result = owner->CastSpell(user, owner->GetGOInfo()->newflag.pickupSpell, CastSpellExtraArgs(TRIGGERED_FULL_MASK));
3378 if (result == SPELL_CAST_OK)
3379 {
3380 Delete();
3381 owner->HandleCustomTypeCommand(GameObjectType::SetNewFlagState(FlagState::Taken, user->ToPlayer()));
3382 return;
3383 }
3384 }
3385 }
3386 }
3387
3388 Delete();
3389 return;
3390 }
3392 {
3393 Player* player = user->ToPlayer();
3394 if (!player)
3395 return;
3396
3397 AssaultCapturePoint(player);
3398 return;
3399 }
3401 {
3402 GameObjectTemplate const* info = GetGOInfo();
3403 if (!info)
3404 return;
3405
3406 if (user->GetTypeId() != TYPEID_PLAYER)
3407 return;
3408
3409 Player* player = user->ToPlayer();
3410
3411 if (!MeetsInteractCondition(player))
3412 return;
3413
3414 switch (info->itemForge.ForgeType)
3415 {
3416 case 0: // Artifact Forge
3417 case 1: // Relic Forge
3418 {
3420 Item const* item = artifactAura ? player->GetItemByGuid(artifactAura->GetCastItemGUID()) : nullptr;
3421 if (!item)
3422 {
3424 return;
3425 }
3426
3428 openArtifactForge.ArtifactGUID = item->GetGUID();
3429 openArtifactForge.ForgeGUID = GetGUID();
3430 player->SendDirectMessage(openArtifactForge.Write());
3431 break;
3432 }
3433 case 2: // Heart Forge
3434 {
3436 if (!item)
3437 return;
3438
3440 openHeartForge.ObjectGUID = GetGUID();
3442 player->SendDirectMessage(openHeartForge.Write());
3443 break;
3444 }
3445 default:
3446 break;
3447 }
3448 return;
3449 }
3451 {
3452 Player* player = user->ToPlayer();
3453 if (!player)
3454 return;
3455
3457 gameObjectUILink.ObjectGUID = GetGUID();
3458 switch (GetGOInfo()->UILink.UILinkType)
3459 {
3460 case 0:
3462 break;
3463 case 1:
3465 break;
3466 case 2:
3468 break;
3469 case 3:
3471 break;
3472 default:
3473 break;
3474 }
3475 player->SendDirectMessage(gameObjectUILink.Write());
3476 return;
3477 }
3479 {
3480 Player* player = user->ToPlayer();
3481 if (!player)
3482 return;
3483
3484 GameObjectTemplate const* info = GetGOInfo();
3485 if (!m_personalLoot.count(player->GetGUID()))
3486 {
3487 if (info->gatheringNode.chestLoot)
3488 {
3489 Loot* loot = new Loot(GetMap(), GetGUID(), LOOT_CHEST, nullptr);
3490 m_personalLoot[player->GetGUID()].reset(loot);
3491
3492 loot->FillLoot(info->gatheringNode.chestLoot, LootTemplates_Gameobject, player, true, false, GetLootMode(), ItemBonusMgr::GetContextForPlayer(GetMap()->GetMapDifficulty(), player));
3493 }
3494
3495 if (info->gatheringNode.triggeredEvent)
3497
3498 // triggering linked GO
3499 if (uint32 trapEntry = info->gatheringNode.linkedTrap)
3500 TriggeringLinkedGameObject(trapEntry, player);
3501
3502 if (info->gatheringNode.xpDifficulty && info->gatheringNode.xpDifficulty < 10)
3503 if (QuestXPEntry const* questXp = sQuestXPStore.LookupEntry(player->GetLevel()))
3504 if (uint32 xp = Quest::RoundXPValue(questXp->Difficulty[info->gatheringNode.xpDifficulty]))
3505 player->GiveXP(xp, nullptr);
3506
3507 spellId = info->gatheringNode.spell;
3508 }
3509
3510 if (m_personalLoot.size() >= info->gatheringNode.MaxNumberofLoots)
3511 {
3514 }
3515
3516 if (getLootState() != GO_ACTIVATED)
3517 {
3518 SetLootState(GO_ACTIVATED, player);
3521 }
3522
3523 // Send loot
3524 if (Loot* loot = GetLootForPlayer(player))
3525 player->SendLoot(*loot);
3526 break;
3527 }
3528 default:
3530 TC_LOG_ERROR("misc", "GameObject::Use(): unit ({}, name: {}) tries to use object ({}, name: {}) of unknown type ({})",
3531 user->GetGUID().ToString(), user->GetName(), GetGUID().ToString(), GetGOInfo()->name, GetGoType());
3532 break;
3533 }
3534
3535 if (m_vignette)
3536 {
3537 if (Player* player = user->ToPlayer())
3538 {
3539 if (Quest const* reward = sObjectMgr->GetQuestTemplate(m_vignette->Data->RewardQuestID))
3540 if (!player->GetQuestRewardStatus(m_vignette->Data->RewardQuestID))
3541 player->RewardQuest(reward, LootItemType::Item, 0, this, false);
3542
3543 if (m_vignette->Data->VisibleTrackingQuestID)
3544 player->SetRewardedQuest(m_vignette->Data->VisibleTrackingQuestID);
3545 }
3546
3547 // only unregister it from visibility (need to keep vignette for other gameobject users in case its usable by multiple players
3548 // to flag their quest completion
3549 if (GetGOInfo()->ClearObjectVignetteonOpening())
3551 }
3552
3553 if (!spellId)
3554 return;
3555
3556 if (!sSpellMgr->GetSpellInfo(spellId, GetMap()->GetDifficultyID()))
3557 {
3558 if (user->GetTypeId() != TYPEID_PLAYER || !sOutdoorPvPMgr->HandleCustomSpell(user->ToPlayer(), spellId, this))
3559 TC_LOG_ERROR("misc", "WORLD: unknown spell id {} at use action for gameobject (Entry: {} GoType: {})", spellId, GetEntry(), GetGoType());
3560 else
3561 TC_LOG_DEBUG("outdoorpvp", "WORLD: {} non-dbc spell was handled by OutdoorPvP", spellId);
3562 return;
3563 }
3564
3565 if (Player* player = user->ToPlayer())
3566 sOutdoorPvPMgr->HandleCustomSpell(player, spellId, this);
3567
3568 SpellCastResult castResult;
3569 if (spellCaster)
3570 castResult = spellCaster->CastSpell(user, spellId, spellArgs);
3571 else
3572 castResult = CastSpell(user, spellId, spellArgs);
3573
3574 if (castResult == SPELL_FAILED_SUCCESS)
3575 {
3576 if (addUse)
3577 AddUse();
3578
3579 switch (GetGoType())
3580 {
3583 break;
3586 if (ZoneScript* zonescript = GetZoneScript())
3587 zonescript->OnFlagTaken(this, Object::ToPlayer(user));
3588
3589 Delete();
3590 break;
3591 default:
3592 break;
3593 }
3594 }
3595}
3596
3598{
3600 customAnim.ObjectGUID = GetGUID();
3601 customAnim.CustomAnim = anim;
3602 SendMessageToSet(customAnim.Write(), true);
3603}
3604
3605bool GameObject::IsInRange(float x, float y, float z, float radius) const
3606{
3608 if (!info)
3609 return IsWithinDist3d(x, y, z, radius);
3610
3611 float sinA = std::sin(GetOrientation());
3612 float cosA = std::cos(GetOrientation());
3613 float dx = x - GetPositionX();
3614 float dy = y - GetPositionY();
3615 float dz = z - GetPositionZ();
3616 float dist = std::sqrt(dx*dx + dy*dy);
3619 if (G3D::fuzzyEq(dist, 0.0f))
3620 return true;
3621
3622 float sinB = dx / dist;
3623 float cosB = dy / dist;
3624 dx = dist * (cosA * cosB + sinA * sinB);
3625 dy = dist * (cosA * sinB - sinA * cosB);
3626 return dx < info->GeoBoxMax.X + radius && dx > info->GeoBoxMin.X - radius
3627 && dy < info->GeoBoxMax.Y + radius && dy > info->GeoBoxMin.Y - radius
3628 && dz < info->GeoBoxMax.Z + radius && dz > info->GeoBoxMin.Z - radius;
3629}
3630
3632{
3634 if (uint32 scriptId = gameObjectData->scriptId)
3635 return scriptId;
3636
3637 return GetGOInfo()->ScriptId;
3638}
3639
3641{
3642 // copy references to stringIds from template and spawn
3643 m_stringIds = parent->m_stringIds;
3644
3645 // then copy script stringId, not just its reference
3646 SetScriptStringId(std::string(parent->GetStringId(StringIdType::Script)));
3647}
3648
3649bool GameObject::HasStringId(std::string_view id) const
3650{
3651 return std::ranges::any_of(m_stringIds, [id](std::string const* stringId) { return stringId && *stringId == id; });
3652}
3653
3655{
3656 if (!id.empty())
3657 {
3658 m_scriptStringId.emplace(std::move(id));
3660 }
3661 else
3662 {
3663 m_scriptStringId.reset();
3665 }
3666}
3667
3669{
3670 if (!player)
3671 return nullptr;
3672
3673 if (GameObjectData const* data = GetGameObjectData())
3674 {
3675 if (data->spawnTrackingData && !data->spawnTrackingQuestObjectives.empty())
3676 {
3677 SpawnTrackingState state = player->GetSpawnTrackingStateByObjectives(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectives);
3678 return &data->spawnTrackingStates[AsUnderlyingType(state)];
3679 }
3680 }
3681
3682 return nullptr;
3683}
3684
3685// overwrite WorldObject function for proper name localization
3687{
3688 if (locale != DEFAULT_LOCALE)
3689 if (GameObjectLocale const* cl = sObjectMgr->GetGameObjectLocale(GetEntry()))
3690 if (cl->Name.size() > locale && !cl->Name[locale].empty())
3691 return cl->Name[locale];
3692
3693 return GetName();
3694}
3695
3696bool GameObject::HasLabel(int32 gameobjectLabel) const
3697{
3698 return advstd::ranges::contains(GetLabels(), gameobjectLabel);
3699}
3700
3701std::span<int32 const> GameObject::GetLabels() const
3702{
3703 return sDB2Manager.GetGameObjectLabels(GetEntry());
3704}
3705
3707{
3708 static const int32 PACK_YZ = 1 << 20;
3709 static const int32 PACK_X = PACK_YZ << 1;
3710
3711 static const int32 PACK_YZ_MASK = (PACK_YZ << 1) - 1;
3712 static const int32 PACK_X_MASK = (PACK_X << 1) - 1;
3713
3714 int8 w_sign = (m_localRotation.w >= 0.f ? 1 : -1);
3715 int64 x = int32(m_localRotation.x * PACK_X) * w_sign & PACK_X_MASK;
3716 int64 y = int32(m_localRotation.y * PACK_YZ) * w_sign & PACK_YZ_MASK;
3717 int64 z = int32(m_localRotation.z * PACK_YZ) * w_sign & PACK_YZ_MASK;
3718 m_packedRotation = z | (y << 21) | (x << 42);
3719}
3720
3721void GameObject::SetLocalRotation(float qx, float qy, float qz, float qw)
3722{
3723 G3D::Quat rotation(qx, qy, qz, qw);
3724 rotation.unitize();
3725 m_localRotation.x = rotation.x;
3726 m_localRotation.y = rotation.y;
3727 m_localRotation.z = rotation.z;
3728 m_localRotation.w = rotation.w;
3730}
3731
3736
3737void GameObject::SetLocalRotationAngles(float z_rot, float y_rot, float x_rot)
3738{
3739 QuaternionData quat = QuaternionData::fromEulerAnglesZYX(z_rot, y_rot, x_rot);
3740 SetLocalRotation(quat.x, quat.y, quat.z, quat.w);
3741}
3742
3744{
3745 QuaternionData localRotation = GetLocalRotation();
3746 if (Transport* transport = dynamic_cast<Transport*>(GetTransport()))
3747 {
3748 QuaternionData worldRotation = transport->GetWorldRotation();
3749
3750 G3D::Quat worldRotationQuat(worldRotation.x, worldRotation.y, worldRotation.z, worldRotation.w);
3751 G3D::Quat localRotationQuat(localRotation.x, localRotation.y, localRotation.z, localRotation.w);
3752
3753 G3D::Quat resultRotation = localRotationQuat * worldRotationQuat;
3754
3755 localRotation = { resultRotation.x, resultRotation.y, resultRotation.z, resultRotation.w };
3756 }
3757 return localRotation;
3758}
3759
3760void GameObject::ModifyHealth(int32 change, WorldObject* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/)
3761{
3762 if (!m_goValue.Building.DestructibleHitpoint || !change)
3763 return;
3764
3765 // prevent double destructions of the same object
3766 if (change < 0 && !m_goValue.Building.Health)
3767 return;
3768
3769 if (int32(m_goValue.Building.Health) + change <= 0)
3773 else
3774 m_goValue.Building.Health += change;
3775
3776 // Set the health bar, value = 255 * healthPct;
3778
3779 // dealing damage, send packet
3780 if (Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr)
3781 {
3783 packet.Caster = attackerOrHealer->GetGUID(); // todo: this can be a GameObject
3784 packet.Target = GetGUID();
3785 packet.Damage = -change;
3786 packet.Owner = player->GetGUID();
3787 packet.SpellID = spellId;
3788 player->SendDirectMessage(packet.Write());
3789 }
3790
3791 if (change < 0 && GetGOInfo()->destructibleBuilding.DamageEvent)
3792 GameEvents::Trigger(GetGOInfo()->destructibleBuilding.DamageEvent, attackerOrHealer, this);
3793
3795
3797 newState = GO_DESTRUCTIBLE_DESTROYED;
3799 newState = GO_DESTRUCTIBLE_DAMAGED;
3801 newState = GO_DESTRUCTIBLE_INTACT;
3802
3803 if (newState == GetDestructibleState())
3804 return;
3805
3806 SetDestructibleState(newState, attackerOrHealer, false);
3807}
3808
3809void GameObject::SetDestructibleState(GameObjectDestructibleState state, WorldObject* attackerOrHealer /*= nullptr*/, bool setHealth /*= false*/)
3810{
3811 // the user calling this must know he is already operating on destructible gameobject
3813
3814 switch (state)
3815 {
3819 if (setHealth && m_goValue.Building.DestructibleHitpoint)
3820 {
3822 SetGoAnimProgress(255);
3823 }
3824 break;
3826 {
3827 if (GetGOInfo()->destructibleBuilding.DamagedEvent && attackerOrHealer)
3828 GameEvents::Trigger(GetGOInfo()->destructibleBuilding.DamagedEvent, attackerOrHealer, this);
3829 AI()->Damaged(attackerOrHealer, m_goInfo->destructibleBuilding.DamagedEvent);
3830
3833
3834 uint32 modelId = m_goInfo->displayId;
3836 if (modelData->State1Wmo)
3837 modelId = modelData->State1Wmo;
3838 SetDisplayId(modelId);
3839
3840 if (setHealth && m_goValue.Building.DestructibleHitpoint)
3841 {
3844 // in this case current health is 0 anyway so just prevent crashing here
3845 if (!maxHealth)
3846 maxHealth = 1;
3847 SetGoAnimProgress(m_goValue.Building.Health * 255 / maxHealth);
3848 }
3849 break;
3850 }
3852 {
3853 if (GetGOInfo()->destructibleBuilding.DestroyedEvent && attackerOrHealer)
3854 GameEvents::Trigger(GetGOInfo()->destructibleBuilding.DestroyedEvent, attackerOrHealer, this);
3856
3859
3860 uint32 modelId = m_goInfo->displayId;
3862 if (modelData->State2Wmo)
3863 modelId = modelData->State2Wmo;
3864 SetDisplayId(modelId);
3865
3866 if (setHealth)
3867 {
3870 }
3871 break;
3872 }
3874 {
3875 if (GetGOInfo()->destructibleBuilding.RebuildingEvent && attackerOrHealer)
3876 GameEvents::Trigger(GetGOInfo()->destructibleBuilding.RebuildingEvent, attackerOrHealer, this);
3878
3879 uint32 modelId = m_goInfo->displayId;
3881 if (modelData->State3Wmo)
3882 modelId = modelData->State3Wmo;
3883 SetDisplayId(modelId);
3884
3885 // restores to full health
3886 if (setHealth && m_goValue.Building.DestructibleHitpoint)
3887 {
3889 SetGoAnimProgress(255);
3890 }
3891 break;
3892 }
3893 }
3894}
3895
3897{
3898 m_lootState = state;
3899 if (unit)
3900 m_lootStateUnitGUID = unit->GetGUID();
3901 else
3903
3904 AI()->OnLootStateChanged(state, unit);
3905
3906 // Start restock timer if the chest is partially looted or not looted at all
3907 if (GetGoType() == GAMEOBJECT_TYPE_CHEST && state == GO_ACTIVATED)
3908 {
3909 GameObjectTemplate const* goInfo = GetGOInfo();
3910 if (goInfo->chest.chestRestockTime > 0 && m_restockTime == 0 && m_loot && m_loot->IsChanged())
3912
3913 // If world chests were opened, despawn them after 5 minutes
3914 if (goInfo->chest.chestRestockTime == 0 && GetMap()->IsWorldMap())
3915 DespawnOrUnsummon(5min);
3916 }
3917
3918 if (GetGoType() == GAMEOBJECT_TYPE_DOOR) // only set collision for doors on SetGoState
3919 return;
3920
3921 if (m_model)
3922 {
3923 bool collision = false;
3924 // Use the current go state
3925 if ((GetGoState() != GO_STATE_READY && (state == GO_ACTIVATED || state == GO_JUST_DEACTIVATED)) || state == GO_READY)
3926 collision = !collision;
3927
3928 EnableCollision(collision);
3929 }
3930}
3931
3933{
3934 // Unlink loot objects from this GameObject before destroying to avoid accessing freed memory from Loot destructor
3935 std::unique_ptr<Loot> loot(std::move(m_loot));
3936 std::unordered_map<ObjectGuid, std::unique_ptr<Loot>> personalLoot(std::move(m_personalLoot));
3937
3938 loot.reset();
3939 personalLoot.clear();
3940 m_unique_users.clear();
3941 m_usetimes = 0;
3942}
3943
3945{
3946 if (m_loot && !m_loot->isLooted())
3947 return false;
3948
3949 for (auto const& [_, loot] : m_personalLoot)
3950 if (!loot->isLooted())
3951 return false;
3952
3953 return true;
3954}
3955
3957{
3958 switch (GetGoType())
3959 {
3961 {
3962 GameObjectTemplate const* goInfo = GetGOInfo();
3963 if (!goInfo->IsDespawnAtAction() && goInfo->chest.chestPersonalLoot)
3964 {
3966 ? Seconds(goInfo->chest.chestRestockTime)
3967 : Seconds(m_respawnDelayTime)); // not hiding this object permanently to prevent infinite growth of m_perPlayerState
3968 // while also maintaining some sort of cheater protection (not getting rid of entries on logout)
3969 }
3970 break;
3971 }
3973 {
3975
3976 UF::ObjectData::Base objMask;
3979
3980 UpdateData udata(GetMapId());
3981 BuildValuesUpdateForPlayerWithMask(&udata, objMask.GetChangesMask(), goMask.GetChangesMask(), looter, false);
3982 WorldPacket packet;
3983 udata.BuildPacket(&packet);
3984 looter->SendDirectMessage(&packet);
3985 break;
3986 }
3987 default:
3988 break;
3989 }
3990}
3991
3993{
3994 GOState oldState = GetGoState();
3996 if (AI())
3997 AI()->OnStateChanged(state);
3998
3999 if (m_goTypeImpl)
4000 m_goTypeImpl->OnStateChanged(oldState, state);
4001
4002 if (m_model && !IsTransport())
4003 {
4004 if (!IsInWorld())
4005 return;
4006
4007 // startOpen determines whether we are going to add or remove the LoS on activation
4008 bool collision = false;
4009 if (state == GO_STATE_READY)
4010 collision = !collision;
4011
4012 EnableCollision(collision);
4013 }
4014}
4015
4017{
4018 if (m_perPlayerState)
4020 if (state->State)
4021 return *state->State;
4022
4023 return GetGoState();
4024}
4025
4026void GameObject::SetGoStateFor(GOState state, Player const* viewer)
4027{
4028 PerPlayerState& perPlayerState = GetOrCreatePerPlayerStates()[viewer->GetGUID()];
4030 perPlayerState.State = state;
4031
4033 setStateLocal.ObjectGUID = GetGUID();
4034 setStateLocal.State = state;
4035 viewer->SendDirectMessage(setStateLocal.Write());
4036}
4037
4043
4045{
4046 switch (GetGoType())
4047 {
4050 {
4051 switch (GetDestructibleState())
4052 {
4054 return modelData->State0NameSet;
4056 return modelData->State1NameSet;
4058 return modelData->State2NameSet;
4060 return modelData->State3NameSet;
4061 default:
4062 break;
4063 }
4064 }
4065 break;
4069 return ((*m_gameObjectData->Flags) >> 8) & 0xF;
4070 default:
4071 break;
4072 }
4073
4074 return 0;
4075}
4076
4078{
4079 if (!m_model)
4080 return;
4081
4082 /*if (enable && !GetMap()->ContainsGameObjectModel(*m_model))
4083 GetMap()->InsertGameObjectModel(*m_model);*/
4084
4085 m_model->EnableCollision(enable);
4086}
4087
4089{
4090 if (!IsInWorld())
4091 return;
4092 bool modelCollisionEnabled;
4093 if (m_model)
4094 {
4095 modelCollisionEnabled = m_model->IsCollisionEnabled();
4096 if (GetMap()->ContainsGameObjectModel(*m_model))
4098 }
4099 else
4100 modelCollisionEnabled = GetGoType() == GAMEOBJECT_TYPE_CHEST ? getLootState() == GO_READY : (GetGoState() == GO_STATE_READY || IsTransport());
4101
4103 std::unique_ptr<GameObjectModel> oldModel = std::exchange(m_model, nullptr);
4104
4105 CreateModel();
4106 if (m_model)
4107 {
4109 if (modelCollisionEnabled)
4110 m_model->EnableCollision(modelCollisionEnabled);
4111 }
4112
4113 switch (GetGoType())
4114 {
4115 // Only update navmesh when display id changes and not on spawn
4116 // default state of destructible buildings is intended to be baked in the mesh produced by mmaps_generator
4121 if (m_model)
4123 else if (oldModel)
4125 break;
4126 default:
4127 break;
4128 }
4129}
4130
4131bool GameObject::IsLootAllowedFor(Player const* player) const
4132{
4133 if (Loot const* loot = GetLootForPlayer(player)) // check only if loot was already generated
4134 {
4135 if (loot->isLooted()) // nothing to loot or everything looted.
4136 return false;
4137 if (!loot->HasAllowedLooter(GetGUID()) || (!loot->hasItemForAll() && !loot->hasItemFor(player))) // no loot in chest for this player
4138 return false;
4139 }
4140
4141 if (HasLootRecipient())
4142 return m_tapList.find(player->GetGUID()) != m_tapList.end();
4143
4144 return true;
4145}
4146
4148{
4149 if (m_personalLoot.empty())
4150 return m_loot.get();
4151
4153 return loot;
4154
4155 return nullptr;
4156}
4157
4162
4164{
4165 m_objectData->WriteCreate(flags, data, target, this);
4166 m_gameObjectData->WriteCreate(flags, data, target, this);
4167}
4168
4170{
4172
4174 m_objectData->WriteUpdate(flags, data, target, this);
4175
4177 m_gameObjectData->WriteUpdate(flags, data, target, this);
4178}
4179
4181 UF::GameObjectData::Mask const& requestedGameObjectMask, Player const* target, bool ignoreNestedChangesMask) const
4182{
4185 if (requestedObjectMask.IsAnySet())
4186 valuesMask.Set(TYPEID_OBJECT);
4187
4188 if (requestedGameObjectMask.IsAnySet())
4189 valuesMask.Set(TYPEID_GAMEOBJECT);
4190
4191 ByteBuffer& buffer = PrepareValuesUpdateBuffer(data);
4192 std::size_t sizePos = buffer.wpos();
4193 buffer << uint32(0);
4195 buffer << uint32(valuesMask.GetBlock(0));
4196
4197 if (valuesMask[TYPEID_OBJECT])
4198 m_objectData->WriteUpdate(requestedObjectMask, buffer, target, this, ignoreNestedChangesMask);
4199
4200 if (valuesMask[TYPEID_GAMEOBJECT])
4201 m_gameObjectData->WriteUpdate(requestedGameObjectMask, buffer, target, this, ignoreNestedChangesMask);
4202
4203 buffer.put<uint32>(sizePos, buffer.wpos() - sizePos - 4);
4204
4205 data->AddUpdateBlock();
4206}
4207
4209{
4210 UpdateData udata(Owner->GetMapId());
4211 WorldPacket packet;
4212
4214
4215 udata.BuildPacket(&packet);
4216 player->SendDirectMessage(&packet);
4217}
4218
4224
4225std::span<uint32 const> GameObject::GetPauseTimes() const
4226{
4227 std::span<uint32 const> result;
4228 if (GameObjectType::Transport const* transport = dynamic_cast<GameObjectType::Transport const*>(m_goTypeImpl.get()))
4229 result = transport->GetPauseTimes();
4230
4231 return result;
4232}
4233
4235{
4236 m_transportPathProgress = progress;
4237}
4238
4240{
4241 if (m_goData)
4242 return m_goData->spawnPoint;
4243
4244 return GetPosition();
4245}
4246
4248{
4249 switch (GetGoType())
4250 {
4252 return static_cast<GameObjectType::Transport const*>(m_goTypeImpl.get());
4254 return static_cast<Transport const*>(this);
4255 default:
4256 break;
4257 }
4258
4259 return nullptr;
4260}
4261
4263{
4266 if (m_goTypeImpl)
4267 m_goTypeImpl->OnRelocated();
4268
4269 // TODO: on heartbeat
4270 if (m_vignette)
4272
4274}
4275
4277{
4278 if (GetGOInfo()->GetInteractRadiusOverride())
4279 return float(GetGOInfo()->GetInteractRadiusOverride()) / 100.0f;
4280
4281 switch (GetGoType())
4282 {
4284 return 0.0f;
4290 return 5.5555553f;
4292 return 10.0f;
4295 return 3.0f;
4297 return 100.0f;
4299 return 20.0f + CONTACT_DISTANCE; // max spell range
4305 return 5.0f;
4306 // Following values are not blizzlike
4309 // 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.
4310 // 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.
4311 return 10.0f; // 5.0f is blizzlike
4312 default:
4313 return INTERACTION_DISTANCE;
4314 }
4315}
4316
4318{
4319 if (!m_model)
4320 return;
4321
4322 if (GetMap()->ContainsGameObjectModel(*m_model))
4323 {
4325 m_model->UpdatePosition();
4327 }
4328}
4329
4330void GameObject::SetAnimKitId(uint16 animKitId, bool oneshot)
4331{
4332 if (_animKitId == animKitId)
4333 return;
4334
4335 if (animKitId && !sAnimKitStore.LookupEntry(animKitId))
4336 return;
4337
4338 if (!oneshot)
4339 _animKitId = animKitId;
4340 else
4341 _animKitId = 0;
4342
4344
4346 activateAnimKit.ObjectGUID = GetGUID();
4347 activateAnimKit.AnimKitID = animKitId;
4348 activateAnimKit.Maintain = !oneshot;
4349 SendMessageToSet(activateAnimKit.Write(), true);
4350}
4351
4353{
4354 if (m_vignette)
4355 {
4356 if (m_vignette->Data->ID == vignetteId)
4357 return;
4358
4360 m_vignette = nullptr;
4361 }
4362
4363 if (VignetteEntry const* vignette = sVignetteStore.LookupEntry(vignetteId))
4364 m_vignette = Vignettes::Create(vignette, this);
4365}
4366
4367void GameObject::SetSpellVisualId(int32 spellVisualId, ObjectGuid activatorGuid)
4368{
4370
4372 packet.ObjectGUID = GetGUID();
4373 packet.ActivatorGUID = activatorGuid;
4374 packet.SpellVisualID = spellVisualId;
4375 SendMessageToSet(packet.Write(), true);
4376}
4377
4379{
4380 if (!CanInteractWithCapturePoint(player))
4381 return;
4382
4383 if (GameObjectAI* ai = AI())
4384 if (ai->OnCapturePointAssaulted(player))
4385 return;
4386
4387 // only supported in battlegrounds
4388 Battleground* battleground = nullptr;
4389 if (BattlegroundMap* map = GetMap()->ToBattlegroundMap())
4390 if (Battleground* bg = map->GetBG())
4391 battleground = bg;
4392
4393 if (!battleground)
4394 return;
4395
4396 // Cancel current timer
4398
4399 if (player->GetBGTeam() == HORDE)
4400 {
4402 {
4403 // defended. capture instantly.
4405 battleground->SendBroadcastText(GetGOInfo()->capturePoint.DefendedBroadcastHorde, CHAT_MSG_BG_SYSTEM_HORDE, player);
4407 if (GetGOInfo()->capturePoint.DefendedEventHorde)
4408 GameEvents::Trigger(GetGOInfo()->capturePoint.DefendedEventHorde, player, this);
4409 return;
4410 }
4411
4413 {
4418 battleground->SendBroadcastText(GetGOInfo()->capturePoint.AssaultBroadcastHorde, CHAT_MSG_BG_SYSTEM_HORDE, player);
4420 if (GetGOInfo()->capturePoint.ContestedEventHorde)
4421 GameEvents::Trigger(GetGOInfo()->capturePoint.ContestedEventHorde, player, this);
4423 break;
4424 default:
4425 break;
4426 }
4427 }
4428 else
4429 {
4431 {
4432 // defended. capture instantly.
4436 if (GetGOInfo()->capturePoint.DefendedEventAlliance)
4437 GameEvents::Trigger(GetGOInfo()->capturePoint.DefendedEventAlliance, player, this);
4438 return;
4439 }
4440
4442 {
4449 if (GetGOInfo()->capturePoint.ContestedEventAlliance)
4450 GameEvents::Trigger(GetGOInfo()->capturePoint.ContestedEventAlliance, player, this);
4452 break;
4453 default:
4454 break;
4455 }
4456 }
4457}
4458
4460{
4462 return;
4463
4464 if (GameObjectAI* ai = AI())
4465 if (ai->OnCapturePointUpdated(m_goValue.CapturePoint.State))
4466 return;
4467
4468 uint32 spellVisualId = 0;
4469 uint32 customAnim = 0;
4470
4472 {
4474 spellVisualId = GetGOInfo()->capturePoint.SpellVisual1;
4475 break;
4477 customAnim = 1;
4478 spellVisualId = GetGOInfo()->capturePoint.SpellVisual2;
4479 break;
4481 customAnim = 2;
4482 spellVisualId = GetGOInfo()->capturePoint.SpellVisual3;
4483 break;
4485 customAnim = 3;
4486 spellVisualId = GetGOInfo()->capturePoint.SpellVisual4;
4487 break;
4489 customAnim = 4;
4490 spellVisualId = GetGOInfo()->capturePoint.SpellVisual5;
4491 break;
4492 default:
4493 break;
4494 }
4495
4496 if (customAnim != 0)
4497 SendCustomAnim(customAnim);
4498
4499 SetSpellVisualId(spellVisualId);
4501
4502 if (BattlegroundMap* map = GetMap()->ToBattlegroundMap())
4503 {
4504 if (Battleground* bg = map->GetBG())
4505 {
4508 packet.CapturePointInfo.Pos = GetPosition();
4509 packet.CapturePointInfo.Guid = GetGUID();
4512 bg->SendPacketToAll(packet.Write());
4513 bg->UpdateWorldState(GetGOInfo()->capturePoint.worldState1, AsUnderlyingType(m_goValue.CapturePoint.State));
4514 }
4515 }
4516
4518}
4519
4538
4540{
4542 return FlagState(0);
4543
4544 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(m_goTypeImpl.get());
4545 if (!newFlag)
4546 return FlagState(0);
4547
4548 return newFlag->GetState();
4549}
4550
4552{
4554 return ObjectGuid::Empty;
4555
4556 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(m_goTypeImpl.get());
4557 if (!newFlag)
4558 return ObjectGuid::Empty;
4559
4560 return newFlag->GetCarrierGUID();
4561}
4562
4564{
4566 return time_t(0);
4567
4568 GameObjectType::NewFlag const* newFlag = dynamic_cast<GameObjectType::NewFlag const*>(m_goTypeImpl.get());
4569 if (!newFlag)
4570 return time_t(0);
4571
4572 return newFlag->GetTakenFromBaseTime();
4573}
4574
4576{
4577 if (GameObjectType::ControlZone const* controlZone = dynamic_cast<GameObjectType::ControlZone const*>(m_goTypeImpl.get()))
4578 return controlZone->GetInsidePlayers();
4579
4580 return nullptr;
4581}
4582
4587
4588std::unordered_map<ObjectGuid, GameObject::PerPlayerState>& GameObject::GetOrCreatePerPlayerStates()
4589{
4590 if (!m_perPlayerState)
4591 m_perPlayerState = std::make_unique<std::unordered_map<ObjectGuid, PerPlayerState>>();
4592
4593 return *m_perPlayerState;
4594}
4595
4597{
4598public:
4599 explicit GameObjectModelOwnerImpl(GameObject* owner) : _owner(owner) { }
4600 virtual ~GameObjectModelOwnerImpl() = default;
4601
4602 bool IsSpawned() const override { return _owner->isSpawned(); }
4603 uint32 GetDisplayId() const override { return _owner->GetDisplayId(); }
4604 uint8 GetNameSetId() const override { return _owner->GetNameSetId(); }
4605 bool IsInPhase(PhaseShift const& phaseShift) const override { return _owner->GetPhaseShift().CanSee(phaseShift); }
4606 G3D::Vector3 GetPosition() const override { return G3D::Vector3(_owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ()); }
4607 G3D::Quat GetRotation() const override { return G3D::Quat(_owner->GetLocalRotation().x, _owner->GetLocalRotation().y, _owner->GetLocalRotation().z, _owner->GetLocalRotation().w); }
4608 int64 GetPackedRotation() const override { return _owner->GetPackedLocalRotation(); }
4609 float GetScale() const override { return _owner->GetObjectScale(); }
4610 void DebugVisualizeCorner(G3D::Vector3 const& corner) const override { _owner->SummonCreature(1, corner.x, corner.y, corner.z, 0, TEMPSUMMON_MANUAL_DESPAWN); }
4611
4612private:
4614};
4615
4621
4623{
4624 if (m_goTypeImpl)
4625 command.Execute(*m_goTypeImpl);
4626}
4627
4629{
4631 return TEAM_NEUTRAL;
4632
4633 GameObjectType::ControlZone const* controlZone = dynamic_cast<GameObjectType::ControlZone const*>(m_goTypeImpl.get());
4634 if (!controlZone)
4635 return TEAM_NEUTRAL;
4636
4637 return controlZone->GetControllingTeam();
4638}
4639
4641{
4642 m_model = GameObjectModel::Create(std::make_unique<GameObjectModelOwnerImpl>(this), sWorld->GetDataPath());
4643 if (m_model)
4644 {
4645 if (m_model->IsMapObject())
4647
4648 switch (GetGoType())
4649 {
4651 m_model->DisableLosBlocking(GetGOInfo()->door.NotLOSBlocking);
4652 break;
4657 m_model->IncludeInNavMesh(true);
4658 break;
4659 default:
4660 break;
4661 }
4662 }
4663}
4664
4665std::string GameObject::GetDebugInfo() const
4666{
4667 std::stringstream sstr;
4668 sstr << WorldObject::GetDebugInfo() << "\n"
4669 << "SpawnId: " << GetSpawnId() << " GoState: " << std::to_string(GetGoState()) << " ScriptId: " << GetScriptId() << " AIName: " << GetAIName();
4670 return sstr.str();
4671}
4672
4673bool GameObject::IsAtInteractDistance(Player const* player, SpellInfo const* spell) const
4674{
4675 if (spell || (spell = GetSpellForLock(player)))
4676 {
4677 float maxRange = spell->GetMaxRange(spell->IsPositive());
4678
4680 return IsInDist(player, maxRange);
4681
4682 if (sGameObjectDisplayInfoStore.LookupEntry(GetGOInfo()->displayId))
4683 return IsAtInteractDistance(*player, maxRange);
4684 }
4685
4687}
4688
4689bool GameObject::IsAtInteractDistance(Position const& pos, float radius) const
4690{
4691 if (GameObjectDisplayInfoEntry const* displayInfo = sGameObjectDisplayInfoStore.LookupEntry(GetGOInfo()->displayId))
4692 {
4693 float scale = GetObjectScale();
4694
4695 float minX = displayInfo->GeoBoxMin.X * scale - radius;
4696 float minY = displayInfo->GeoBoxMin.Y * scale - radius;
4697 float minZ = displayInfo->GeoBoxMin.Z * scale - radius;
4698 float maxX = displayInfo->GeoBoxMax.X * scale + radius;
4699 float maxY = displayInfo->GeoBoxMax.Y * scale + radius;
4700 float maxZ = displayInfo->GeoBoxMax.Z * scale + radius;
4701
4702 QuaternionData worldRotation = GetWorldRotation();
4703 G3D::Quat worldRotationQuat(worldRotation.x, worldRotation.y, worldRotation.z, worldRotation.w);
4704
4705 return G3D::CoordinateFrame { { worldRotationQuat }, { GetPositionX(), GetPositionY(), GetPositionZ() } }
4706 .toWorldSpace(G3D::Box { { minX, minY, minZ }, { maxX, maxY, maxZ } })
4707 .contains({ pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ() });
4708 }
4709
4710 return GetExactDist(&pos) <= radius;
4711}
4712
4713bool GameObject::IsWithinDistInMap(Player const* player) const
4714{
4715 return IsInMap(player) && InSamePhase(player) && IsAtInteractDistance(player);
4716}
4717
4719{
4720 if (!player)
4721 return nullptr;
4722
4723 uint32 lockId = GetGOInfo()->GetLockId();
4724 if (!lockId)
4725 return nullptr;
4726
4727 LockEntry const* lock = sLockStore.LookupEntry(lockId);
4728 if (!lock)
4729 return nullptr;
4730
4731 for (uint8 i = 0; i < MAX_LOCK_CASE; ++i)
4732 {
4733 if (!lock->Type[i])
4734 continue;
4735
4736 if (lock->Type[i] == LOCK_KEY_SPELL)
4737 if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(lock->Index[i], GetMap()->GetDifficultyID()))
4738 return spell;
4739
4740 if (lock->Type[i] != LOCK_KEY_SKILL)
4741 break;
4742
4743 for (auto&& playerSpell : player->GetSpellMap())
4744 if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(playerSpell.first, GetMap()->GetDifficultyID()))
4745 for (auto&& effect : spell->GetEffects())
4746 if (effect.Effect == SPELL_EFFECT_OPEN_LOCK && effect.MiscValue == lock->Index[i])
4747 if (effect.CalcValueAsInt(player) >= int32(lock->Skill[i]))
4748 return spell;
4749 }
4750
4751 return nullptr;
4752}
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:579
#define MAX_LOCK_CASE
ItemContext
Definition DBCEnums.h:1324
@ 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:72
GameObjectActions
LootState
Definition GameObject.h:161
@ GO_ACTIVATED
Definition GameObject.h:164
@ GO_READY
Definition GameObject.h:163
@ GO_NOT_READY
Definition GameObject.h:162
@ GO_JUST_DEACTIVATED
Definition GameObject.h:165
FlagState
Definition GameObject.h:47
#define FISHING_BOBBER_READY_TIME
Definition GameObject.h:169
@ GRID_MAP_TYPE_MASK_PLAYER
Definition GridDefines.h:80
@ GRID_MAP_TYPE_MASK_CREATURE
Definition GridDefines.h:77
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:173
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
#define TC_LOG_FATAL(filterType__, message__,...)
Definition Log.h:185
#define TC_LOG_WARN(filterType__, message__,...)
Definition Log.h:179
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:1036
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:1873
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:911
@ TELE_TO_NOT_UNSUMMON_PET
Definition Player.h:912
@ TELE_TO_NOT_LEAVE_TRANSPORT
Definition Player.h:910
#define sPoolMgr
Definition PoolMgr.h:179
constexpr QuestGiverStatus QuestGiverStatusFutureMask
Definition QuestDef.h:206
@ QUEST_STATUS_INCOMPLETE
Definition QuestDef.h:146
int32 irand(int32 min, int32 max)
Definition Random.cpp:36
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:43
@ STEALTH_TRAP
@ SPELL_EFFECT_OPEN_LOCK
#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
@ 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.
@ TARGET_CHECK_ENEMY
Definition SpellInfo.h:83
@ TARGET_CHECK_DEFAULT
Definition SpellInfo.h:81
@ TARGET_CHECK_ALLY
Definition SpellInfo.h:84
@ TARGET_OBJECT_TYPE_UNIT
Definition SpellInfo.h:68
#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:611
@ 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:462
void put(std::size_t pos, T value)
Definition ByteBuffer.h:261
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:83
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:251
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:497
SpawnTrackingStateData const * GetSpawnTrackingStateDataForPlayer(Player const *player) const override
GOState GetGoStateFor(ObjectGuid const &viewer) const
GameObjectTemplate const * GetGOInfo() const
Definition GameObject.h:209
void UpdateDynamicFlagsForNearbyPlayers()
void CreateModel()
GOState GetGoState() const
Definition GameObject.h:290
bool MeetsInteractCondition(Player const *user) const
time_t m_respawnTime
Definition GameObject.h:471
bool IsInvisibleDueToDespawn(WorldObject const *seer) const override
void ClearLoot()
std::string GetDebugInfo() const override
time_t m_cooldownTime
Definition GameObject.h:479
std::unordered_map< ObjectGuid, PerPlayerState > & GetOrCreatePerPlayerStates()
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool=true)
void AddUse()
Definition GameObject.h:330
void RemoveFlag(GameObjectFlags flags)
Definition GameObject.h:284
uint32 GetGoArtKit() const
Definition GameObject.h:294
bool hasInvolvedQuest(uint32 quest_id) const override
ObjectGuid const & GetFlagCarrierGUID() const
bool HasFlag(GameObjectFlags flags) const
Definition GameObject.h:282
void SetSpellVisualId(int32 spellVisualId, ObjectGuid activatorGuid=ObjectGuid::Empty)
GameObjectData const * m_goData
Definition GameObject.h:495
LootState getLootState() const
Definition GameObject.h:308
uint32 GetSpellId() const
Definition GameObject.h:258
uint8 GetGoAnimProgress() const
Definition GameObject.h:296
int64 m_packedRotation
Definition GameObject.h:501
Position m_stationaryPosition
Definition GameObject.h:503
void InheritStringIds(GameObject const *parent)
bool IsFullyLooted() const
QuaternionData const & GetLocalRotation() const
Definition GameObject.h:224
time_t m_restockTime
Definition GameObject.h:478
uint32 GetUniqueUseCount() const
Definition GameObject.h:333
void ReplaceAllFlags(GameObjectFlags flags)
Definition GameObject.h:285
void RemoveFromWorld() override
void EnableCollision(bool enable)
std::unordered_map< ObjectGuid, std::unique_ptr< Loot > > m_personalLoot
Definition GameObject.h:338
void SetGoAnimProgress(uint8 animprogress)
Definition GameObject.h:297
ObjectGuid m_lootStateUnitGUID
Definition GameObject.h:476
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:265
void SetOwnerGUID(ObjectGuid owner)
Definition GameObject.h:241
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:490
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:283
Loot * GetFishLootJunk(Player *lootOwner)
uint32 m_spellId
Definition GameObject.h:470
bool IsAlwaysVisibleFor(WorldObject const *seer) const override
std::array< std::string const *, 3 > m_stringIds
Definition GameObject.h:498
void SetWorldEffectID(uint32 worldEffectID)
Definition GameObject.h:437
bool HasLootRecipient() const
Definition GameObject.h:343
void SetLootState(LootState s, Unit *unit=nullptr)
static GameObject * CreateGameObjectFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap=true)
uint32 m_respawnDelayTime
Definition GameObject.h:472
SpellInfo const * GetSpellForLock(Player const *player) const
GameObjectAI * AI() const
Definition GameObject.h:390
void ResetLootMode()
Definition GameObject.h:317
GuidSet m_unique_users
Definition GameObject.h:486
GameObject * GetLinkedTrap()
GuidUnorderedSet const * GetInsidePlayers() const
std::unique_ptr< Vignettes::VignetteData > m_vignette
Definition GameObject.h:528
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:385
GameobjectTypes GetGoType() const
Definition GameObject.h:288
void SendGameObjectDespawn()
void SetGoType(GameobjectTypes type)
Definition GameObject.h:289
uint32 m_despawnDelay
Definition GameObject.h:473
Seconds m_despawnRespawnTime
Definition GameObject.h:474
QuaternionData m_localRotation
Definition GameObject.h:502
void SetLinkedTrap(GameObject *linkedTrap)
Definition GameObject.h:347
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:212
bool m_respawnCompatibilityMode
Definition GameObject.h:523
GameObjectTemplateAddon const * m_goTemplateAddon
Definition GameObject.h:494
ObjectGuid::LowType GetSpawnId() const
Definition GameObject.h:218
std::span< uint32 const > GetPauseTimes() const
void Use(Unit *user, bool ignoreCastInProgress=false)
Position const & GetStationaryPosition() const override
Definition GameObject.h:415
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:505
GameObjectAI * m_AI
Definition GameObject.h:522
uint32 GetDisplayId() const
Definition GameObject.h:400
void ResetDoorOrButton()
std::unique_ptr< Loot > m_loot
Definition GameObject.h:337
void SetLevel(uint32 level)
Definition GameObject.h:287
Optional< float > m_transportPathProgress
Definition GameObject.h:526
uint32 GetScriptId() const
uint8 GetNameSetId() const
bool CanInteractWithCapturePoint(Player const *target) const
Optional< std::string > m_scriptStringId
Definition GameObject.h:499
void UpdateModelPosition()
void AfterRelocation()
void AIM_Destroy()
void SetPathProgressForClient(float progress)
void Update(uint32 diff) override
GameObjectTemplateAddon const * GetTemplateAddon() const
Definition GameObject.h:210
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:524
void SetDisplayId(uint32 displayid)
GameObjectTemplate const * m_goInfo
Definition GameObject.h:493
GuidUnorderedSet const & GetTapList() const
Definition GameObject.h:340
ObjectGuid::LowType m_spawnId
For new or temporary gameobjects is 0 for saved it is lowguid.
Definition GameObject.h:492
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:395
void UpdateModel()
void ActivateObject(GameObjectActions action, int32 param, WorldObject *spellCaster=nullptr, uint32 spellId=0, int32 effectIndex=-1)
ObjectGuid m_linkedTrap
Definition GameObject.h:508
void SwitchDoorOrButton(bool activate, bool alternative=false)
void CleanupsBeforeDelete(bool finalCleanup=true) override
bool HasConditionalInteraction() const
bool IsDestructibleBuilding() const
bool IsNeverVisibleFor(WorldObject const *seer, bool allowServersideObjects) const override
void SaveToDB()
Loot * GetLootForPlayer(Player const *) const override
uint16 GetLootMode() const
Definition GameObject.h:312
TransportBase * ToTransportBase()
Definition GameObject.h:409
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:412
bool m_spawnedByDefault
Definition GameObject.h:477
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:375
std::unique_ptr< std::unordered_map< ObjectGuid, PerPlayerState > > m_perPlayerState
Definition GameObject.h:537
bool IsLootAllowedFor(Player const *player) const
LootState m_lootState
Definition GameObject.h:475
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:463
void SetParentRotation(QuaternionData const &rotation)
Position GetRespawnPosition() const
std::unique_ptr< GameObjectTypeBase > m_goTypeImpl
Definition GameObject.h:496
ObjectGuid m_ritualOwnerGUID
Definition GameObject.h:485
std::unique_ptr< GameObjectModel > m_model
Definition GameObject.h:406
uint32 m_usetimes
Definition GameObject.h:487
void SetSpellId(uint32 id)
Definition GameObject.h:253
GOState m_prevGoState
Definition GameObject.h:481
bool IsTransport() const
void SetFaction(uint32 faction) override
Definition GameObject.h:404
void BuildValuesUpdate(UF::UpdateFieldFlag flags, ByteBuffer &data, Player const *target) const override
GuidSet m_SkillupList
Definition GameObject.h:483
Definition Group.h:209
bool IsMember(ObjectGuid guid) const
Definition Group.cpp:1655
Definition Item.h:185
bool HaveQuestLootForPlayer(uint32 loot_id, Player const *player) const
Definition LootMgr.cpp:198
Definition Map.h:227
void GameObjectRelocation(GameObject *go, float x, float y, float z, float orientation, bool respawnRelocationOnFail=true)
Definition Map.cpp:1090
void UpdateSpawnGroupConditions()
Definition Map.cpp:2516
MapStoredObjectTypesContainer & GetObjectsStore()
Definition Map.h:463
MapDifficultyEntry const * GetMapDifficulty() const
Definition Map.cpp:3283
void RemoveGameObjectModel(GameObjectModel const &model)
Definition Map.h:503
bool AddToMap(T *)
Definition Map.cpp:527
time_t GetLinkedRespawnTime(ObjectGuid guid) const
Definition Map.cpp:3741
void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, CharacterDatabaseTransaction dbTrans=nullptr, bool alwaysDeleteFromDB=false)
Definition Map.h:730
void ApplyDynamicModeRespawnScaling(WorldObject const *obj, ObjectGuid::LowType spawnId, uint32 &respawnDelay, uint32 mode) const
Definition Map.cpp:2287
ObjectGuid::LowType GenerateLowGuid()
Definition Map.h:564
void RequestRebuildNavMeshOnGameObjectModelChange(GameObjectModel const &model, PhaseShift const &phaseShift)
Definition Map.cpp:1785
time_t GetGORespawnTime(ObjectGuid::LowType spawnId) const
Definition Map.h:530
void SaveRespawnInfoDB(RespawnInfo const &info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition Map.cpp:3683
Difficulty GetDifficultyID() const
Definition Map.h:363
void SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 gridId, CharacterDatabaseTransaction dbTrans=nullptr, bool startup=false)
Definition Map.cpp:3650
GameObjectBySpawnIdContainer & GetGameObjectBySpawnIdStore()
Definition Map.h:470
void InsertGameObjectModel(GameObjectModel const &model)
Definition Map.h:504
bool Instanceable() const
Definition Map.cpp:3293
uint32 GetId() const
Definition Map.cpp:3288
SpawnedPoolData & GetPoolData()
Definition Map.h:758
void Respawn(RespawnInfo *info, CharacterDatabaseTransaction dbTrans=nullptr)
Definition Map.cpp:2070
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:1608
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:3047
bool IsInSameRaidWith(Player const *p) const
Definition Player.cpp:2060
void SendDirectMessage(WorldPacket const *data) const
Definition Player.cpp:6193
uint16 GetSkillValue(uint32 skill) const
Definition Player.cpp:5920
bool CanUseBattlegroundObject(GameObject *gameobject) const
Definition Player.cpp:26930
void GiveXP(uint32 xp, Unit *victim, float group_rate=1.0f)
Definition Player.cpp:2110
Item * GetItemByEntry(uint32 entry, ItemSearchLocation where=ItemSearchLocation::Default) const
Definition Player.cpp:12393
void SendPreparedGossip(WorldObject *source)
Definition Player.cpp:13985
QuestGiverStatus GetQuestDialogStatus(Object const *questGiver) const
Definition Player.cpp:16096
void UpdateVisibilityOf(WorldObject *target)
Definition Player.cpp:24602
PlayerSpellMap const & GetSpellMap() const
Definition Player.h:2056
void SendLoot(Loot &loot, bool aeLooting=false)
Definition Player.cpp:9075
void UpdateCriteria(CriteriaType type, uint64 miscValue1=0, uint64 miscValue2=0, uint64 miscValue3=0, WorldObject *ref=nullptr)
Definition Player.cpp:27532
bool HasQuestForGO(int32 goId) const
Definition Player.cpp:25859
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:1181
void SendCinematicStart(uint32 CinematicSequenceId) const
Definition Player.cpp:6198
uint32 GetProfessionSkillForExp(uint32 skill, int32 expansion) const
Definition Player.cpp:5873
QuestStatus GetQuestStatus(uint32 quest_id) const
Definition Player.cpp:15929
void KillCreditGO(uint32 entry, ObjectGuid guid=ObjectGuid::Empty)
Definition Player.cpp:16670
Group * GetGroup(Optional< uint8 > partyIndex)
Definition Player.h:2781
void PrepareGossipMenu(WorldObject *source, uint32 menuId, bool showQuests=false)
Definition Player.cpp:13884
bool UpdateFishingSkill(int32 expansion)
Definition Player.cpp:5502
Item * GetItemByGuid(ObjectGuid guid) const
Definition Player.cpp:9522
SpawnTrackingState GetSpawnTrackingStateByObjectives(uint32 spawnTrackingId, std::vector< uint32 > const &questObjectives) const
Definition Player.cpp:17526
Team GetBGTeam() const
Definition Player.cpp:24277
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:849
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:37
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:647
Vehicle * GetVehicle() const
Definition Unit.h:1800
void RemoveAurasByType(AuraType auraType, std::function< bool(AuraApplication const *)> const &check, AuraRemoveMode removeMode=AURA_REMOVE_BY_DEFAULT)
Definition Unit.cpp:3973
void RemoveGameObject(GameObject *gameObj, bool del)
Definition Unit.cpp:5436
void SetStandState(UnitStandStateType state, uint32 animKitID=0)
Definition Unit.cpp:10804
void FinishSpell(CurrentSpellTypes spellType, SpellCastResult result=SPELL_CAST_OK)
Definition Unit.cpp:3189
bool IsAlive() const
Definition Unit.h:1205
Aura * GetAura(uint32 spellId, ObjectGuid casterGUID=ObjectGuid::Empty, ObjectGuid itemCasterGUID=ObjectGuid::Empty, uint32 reqEffMask=0) const
Definition Unit.cpp:4718
void NearTeleportTo(TeleportLocation const &target, bool casting=false)
Definition Unit.cpp:13054
ObjectGuid GetTarget() const
Definition Unit.h:1847
uint8 GetLevel() const
Definition Unit.h:769
Spell * GetCurrentSpell(CurrentSpellTypes spellType) const
Definition Unit.h:1490
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:474
virtual void SendMessageToSet(WorldPacket const *data, bool self) const
Definition Object.cpp:1099
Map * GetMap() const
Definition Object.h:411
void AddToWorld() override
Definition Object.cpp:363
virtual bool IsAlwaysVisibleFor(WorldObject const *seer) const
Definition Object.h:606
void RemoveFromWorld() override
Definition Object.cpp:369
virtual void UpdateObjectVisibilityOnDestroy()
Definition Object.h:511
SpellCastResult CastSpell(CastSpellTargetArg const &targets, uint32 spellId, CastSpellExtraArgs const &args={ })
Definition Object.cpp:2222
std::string GetDebugInfo() const override
Definition Object.cpp:3139
uint32 LastUsedScriptID
Definition Object.h:534
PhaseShift & GetPhaseShift()
Definition Object.h:310
Unit * GetOwner() const
Definition Object.cpp:1603
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:1389
TransportBase * GetTransport() const
Definition Object.h:537
void setActive(bool isActiveObject)
Definition Object.cpp:274
float GetDistance2d(WorldObject const *obj) const
Definition Object.cpp:448
ZoneScript * m_zoneScript
Definition Object.h:584
virtual bool IsNeverVisibleFor(WorldObject const *seer, bool allowServersideObjects) const
Definition Object.h:605
std::string const & GetName() const
Definition Object.h:342
virtual void SetMap(Map *map)
Definition Object.cpp:1149
void AddObjectToRemoveList()
Definition Object.cpp:1179
void UpdatePositionData()
Definition Object.cpp:344
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition Object.cpp:1626
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:297
MovementInfo m_movementInfo
Definition Object.h:548
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2191
bool IsInMap(WorldObject const *obj) const
Definition Object.cpp:467
virtual void Update(uint32 diff)
Definition Object.cpp:243
int32 GetDBPhase() const
Definition Object.h:327
virtual void UpdateObjectVisibility(bool forced=true)
Definition Object.cpp:3050
virtual void CleanupsBeforeDelete(bool finalCleanup=true)
Definition Object.cpp:333
void SetTransport(TransportBase *t)
Definition Object.cpp:3103
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:919
@ CONFIG_RESPAWN_DYNAMICMODE
Definition World.h:420
@ CONFIG_LOAD_LOCALES
Definition World.h:201
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
constexpr 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 VisitWorldObjects(WorldObject const *obj, T &visitor, float radius)
Definition CellImpl.h:150
static void VisitAllObjects(WorldObject const *obj, T &visitor, float radius)
Definition CellImpl.h:160
static void VisitGridObjects(WorldObject const *obj, T &visitor, float radius)
Definition CellImpl.h:140
uint32 GetMaxHealth() const
QuaternionData ParentRotation
InvisibilityType invisibilityType
QuaternionData rotation
std::array< uint32, 5 > ArtKits
uint32 GetAutoCloseTime() const
struct GameObjectTemplate::@199::@266 raw
bool IsLargeGameObject() const
struct GameObjectTemplate::@199::@248 itemForge
bool IsUsableMounted() const
uint32 GetNoDamageImmune() const
bool IsGiganticGameObject() const
WorldPacket BuildQueryData(LocaleConstant loc) const
struct GameObjectTemplate::@199::@211 goober
struct GameObjectTemplate::@199::@207 trap
struct GameObjectTemplate::@199::@208 chair
bool IsInfiniteGameObject() const
bool IsDespawnAtAction() const
struct GameObjectTemplate::@199::@234 destructibleBuilding
struct GameObjectTemplate::@199::@212 transport
struct GameObjectTemplate::@199::@233 barberChair
uint32 GetCharges() const
struct GameObjectTemplate::@199::@223 spellCaster
struct GameObjectTemplate::@199::@230 controlZone
struct GameObjectTemplate::@199::@243 capturePoint
struct GameObjectTemplate::@199::@244 phaseableMO
struct GameObjectTemplate::@199::@227 flagDrop
std::string castBarCaption
std::unique_ptr< WorldPacket[]> QueryData
bool IsUsingGroupLootRules() const
struct GameObjectTemplate::@199::@237 newflag
uint32 GetLockId() const
struct GameObjectTemplate::@199::@251 gatheringNode
struct GameObjectTemplate::@199::@204 chest
struct GameObjectTemplate::@199::@225 flagStand
struct GameObjectTemplate::@199::@219 ritual
uint32 GetConditionID1() const
uint32 GetLootId() const
SystemTimePoint ValidUntil
Definition GameObject.h:532
Optional< GOState > State
Definition GameObject.h:533
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 isLooted() const
Definition Loot.h:312
bool AutoStore(Player *player, uint8 bag, uint8 slot, bool broadcast=false, bool pushed=false, bool createdByPlayer=false)
Definition Loot.cpp:976
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
static QuaternionData fromOrientation(float orientation)
bool isUnit() const
static QuaternionData fromEulerAnglesZYX(float Z, float Y, float X)
ObjectGuid::LowType spawnId
Definition Map.h:174
time_t respawnTime
Definition Map.h:176
SpawnObjectType type
Definition Map.h:173
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, 14 > ParentRotation
UpdateField< int8, 0, 16 > State
UpdateField< uint32, 0, 5 > SpellVisualID
UpdateField< uint32, 0, 9 > StateWorldEffectsQuestObjectiveID
UpdateField< uint32, 0, 19 > ArtKit
DynamicUpdateField< int32, 0, 2 > EnableDoodadSets
UpdateField< uint32, 0, 2 > DynamicFlags
std::array< uint32, MAX_GAMEOBJECT_DATA > Data
void Add(EntityFragment fragment, bool update, void const *data=nullptr)
struct GameObjectValue::@194 Trap
TeamId LastTeamCapture
Definition GameObject.h:150
uint32 AssaultTimer
Definition GameObject.h:152
struct GameObjectValue::@197 Building
SpellTargetCheckTypes TargetSearcherCheckType
Definition GameObject.h:129
WorldPackets::Battleground::BattlegroundCapturePointState State
Definition GameObject.h:151
::DestructibleHitpoint const * DestructibleHitpoint
Definition GameObject.h:145
struct GameObjectValue::@198 CapturePoint
struct GameObjectValue::@195 FishingHole