TrinityCore
Creature.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 "Creature.h"
19#include "BattlegroundMgr.h"
20#include "CellImpl.h"
21#include "CharmInfo.h"
22#include "CombatPackets.h"
23#include "Containers.h"
24#include "CreatureAI.h"
25#include "CreatureAISelector.h"
26#include "CreatureGroups.h"
27#include "DB2Stores.h"
28#include "DatabaseEnv.h"
29#include "Formulas.h"
30#include "GameEventMgr.h"
31#include "GameTime.h"
32#include "GridNotifiersImpl.h"
33#include "Group.h"
34#include "ItemTemplate.h"
35#include "Log.h"
36#include "Loot.h"
37#include "LootMgr.h"
38#include "MapManager.h"
39#include "MapUtils.h"
40#include "MiscPackets.h"
41#include "MotionMaster.h"
42#include "ObjectAccessor.h"
43#include "ObjectMgr.h"
44#include "PhasingHandler.h"
45#include "Player.h"
46#include "PoolMgr.h"
47#include "QueryPackets.h"
48#include "ScriptedGossip.h"
49#include "Spell.h"
50#include "SpellAuraEffects.h"
51#include "SpellMgr.h"
52#include "TemporarySummon.h"
53#include "Vehicle.h"
54#include "World.h"
55#include "ZoneScript.h"
56#include <G3D/g3dmath.h>
57#include <sstream>
58
60Random(CreatureRandomMovementType::Walk), InteractionPauseTimer(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)) { }
61
63{
64 char const* const ChaseStates[] = { "Run", "CanWalk", "AlwaysWalk" };
65 char const* const RandomStates[] = { "Walk", "CanRun", "AlwaysRun" };
66
67 std::ostringstream str;
68 str << std::boolalpha
69 << ", HoverInitiallyEnabled: " << HoverInitiallyEnabled
70 << ", Chase: " << ChaseStates[AsUnderlyingType(Chase)]
71 << ", Random: " << RandomStates[AsUnderlyingType(Random)];
72 str << ", InteractionPauseTimer: " << InteractionPauseTimer;
73
74 return str.str();
75}
76
78 : itemId(_item), count(_count), lastIncrementTime(GameTime::GetGameTime()) { }
79
81{
82 auto newEnd = std::remove_if(m_items.begin(), m_items.end(), [=](VendorItem const& vendorItem)
83 {
84 return vendorItem.item == item_id && vendorItem.Type == type;
85 });
86
87 bool found = newEnd != m_items.end();
88 m_items.erase(newEnd, m_items.end());
89 return found;
90}
91
92VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extendedCost, uint8 type) const
93{
94 for (VendorItem const& vendorItem : m_items)
95 if (vendorItem.item == item_id && vendorItem.ExtendedCost == extendedCost && vendorItem.Type == type)
96 return &vendorItem;
97 return nullptr;
98}
99
102
104{
105 return idx < Models.size() ? &Models[idx] : nullptr;
106}
107
109{
110 if (!Models.size())
111 return nullptr;
112
113 // If only one element, ignore the Probability (even if 0)
114 if (Models.size() == 1)
115 return &Models[0];
116
118 {
119 return model.Probability;
120 });
121
122 return &(*selectedItr);
123}
124
126{
127 for (CreatureModel const& model : Models)
128 if (model.CreatureDisplayID)
129 return &model;
130
131 return nullptr;
132}
133
135{
136 for (CreatureModel const& model : Models)
137 if (displayId == model.CreatureDisplayID)
138 return &model;
139
140 return nullptr;
141}
142
144{
145 for (CreatureModel const& model : Models)
146 if (CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(model.CreatureDisplayID))
147 if (modelInfo && modelInfo->is_trigger)
148 return &model;
149
151}
152
154{
155 for (CreatureModel const& model : Models)
156 if (CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(model.CreatureDisplayID))
157 if (modelInfo && !modelInfo->is_trigger)
158 return &model;
159
161}
162
164{
165 for (uint8 loc = LOCALE_enUS; loc < TOTAL_LOCALES; ++loc)
166 {
167 if (!sWorld->getBoolConfig(CONFIG_LOAD_LOCALES) && loc != DEFAULT_LOCALE)
168 continue;
169
170 QueryData[loc] = BuildQueryData(static_cast<LocaleConstant>(loc), DIFFICULTY_NONE);
171 }
172}
173
175{
176 CreatureDifficulty const* creatureDifficulty = GetDifficulty(difficulty);
177
179
180 queryTemp.CreatureID = Entry;
181
182 queryTemp.Allow = true;
183
184 WorldPackets::Query::CreatureStats& stats = queryTemp.Stats;
185
186 stats.Leader = RacialLeader;
187
188 stats.Name[0] = Name;
189 stats.NameAlt[0] = FemaleName;
190
191 stats.Flags[0] = creatureDifficulty->TypeFlags;
192 stats.Flags[1] = creatureDifficulty->TypeFlags2;
193
194 stats.CreatureType = type;
195 stats.CreatureFamily = family;
197
198 for (uint32 i = 0; i < MAX_KILL_CREDIT; ++i)
199 stats.ProxyCreatureID[i] = KillCredit[i];
200
201 std::transform(Models.begin(), Models.end(), std::back_inserter(stats.Display.CreatureDisplay),
203 {
204 stats.Display.TotalProbability += model.Probability;
205 return { model.CreatureDisplayID, model.DisplayScale, model.Probability };
206 });
207
208 stats.HpMulti = creatureDifficulty->HealthModifier;
209 stats.EnergyMulti = creatureDifficulty->ManaModifier;
210
211 stats.CreatureMovementInfoID = movementId;
212 stats.RequiredExpansion = RequiredExpansion;
213 stats.HealthScalingExpansion = creatureDifficulty->HealthScalingExpansion;
214 stats.VignetteID = VignetteID;
215 stats.Class = unit_class;
216 stats.CreatureDifficultyID = creatureDifficulty->CreatureDifficultyID;
217 stats.WidgetSetID = WidgetSetID;
218 stats.WidgetSetUnitConditionID = WidgetSetUnitConditionID;
219
220 stats.Title = SubName;
221 stats.TitleAlt = TitleAlt;
222 stats.CursorName = IconName;
223
224 if (std::vector<uint32> const* items = sObjectMgr->GetCreatureQuestItemList(Entry, difficulty))
225 stats.QuestItems.assign(items->begin(), items->end());
226
227 if (std::vector<int32> const* currencies = sObjectMgr->GetCreatureQuestCurrencyList(Entry))
228 stats.QuestCurrencies.assign(currencies->begin(), currencies->end());
229
230 if (loc != LOCALE_enUS)
231 if (CreatureLocale const* creatureLocale = sObjectMgr->GetCreatureLocale(Entry))
232 {
233 ObjectMgr::GetLocaleString(creatureLocale->Name, loc, stats.Name[0]);
234 ObjectMgr::GetLocaleString(creatureLocale->NameAlt, loc, stats.NameAlt[0]);
235 ObjectMgr::GetLocaleString(creatureLocale->Title, loc, stats.Title);
236 ObjectMgr::GetLocaleString(creatureLocale->TitleAlt, loc, stats.TitleAlt);
237 }
238
239 queryTemp.Write();
240 queryTemp.ShrinkToFit();
241 return queryTemp.Move();
242}
243
245{
246 auto it = difficultyStore.find(difficulty);
247 if (it != difficultyStore.end())
248 return &it->second;
249
250 // If there is no data for the difficulty, try to get data for the fallback difficulty
251 DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty);
252 if (difficultyEntry)
253 return GetDifficulty(Difficulty(difficultyEntry->FallbackDifficultyID));
254
255 // No data for DIFFICULTY_NONE (0)
256 struct DefaultCreatureDifficulty : public CreatureDifficulty
257 {
258 DefaultCreatureDifficulty()
259 {
260 DeltaLevelMin = 0;
261 DeltaLevelMax = 0;
262 ContentTuningID = 0;
263 HealthScalingExpansion = 0;
264 HealthModifier = 1.f;
265 ManaModifier = 1.f;
266 ArmorModifier = 1.f;
267 DamageModifier = 1.f;
268 CreatureDifficultyID = 0;
269 TypeFlags = 0;
270 TypeFlags2 = 0;
271 LootID = 0;
272 PickPocketLootID = 0;
273 SkinLootID = 0;
274 GoldMin = 0;
275 GoldMax = 0;
276 }
277 };
278 static const DefaultCreatureDifficulty defDifficulty;
279 return &defDifficulty;
280}
281
282bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
283{
285 {
286 while (!m_assistants.empty())
287 {
289 m_assistants.pop_front();
290
291 if (assistant && assistant->CanAssistTo(&m_owner, victim))
292 {
293 assistant->SetNoCallAssistance(true);
294 assistant->EngageWithTarget(victim);
295 }
296 }
297 }
298 return true;
299}
300
302{
303 return sObjectMgr->GetCreatureBaseStats(level, unitClass);
304}
305
307{
308 m_owner.DespawnOrUnsummon(0s, m_respawnTimer); // since we are here, we are not TempSummon as object type cannot change during runtime
309 return true;
310}
311
312Creature::Creature(bool isWorldObject) : Unit(isWorldObject), MapObject(), m_PlayerDamageReq(0), m_dontClearTapListOnEvade(false), _pickpocketLootRestore(0),
313 m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_ignoreCorpseDecayRatio(false), m_wanderDistance(0.0f),
314 m_boundaryCheckTime(2500), m_reactState(REACT_AGGRESSIVE),
315 m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0),
316 m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0),
317 m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(),
318 m_creatureInfo(nullptr), m_creatureData(nullptr), m_creatureDifficulty(nullptr), m_stringIds(), _waypointPathId(0), _currentWaypointNodeInfo(0, 0),
319 m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _aggroGracePeriodExpired(false), _lastDamagedTime(0),
320 _regenerateHealth(true), _creatureImmunitiesId(0), _gossipMenuId(0), _sparringHealthPct(0)
321{
323
324 for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
325 m_spells[i] = 0;
326
327 DisableReputationGain = false;
328
330 m_CombatDistance = 0;//MELEE_RANGE;
331
332 ResetLootMode(); // restore default loot mode
333 m_isTempWorldObject = false;
334}
335
336Creature::~Creature() = default;
337
339{
341 if (!IsInWorld())
342 {
344 if (m_spawnId)
345 GetMap()->GetCreatureBySpawnIdStore().insert(std::make_pair(m_spawnId, this));
346
350 if (IsVehicle())
352
353 if (GetZoneScript())
355 }
356}
357
359{
360 if (IsInWorld())
361 {
362 if (GetZoneScript())
364
365 if (m_formation)
367
369
370 if (m_spawnId)
371 Trinity::Containers::MultimapErasePair(GetMap()->GetCreatureBySpawnIdStore(), m_spawnId, this);
373 }
374}
375
377{
378 if (GetMotionMaster()->GetCurrentMovementGeneratorType() == HOME_MOTION_TYPE)
379 return true;
380
381 return false;
382}
383
385{
386 if (IsSummon())
387 return;
388
390 if (!lowguid)
391 return;
392
393 if (FormationInfo const* formationInfo = sFormationMgr->GetFormationInfo(lowguid))
394 FormationMgr::AddCreatureToGroup(formationInfo->LeaderSpawnId, this);
395}
396
398{
399 if (!m_formation)
400 return false;
401
402 return m_formation->IsLeader(this);
403}
404
406{
407 if (!m_formation)
408 return;
409
410 if (!m_formation->IsLeader(this))
411 return;
412
414}
415
417{
418 if (!m_formation)
419 return false;
420
422}
423
424void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers)
425{
426 if (getDeathState() != CORPSE)
427 return;
428
430 {
434 m_loot = nullptr;
435 uint32 respawnDelay = m_respawnDelay;
436 if (CreatureAI* ai = AI())
437 ai->CorpseRemoved(respawnDelay);
438
439 if (destroyForNearbyPlayers)
441
442 // Should get removed later, just keep "compatibility" with scripts
443 if (setSpawnTime)
444 m_respawnTime = std::max<time_t>(GameTime::GetGameTime() + respawnDelay, m_respawnTime);
445
446 // if corpse was removed during falling, the falling will continue and override relocation to respawn position
447 if (IsFalling())
448 StopMoving();
449
450 float x, y, z, o;
451 GetRespawnPosition(x, y, z, &o);
452
453 // We were spawned on transport, calculate real position
455 {
457 pos.m_positionX = x;
458 pos.m_positionY = y;
459 pos.m_positionZ = z;
460 pos.SetOrientation(o);
461
462 if (TransportBase* transport = GetDirectTransport())
463 transport->CalculatePassengerPosition(x, y, z, &o);
464 }
465
466 UpdateAllowedPositionZ(x, y, z);
467 SetHomePosition(x, y, z, o);
468 GetMap()->CreatureRelocation(this, x, y, z, o);
469 }
470 else
471 {
472 if (CreatureAI* ai = AI())
473 ai->CorpseRemoved(m_respawnDelay);
474
475 // In case this is called directly and normal respawn timer not set
476 // Since this timer will be longer than the already present time it
477 // will be ignored if the correct place added a respawn timer
478 if (setSpawnTime)
479 {
480 uint32 respawnDelay = m_respawnDelay;
481 m_respawnTime = std::max<time_t>(GameTime::GetGameTime() + respawnDelay, m_respawnTime);
482
484 }
485
486 if (TempSummon* summon = ToTempSummon())
487 summon->UnSummon();
488 else
490 }
491}
492
496bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
497{
498 CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(entry);
499 if (!creatureInfo)
500 {
501 TC_LOG_ERROR("sql.sql", "Creature::InitEntry creature entry {} does not exist.", entry);
502 return false;
503 }
504
505 m_creatureInfo = creatureInfo;
506 SetEntry(entry);
507 m_creatureDifficulty = creatureInfo->GetDifficulty(!IsPet() ? GetMap()->GetDifficultyID() : DIFFICULTY_NONE);
508
509 // equal to player Race field, but creature does not have race
511
512 // known valid are: CLASS_WARRIOR, CLASS_PALADIN, CLASS_ROGUE, CLASS_MAGE
513 SetClass(uint8(creatureInfo->unit_class));
514
515 // Cancel load if no model defined
516 if (!(creatureInfo->GetFirstValidModel()))
517 {
518 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has no model defined in table `creature_template`, can't load. ", entry);
519 return false;
520 }
521
522 CreatureModel model = *ObjectMgr::ChooseDisplayId(creatureInfo, data);
523 CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&model, creatureInfo);
524 if (!minfo) // Cancel load if no model defined
525 {
526 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid model {} defined in table `creature_template_model`, can't load.", entry, model.CreatureDisplayID);
527 return false;
528 }
529
530 SetDisplayId(model.CreatureDisplayID, true);
531
532 // Load creature equipment
533 if (!data)
534 LoadEquipment(); // use default equipment (if available) for summons
535 else if (data->equipmentId == 0)
536 LoadEquipment(0); // 0 means no equipment for creature table
537 else
538 {
541 }
542
543 SetName(creatureInfo->Name); // at normal entry always
544
545 SetModCastingSpeed(1.0f);
546 SetModSpellHaste(1.0f);
547 SetModHaste(1.0f);
548 SetModRangedHaste(1.0f);
549 SetModHasteRegen(1.0f);
550 SetModTimeRate(1.0f);
552
553 SetSpeedRate(MOVE_WALK, creatureInfo->speed_walk);
554 SetSpeedRate(MOVE_RUN, creatureInfo->speed_run);
555 SetSpeedRate(MOVE_SWIM, 1.0f); // using 1.0 rate
556 SetSpeedRate(MOVE_FLIGHT, 1.0f); // using 1.0 rate
557
558 // Will set UNIT_FIELD_BOUNDINGRADIUS, UNIT_FIELD_COMBATREACH and UNIT_FIELD_DISPLAYSCALE
560
562
563 // checked at loading
567
568 for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
570
572 ApplyAllStaticFlags(staticFlags);
573
575 || IsPet()
576 || IsTotem()
577 || creatureInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_XP);
578
579 // TODO: migrate these in DB
584
585 return true;
586}
587
588bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, bool updateLevel /* = true */)
589{
590 if (!InitEntry(entry, data))
591 return false;
592
593 CreatureTemplate const* cInfo = GetCreatureTemplate();
594
596
597 // creatures always have melee weapon ready if any unless specified otherwise
598 if (!GetCreatureAddon())
600
601 SetFaction(cInfo->faction);
602
603 uint64 npcFlags;
604 uint32 unitFlags, unitFlags2, unitFlags3;
605 ObjectMgr::ChooseCreatureFlags(cInfo, &npcFlags, &unitFlags, &unitFlags2, &unitFlags3, _staticFlags, data);
606
608 npcFlags |= sGameEventMgr->GetNPCFlag(this);
609
610 if (IsVendor() && !(npcFlags & UNIT_NPC_FLAG_VENDOR_MASK))
612
613 ReplaceAllNpcFlags(NPCFlags(npcFlags & 0xFFFFFFFF));
614 ReplaceAllNpcFlags2(NPCFlags2(npcFlags >> 32));
615
616 if (npcFlags & UNIT_NPC_FLAG_VENDOR_MASK)
618
619 SetPetitioner((npcFlags & UNIT_NPC_FLAG_PETITIONER) != 0);
620
621 // if unit is in combat, keep this flag
622 unitFlags &= ~UNIT_FLAG_IN_COMBAT;
623 if (IsInCombat())
624 unitFlags |= UNIT_FLAG_IN_COMBAT;
625
626 ReplaceAllUnitFlags(UnitFlags(unitFlags));
627 ReplaceAllUnitFlags2(UnitFlags2(unitFlags2));
628 ReplaceAllUnitFlags3(UnitFlags3(unitFlags3));
629
631
633
635
639
640 if (updateLevel)
641 SelectLevel();
642 else if (!IsGuardian())
643 {
644 uint32 previousHealth = GetHealth();
645 UpdateLevelDependantStats(); // We still re-initialize level dependant stats on entry update
646 if (previousHealth > 0)
647 SetHealth(previousHealth);
648 }
649
650 // Do not update guardian stats here - they are handled in Guardian::InitStatsForLevel()
651 if (!IsGuardian())
652 {
660
661 SetCanModifyStats(true);
663 }
664
665 // checked and error show at loading templates
666 if (FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction))
667 {
668 SetPvP((factionTemplate->Flags & FACTION_TEMPLATE_FLAG_PVP) != 0);
669 if (IsTaxi())
670 {
671 uint32 taxiNodesId = sObjectMgr->GetNearestTaxiNode(GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(),
672 factionTemplate->FactionGroup & FACTION_MASK_ALLIANCE ? ALLIANCE : HORDE);
674 }
675 }
676
677 // updates spell bars for vehicles and set player's faction - should be called here, to overwrite faction that is set from the new template
678 if (IsVehicle())
679 {
680 if (Player* owner = Creature::GetCharmerOrOwnerPlayerOrPlayerItself()) // this check comes in case we don't have a player
681 {
682 SetFaction(owner->GetFaction()); // vehicles should have same as owner faction
683 owner->VehicleSpellInitialize();
684 }
685 }
686
687 // trigger creature is always uninteractible and can not be attacked
688 if (IsTrigger())
689 SetUninteractible(true);
690
693
695
697 {
700 }
701
703
705
709
711
712 //We must update last scriptId or it looks like we reloaded a script, breaking some things such as gossip temporarily
714
716
717 return true;
718}
719
721{
722 CreatureStaticFlagsOverride const* staticFlagsOverride = sObjectMgr->GetCreatureStaticFlagsOverride(spawnId, difficultyId);
723 if (!staticFlagsOverride)
724 return creatureDifficulty->StaticFlags;
725
727 staticFlagsOverride->StaticFlags1.value_or(creatureDifficulty->StaticFlags.GetFlags()),
728 staticFlagsOverride->StaticFlags2.value_or(creatureDifficulty->StaticFlags.GetFlags2()),
729 staticFlagsOverride->StaticFlags3.value_or(creatureDifficulty->StaticFlags.GetFlags3()),
730 staticFlagsOverride->StaticFlags4.value_or(creatureDifficulty->StaticFlags.GetFlags4()),
731 staticFlagsOverride->StaticFlags5.value_or(creatureDifficulty->StaticFlags.GetFlags5()),
732 staticFlagsOverride->StaticFlags6.value_or(creatureDifficulty->StaticFlags.GetFlags6()),
733 staticFlagsOverride->StaticFlags7.value_or(creatureDifficulty->StaticFlags.GetFlags7()),
734 staticFlagsOverride->StaticFlags8.value_or(creatureDifficulty->StaticFlags.GetFlags8()));
735}
736
738{
740
741 // Apply all other side effects of flag changes
743}
744
746{
748 {
751
754 m_triggerJustAppeared = false;
755 AI()->JustAppeared();
756 }
757
759
760 GetThreatManager().Update(diff);
761
762 switch (m_deathState)
763 {
764 case JUST_RESPAWNED:
765 // Must not be called, see Creature::setDeathState JUST_RESPAWNED -> ALIVE promoting.
766 TC_LOG_ERROR("entities.unit", "Creature {} in wrong state: JUST_RESPAWNED (4)", GetGUID().ToString());
767 break;
768 case JUST_DIED:
769 // Must not be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
770 TC_LOG_ERROR("entities.unit", "Creature {} in wrong state: JUST_DIED (1)", GetGUID().ToString());
771 break;
772 case DEAD:
773 {
775 {
776 TC_LOG_ERROR("entities.unit", "Creature {} in wrong state: DEAD (3)", GetGUID().ToString());
777 break;
778 }
779 time_t now = GameTime::GetGameTime();
780 if (m_respawnTime <= now)
781 {
782 // Delay respawn if spawn group is not active
783 if (m_creatureData && !GetMap()->IsSpawnGroupActive(m_creatureData->spawnGroupData->groupId))
784 {
785 m_respawnTime = now + urand(4,7);
786 break; // Will be rechecked on next Update call after delay expires
787 }
788
789 ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Creature>(GetMapId(), GetEntry(), m_spawnId);
790 time_t linkedRespawnTime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
791 if (!linkedRespawnTime) // Can respawn
792 Respawn();
793 else // the master is dead
794 {
795 ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
796 if (targetGuid == dbtableHighGuid) // if linking self, never respawn
798 else
799 {
800 // else copy time from master and add a little
801 time_t baseRespawnTime = std::max(linkedRespawnTime, now);
802 time_t const offset = urand(5, MINUTE);
803
804 // linked guid can be a boss, uses std::numeric_limits<time_t>::max to never respawn in that instance
805 // we shall inherit it instead of adding and causing an overflow
806 if (baseRespawnTime <= std::numeric_limits<time_t>::max() - offset)
807 m_respawnTime = baseRespawnTime + offset;
808 else
809 m_respawnTime = std::numeric_limits<time_t>::max();
810 }
811 SaveRespawnTime(); // also save to DB immediately
812 }
813 }
814 break;
815 }
816 case CORPSE:
817 {
818 Unit::Update(diff);
819 // deathstate changed on spells update, prevent problems
820 if (m_deathState != CORPSE)
821 break;
822
823 if (IsEngaged())
824 Unit::AIUpdateTick(diff);
825
826 if (m_loot)
827 m_loot->Update();
828
829 for (auto&& [playerOwner, loot] : m_personalLoot)
830 loot->Update();
831
833 {
834 RemoveCorpse(false);
835 TC_LOG_DEBUG("entities.unit", "Removing corpse... {} ", GetEntry());
836 }
837 break;
838 }
839 case ALIVE:
840 {
841 Unit::Update(diff);
842
843 // creature can be dead after Unit::Update call
844 // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
845 if (!IsAlive())
846 break;
847
848 if (_spellFocusInfo.Delay)
849 {
850 if (_spellFocusInfo.Delay <= diff)
852 else
853 _spellFocusInfo.Delay -= diff;
854 }
855
856 // periodic check to see if the creature has passed an evade boundary
857 if (IsAIEnabled() && !IsInEvadeMode() && IsEngaged())
858 {
859 if (diff >= m_boundaryCheckTime)
860 {
861 AI()->CheckInRoom();
862 m_boundaryCheckTime = 2500;
863 } else
864 m_boundaryCheckTime -= diff;
865 }
866
867 Unit::AIUpdateTick(diff);
868
870
871 // creature can be dead after UpdateAI call
872 // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
873 if (!IsAlive())
874 break;
875
876 if (m_regenTimer > 0)
877 {
878 if (diff >= m_regenTimer)
879 m_regenTimer = 0;
880 else
881 m_regenTimer -= diff;
882 }
883
884 if (m_regenTimer == 0)
885 {
886 if (!IsInEvadeMode())
887 {
888 // regenerate health if not in combat or if polymorphed)
889 if (!IsEngaged() || IsPolymorphed())
891 else if (CanNotReachTarget())
892 {
893 // regenerate health if cannot reach the target and the setting is set to do so.
894 // this allows to disable the health regen of raid bosses if pathfinding has issues for whatever reason
895 if (sWorld->getBoolConfig(CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID) || !GetMap()->IsRaid())
896 {
898 TC_LOG_DEBUG("entities.unit.chase", "RegenerateHealth() enabled because Creature cannot reach the target. Detail: {}", GetDebugInfo());
899 }
900 else
901 TC_LOG_DEBUG("entities.unit.chase", "RegenerateHealth() disabled even if the Creature cannot reach the target. Detail: {}", GetDebugInfo());
902 }
903 }
904
905 if (GetPowerType() == POWER_ENERGY)
907 else
909
911 }
912
913 if (CanNotReachTarget() && !IsInEvadeMode() && !GetMap()->IsRaid())
914 {
915 m_cannotReachTimer += diff;
917 if (CreatureAI* ai = AI())
918 ai->EnterEvadeMode(EvadeReason::NoPath);
919 }
920 break;
921 }
922 default:
923 break;
924 }
925}
926
928{
930
931 // Creatures with CREATURE_STATIC_FLAG_2_FORCE_PARTY_MEMBERS_INTO_COMBAT periodically force party members into combat
933
934 // creatures should only attack surroundings initially after heartbeat has passed or until attacked
936 {
938
939 // trigger MoveInLineOfSight
941 Cell::VisitAllObjects(this, notifier, GetVisibilityRange());
942 }
943}
944
946{
947 uint32 curValue = GetPower(power);
948 uint32 maxValue = GetMaxPower(power);
949
951 return;
952
953 if (curValue >= maxValue)
954 return;
955
956 float addvalue = 0.0f;
957
958 switch (power)
959 {
960 case POWER_FOCUS:
961 {
962 // For hunter pets.
963 addvalue = 24 * sWorld->getRate(RATE_POWER_FOCUS);
964 break;
965 }
966 case POWER_ENERGY:
967 {
968 // For deathknight's ghoul.
969 addvalue = 20;
970 break;
971 }
972 case POWER_MANA:
973 {
974 // Combat and any controlled creature
975 if (IsInCombat() || GetCharmerOrOwnerGUID().IsEmpty())
976 {
977 float ManaIncreaseRate = sWorld->getRate(RATE_POWER_MANA);
978
979 addvalue = uint32((27.0f / 5.0f + 17.0f) * ManaIncreaseRate);
980 }
981 else
982 addvalue = maxValue / 3;
983
984 break;
985 }
986 default:
987 return;
988 }
989
990 // Apply modifiers (if any).
992
994
995 ModifyPower(power, int32(addvalue));
996}
997
999{
1000 if (!CanRegenerateHealth())
1001 return;
1002
1003 uint32 curValue = GetHealth();
1004 uint32 maxValue = GetMaxHealth();
1005
1006 if (curValue >= maxValue)
1007 return;
1008
1009 uint32 addvalue = 0;
1010
1011 // Not only pet, but any controlled creature (and not polymorphed)
1012 if (!GetCharmerOrOwnerGUID().IsEmpty() && !IsPolymorphed())
1013 {
1014 float HealthIncreaseRate = sWorld->getRate(RATE_HEALTH);
1015
1016 addvalue = 0.015f * ((float)GetMaxHealth()) * HealthIncreaseRate;
1017 }
1018 else
1019 addvalue = maxValue/3;
1020
1021 // Apply modifiers (if any).
1023
1025
1026 ModifyHealth(addvalue);
1027}
1028
1030{
1031 if (!GetVictim())
1032 return;
1033
1035 return;
1036
1037 float radius = sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS);
1038 if (radius >0)
1039 {
1040 Creature* creature = nullptr;
1043 Cell::VisitGridObjects(this, searcher, radius);
1044
1046
1047 if (!creature)
1050 else
1051 GetMotionMaster()->MoveSeekAssistance(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ());
1052 }
1053}
1054
1056{
1057 PopAI();
1058 RefreshAI();
1059 return true;
1060}
1061
1062bool Creature::AIM_Create(CreatureAI* ai /*= nullptr*/)
1063{
1065
1066 SetAI(ai ? ai : FactorySelector::SelectAI(this));
1067
1068 return true;
1069}
1070
1072{
1073 if (!AIM_Create(ai))
1074 return false;
1075
1076 AI()->InitializeAI();
1077 if (GetVehicleKit())
1078 GetVehicleKit()->Reset();
1079 return true;
1080}
1081
1083{
1084 if (m_formation)
1085 {
1086 if (m_formation->GetLeader() == this)
1088 else if (m_formation->IsFormed())
1089 {
1090 GetMotionMaster()->MoveIdle(); // wait the order of leader
1091 return;
1092 }
1093 }
1094
1096}
1097
1098bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, Position const& pos, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/, bool dynamic)
1099{
1100 ASSERT(map);
1101 SetMap(map);
1102
1103 if (data)
1104 {
1107 }
1108
1109 // Set if this creature can handle dynamic spawns
1110 if (!dynamic)
1112
1113 CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
1114 if (!cinfo)
1115 {
1116 TC_LOG_ERROR("sql.sql", "Creature::Create(): creature template (guidlow: {}, entry: {}) does not exist.", guidlow, entry);
1117 return false;
1118 }
1119
1120 CreatureDifficulty const* creatureDifficulty = cinfo->GetDifficulty(GetMap()->GetDifficultyID());
1121
1124 Relocate(pos);
1125
1126 // Check if the position is valid before calling CreateFromProto(), otherwise we might add Auras to Creatures at
1127 // invalid position, triggering a crash about Auras not removed in the destructor
1128 if (!IsPositionValid())
1129 {
1130 TC_LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow {}, entry {}) are not valid (X: {}, Y: {}, Z: {}, O: {})", guidlow, entry, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation());
1131 return false;
1132 }
1133 {
1134 // area/zone id is needed immediately for ZoneScript::GetCreatureEntry hook before it is known which creature template to load (no model/scale available yet)
1135 PositionFullTerrainStatus terrainStatus;
1137 ProcessPositionDataChanged(terrainStatus);
1138 }
1139
1140 // Allow players to see those units while dead, do it here (mayby altered by addon auras)
1141 if (creatureDifficulty->TypeFlags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS)
1143
1144 if (!CreateFromProto(guidlow, entry, data, vehId))
1145 return false;
1146
1147 cinfo = GetCreatureTemplate(); // might be different than initially requested
1149 m_respawnDelay = 0; // special value, prevents respawn for dungeon bosses unless overridden
1150
1151 switch (GetCreatureClassification())
1152 {
1155 break;
1158 break;
1161 break;
1164 break;
1167 break;
1170 break;
1171 default:
1173 break;
1174 }
1175
1178
1181
1183
1185 {
1188 }
1189
1192
1194 {
1197 }
1198
1200
1201 return true;
1202}
1203
1204Creature* Creature::CreateCreature(uint32 entry, Map* map, Position const& pos, uint32 vehId /*= 0*/)
1205{
1206 CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(entry);
1207 if (!cInfo)
1208 return nullptr;
1209
1210 ObjectGuid::LowType lowGuid;
1211 if (vehId || cInfo->VehicleId)
1212 lowGuid = map->GenerateLowGuid<HighGuid::Vehicle>();
1213 else
1214 lowGuid = map->GenerateLowGuid<HighGuid::Creature>();
1215
1216 Creature* creature = new Creature();
1217 if (!creature->Create(lowGuid, map, entry, pos, nullptr, vehId))
1218 {
1219 delete creature;
1220 return nullptr;
1221 }
1222
1223 return creature;
1224}
1225
1226Creature* Creature::CreateCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap /*= true*/, bool allowDuplicate /*= false*/)
1227{
1228 Creature* creature = new Creature();
1229 if (!creature->LoadFromDB(spawnId, map, addToMap, allowDuplicate))
1230 {
1231 delete creature;
1232 return nullptr;
1233 }
1234
1235 return creature;
1236}
1237
1239{
1240 Unit* target = nullptr;
1241
1242 if (CanHaveThreatList())
1244 else if (!HasReactState(REACT_PASSIVE))
1245 {
1246 // We're a player pet, probably
1247 target = getAttackerForHelper();
1248 if (!target && IsSummon())
1249 {
1250 if (Unit* owner = ToTempSummon()->GetOwner())
1251 {
1252 if (owner->IsInCombat())
1253 target = owner->getAttackerForHelper();
1254 if (!target)
1255 {
1256 for (ControlList::const_iterator itr = owner->m_Controlled.begin(); itr != owner->m_Controlled.end(); ++itr)
1257 {
1258 if ((*itr)->IsInCombat())
1259 {
1260 target = (*itr)->getAttackerForHelper();
1261 if (target)
1262 break;
1263 }
1264 }
1265 }
1266 }
1267 }
1268 }
1269 else
1270 return nullptr;
1271
1272 if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target))
1273 {
1274 if (!HasSpellFocus())
1275 SetInFront(target);
1276 return target;
1277 }
1278
1280 if (GetVehicle())
1281 return nullptr;
1282
1284 if (!iAuras.empty())
1285 {
1286 for (Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr)
1287 {
1288 if ((*itr)->GetBase()->IsPermanent())
1289 {
1291 break;
1292 }
1293 }
1294 return nullptr;
1295 }
1296
1297 // enter in evade mode in other case
1299
1300 return nullptr;
1301}
1302
1304{
1307 /*
1308 else if (IsCivilian())
1309 SetReactState(REACT_DEFENSIVE);
1310 */
1311 else
1313}
1314
1316{
1317 if (!IsBattleMaster())
1318 return false;
1319
1320 BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(GetEntry());
1321 if (!msg)
1322 return player->GetBGAccessByLevel(bgTypeId);
1323
1324 if (!player->GetBGAccessByLevel(bgTypeId))
1325 {
1326 ClearGossipMenuFor(player);
1327 switch (bgTypeId)
1328 {
1329 case BATTLEGROUND_AV: SendGossipMenuFor(player, 7616, this); break;
1330 case BATTLEGROUND_WS: SendGossipMenuFor(player, 7599, this); break;
1331 case BATTLEGROUND_AB: SendGossipMenuFor(player, 7642, this); break;
1332 case BATTLEGROUND_EY:
1333 case BATTLEGROUND_NA:
1334 case BATTLEGROUND_BE:
1335 case BATTLEGROUND_AA:
1336 case BATTLEGROUND_RL:
1337 case BATTLEGROUND_SA:
1338 case BATTLEGROUND_DS:
1339 case BATTLEGROUND_RV: SendGossipMenuFor(player, 10024, this); break;
1340 default: break;
1341 }
1342 return false;
1343 }
1344 return true;
1345}
1346
1348{
1349 return player->GetLevel() >= 15
1350 && player->GetClass() == GetCreatureTemplate()->trainer_class;
1351}
1352
1354{
1355 if (m_lootId)
1356 return *m_lootId;
1357
1358 return GetCreatureDifficulty()->LootID;
1359}
1360
1362{
1363 m_lootId = lootId;
1364}
1365
1366void Creature::SetTappedBy(Unit const* unit, bool withGroup)
1367{
1368 // set the player whose group should receive the right
1369 // to loot the creature after it dies
1370 // should be set to nullptr after the loot disappears
1371
1372 if (!unit)
1373 {
1374 m_tapList.clear();
1376 return;
1377 }
1378
1380 return;
1381
1382 if (unit->GetTypeId() != TYPEID_PLAYER && !unit->IsVehicle())
1383 return;
1384
1386 if (!player) // normal creature, no player involved
1387 return;
1388
1389 m_tapList.insert(player->GetGUID());
1390 if (withGroup)
1391 if (Group const* group = player->GetGroup())
1392 for (auto const* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
1393 if (GetMap()->IsRaid() || group->SameSubGroup(player, itr->GetSource()))
1394 m_tapList.insert(itr->GetSource()->GetGUID());
1395
1398}
1399
1401{
1402 // only temporary summons are allowed to not clear their tap list
1403 if (!m_spawnId)
1404 m_dontClearTapListOnEvade = dontClear;
1405}
1406
1407// return true if this creature is tapped by the player or by a member of his group.
1408bool Creature::isTappedBy(Player const* player) const
1409{
1410 return m_tapList.find(player->GetGUID()) != m_tapList.end();
1411}
1412
1414{
1415 if (m_personalLoot.empty())
1416 return m_loot.get();
1417
1419 return loot;
1420
1421 return nullptr;
1422}
1423
1425{
1426 if (m_loot && !m_loot->isLooted())
1427 return false;
1428
1429 for (auto const& [_, loot] : m_personalLoot)
1430 if (!loot->isLooted())
1431 return false;
1432
1433 return true;
1434}
1435
1436bool Creature::IsSkinnedBy(Player const* player) const
1437{
1438 if (Loot* loot = GetLootForPlayer(player))
1439 return loot->loot_type == LOOT_SKINNING;
1440
1441 return false;
1442}
1443
1445{
1446 // this should only be used when the creature has already been loaded
1447 // preferably after adding to map, because mapid may not be valid otherwise
1448 CreatureData const* data = sObjectMgr->GetCreatureData(m_spawnId);
1449 if (!data)
1450 {
1451 TC_LOG_ERROR("entities.unit", "Creature::SaveToDB failed, cannot get creature data!");
1452 return;
1453 }
1454
1455 uint32 mapId = GetMapId();
1456 if (TransportBase* transport = GetTransport())
1457 if (transport->GetMapIdForSpawning() >= 0)
1458 mapId = transport->GetMapIdForSpawning();
1459
1460 SaveToDB(mapId, data->spawnDifficulties);
1461}
1462
1463void Creature::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDifficulties)
1464{
1465 // update in loaded data
1466 if (!m_spawnId)
1467 m_spawnId = sObjectMgr->GenerateCreatureSpawnId();
1468
1469 CreatureData& data = sObjectMgr->NewOrExistCreatureData(m_spawnId);
1470
1471 uint32 displayId = GetNativeDisplayId();
1472 uint64 spawnNpcFlags = (uint64(GetNpcFlags2()) << 32) | GetNpcFlags();
1473 Optional<uint64> npcflag;
1474 Optional<uint32> unitFlags;
1475 Optional<uint32> unitFlags2;
1476 Optional<uint32> unitFlags3;
1477
1478 // check if it's a custom model and if not, use 0 for displayId
1479 CreatureTemplate const* cinfo = GetCreatureTemplate();
1480 if (cinfo)
1481 {
1482 for (CreatureModel const& model : cinfo->Models)
1483 if (displayId && displayId == model.CreatureDisplayID)
1484 displayId = 0;
1485
1486 if (spawnNpcFlags != cinfo->npcflag)
1487 npcflag = spawnNpcFlags;
1488
1489 if (m_unitData->Flags != cinfo->unit_flags)
1490 unitFlags = m_unitData->Flags;
1491
1492 if (m_unitData->Flags2 != cinfo->unit_flags2)
1493 unitFlags2 = m_unitData->Flags2;
1494
1495 if (m_unitData->Flags3 != cinfo->unit_flags3)
1496 unitFlags3 = m_unitData->Flags3;
1497 }
1498
1499 if (!data.spawnId)
1500 data.spawnId = m_spawnId;
1501 ASSERT(data.spawnId == m_spawnId);
1502 data.id = GetEntry();
1503 if (displayId)
1504 data.display.emplace(displayId, DEFAULT_PLAYER_DISPLAY_SCALE, 1.0f);
1505 else
1506 data.display.reset();
1508 if (!GetTransport())
1509 {
1510 data.mapId = GetMapId();
1511 data.spawnPoint.Relocate(this);
1512 }
1513 else
1514 {
1515 data.mapId = mapid;
1517 }
1519 // prevent add data integrity problems
1521 data.currentwaypoint = 0;
1523 // prevent add data integrity problems
1526 data.spawnDifficulties = spawnDifficulties;
1527 data.npcflag = npcflag;
1528 data.unit_flags = unitFlags;
1529 data.unit_flags2 = unitFlags2;
1530 data.unit_flags3 = unitFlags3;
1531 if (!data.spawnGroupData)
1532 data.spawnGroupData = sObjectMgr->GetDefaultSpawnGroup();
1533
1534 data.phaseId = GetDBPhase() > 0 ? GetDBPhase() : data.phaseId;
1535 data.phaseGroup = GetDBPhase() < 0 ? -GetDBPhase() : data.phaseGroup;
1536
1537 // update in DB
1538 WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
1539
1541 stmt->setUInt64(0, m_spawnId);
1542 trans->Append(stmt);
1543
1544 uint8 index = 0;
1545
1546 stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE);
1547 stmt->setUInt64(index++, m_spawnId);
1548 stmt->setUInt32(index++, GetEntry());
1549 stmt->setUInt16(index++, uint16(mapid));
1550 stmt->setString(index++, [&data]() -> std::string
1551 {
1552 if (data.spawnDifficulties.empty())
1553 return "";
1554
1555 std::ostringstream os;
1556 auto itr = data.spawnDifficulties.begin();
1557 os << int32(*itr++);
1558
1559 for (; itr != data.spawnDifficulties.end(); ++itr)
1560 os << ',' << int32(*itr);
1561
1562 return os.str();
1563 }());
1564 stmt->setUInt32(index++, data.phaseId);
1565 stmt->setUInt32(index++, data.phaseGroup);
1566 stmt->setUInt32(index++, displayId);
1567 stmt->setUInt8(index++, GetCurrentEquipmentId());
1568 stmt->setFloat(index++, GetPositionX());
1569 stmt->setFloat(index++, GetPositionY());
1570 stmt->setFloat(index++, GetPositionZ());
1571 stmt->setFloat(index++, GetOrientation());
1572 stmt->setUInt32(index++, m_respawnDelay);
1573 stmt->setFloat(index++, m_wanderDistance);
1574 stmt->setUInt32(index++, 0);
1575 stmt->setUInt32(index++, uint32(GetHealthPct()));
1576 stmt->setUInt8(index++, uint8(GetDefaultMovementType()));
1577 if (npcflag.has_value())
1578 stmt->setUInt64(index++, *npcflag);
1579 else
1580 stmt->setNull(index++);
1581
1582 if (unitFlags.has_value())
1583 stmt->setUInt32(index++, *unitFlags);
1584 else
1585 stmt->setNull(index++);
1586
1587 if (unitFlags2.has_value())
1588 stmt->setUInt32(index++, *unitFlags2);
1589 else
1590 stmt->setNull(index++);
1591
1592 if (unitFlags3.has_value())
1593 stmt->setUInt32(index++, *unitFlags3);
1594 else
1595 stmt->setNull(index++);
1596 trans->Append(stmt);
1597
1598 WorldDatabase.CommitTransaction(trans);
1599}
1600
1602{
1603 // Level
1605 int32 levelWithDelta = m_unitData->ScalingLevelMax + m_unitData->ScalingLevelDelta;
1606 uint8 level = RoundToInterval<int32>(levelWithDelta, 1, STRONG_MAX_LEVEL);
1607 SetLevel(level);
1608
1610}
1611
1613{
1614 CreatureTemplate const* cInfo = GetCreatureTemplate();
1616 uint8 level = GetLevel();
1617 CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(level, cInfo->unit_class);
1618
1619 // health
1620 float healthmod = GetHealthMod(classification);
1621
1622 uint32 basehp = GetMaxHealthByLevel(level);
1623 uint32 health = uint32(basehp * healthmod);
1624
1625 SetCreateHealth(health);
1626 SetMaxHealth(health);
1627 SetHealth(health);
1629
1631
1632 // mana
1633 Powers powerType = CalculateDisplayPowerType();
1634 SetCreateMana(stats->BaseMana);
1636 SetPowerType(powerType, true, true);
1637
1638 // damage
1639 float basedamage = GetBaseDamageForLevel(level);
1640
1641 float weaponBaseMinDamage = basedamage;
1642 float weaponBaseMaxDamage = basedamage * 1.5f;
1643
1644 SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, weaponBaseMinDamage);
1645 SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, weaponBaseMaxDamage);
1646
1647 SetBaseWeaponDamage(OFF_ATTACK, MINDAMAGE, weaponBaseMinDamage);
1648 SetBaseWeaponDamage(OFF_ATTACK, MAXDAMAGE, weaponBaseMaxDamage);
1649
1650 SetBaseWeaponDamage(RANGED_ATTACK, MINDAMAGE, weaponBaseMinDamage);
1651 SetBaseWeaponDamage(RANGED_ATTACK, MAXDAMAGE, weaponBaseMaxDamage);
1652
1655
1656 float armor = GetBaseArmorForLevel(level);
1658}
1659
1661{
1662 if (IsWildBattlePet())
1663 {
1664 uint8 wildBattlePetLevel = WILD_BATTLE_PET_DEFAULT_LEVEL;
1665
1666 if (AreaTableEntry const* areaTable = sAreaTableStore.LookupEntry(GetZoneId()))
1667 if (areaTable->WildBattlePetLevelMin > 0)
1668 wildBattlePetLevel = urand(areaTable->WildBattlePetLevelMin, areaTable->WildBattlePetLevelMax);
1669
1670 SetWildBattlePetLevel(wildBattlePetLevel);
1671 }
1672}
1673
1675{
1676 switch (classification)
1677 {
1679 return sWorld->getRate(RATE_CREATURE_HP_NORMAL);
1681 return sWorld->getRate(RATE_CREATURE_HP_ELITE);
1683 return sWorld->getRate(RATE_CREATURE_HP_RAREELITE);
1685 return sWorld->getRate(RATE_CREATURE_HP_OBSOLETE);
1687 return sWorld->getRate(RATE_CREATURE_HP_RARE);
1689 return sWorld->getRate(RATE_CREATURE_HP_TRIVIAL);
1691 return sWorld->getRate(RATE_CREATURE_HP_MINUSMOB);
1692 default:
1693 return sWorld->getRate(RATE_CREATURE_HP_ELITE);
1694 }
1695}
1696
1698{
1700 m_PlayerDamageReq > unDamage ? m_PlayerDamageReq -= unDamage : m_PlayerDamageReq = 0;
1701}
1702
1704{
1705 switch (classification)
1706 {
1708 return sWorld->getRate(RATE_CREATURE_DAMAGE_NORMAL);
1710 return sWorld->getRate(RATE_CREATURE_DAMAGE_ELITE);
1712 return sWorld->getRate(RATE_CREATURE_DAMAGE_RAREELITE);
1714 return sWorld->getRate(RATE_CREATURE_DAMAGE_OBSOLETE);
1716 return sWorld->getRate(RATE_CREATURE_DAMAGE_RARE);
1718 return sWorld->getRate(RATE_CREATURE_DAMAGE_TRIVIAL);
1720 return sWorld->getRate(RATE_CREATURE_DAMAGE_MINUSMOB);
1721 default:
1722 return sWorld->getRate(RATE_CREATURE_DAMAGE_ELITE);
1723 }
1724}
1725
1727{
1728 switch (classification)
1729 {
1733 return sWorld->getRate(RATE_CREATURE_SPELLDAMAGE_ELITE);
1739 return sWorld->getRate(RATE_CREATURE_SPELLDAMAGE_RARE);
1744 default:
1745 return sWorld->getRate(RATE_CREATURE_SPELLDAMAGE_ELITE);
1746 }
1747}
1748
1749void Creature::OverrideSparringHealthPct(std::vector<float> const& healthPct)
1750{
1752}
1753
1755{
1756 if (GetSparringHealthPct() == 0)
1757 return damage;
1758
1759 if (!attacker)
1760 return damage;
1761
1763 return damage;
1764
1766 return 0;
1767
1768 uint32 sparringHealth = GetMaxHealth() * GetSparringHealthPct() / 100;
1769 if (GetHealth() - damage <= sparringHealth)
1770 return GetHealth() - sparringHealth;
1771
1772 if (damage >= GetHealth())
1773 return GetHealth() - 1;
1774
1775 return damage;
1776}
1777
1779{
1780 if (!GetSparringHealthPct())
1781 return false;
1782
1783 if (!attacker)
1784 return false;
1785
1786 if (!attacker->IsCreature())
1787 return false;
1788
1790 return false;
1791
1793 return false;
1794
1795 return true;
1796}
1797
1798bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const* data /*= nullptr*/, uint32 vehId /*= 0*/)
1799{
1800 SetZoneScript();
1801 if (GetZoneScript() && data)
1802 {
1803 entry = GetZoneScript()->GetCreatureEntry(guidlow, data);
1804 if (!entry)
1805 return false;
1806 }
1807
1808 CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry);
1809 if (!cinfo)
1810 {
1811 TC_LOG_ERROR("sql.sql", "Creature::CreateFromProto(): creature template (guidlow: {}, entry: {}) does not exist.", guidlow, entry);
1812 return false;
1813 }
1814
1815 SetOriginalEntry(entry);
1816
1817 if (vehId || cinfo->VehicleId)
1818 Object::_Create(ObjectGuid::Create<HighGuid::Vehicle>(GetMapId(), entry, guidlow));
1819 else
1820 Object::_Create(ObjectGuid::Create<HighGuid::Creature>(GetMapId(), entry, guidlow));
1821
1822 if (!UpdateEntry(entry, data))
1823 return false;
1824
1825 if (!vehId)
1826 {
1827 if (GetCreatureTemplate()->VehicleId)
1828 {
1829 vehId = GetCreatureTemplate()->VehicleId;
1830 entry = GetCreatureTemplate()->Entry;
1831 }
1832 else
1833 vehId = cinfo->VehicleId;
1834 }
1835
1836 if (vehId)
1837 if (CreateVehicleKit(vehId, entry, true))
1839
1840 if (!IsPet())
1841 if (uint32 vignetteId = GetCreatureTemplate()->VignetteID)
1842 SetVignette(vignetteId);
1843
1844 return true;
1845}
1846
1847bool Creature::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate)
1848{
1849 if (!allowDuplicate)
1850 {
1851 // If an alive instance of this spawnId is already found, skip creation
1852 // If only dead instance(s) exist, despawn them and spawn a new (maybe also dead) version
1853 const auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(spawnId);
1854 std::vector <Creature*> despawnList;
1855
1856 if (creatureBounds.first != creatureBounds.second)
1857 {
1858 for (auto itr = creatureBounds.first; itr != creatureBounds.second; ++itr)
1859 {
1860 if (itr->second->IsAlive())
1861 {
1862 TC_LOG_DEBUG("maps", "Would have spawned {} but {} already exists", spawnId, creatureBounds.first->second->GetGUID().ToString());
1863 return false;
1864 }
1865 else
1866 {
1867 despawnList.push_back(itr->second);
1868 TC_LOG_DEBUG("maps", "Despawned dead instance of spawn {} ({})", spawnId, itr->second->GetGUID().ToString());
1869 }
1870 }
1871
1872 for (Creature* despawnCreature : despawnList)
1873 {
1874 despawnCreature->AddObjectToRemoveList();
1875 }
1876 }
1877 }
1878
1879 CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
1880 if (!data)
1881 {
1882 TC_LOG_ERROR("sql.sql", "Creature (SpawnID {}) not found in table `creature`, can't load. ", spawnId);
1883 return false;
1884 }
1885
1886 m_spawnId = spawnId;
1887
1889 m_creatureData = data;
1892
1893 if (!Create(map->GenerateLowGuid<HighGuid::Creature>(), map, data->id, data->spawnPoint, data, 0U , !m_respawnCompatibilityMode))
1894 return false;
1895
1896 //We should set first home position, because then AI calls home movement
1897 SetHomePosition(*this);
1898
1900
1902
1904 {
1906 {
1907 // @todo pools need fixing! this is just a temporary thing, but they violate dynspawn principles
1908 if (!data->poolId)
1909 {
1910 TC_LOG_ERROR("entities.unit", "Creature (SpawnID {}) trying to load in inactive spawn group '{}':\n{}", spawnId, data->spawnGroupData->name, GetDebugInfo());
1911 return false;
1912 }
1913 }
1914
1916 }
1917
1918 if (m_respawnTime)
1919 {
1921 {
1922 // @todo same as above
1923 if (!data->poolId)
1924 {
1925 TC_LOG_ERROR("entities.unit", "Creature (SpawnID {}) trying to load despite a respawn timer in progress:\n{}", spawnId, GetDebugInfo());
1926 return false;
1927 }
1928 }
1929 else
1930 {
1931 // compatibility mode creatures will be respawned in ::Update()
1933 }
1934
1935 if (CanFly())
1936 {
1937 float tz = map->GetHeight(GetPhaseShift(), data->spawnPoint, true, MAX_FALL_DISTANCE);
1938 if (data->spawnPoint.GetPositionZ() - tz > 0.1f && Trinity::IsValidMapCoord(tz))
1940 }
1941 }
1942
1944
1946
1947 // checked at creature_template loading
1949
1951
1952 if (addToMap && !GetMap()->AddToMap(this))
1953 return false;
1954 return true;
1955}
1956
1958{
1959 Unit::SetCanDualWield(value);
1961}
1962
1963void Creature::LoadEquipment(int8 id, bool force /*= true*/)
1964{
1965 if (id == 0)
1966 {
1967 if (force)
1968 {
1969 for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
1970 SetVirtualItem(i, 0);
1971 m_equipmentId = 0;
1972 }
1973
1974 return;
1975 }
1976
1977 EquipmentInfo const* einfo = sObjectMgr->GetEquipmentInfo(GetEntry(), id);
1978 if (!einfo)
1979 return;
1980
1981 m_equipmentId = id;
1982 for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
1983 SetVirtualItem(i, einfo->Items[i].ItemId, einfo->Items[i].AppearanceModId, einfo->Items[i].ItemVisual);
1984}
1985
1987{
1990}
1991
1992bool Creature::hasQuest(uint32 quest_id) const
1993{
1994 return sObjectMgr->GetCreatureQuestRelations(GetEntry()).HasQuest(quest_id);
1995}
1996
1998{
1999 return sObjectMgr->GetCreatureQuestInvolvedRelations(GetEntry()).HasQuest(quest_id);
2000}
2001
2003{
2004 CreatureData const* data = sObjectMgr->GetCreatureData(spawnId);
2005 if (!data)
2006 return false;
2007
2008 CharacterDatabaseTransaction charTrans = CharacterDatabase.BeginTransaction();
2009
2010 sMapMgr->DoForAllMapsWithMapId(data->mapId,
2011 [spawnId, charTrans](Map* map) -> void
2012 {
2013 // despawn all active creatures, and remove their respawns
2014 std::vector<Creature*> toUnload;
2015 for (auto const& pair : Trinity::Containers::MapEqualRange(map->GetCreatureBySpawnIdStore(), spawnId))
2016 toUnload.push_back(pair.second);
2017 for (Creature* creature : toUnload)
2018 map->AddObjectToRemoveList(creature);
2019 map->RemoveRespawnTime(SPAWN_TYPE_CREATURE, spawnId, charTrans);
2020 }
2021 );
2022
2023 // delete data from memory ...
2024 sObjectMgr->DeleteCreatureData(spawnId);
2025
2026 CharacterDatabase.CommitTransaction(charTrans);
2027
2028 WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
2029
2030 // ... and the database
2032 stmt->setUInt64(0, spawnId);
2033 trans->Append(stmt);
2034
2035 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_SPAWNGROUP_MEMBER);
2037 stmt->setUInt64(1, spawnId);
2038 trans->Append(stmt);
2039
2040 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE_ADDON);
2041 stmt->setUInt64(0, spawnId);
2042 trans->Append(stmt);
2043
2044 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_EVENT_CREATURE);
2045 stmt->setUInt64(0, spawnId);
2046 trans->Append(stmt);
2047
2048 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_EVENT_MODEL_EQUIP);
2049 stmt->setUInt64(0, spawnId);
2050 trans->Append(stmt);
2051
2052 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN);
2053 stmt->setUInt64(0, spawnId);
2055 trans->Append(stmt);
2056
2057 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN);
2058 stmt->setUInt64(0, spawnId);
2060 trans->Append(stmt);
2061
2062 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN_MASTER);
2063 stmt->setUInt64(0, spawnId);
2065 trans->Append(stmt);
2066
2067 stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_LINKED_RESPAWN_MASTER);
2068 stmt->setUInt64(0, spawnId);
2070 trans->Append(stmt);
2071
2072 WorldDatabase.CommitTransaction(trans);
2073
2074 return true;
2075}
2076
2078{
2080 return true;
2081
2083 return false;
2084
2085 return true;
2086}
2087
2089{
2090 if (IsAIEnabled() && AI()->CanSeeAlways(obj))
2091 return true;
2092
2093 return false;
2094}
2095
2096bool Creature::CanStartAttack(Unit const* who, bool force) const
2097{
2098 if (IsCivilian())
2099 return false;
2100
2101 // This set of checks is should be done only for creatures
2104 return false;
2105
2106 // Do not attack non-combat pets
2108 return false;
2109
2111 //|| who->IsControlledByPlayer() && who->IsFlying()))
2112 // we cannot check flying for other creatures, too much map/vmap calculation
2114 return false;
2115
2116 if (!force)
2117 {
2118 if (!_IsTargetAcceptable(who))
2119 return false;
2120
2122 return false;
2123 }
2124
2125 if (!CanCreatureAttack(who, force))
2126 return false;
2127
2128 // No aggro from gray creatures
2130 return false;
2131
2132 return IsWithinLOSInMap(who);
2133}
2134
2135bool Creature::CheckNoGrayAggroConfig(uint32 playerLevel, uint32 creatureLevel) const
2136{
2137 if (Trinity::XP::GetColorCode(playerLevel, creatureLevel) != XP_GRAY)
2138 return false;
2139
2140 uint32 notAbove = sWorld->getIntConfig(CONFIG_NO_GRAY_AGGRO_ABOVE);
2141 uint32 notBelow = sWorld->getIntConfig(CONFIG_NO_GRAY_AGGRO_BELOW);
2142 if (notAbove == 0 && notBelow == 0)
2143 return false;
2144
2145 if (playerLevel <= notBelow || (playerLevel >= notAbove && notAbove > 0))
2146 return true;
2147 return false;
2148}
2149
2150float Creature::GetAttackDistance(Unit const* player) const
2151{
2152 float aggroRate = sWorld->getRate(RATE_CREATURE_AGGRO);
2153 if (aggroRate == 0)
2154 return 0.0f;
2155
2156 // WoW Wiki: the minimum radius seems to be 5 yards, while the maximum range is 45 yards
2157 float maxRadius = 45.0f * aggroRate;
2158 float minRadius = 5.0f * aggroRate;
2159
2160 int32 expansionMaxLevel = int32(GetMaxLevelForExpansion(GetCreatureTemplate()->RequiredExpansion));
2161 int32 playerLevel = player->GetLevelForTarget(this);
2162 int32 creatureLevel = GetLevelForTarget(player);
2163 int32 levelDifference = creatureLevel - playerLevel;
2164
2165 // The aggro radius for creatures with equal level as the player is 20 yards.
2166 // The combatreach should not get taken into account for the distance so we drop it from the range (see Supremus as expample)
2167 float baseAggroDistance = 20.0f - GetCombatReach();
2168
2169 // + - 1 yard for each level difference between player and creature
2170 float aggroRadius = baseAggroDistance + float(levelDifference);
2171
2172 // detect range auras
2173 if (uint32(creatureLevel + 5) <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
2174 {
2177 }
2178
2179 // The aggro range of creatures with higher levels than the total player level for the expansion should get the maxlevel treatment
2180 // This makes sure that creatures such as bosses wont have a bigger aggro range than the rest of the npc's
2181 // The following code is used for blizzlike behaviour such as skippable bosses
2182 if (creatureLevel > expansionMaxLevel)
2183 aggroRadius = baseAggroDistance + float(expansionMaxLevel - playerLevel);
2184
2185 // Make sure that we wont go over the total range limits
2186 if (aggroRadius > maxRadius)
2187 aggroRadius = maxRadius;
2188 else if (aggroRadius < minRadius)
2189 aggroRadius = minRadius;
2190
2191 return (aggroRadius * aggroRate);
2192}
2193
2195{
2197
2198 if (s == JUST_DIED)
2199 {
2201
2202 uint32 respawnDelay = m_respawnDelay;
2203 if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
2204 GetMap()->ApplyDynamicModeRespawnScaling(this, m_spawnId, respawnDelay, scalingMode);
2205
2206 // @todo remove the boss respawn time hack in a dynspawn follow-up once we have creature groups in instances
2208 {
2209 if (IsDungeonBoss() && !m_respawnDelay)
2210 m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance
2211 else
2213 }
2214 else
2215 {
2216 if (IsDungeonBoss() && !m_respawnDelay)
2217 m_respawnTime = std::numeric_limits<time_t>::max(); // never respawn in this instance
2218 else
2219 m_respawnTime = GameTime::GetGameTime() + respawnDelay;
2220 }
2221
2223
2224 ReleaseSpellFocus(nullptr, false); // remove spellcast focus
2225 DoNotReacquireSpellFocusTarget(); // cancel delayed re-target
2226 SetTarget(ObjectGuid::Empty); // drop target - dead mobs shouldn't ever target things
2227
2230
2231 SetMountDisplayId(0); // if creature is mounted on a virtual mount, remove it at death
2232
2233 setActive(false);
2234
2235 SetNoSearchAssistance(false);
2236
2237 //Dismiss group if is leader
2238 if (m_formation)
2239 {
2240 if (m_formation->GetLeader() == this)
2242
2243 if (ZoneScript* script = GetZoneScript())
2245 script->OnCreatureGroupDepleted(m_formation);
2246 }
2247
2248 bool needsFalling = (IsFlying() || IsHovering()) && !IsUnderWater() && !HasUnitState(UNIT_STATE_ROOT);
2249 SetHover(false, false);
2250 SetDisableGravity(false, false);
2251
2252 if (needsFalling)
2254
2256 }
2257 else if (s == JUST_RESPAWNED)
2258 {
2259 if (IsPet())
2260 SetFullHealth();
2261 else
2263
2264 SetTappedBy(nullptr);
2266
2267 SetCannotReachTarget(false);
2269
2271
2272 if (!IsPet())
2273 {
2275 CreatureTemplate const* cInfo = GetCreatureTemplate();
2276
2277 uint64 npcFlags;
2278 uint32 unitFlags, unitFlags2, unitFlags3;
2279 ObjectMgr::ChooseCreatureFlags(cInfo, &npcFlags, &unitFlags, &unitFlags2, &unitFlags3, _staticFlags, creatureData);
2280
2282 npcFlags |= sGameEventMgr->GetNPCFlag(this);
2283
2284 ReplaceAllNpcFlags(NPCFlags(npcFlags & 0xFFFFFFFF));
2285 ReplaceAllNpcFlags2(NPCFlags2(npcFlags >> 32));
2286
2287 ReplaceAllUnitFlags(UnitFlags(unitFlags));
2288 ReplaceAllUnitFlags2(UnitFlags2(unitFlags2));
2289 ReplaceAllUnitFlags3(UnitFlags3(unitFlags3));
2291
2293
2295
2296 if (uint32 vignetteId = cInfo->VignetteID)
2297 SetVignette(vignetteId);
2298 }
2299
2304 }
2305}
2306
2307void Creature::Respawn(bool force)
2308{
2309 if (force)
2310 {
2311 if (IsAlive())
2313 else if (getDeathState() != CORPSE)
2315 }
2316
2318 {
2320 RemoveCorpse(false, false);
2321
2322 if (getDeathState() == DEAD)
2323 {
2324 TC_LOG_DEBUG("entities.unit", "Respawning creature {} ({})", GetName(), GetGUID().ToString());
2325 m_respawnTime = 0;
2327 m_loot = nullptr;
2328
2329 if (m_originalEntry != GetEntry())
2331
2332 SelectLevel();
2333
2335
2337 if (sObjectMgr->GetCreatureModelRandomGender(&display, GetCreatureTemplate()))
2338 SetDisplayId(display.CreatureDisplayID, true);
2339
2341
2342 // Re-initialize reactstate that could be altered by movementgenerators
2344
2345 if (UnitAI* ai = AI()) // reset the AI to be sure no dirty or uninitialized values will be used till next tick
2346 ai->Reset();
2347
2348 m_triggerJustAppeared = true;
2350
2351 uint32 poolid = GetCreatureData() ? GetCreatureData()->poolId : 0;
2352 if (poolid)
2353 sPoolMgr->UpdatePool<Creature>(GetMap()->GetPoolData(), poolid, GetSpawnId());
2354 }
2356 }
2357 else
2358 {
2359 if (m_spawnId)
2361 }
2362
2363 TC_LOG_DEBUG("entities.unit", "Respawning creature {} ({})",
2364 GetName(), GetGUID().ToString());
2365
2366}
2367
2368void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds forceRespawnTimer)
2369{
2370 if (timeMSToDespawn)
2371 {
2372 m_Events.AddEvent(new ForcedDespawnDelayEvent(*this, forceRespawnTimer), m_Events.CalculateTime(Milliseconds(timeMSToDespawn)));
2373 return;
2374 }
2375
2377 {
2378 uint32 corpseDelay = GetCorpseDelay();
2379 uint32 respawnDelay = GetRespawnDelay();
2380
2381 // do it before killing creature
2383
2384 bool overrideRespawnTime = false;
2385 if (IsAlive())
2386 {
2387 if (forceRespawnTimer > Seconds::zero())
2388 {
2389 SetCorpseDelay(0);
2390 SetRespawnDelay(forceRespawnTimer.count());
2391 overrideRespawnTime = true;
2392 }
2393
2395 }
2396
2397 // Skip corpse decay time
2398 RemoveCorpse(!overrideRespawnTime, false);
2399
2400 SetCorpseDelay(corpseDelay);
2401 SetRespawnDelay(respawnDelay);
2402 }
2403 else
2404 {
2405 if (forceRespawnTimer > Seconds::zero())
2406 SaveRespawnTime(forceRespawnTimer.count());
2407 else
2408 {
2409 uint32 respawnDelay = m_respawnDelay;
2410 if (uint32 scalingMode = sWorld->getIntConfig(CONFIG_RESPAWN_DYNAMICMODE))
2411 GetMap()->ApplyDynamicModeRespawnScaling(this, m_spawnId, respawnDelay, scalingMode);
2412 m_respawnTime = GameTime::GetGameTime() + respawnDelay;
2414 }
2415
2417 }
2418}
2419
2420void Creature::DespawnOrUnsummon(Milliseconds timeToDespawn /*= 0s*/, Seconds forceRespawnTimer /*= 0s*/)
2421{
2422 if (TempSummon* summon = ToTempSummon())
2423 summon->UnSummon(timeToDespawn.count());
2424 else
2425 ForcedDespawn(timeToDespawn.count(), forceRespawnTimer);
2426}
2427
2428void Creature::LoadTemplateImmunities(int32 creatureImmunitiesId)
2429{
2430 // uint32 max used for "spell id", the immunity system will not perform SpellInfo checks against invalid spells
2431 // used so we know which immunities were loaded from template
2432 static uint32 constexpr placeholderSpellId = std::numeric_limits<uint32>::max();
2433
2434 auto applyCreatureImmunities = [this](CreatureImmunities const* immunities, bool apply)
2435 {
2436 for (std::size_t i = 0; i < immunities->School.size(); ++i)
2437 if (immunities->School[i])
2438 ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, apply);
2439
2440 for (std::size_t i = 0; i < immunities->DispelType.size(); ++i)
2441 if (immunities->DispelType[i])
2442 ApplySpellImmune(placeholderSpellId, IMMUNITY_DISPEL, i, apply);
2443
2444 for (std::size_t i = 0; i < immunities->Mechanic.size(); ++i)
2445 if (immunities->Mechanic[i])
2446 ApplySpellImmune(placeholderSpellId, IMMUNITY_MECHANIC, i, apply);
2447
2448 for (SpellEffectName effect : immunities->Effect)
2449 ApplySpellImmune(placeholderSpellId, IMMUNITY_EFFECT, effect, apply);
2450
2451 for (AuraType aura : immunities->Aura)
2452 ApplySpellImmune(placeholderSpellId, IMMUNITY_STATE, aura, apply);
2453
2454 if (immunities->Other != SpellOtherImmunity::None)
2455 ApplySpellImmune(placeholderSpellId, IMMUNITY_OTHER, immunities->Other.AsUnderlyingType(), apply);
2456 };
2457
2458 // unapply template immunities (in case we're updating entry)
2460 applyCreatureImmunities(immunities, false);
2461
2462 // apply new immunities
2463 if (CreatureImmunities const* immunities = SpellMgr::GetCreatureImmunities(creatureImmunitiesId))
2464 {
2465 _creatureImmunitiesId = creatureImmunitiesId;
2466 applyCreatureImmunities(immunities, true);
2467 }
2468 else
2470}
2471
2472bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster,
2473 bool requireImmunityPurgesEffectAttribute /*= false*/) const
2474{
2476 return true;
2477
2478 return Unit::IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster, requireImmunityPurgesEffectAttribute);
2479}
2480
2482{
2483 if (IsPet())
2484 return false;
2485
2487}
2488
2490{
2491 if (IsPet())
2492 return false;
2493
2494 return (GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_BOSS_MOB) != 0;
2495}
2496
2497// select nearest hostile unit within the given distance (regardless of threat list).
2498Unit* Creature::SelectNearestTarget(float dist, bool playerOnly /* = false */) const
2499{
2500 if (dist == 0.0f)
2502
2503 Unit* target = nullptr;
2504 Trinity::NearestHostileUnitCheck u_check(this, dist, playerOnly);
2506 Cell::VisitAllObjects(this, searcher, dist);
2507 return target;
2508}
2509
2510// select nearest hostile unit within the given attack distance (i.e. distance is ignored if > than ATTACK_DISTANCE), regardless of threat list.
2512{
2513 if (dist > MAX_VISIBILITY_DISTANCE)
2514 {
2515 TC_LOG_ERROR("entities.unit", "Creature {} SelectNearestTargetInAttackDistance called with dist > MAX_VISIBILITY_DISTANCE. Distance set to ATTACK_DISTANCE.", GetGUID().ToString());
2516 dist = ATTACK_DISTANCE;
2517 }
2518
2519 Unit* target = nullptr;
2522 Cell::VisitAllObjects(this, searcher, std::max(dist, ATTACK_DISTANCE));
2523 return target;
2524}
2525
2527{
2529
2530 packet.UnitGUID = GetGUID();
2531 packet.Reaction = reactionType;
2532
2533 SendMessageToSet(packet.Write(), true);
2534
2535 TC_LOG_DEBUG("network", "WORLD: Sent SMSG_AI_REACTION, type {}.", reactionType);
2536}
2537
2539{
2540 if (!m_AlreadyCallAssistance && GetVictim() && !IsPet() && !IsCharmed())
2541 {
2542 SetNoCallAssistance(true);
2543
2544 float radius = sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS);
2545
2546 if (radius > 0)
2547 {
2548 std::list<Creature*> assistList;
2549 Trinity::AnyAssistCreatureInRangeCheck u_check(this, GetVictim(), radius);
2551 Cell::VisitGridObjects(this, searcher, radius);
2552
2553 if (!assistList.empty())
2554 {
2556 while (!assistList.empty())
2557 {
2558 // Pushing guids because in delay can happen some creature gets despawned => invalid pointer
2559 e->AddAssistant((*assistList.begin())->GetGUID());
2560 assistList.pop_front();
2561 }
2563 }
2564 }
2565 }
2566}
2567
2568void Creature::CallForHelp(float radius)
2569{
2570 if (radius <= 0.0f || !IsEngaged() || !IsAlive() || IsPet() || IsCharmed())
2571 return;
2572
2574 if (!target)
2575 target = GetThreatManager().GetAnyTarget();
2576 if (!target)
2577 target = GetCombatManager().GetAnyTarget();
2578
2579 if (!target)
2580 {
2581 TC_LOG_ERROR("entities.unit", "Creature {} ({}) trying to call for help without being in combat.", GetEntry(), GetName());
2582 return;
2583 }
2584
2585 Trinity::CallOfHelpCreatureInRangeDo u_do(this, target, radius);
2587 Cell::VisitGridObjects(this, worker, radius);
2588}
2589
2590bool Creature::CanAssistTo(Unit const* u, Unit const* enemy, bool checkfaction /*= true*/) const
2591{
2592 // is it true?
2594 return false;
2595
2596 // we don't need help from zombies :)
2597 if (!IsAlive())
2598 return false;
2599
2600 // we cannot assist in evade mode
2601 if (IsInEvadeMode())
2602 return false;
2603
2604 // or if enemy is in evade mode
2605 if (enemy->GetTypeId() == TYPEID_UNIT && enemy->ToCreature()->IsInEvadeMode())
2606 return false;
2607
2608 // we don't need help from non-combatant ;)
2609 if (IsCivilian())
2610 return false;
2611
2613 return false;
2614
2615 // skip fighting creature
2616 if (IsEngaged())
2617 return false;
2618
2619 // only free creature
2620 if (!GetCharmerOrOwnerGUID().IsEmpty())
2621 return false;
2622
2623 // only from same creature faction
2624 if (checkfaction)
2625 {
2626 if (GetFaction() != u->GetFaction())
2627 return false;
2628 }
2629 else
2630 {
2631 if (!IsFriendlyTo(u))
2632 return false;
2633 }
2634
2635 // skip non hostile to caster enemy creatures
2636 if (!IsHostileTo(enemy))
2637 return false;
2638
2639 return true;
2640}
2641
2642// use this function to avoid having hostile creatures attack
2643// friendlies and other mobs they shouldn't attack
2644bool Creature::_IsTargetAcceptable(Unit const* target) const
2645{
2646 ASSERT(target);
2647
2648 // if the target cannot be attacked, the target is not acceptable
2649 if (IsFriendlyTo(target)
2650 || !target->isTargetableForAttack(false)
2651 || (m_vehicle && (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target))))
2652 return false;
2653
2654 if (target->HasUnitState(UNIT_STATE_DIED))
2655 {
2656 // some creatures can detect fake death
2658 return true;
2659 else
2660 return false;
2661 }
2662
2663 // if I'm already fighting target, or I'm hostile towards the target, the target is acceptable
2664 if (IsEngagedBy(target) || IsHostileTo(target))
2665 return true;
2666
2667 // if the target's victim is not friendly, or the target is friendly, the target is not acceptable
2668 return false;
2669}
2670
2672{
2674 return;
2675
2677 {
2678 RespawnInfo ri;
2680 ri.spawnId = m_spawnId;
2683 return;
2684 }
2685
2686 time_t thisRespawnTime = forceDelay ? GameTime::GetGameTime() + forceDelay : m_respawnTime;
2688}
2689
2690// this should not be called by petAI or
2691bool Creature::CanCreatureAttack(Unit const* victim, bool /*force*/) const
2692{
2693 if (!victim->IsInMap(this))
2694 return false;
2695
2696 if (!IsValidAttackTarget(victim))
2697 return false;
2698
2699 if (!victim->isInAccessiblePlaceFor(this))
2700 return false;
2701
2702 if (CreatureAI* ai = AI())
2703 if (!ai->CanAIAttack(victim))
2704 return false;
2705
2706 // we cannot attack in evade mode
2707 if (IsInEvadeMode())
2708 return false;
2709
2710 // or if enemy is in evade mode
2711 if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode())
2712 return false;
2713
2715 {
2716 if (GetMap()->IsDungeon())
2717 return true;
2718
2719 // don't check distance to home position if recently damaged, this should include taunt auras
2721 return true;
2722 }
2723
2724 // Map visibility range, but no more than 2*cell size
2725 float dist = std::min<float>(GetMap()->GetVisibilityRange(), SIZE_OF_GRID_CELL * 2);
2726
2727 if (Unit* unit = GetCharmerOrOwner())
2728 return victim->IsWithinDist(unit, dist);
2729 else
2730 {
2731 // include sizes for huge npcs
2732 dist += GetCombatReach() + victim->GetCombatReach();
2733
2734 // to prevent creatures in air ignore attacks because distance is already too high...
2735 if (CanFly())
2736 return victim->IsInDist2d(&m_homePosition, dist);
2737 else
2738 return victim->IsInDist(&m_homePosition, dist);
2739 }
2740}
2741
2743{
2744 if (m_spawnId)
2745 {
2746 if (CreatureAddon const* addon = sObjectMgr->GetCreatureAddon(m_spawnId))
2747 return addon;
2748 }
2749
2750 return sObjectMgr->GetCreatureTemplateAddon(GetEntry());
2751}
2752
2753//creature_addon table
2755{
2756 CreatureAddon const* creatureAddon = GetCreatureAddon();
2757 if (!creatureAddon)
2758 return false;
2759
2760 if (uint32 mountDisplayId = _defaultMountDisplayIdOverride.value_or(creatureAddon->mount); mountDisplayId != 0)
2761 Mount(mountDisplayId);
2762
2764 ReplaceAllVisFlags(UnitVisFlags(creatureAddon->visFlags));
2765 SetAnimTier(AnimTier(creatureAddon->animTier), false);
2766
2767 SetSheath(SheathState(creatureAddon->sheathState));
2769
2770 // These fields must only be handled by core internals and must not be modified via scripts/DB dat
2773
2774 if (creatureAddon->emote != 0)
2775 SetEmoteState(Emote(creatureAddon->emote));
2776
2777 SetAIAnimKitId(creatureAddon->aiAnimKit);
2778 SetMovementAnimKitId(creatureAddon->movementAnimKit);
2779 SetMeleeAnimKitId(creatureAddon->meleeAnimKit);
2780
2781 // Check if visibility distance different
2784
2785 // Load Path
2786 if (creatureAddon->PathId != 0)
2787 _waypointPathId = creatureAddon->PathId;
2788
2789 if (!creatureAddon->auras.empty())
2790 {
2791 for (std::vector<uint32>::const_iterator itr = creatureAddon->auras.begin(); itr != creatureAddon->auras.end(); ++itr)
2792 {
2793 SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(*itr, GetMap()->GetDifficultyID());
2794 if (!AdditionalSpellInfo)
2795 {
2796 TC_LOG_ERROR("sql.sql", "Creature {} has wrong spell {} defined in `auras` field.", GetGUID().ToString(), *itr);
2797 continue;
2798 }
2799
2800 // skip already applied aura
2801 if (HasAura(*itr))
2802 continue;
2803
2804 AddAura(*itr, this);
2805 TC_LOG_DEBUG("entities.unit", "Spell: {} added to creature {}", *itr, GetGUID().ToString());
2806 }
2807 }
2808 return true;
2809}
2810
2811void Creature::LoadCreaturesSparringHealth(bool force /*= false*/)
2812{
2813 if (std::vector<float> const* templateValues = sObjectMgr->GetCreatureTemplateSparringValues(GetCreatureTemplate()->Entry))
2814 if (force || std::find(templateValues->begin(), templateValues->end(), _sparringHealthPct) != templateValues->end()) // only re-randomize sparring value if it was loaded from template (not when set to custom value from script)
2816}
2817
2820{
2822 packet.AreaID = GetAreaId();
2823 packet.Write();
2824
2825 Team enemyTeam = attacker->GetTeam();
2826 if (enemyTeam != ALLIANCE)
2827 sWorld->SendGlobalMessage(packet.GetRawPacket(), nullptr, ALLIANCE);
2828 if (enemyTeam != HORDE)
2829 sWorld->SendGlobalMessage(packet.GetRawPacket(), nullptr, HORDE);
2830}
2831
2832void Creature::SetCanMelee(bool canMelee, bool fleeFromMelee /*= false*/)
2833{
2834 bool wasFleeingFromMelee = HasFlag(CREATURE_STATIC_FLAG_NO_MELEE_FLEE);
2835
2836 _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_NO_MELEE_FLEE, !canMelee && fleeFromMelee);
2838
2839 if (wasFleeingFromMelee == HasFlag(CREATURE_STATIC_FLAG_NO_MELEE_FLEE))
2840 return;
2841
2842 Unit* victim = GetVictim();
2843 if (!victim)
2844 return;
2845
2847 if (!currentMovement)
2848 return;
2849
2850 bool canChangeMovement = [&]
2851 {
2852 if (wasFleeingFromMelee)
2854
2855 return currentMovement->GetMovementGeneratorType() == CHASE_MOTION_TYPE;
2856 }();
2857
2858 if (!canChangeMovement)
2859 return;
2860
2861 GetMotionMaster()->Remove(currentMovement);
2863}
2864
2866{
2868 GetMotionMaster()->MoveChase(victim, range, angle);
2869 else
2870 GetMotionMaster()->MoveFleeing(victim);
2871}
2872
2873bool Creature::HasSpell(uint32 spellID) const
2874{
2875 return std::find(std::begin(m_spells), std::end(m_spells), spellID) != std::end(m_spells);
2876}
2877
2879{
2880 time_t now = GameTime::GetGameTime();
2881 if (m_respawnTime > now)
2882 return m_respawnTime;
2883 else
2884 return now;
2885}
2886
2888{
2889 m_respawnTime = respawn ? GameTime::GetGameTime() + respawn : 0;
2890}
2891
2892void Creature::GetRespawnPosition(float &x, float &y, float &z, float* ori, float* dist) const
2893{
2894 if (m_creatureData)
2895 {
2896 if (ori)
2897 m_creatureData->spawnPoint.GetPosition(x, y, z, *ori);
2898 else
2900
2901 if (dist)
2903 }
2904 else
2905 {
2906 Position const& homePos = GetHomePosition();
2907 if (ori)
2908 homePos.GetPosition(x, y, z, *ori);
2909 else
2910 homePos.GetPosition(x, y, z);
2911 if (dist)
2912 *dist = 0;
2913 }
2914}
2915
2917{
2918 SetHover(GetMovementTemplate().IsHoverInitiallyEnabled());
2921
2922 // If an amphibious creatures was swimming while engaged, disable swimming again
2925
2927}
2928
2930{
2931 // Do not update movement flags if creature is controlled by a player (charm/vehicle)
2932 if (m_playerMovingMe)
2933 return;
2934
2935 // Set the movement flags if the creature is in that mode. (Only fly if actually in air, only swim if in water, etc)
2936 float ground = GetFloorZ();
2937 bool isInAir = (G3D::fuzzyGt(GetPositionZ(), ground + GetHoverOffset() + GROUND_HEIGHT_TOLERANCE) || G3D::fuzzyLt(GetPositionZ(), ground - GROUND_HEIGHT_TOLERANCE)); // Can be underground too, prevent the falling
2938 if (!isInAir)
2940
2941 // Some Amphibious creatures toggle swimming while engaged
2943 if (!IsSwimPrevented() || (GetVictim() && !GetVictim()->IsOnOceanFloor()))
2945
2946 SetSwim(IsInWater() && CanSwim());
2947}
2948
2950{
2951 if (CreatureMovementData const* movementOverride = sObjectMgr->GetCreatureMovementOverride(m_spawnId))
2952 return *movementOverride;
2953
2954 return GetCreatureTemplate()->Movement;
2955}
2956
2958{
2959 if (Unit::CanSwim())
2960 return true;
2961
2962 if (IsPet())
2963 return true;
2964
2965 return false;
2966}
2967
2969{
2970 time_t now = GameTime::GetGameTime();
2971 // Do not reset corpse remove time if corpse is already removed
2972 if (m_corpseRemoveTime <= now)
2973 return;
2974
2975 // Scripts can choose to ignore RATE_CORPSE_DECAY_LOOTED by calling SetCorpseDelay(timer, true)
2976 float decayRate = m_ignoreCorpseDecayRatio ? 1.f : sWorld->getRate(RATE_CORPSE_DECAY_LOOTED);
2977
2978 // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
2979 bool isFullySkinned = [&]() -> bool
2980 {
2981 if (m_loot && m_loot->loot_type == LOOT_SKINNING && m_loot->isLooted())
2982 return true;
2983
2984 bool hasSkinningLoot = false;
2985 for (auto const& [_, loot] : m_personalLoot)
2986 {
2987 if (loot->loot_type == LOOT_SKINNING)
2988 {
2989 if (!loot->isLooted())
2990 return false;
2991
2992 hasSkinningLoot = true;
2993 }
2994 }
2995
2996 return hasSkinningLoot;
2997 }();
2998
2999 if (isFullySkinned)
3000 m_corpseRemoveTime = now;
3001 else
3002 m_corpseRemoveTime = now + uint32(m_corpseDelay * decayRate);
3003
3005}
3006
3008{
3010 Unit::SetInteractionAllowedWhileHostile(interactionAllowed);
3011}
3012
3013void Creature::SetInteractionAllowedInCombat(bool interactionAllowed)
3014{
3016 Unit::SetInteractionAllowedInCombat(interactionAllowed);
3017}
3018
3020{
3022
3023 // If as a result of npcflag updates we stop seeing UNIT_NPC_FLAG_QUESTGIVER then
3024 // we must also send SMSG_QUEST_GIVER_STATUS_MULTIPLE because client will not request it automatically
3025 if (IsQuestGiver())
3026 {
3027 auto sender = [&](Player const* receiver)
3028 {
3029 receiver->PlayerTalkClass->SendQuestGiverStatus(receiver->GetQuestDialogStatus(this), GetGUID());
3030 };
3031 Trinity::MessageDistDeliverer notifier(this, sender, GetVisibilityRange());
3033 }
3034}
3035
3037{
3038 return m_unitData->ContentTuningID != 0;
3039}
3040
3042{
3043 CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty();
3044
3045 if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(creatureDifficulty->ContentTuningID, 0))
3046 {
3049 }
3050
3051 int32 mindelta = std::min(creatureDifficulty->DeltaLevelMax, creatureDifficulty->DeltaLevelMin);
3052 int32 maxdelta = std::max(creatureDifficulty->DeltaLevelMax, creatureDifficulty->DeltaLevelMin);
3053 int32 delta = mindelta == maxdelta ? mindelta : irand(mindelta, maxdelta);
3054
3057}
3058
3060{
3061 CreatureTemplate const* cInfo = GetCreatureTemplate();
3062 CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty();
3063 float baseHealth = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class), 0);
3064 return std::max(baseHealth * creatureDifficulty->HealthModifier, 1.0f);
3065}
3066
3068{
3069 if (!HasScalableLevels())
3070 return 1.0f;
3071
3072 uint8 levelForTarget = GetLevelForTarget(target);
3073 if (GetLevel() < levelForTarget)
3074 return 1.0f;
3075
3076 return double(GetMaxHealthByLevel(levelForTarget)) / double(GetCreateHealth());
3077}
3078
3080{
3081 CreatureTemplate const* cInfo = GetCreatureTemplate();
3082 CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty();
3083 return sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureAutoAttackDps, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class), 0);
3084}
3085
3087{
3088 if (!HasScalableLevels())
3089 return 1.0f;
3090
3091 uint8 levelForTarget = GetLevelForTarget(target);
3092
3093 return GetBaseDamageForLevel(levelForTarget) / GetBaseDamageForLevel(GetLevel());
3094}
3095
3097{
3098 CreatureTemplate const* cInfo = GetCreatureTemplate();
3099 CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty();
3100 float baseArmor = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureArmor, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class), 0);
3101 return baseArmor * creatureDifficulty->ArmorModifier;
3102}
3103
3105{
3106 if (!HasScalableLevels())
3107 return 1.0f;
3108
3109 uint8 levelForTarget = GetLevelForTarget(target);
3110
3111 return GetBaseArmorForLevel(levelForTarget) / GetBaseArmorForLevel(GetLevel());
3112}
3113
3115{
3116 if (Unit const* unitTarget = target->ToUnit())
3117 {
3118 if (isWorldBoss())
3119 {
3120 uint8 level = unitTarget->GetLevel() + sWorld->getIntConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF);
3121 return RoundToInterval<uint8>(level, 1u, 255u);
3122 }
3123
3124 // If this creature should scale level, adapt level depending of target level
3125 // between UNIT_FIELD_SCALING_LEVEL_MIN and UNIT_FIELD_SCALING_LEVEL_MAX
3126 if (HasScalableLevels())
3127 {
3128 int32 scalingLevelMin = m_unitData->ScalingLevelMin;
3129 int32 scalingLevelMax = m_unitData->ScalingLevelMax;
3130 int32 scalingLevelDelta = m_unitData->ScalingLevelDelta;
3131 int32 scalingFactionGroup = m_unitData->ScalingFactionGroup;
3132 int32 targetLevel = unitTarget->m_unitData->EffectiveLevel;
3133 if (!targetLevel)
3134 targetLevel = unitTarget->GetLevel();
3135
3136 int32 targetLevelDelta = 0;
3137
3138 if (Player const* playerTarget = target->ToPlayer())
3139 {
3140 if (scalingFactionGroup && sFactionTemplateStore.AssertEntry(sChrRacesStore.AssertEntry(playerTarget->GetRace())->FactionID)->FactionGroup != scalingFactionGroup)
3141 scalingLevelMin = scalingLevelMax;
3142
3143 int32 maxCreatureScalingLevel = playerTarget->m_activePlayerData->MaxCreatureScalingLevel;
3144 targetLevelDelta = std::min(maxCreatureScalingLevel > 0 ? maxCreatureScalingLevel - targetLevel : 0, *playerTarget->m_activePlayerData->ScalingPlayerLevelDelta);
3145 }
3146
3147 int32 levelWithDelta = targetLevel + targetLevelDelta;
3148 int32 level = RoundToInterval(levelWithDelta, scalingLevelMin, scalingLevelMax) + scalingLevelDelta;
3149 return RoundToInterval(level, 1, MAX_LEVEL + 3);
3150 }
3151 }
3152
3153 return Unit::GetLevelForTarget(target);
3154}
3155
3156std::string const& Creature::GetAIName() const
3157{
3158 return sObjectMgr->GetCreatureTemplate(GetEntry())->AIName;
3159}
3160
3161std::string Creature::GetScriptName() const
3162{
3163 return sObjectMgr->GetScriptName(GetScriptId());
3164}
3165
3167{
3169 if (uint32 scriptId = creatureData->scriptId)
3170 return scriptId;
3171
3172 return ASSERT_NOTNULL(sObjectMgr->GetCreatureTemplate(GetEntry()))->ScriptID;
3173}
3174
3176{
3177 // copy references to stringIds from template and spawn
3178 m_stringIds = parent->m_stringIds;
3179
3180 // then copy script stringId, not just its reference
3181 SetScriptStringId(std::string(parent->GetStringId(StringIdType::Script)));
3182}
3183
3184bool Creature::HasStringId(std::string_view id) const
3185{
3186 return std::ranges::any_of(m_stringIds, [id](std::string const* stringId) { return stringId && *stringId == id; });
3187}
3188
3190{
3191 if (!id.empty())
3192 {
3193 m_scriptStringId.emplace(std::move(id));
3195 }
3196 else
3197 {
3198 m_scriptStringId.reset();
3200 }
3201}
3202
3204{
3205 if (!player)
3206 return nullptr;
3207
3208 if (SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(SPAWN_TYPE_CREATURE, GetSpawnId()))
3209 {
3210 if (data->spawnTrackingQuestObjectiveId && data->spawnTrackingData)
3211 {
3212 SpawnTrackingState state = player->GetSpawnTrackingStateByObjective(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectiveId);
3213 return &data->spawnTrackingStates[AsUnderlyingType(state)];
3214 }
3215 }
3216
3217 return nullptr;
3218}
3219
3221{
3222 return sObjectMgr->GetNpcVendorItemList(GetEntry());
3223}
3224
3226{
3227 if (!vItem->maxcount)
3228 return vItem->maxcount;
3229
3230 VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
3231 for (; itr != m_vendorItemCounts.end(); ++itr)
3232 if (itr->itemId == vItem->item)
3233 break;
3234
3235 if (itr == m_vendorItemCounts.end())
3236 return vItem->maxcount;
3237
3238 VendorItemCount* vCount = &*itr;
3239
3240 time_t ptime = GameTime::GetGameTime();
3241
3242 if (time_t(vCount->lastIncrementTime + vItem->incrtime) <= ptime)
3243 if (ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(vItem->item))
3244 {
3245 uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
3246 if ((vCount->count + diff * pProto->GetBuyCount()) >= vItem->maxcount)
3247 {
3248 m_vendorItemCounts.erase(itr);
3249 return vItem->maxcount;
3250 }
3251
3252 vCount->count += diff * pProto->GetBuyCount();
3253 vCount->lastIncrementTime = ptime;
3254 }
3255
3256 return vCount->count;
3257}
3258
3260{
3261 if (!vItem->maxcount)
3262 return 0;
3263
3264 VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
3265 for (; itr != m_vendorItemCounts.end(); ++itr)
3266 if (itr->itemId == vItem->item)
3267 break;
3268
3269 if (itr == m_vendorItemCounts.end())
3270 {
3271 uint32 new_count = vItem->maxcount > used_count ? vItem->maxcount-used_count : 0;
3272 m_vendorItemCounts.push_back(VendorItemCount(vItem->item, new_count));
3273 return new_count;
3274 }
3275
3276 VendorItemCount* vCount = &*itr;
3277
3278 time_t ptime = GameTime::GetGameTime();
3279
3280 if (time_t(vCount->lastIncrementTime + vItem->incrtime) <= ptime)
3281 if (ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(vItem->item))
3282 {
3283 uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
3284 if ((vCount->count + diff * pProto->GetBuyCount()) < vItem->maxcount)
3285 vCount->count += diff * pProto->GetBuyCount();
3286 else
3287 vCount->count = vItem->maxcount;
3288 }
3289
3290 vCount->count = vCount->count > used_count ? vCount->count-used_count : 0;
3291 vCount->lastIncrementTime = ptime;
3292 return vCount->count;
3293}
3294
3296{
3298 VendorDataTypeFlags vendorFlags = static_cast<VendorDataTypeFlags>(AsUnderlyingType(flags) >> 7);
3299 if (apply)
3300 {
3301 if (!m_vendorData)
3303
3306 }
3307 else if (m_vendorData)
3308 {
3311 if (!m_vendorData->Flags)
3312 {
3315 }
3316 }
3317}
3318
3320{
3321 if (apply)
3322 {
3323 if (!m_vendorData)
3325
3328 }
3329 else if (m_vendorData)
3330 {
3333 if (!m_vendorData->Flags)
3334 {
3337 }
3338 }
3339}
3340
3341// overwrite WorldObject function for proper name localization
3343{
3344 if (locale != DEFAULT_LOCALE)
3345 if (CreatureLocale const* cl = sObjectMgr->GetCreatureLocale(GetEntry()))
3346 if (cl->Name.size() > locale && !cl->Name[locale].empty())
3347 return cl->Name[locale];
3348
3349 return GetName();
3350}
3351
3352bool Creature::HasLabel(int32 cretureLabel) const
3353{
3354 return advstd::ranges::contains(GetLabels(), cretureLabel);
3355}
3356
3357std::span<int32 const> Creature::GetLabels() const
3358{
3359 return sDB2Manager.GetCreatureLabels(GetCreatureDifficulty()->CreatureDifficultyID);
3360}
3361
3363{
3364 return MAX_SPELL_CHARM;
3365}
3366
3368{
3369 if (pos >= MAX_SPELL_CHARM || !m_charmInfo || m_charmInfo->GetCharmSpell(pos)->GetType() != ACT_ENABLED)
3370 return 0;
3371 else
3372 return m_charmInfo->GetCharmSpell(pos)->GetAction();
3373}
3374
3376{
3377 float range = 0.f;
3378
3379 for (uint8 i = 0; i < GetPetAutoSpellSize(); ++i)
3380 {
3381 uint32 spellID = GetPetAutoSpellOnPos(i);
3382 if (!spellID)
3383 continue;
3384
3385 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID, GetMap()->GetDifficultyID()))
3386 {
3387 if (spellInfo->GetRecoveryTime() == 0 && spellInfo->RangeEntry && spellInfo->RangeEntry->ID != 1 /*Self*/ && spellInfo->RangeEntry->ID != 2 /*Combat Range*/ && spellInfo->GetMaxRange() > range)
3388 range = spellInfo->GetMaxRange();
3389 }
3390 }
3391
3392 return range;
3393}
3394
3396{
3397 if (cannotReach == m_cannotReachTarget)
3398 return;
3399 m_cannotReachTarget = cannotReach;
3401
3402 if (cannotReach)
3403 TC_LOG_DEBUG("entities.unit.chase", "Creature::SetCannotReachTarget() called with true. Details: {}", GetDebugInfo());
3404}
3405
3407{
3408 if (mountCreatureDisplayId && !sCreatureDisplayInfoStore.HasRecord(*mountCreatureDisplayId))
3409 mountCreatureDisplayId.reset();
3410
3411 _defaultMountDisplayIdOverride = mountCreatureDisplayId;
3412}
3413
3414float Creature::GetAggroRange(Unit const* target) const
3415{
3416 // Determines the aggro range for creatures (usually pets), used mainly for aggressive pet target selection.
3417 // Based on data from wowwiki due to lack of 3.3.5a data
3418
3419 if (target && IsPet())
3420 {
3421 uint32 targetLevel = 0;
3422
3423 if (target->GetTypeId() == TYPEID_PLAYER)
3424 targetLevel = target->GetLevelForTarget(this);
3425 else if (target->GetTypeId() == TYPEID_UNIT)
3426 targetLevel = target->ToCreature()->GetLevelForTarget(this);
3427
3428 uint32 myLevel = GetLevelForTarget(target);
3429 int32 levelDiff = int32(targetLevel) - int32(myLevel);
3430
3431 // The maximum Aggro Radius is capped at 45 yards (25 level difference)
3432 if (levelDiff < -25)
3433 levelDiff = -25;
3434
3435 // The base aggro radius for mob of same level
3436 float aggroRadius = 20;
3437
3438 // Aggro Radius varies with level difference at a rate of roughly 1 yard/level
3439 aggroRadius -= (float)levelDiff;
3440
3441 // detect range auras
3443
3444 // detected range auras
3446
3447 // Just in case, we don't want pets running all over the map
3448 if (aggroRadius > MAX_AGGRO_RADIUS)
3449 aggroRadius = MAX_AGGRO_RADIUS;
3450
3451 // Minimum Aggro Radius for a mob seems to be combat range (5 yards)
3452 // hunter pets seem to ignore minimum aggro radius so we'll default it a little higher
3453 if (aggroRadius < 10)
3454 aggroRadius = 10;
3455
3456 return (aggroRadius);
3457 }
3458
3459 // Default
3460 return 0.0f;
3461}
3462
3463Unit* Creature::SelectNearestHostileUnitInAggroRange(bool useLOS, bool ignoreCivilians) const
3464{
3465 // Selects nearest hostile target within creature's aggro range. Used primarily by
3466 // pets set to aggressive. Will not return neutral or friendly targets.
3467
3468 Unit* target = nullptr;
3469
3470 Trinity::NearestHostileUnitInAggroRangeCheck u_check(this, useLOS, ignoreCivilians);
3472
3474
3475 return target;
3476}
3477
3479{
3480 return GetCreatureTemplate()->scale;
3481}
3482
3484{
3485 Unit::SetObjectScale(scale);
3486
3487 if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(GetDisplayId()))
3488 {
3489 SetBoundingRadius((IsPet() ? 1.0f : minfo->bounding_radius) * scale * GetDisplayScale());
3490 SetCombatReach((IsPet() ? DEFAULT_PLAYER_COMBAT_REACH : minfo->combat_reach) * scale * GetDisplayScale());
3491 }
3492}
3493
3494void Creature::SetDisplayId(uint32 displayId, bool setNative /*= false*/)
3495{
3496 Unit::SetDisplayId(displayId, setNative);
3497
3498 if (CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(displayId))
3499 {
3500 SetBoundingRadius((IsPet() ? 1.0f : modelInfo->bounding_radius) * GetObjectScale() * GetDisplayScale());
3501 SetCombatReach((IsPet() ? DEFAULT_PLAYER_COMBAT_REACH : modelInfo->combat_reach) * GetObjectScale() * GetDisplayScale());
3502 }
3503}
3504
3506{
3507 if (CreatureModel const* model = GetCreatureTemplate()->GetModelByIdx(modelIdx))
3508 SetDisplayId(model->CreatureDisplayID);
3509}
3510
3512{
3513 if (HasSpellFocus())
3514 _spellFocusInfo.Target = guid;
3515 else
3517}
3518
3519void Creature::SetSpellFocus(Spell const* focusSpell, WorldObject const* target)
3520{
3521 // Pointer validation and checking for a already existing focus
3522 if (_spellFocusInfo.Spell || !focusSpell)
3523 return;
3524
3525 // Prevent dead / feign death creatures from setting a focus target
3527 return;
3528
3529 // Don't allow stunned creatures to set a focus target
3531 return;
3532
3533 // some spells shouldn't track targets
3534 if (focusSpell->IsFocusDisabled())
3535 return;
3536
3537 SpellInfo const* spellInfo = focusSpell->GetSpellInfo();
3538
3539 // don't use spell focus for vehicle spells
3540 if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
3541 return;
3542
3543 // instant non-channeled casts and non-target spells don't need facing updates
3544 if (!target && (!focusSpell->GetCastTime() && !spellInfo->IsChanneled()))
3545 return;
3546
3547 // store pre-cast values for target and orientation (used to later restore)
3548 if (!_spellFocusInfo.Delay)
3549 { // only overwrite these fields if we aren't transitioning from one spell focus to another
3550 _spellFocusInfo.Target = GetTarget();
3551 _spellFocusInfo.Orientation = GetOrientation();
3552 }
3553 else // don't automatically reacquire target for the previous spellcast
3554 _spellFocusInfo.Delay = 0;
3555
3556 _spellFocusInfo.Spell = focusSpell;
3557
3558 bool const noTurnDuringCast = spellInfo->HasAttribute(SPELL_ATTR5_AI_DOESNT_FACE_TARGET);
3559 bool const turnDisabled = CannotTurn();
3560 // set target, then force send update packet to players if it changed to provide appropriate facing
3561 ObjectGuid newTarget = (target && !noTurnDuringCast && !turnDisabled) ? target->GetGUID() : ObjectGuid::Empty;
3562 if (GetTarget() != newTarget)
3564
3565 // If we are not allowed to turn during cast but have a focus target, face the target
3566 if (!turnDisabled && noTurnDuringCast && target)
3567 SetFacingToObject(target, false);
3568
3569 if (noTurnDuringCast)
3571}
3572
3573bool Creature::HasSpellFocus(Spell const* focusSpell) const
3574{
3575 if (isDead()) // dead creatures cannot focus
3576 {
3577 if (_spellFocusInfo.Spell || _spellFocusInfo.Delay)
3578 {
3579 TC_LOG_WARN("entities.unit", "Creature '{}' (entry {}) has spell focus (spell id {}, delay {}ms) despite being dead.",
3580 GetName(), GetEntry(), _spellFocusInfo.Spell ? _spellFocusInfo.Spell->GetSpellInfo()->Id : 0, _spellFocusInfo.Delay);
3581 }
3582 return false;
3583 }
3584
3585 if (focusSpell)
3586 return (focusSpell == _spellFocusInfo.Spell);
3587 else
3588 return (_spellFocusInfo.Spell || _spellFocusInfo.Delay);
3589}
3590
3591void Creature::ReleaseSpellFocus(Spell const* focusSpell, bool withDelay)
3592{
3593 if (!_spellFocusInfo.Spell)
3594 return;
3595
3596 // focused to something else
3597 if (focusSpell && focusSpell != _spellFocusInfo.Spell)
3598 return;
3599
3600 if (_spellFocusInfo.Spell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_AI_DOESNT_FACE_TARGET))
3602
3603 if (IsPet()) // player pets do not use delay system
3604 {
3605 if (!CannotTurn())
3607 }
3608 else // don't allow re-target right away to prevent visual bugs
3609 _spellFocusInfo.Delay = withDelay ? 1000 : 1;
3610
3611 _spellFocusInfo.Spell = nullptr;
3612}
3613
3615{
3616 if (!HasSpellFocus())
3617 {
3618 TC_LOG_ERROR("entities.unit", "Creature::ReacquireSpellFocusTarget() being called with HasSpellFocus() returning false. {}", GetDebugInfo());
3619 return;
3620 }
3621
3623
3624 if (!CannotTurn())
3625 {
3626 if (!_spellFocusInfo.Target.IsEmpty())
3627 {
3628 if (WorldObject const* objTarget = ObjectAccessor::GetWorldObject(*this, _spellFocusInfo.Target))
3629 SetFacingToObject(objTarget, false);
3630 }
3631 else
3632 SetFacingTo(_spellFocusInfo.Orientation, false);
3633 }
3634 _spellFocusInfo.Delay = 0;
3635}
3636
3638{
3639 _spellFocusInfo.Delay = 0;
3640 _spellFocusInfo.Spell = nullptr;
3641}
3642
3644{
3646 return false;
3647
3648 return true;
3649}
3650
3652{
3654}
3655
3657{
3659}
3660
3662{
3663 CreatureTextRepeatIds& repeats = m_textRepeat[textGroup];
3664 if (std::find(repeats.begin(), repeats.end(), id) == repeats.end())
3665 repeats.push_back(id);
3666 else
3667 TC_LOG_ERROR("sql.sql", "CreatureTextMgr: TextGroup {} for Creature({}) {}, id {} already added", uint32(textGroup), GetName(), GetGUID().ToString(), uint32(id));
3668}
3669
3671{
3673
3674 CreatureTextRepeatGroup::const_iterator groupItr = m_textRepeat.find(textGroup);
3675 if (groupItr != m_textRepeat.end())
3676 ids = groupItr->second;
3677
3678 return ids;
3679}
3680
3682{
3683 CreatureTextRepeatGroup::iterator groupItr = m_textRepeat.find(textGroup);
3684 if (groupItr != m_textRepeat.end())
3685 groupItr->second.clear();
3686}
3687
3689{
3691}
3692
3694{
3695 if (CreatureAI const* ai = AI())
3696 return ai->IsEngaged();
3697 return false;
3698}
3699
3701{
3702 Unit::AtEngage(target);
3703
3705
3707
3709 Dismount();
3710
3711 if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed
3712 {
3716 }
3717
3719 if (movetype == WAYPOINT_MOTION_TYPE || movetype == POINT_MOTION_TYPE || (IsAIEnabled() && AI()->IsEscorted()))
3720 {
3722
3723 // if its a vehicle, set the home positon of every creature passenger at engage
3724 // so that they are in combat range if hostile
3725 if (Vehicle* vehicle = GetVehicleKit())
3726 {
3727 for (auto seat = vehicle->Seats.begin(); seat != vehicle->Seats.end(); ++seat)
3728 if (Unit* passenger = ObjectAccessor::GetUnit(*this, seat->second.Passenger.Guid))
3729 if (Creature* creature = passenger->ToCreature())
3730 creature->SetHomePosition(GetPosition());
3731 }
3732 }
3733
3734 if (CreatureAI* ai = AI())
3735 ai->JustEngagedWith(target);
3736 if (CreatureGroup* formation = GetFormation())
3737 formation->MemberEngagingTarget(this, target);
3738
3739 // Creatures with CREATURE_STATIC_FLAG_2_FORCE_PARTY_MEMBERS_INTO_COMBAT periodically force party members into combat
3741}
3742
3744{
3746
3750
3751 if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed
3752 {
3756 }
3757}
3758
3760{
3762 return;
3763
3764 Trinity::Containers::FlatSet<Group const*> partiesToForceIntoCombat;
3765 for (auto const& [_, combatReference] : GetCombatManager().GetPvECombatRefs())
3766 {
3767 if (combatReference->IsSuppressedFor(this))
3768 continue;
3769
3770 Player* player = Object::ToPlayer(combatReference->GetOther(this));
3771 if (!player || player->IsGameMaster())
3772 continue;
3773
3774 if (Group const* group = player->GetGroup())
3775 partiesToForceIntoCombat.insert(group);
3776 }
3777
3778 for (Group const* partyToForceIntoCombat : partiesToForceIntoCombat)
3779 {
3780 for (GroupReference const* ref = partyToForceIntoCombat->GetFirstMember(); ref != nullptr; ref = ref->next())
3781 {
3782 Player* player = ref->GetSource();
3783 if (!player || !player->IsInWorld() || player->GetMap() != GetMap() || player->IsGameMaster())
3784 continue;
3785
3786 EngageWithTarget(player);
3787 }
3788 }
3789}
3790
3792{
3793 if (CreatureAI const* ai = AI())
3794 return ai->IsEscorted();
3795 return false;
3796}
3797
3798std::string Creature::GetDebugInfo() const
3799{
3800 std::stringstream sstr;
3801 sstr << Unit::GetDebugInfo() << "\n"
3802 << "AIName: " << GetAIName() << " ScriptName: " << GetScriptName()
3803 << " WaypointPath: " << GetWaypointPathId() << " SpawnId: " << GetSpawnId();
3804 return sstr.str();
3805}
3806
3807void Creature::ExitVehicle(Position const* /*exitPosition*/)
3808{
3810
3811 // if the creature exits a vehicle, set it's home position to the
3812 // exited position so it won't run away (home) and evade if it's hostile
3814}
3815
3817{
3818 return _gossipMenuId;
3819}
3820
3822{
3823 _gossipMenuId = gossipMenuId;
3824}
3825
3827{
3828 if (_trainerId)
3829 return *_trainerId;
3830
3831 return sObjectMgr->GetCreatureDefaultTrainer(GetEntry());
3832}
3833
3835{
3836 _trainerId = trainerId;
3837}
3838
3840{
3844
3846{
3847 if (!IsAreaSpiritHealer())
3848 return;
3849
3851
3852 // maybe NPC is summoned with these spells:
3853 // ID - 24237 Summon Alliance Graveyard Teleporter (SERVERSIDE)
3854 // ID - 46894 Summon Horde Graveyard Teleporter (SERVERSIDE)
3855 SummonCreature(npcEntry, GetPosition(), TEMPSUMMON_TIMED_DESPAWN, 1s, 0, 0);
3856}
3857
3859{
3860 auto clickBounds = sObjectMgr->GetSpellClickInfoMapBounds(GetEntry());
3861 auto itr = clickBounds.begin();
3862 // Set InteractSpellID if there is only one row in npc_spellclick_spells in db for this creature
3863 if (itr != clickBounds.end() && ++itr == clickBounds.end())
3864 SetInteractSpellId(clickBounds.begin()->second.spellId);
3865 else
3867}
3868
3870{
3871 m_objectData->WriteCreate(*data, flags, this, target);
3872 m_unitData->WriteCreate(*data, flags, this, target);
3873
3874 if (m_vendorData)
3875 m_vendorData->WriteCreate(*data, flags, this, target);
3876}
3877
3879{
3881 {
3883
3885 m_objectData->WriteUpdate(*data, flags, this, target);
3886
3888 m_unitData->WriteUpdate(*data, flags, this, target);
3889 }
3890
3892 m_vendorData->WriteUpdate(*data, flags, this, target);
3893}
3894
3896{
3898 valuesMask.Set(TYPEID_UNIT);
3899
3900 *data << uint32(valuesMask.GetBlock(0));
3901
3902 UF::UnitData::Mask mask;
3903 m_unitData->AppendAllowedFieldsMaskForFlag(mask, flags);
3904 m_unitData->WriteUpdate(*data, mask, true, this, target);
3905}
3906
3908 UF::UnitData::Mask const& requestedUnitMask, Player const* target) const
3909{
3912 if (requestedObjectMask.IsAnySet())
3913 valuesMask.Set(TYPEID_OBJECT);
3914
3915 UF::UnitData::Mask unitMask = requestedUnitMask;
3916 m_unitData->FilterDisallowedFieldsMaskForFlag(unitMask, flags);
3917 if (unitMask.IsAnySet())
3918 valuesMask.Set(TYPEID_UNIT);
3919
3920 ByteBuffer& buffer = PrepareValuesUpdateBuffer(data);
3921 std::size_t sizePos = buffer.wpos();
3922 buffer << uint32(0);
3924 buffer << uint32(valuesMask.GetBlock(0));
3925
3926 if (valuesMask[TYPEID_OBJECT])
3927 m_objectData->WriteUpdate(buffer, requestedObjectMask, true, this, target);
3928
3929 if (valuesMask[TYPEID_UNIT])
3930 m_unitData->WriteUpdate(buffer, unitMask, true, this, target);
3931
3932 buffer.put<uint32>(sizePos, buffer.wpos() - sizePos - 4);
3933
3934 data->AddUpdateBlock();
3935}
3936
3938{
3939 UpdateData udata(Owner->GetMapId());
3940 WorldPacket packet;
3941
3943
3944 udata.BuildPacket(&packet);
3945 player->SendDirectMessage(&packet);
3946}
3947
3949{
3950 if (m_vendorData)
3952
3953 Unit::ClearUpdateMask(remove);
3954}
#define sBattlegroundMgr
@ SPELL_SPIRIT_HEAL_CHANNEL_AOE
Definition: Battleground.h:101
constexpr uint8 MAX_SPELL_CHARM
Definition: CharmInfo.h:28
LocaleConstant
Definition: Common.h:48
@ TOTAL_LOCALES
Definition: Common.h:62
@ LOCALE_enUS
Definition: Common.h:49
#define DEFAULT_LOCALE
Definition: Common.h:66
@ IN_MILLISECONDS
Definition: Common.h:35
@ MINUTE
Definition: Common.h:29
@ WEEK
Definition: Common.h:32
const uint32 CREATURE_REGEN_INTERVAL
Definition: CreatureData.h:421
@ CREATURE_FLAG_EXTRA_DUNGEON_BOSS
Definition: CreatureData.h:371
@ CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING
Definition: CreatureData.h:372
@ CREATURE_FLAG_EXTRA_CANNOT_ENTER_COMBAT
Definition: CreatureData.h:356
@ CREATURE_FLAG_EXTRA_NO_XP
Definition: CreatureData.h:349
@ CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK
Definition: CreatureData.h:373
@ CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH
Definition: CreatureData.h:359
@ CREATURE_FLAG_EXTRA_NO_TAUNT
Definition: CreatureData.h:351
@ CREATURE_FLAG_EXTRA_GHOST_VISIBILITY
Definition: CreatureData.h:353
@ CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK
Definition: CreatureData.h:354
@ CREATURE_FLAG_EXTRA_WORLDEVENT
Definition: CreatureData.h:357
@ CREATURE_STATIC_FLAG_IGNORE_COMBAT
Definition: CreatureData.h:63
@ CREATURE_STATIC_FLAG_CAN_SWIM
Definition: CreatureData.h:66
@ CREATURE_STATIC_FLAG_NO_XP
Definition: CreatureData.h:39
@ CREATURE_STATIC_FLAG_NO_MELEE_FLEE
Definition: CreatureData.h:58
CreatureChaseMovementType
Definition: CreatureData.h:385
const uint8 MAX_KILL_CREDIT
Definition: CreatureData.h:425
@ CREATURE_STATIC_FLAG_4_NO_BIRTH_ANIM
Definition: CreatureData.h:152
@ CREATURE_STATIC_FLAG_4_NO_MELEE_APPROACH
Definition: CreatureData.h:161
@ CREATURE_STATIC_FLAG_5_INTERACT_WHILE_HOSTILE
Definition: CreatureData.h:198
CreatureRandomMovementType
Definition: CreatureData.h:394
const uint32 CREATURE_NOPATH_EVADE_TIME
Definition: CreatureData.h:423
@ CREATURE_STATIC_FLAG_3_ALLOW_INTERACTION_WHILE_IN_COMBAT
Definition: CreatureData.h:132
@ CREATURE_STATIC_FLAG_2_FORCE_PARTY_MEMBERS_INTO_COMBAT
Definition: CreatureData.h:77
@ CREATURE_STATIC_FLAG_2_ALLOW_MOUNTED_COMBAT
Definition: CreatureData.h:94
const uint32 MAX_CREATURE_SPELLS
Definition: CreatureData.h:428
const uint32 PET_FOCUS_REGEN_INTERVAL
Definition: CreatureData.h:422
#define sFormationMgr
AreaSpiritHealerData
Definition: Creature.cpp:3840
@ NPC_ALLIANCE_GRAVEYARD_TELEPORT
Definition: Creature.cpp:3841
@ NPC_HORDE_GRAVEYARD_TELEPORT
Definition: Creature.cpp:3842
VendorDataTypeFlags
Definition: Creature.h:63
std::vector< uint8 > CreatureTextRepeatIds
Definition: Creature.h:78
#define CREATURE_Z_ATTACK_RANGE
Definition: Creature.h:52
static constexpr uint8 WILD_BATTLE_PET_DEFAULT_LEVEL
Definition: Creature.h:74
static constexpr size_t CREATURE_TAPPERS_SOFT_CAP
Definition: Creature.h:75
DB2Storage< DifficultyEntry > sDifficultyStore("Difficulty.db2", &DifficultyLoadInfo::Instance)
DB2Storage< ChrRacesEntry > sChrRacesStore("ChrRaces.db2", &ChrRacesLoadInfo::Instance)
DB2Storage< CreatureDisplayInfoEntry > sCreatureDisplayInfoStore("CreatureDisplayInfo.db2", &CreatureDisplayInfoLoadInfo::Instance)
DB2Storage< FactionTemplateEntry > sFactionTemplateStore("FactionTemplate.db2", &FactionTemplateLoadInfo::Instance)
DB2Storage< AreaTableEntry > sAreaTableStore("AreaTable.db2", &AreaTableLoadInfo::Instance)
#define sDB2Manager
Definition: DB2Stores.h:553
@ MAX_LEVEL
Definition: DBCEnums.h:51
@ STRONG_MAX_LEVEL
Definition: DBCEnums.h:55
Difficulty
Definition: DBCEnums.h:918
@ DIFFICULTY_NONE
Definition: DBCEnums.h:919
@ FACTION_MASK_ALLIANCE
Definition: DBCEnums.h:995
@ FACTION_TEMPLATE_FLAG_PVP
Definition: DBCEnums.h:987
SQLTransaction< WorldDatabaseConnection > WorldDatabaseTransaction
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
uint8_t uint8
Definition: Define.h:144
int8_t int8
Definition: Define.h:140
int32_t int32
Definition: Define.h:138
uint64_t uint64
Definition: Define.h:141
#define UI64LIT(N)
Definition: Define.h:127
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
uint16 flags
Definition: DisableMgr.cpp:49
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition: Duration.h:27
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition: Duration.h:24
#define ASSERT_NOTNULL(pointer)
Definition: Errors.h:84
#define ASSERT
Definition: Errors.h:68
#define sGameEventMgr
Definition: GameEventMgr.h:177
#define MAX_FALL_DISTANCE
Definition: GridDefines.h:62
#define SIZE_OF_GRID_CELL
Definition: GridDefines.h:48
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition: Log.h:179
#define TC_LOG_ERROR(filterType__, message__,...)
Definition: Log.h:188
#define TC_LOG_WARN(filterType__, message__,...)
Definition: Log.h:185
@ LOOT_SKINNING
Definition: Loot.h:107
#define sMapMgr
Definition: MapManager.h:186
MovementGeneratorType
@ IDLE_MOTION_TYPE
@ CHASE_MOTION_TYPE
@ WAYPOINT_MOTION_TYPE
@ FLEEING_MOTION_TYPE
@ HOME_MOTION_TYPE
@ POINT_MOTION_TYPE
@ RANDOM_MOTION_TYPE
#define ATTACK_DISTANCE
Definition: ObjectDefines.h:25
#define MAX_VISIBILITY_DISTANCE
Definition: ObjectDefines.h:28
#define DEFAULT_PLAYER_COMBAT_REACH
Definition: ObjectDefines.h:40
@ TEMPSUMMON_TIMED_DESPAWN
Definition: ObjectDefines.h:65
#define DEFAULT_PLAYER_DISPLAY_SCALE
Definition: ObjectDefines.h:41
@ TYPEID_OBJECT
Definition: ObjectGuid.h:37
@ TYPEID_UNIT
Definition: ObjectGuid.h:42
@ TYPEID_PLAYER
Definition: ObjectGuid.h:43
#define sObjectMgr
Definition: ObjectMgr.h:1995
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
#define sPoolMgr
Definition: PoolMgr.h:179
@ RACE_NONE
Definition: RaceMask.h:27
int32 irand(int32 min, int32 max)
Definition: Random.cpp:35
uint32 urand(uint32 min, uint32 max)
Definition: Random.cpp:42
void SendGossipMenuFor(Player *player, uint32 npcTextID, ObjectGuid const &guid)
void ClearGossipMenuFor(Player *player)
@ SERVERSIDE_VISIBILITY_GHOST
Classes
constexpr uint32 GetMaxLevelForExpansion(uint32 expansion)
@ SPELL_ATTR5_AI_DOESNT_FACE_TARGET
@ XP_GRAY
Emote
@ SPELL_SCHOOL_MASK_NORMAL
@ CREATURE_TYPE_CRITTER
@ CREATURE_TYPE_NON_COMBAT_PET
@ CREATURE_TYPE_MECHANICAL
@ UNIT_DYNFLAG_TAPPED
@ UNIT_DYNFLAG_LOOTABLE
@ UNIT_DYNFLAG_NONE
AiReaction
SpellEffectName
@ SPELL_EFFECT_HEAL
@ SPELL_EFFECT_ATTACK_ME
@ SPELL_EFFECT_KNOCK_BACK_DEST
@ SPELL_EFFECT_KNOCK_BACK
@ OFF_ATTACK
@ BASE_ATTACK
@ RANGED_ATTACK
float const GROUND_HEIGHT_TOLERANCE
Definition: SharedDefines.h:25
@ IMMUNITY_STATE
@ IMMUNITY_EFFECT
@ IMMUNITY_MECHANIC
@ IMMUNITY_SCHOOL
@ IMMUNITY_OTHER
@ IMMUNITY_DISPEL
Team
@ ALLIANCE
@ HORDE
Powers
@ POWER_ENERGY
@ POWER_MANA
@ POWER_FOCUS
BattlegroundTypeId
@ BATTLEGROUND_AA
@ BATTLEGROUND_WS
@ BATTLEGROUND_EY
@ BATTLEGROUND_AV
@ BATTLEGROUND_BE
@ BATTLEGROUND_RV
@ BATTLEGROUND_NA
@ BATTLEGROUND_DS
@ BATTLEGROUND_SA
@ BATTLEGROUND_AB
@ BATTLEGROUND_RL
@ GHOST_VISIBILITY_ALIVE
@ GHOST_VISIBILITY_GHOST
CreatureClassifications
@ FACTION_ALLIANCE_GENERIC
SpellSchools
@ SPELL_SCHOOL_SHADOW
@ SPELL_SCHOOL_NATURE
@ SPELL_SCHOOL_FROST
@ SPELL_SCHOOL_ARCANE
@ SPELL_SCHOOL_FIRE
@ SPELL_SCHOOL_HOLY
@ CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS
@ CREATURE_TYPE_FLAG_BOSS_MOB
@ CREATURE_TYPE_FLAG_ALLOW_INTERACTION_WHILE_IN_COMBAT
@ CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT
Creature can be targeted by spells that require target to be in caster's party/raid.
@ CREATURE_TYPE_FLAG_ALLOW_MOUNTED_COMBAT
@ SPAWNGROUP_FLAG_COMPATIBILITY_MODE
Definition: SpawnData.h:56
@ LINKED_RESPAWN_CREATURE_TO_GO
Definition: SpawnData.h:153
@ LINKED_RESPAWN_CREATURE_TO_CREATURE
Definition: SpawnData.h:152
@ LINKED_RESPAWN_GO_TO_CREATURE
Definition: SpawnData.h:155
@ SPAWN_TYPE_CREATURE
Definition: SpawnData.h:35
SpawnTrackingState
Definition: SpawnData.h:93
AuraType
@ SPELL_AURA_MOD_POWER_REGEN
@ SPELL_AURA_PREVENTS_FLEEING
@ SPELL_AURA_CONTROL_VEHICLE
@ SPELL_AURA_MOD_INVISIBILITY
@ SPELL_AURA_MOD_HEALTH_REGEN_PERCENT
@ SPELL_AURA_MOD_DETECTED_RANGE
@ SPELL_AURA_MOD_TAUNT
@ SPELL_AURA_FEIGN_DEATH
@ SPELL_AURA_MOD_POWER_REGEN_PERCENT
@ SPELL_AURA_MOD_DETECT_RANGE
@ SPELL_AURA_MOD_REGEN
@ FORM_NONE
#define sSpellMgr
Definition: SpellMgr.h:861
@ MOVE_FLIGHT
Definition: UnitDefines.h:123
@ MOVE_SWIM
Definition: UnitDefines.h:120
@ MOVE_RUN
Definition: UnitDefines.h:118
@ MOVE_WALK
Definition: UnitDefines.h:117
UnitFlags2
Definition: UnitDefines.h:216
@ UNIT_FLAG2_FEIGN_DEATH
Definition: UnitDefines.h:217
@ UNIT_FLAG2_REGENERATE_POWER
Definition: UnitDefines.h:228
@ UNIT_PET_FLAG_NONE
Definition: UnitDefines.h:108
@ REACT_PASSIVE
Definition: UnitDefines.h:537
@ REACT_AGGRESSIVE
Definition: UnitDefines.h:539
UnitStandStateType
Definition: UnitDefines.h:41
constexpr NPCFlags UNIT_NPC_FLAG_VENDOR_MASK
Definition: UnitDefines.h:356
@ MOVEMENTFLAG_FALLING
Definition: UnitDefines.h:397
@ ACT_ENABLED
Definition: UnitDefines.h:529
NPCFlags
Non Player Character flags.
Definition: UnitDefines.h:318
@ UNIT_NPC_FLAG_NONE
Definition: UnitDefines.h:319
@ UNIT_NPC_FLAG_SPELLCLICK
Definition: UnitDefines.h:344
@ UNIT_NPC_FLAG_PETITIONER
Definition: UnitDefines.h:338
UnitFlags3
Definition: UnitDefines.h:268
SheathState
Definition: UnitDefines.h:81
@ SHEATH_STATE_MELEE
Definition: UnitDefines.h:83
#define MAX_EQUIPMENT_ITEMS
Definition: UnitDefines.h:37
NPCFlags2
Definition: UnitDefines.h:361
@ UNIT_NPC_FLAG_2_NONE
Definition: UnitDefines.h:362
UnitPVPStateFlags
Definition: UnitDefines.h:91
AnimTier
Definition: UnitDefines.h:69
UnitVisFlags
Definition: UnitDefines.h:58
UnitFlags
Definition: UnitDefines.h:166
@ UNIT_FLAG_STUNNED
Definition: UnitDefines.h:185
@ UNIT_FLAG_NON_ATTACKABLE
Definition: UnitDefines.h:168
@ UNIT_FLAG_IN_COMBAT
Definition: UnitDefines.h:186
@ UNIT_FLAG_CAN_SWIM
Definition: UnitDefines.h:182
@ UNIT_FLAG_FLEEING
Definition: UnitDefines.h:190
@ UNIT_FLAG_CANT_SWIM
Definition: UnitDefines.h:181
@ UNIT_FLAG_PLAYER_CONTROLLED
Definition: UnitDefines.h:170
@ BASE_VALUE
Definition: Unit.h:154
@ MINDAMAGE
Definition: Unit.h:169
@ MAXDAMAGE
Definition: Unit.h:170
UnitMods
Definition: Unit.h:174
@ UNIT_MOD_ARMOR
Definition: Unit.h:206
@ UNIT_MOD_RESISTANCE_SHADOW
Definition: Unit.h:211
@ UNIT_MOD_RESISTANCE_FROST
Definition: Unit.h:210
@ UNIT_MOD_ATTACK_POWER
Definition: Unit.h:213
@ UNIT_MOD_RESISTANCE_HOLY
Definition: Unit.h:207
@ UNIT_MOD_RESISTANCE_ARCANE
Definition: Unit.h:212
@ UNIT_MOD_HEALTH
Definition: Unit.h:179
@ UNIT_MOD_POWER_START
Definition: Unit.h:224
@ UNIT_MOD_RESISTANCE_FIRE
Definition: Unit.h:208
@ UNIT_MOD_RESISTANCE_NATURE
Definition: Unit.h:209
@ UNIT_MOD_ATTACK_POWER_RANGED
Definition: Unit.h:214
#define MAX_AGGRO_RADIUS
Definition: Unit.h:45
DeathState
Definition: Unit.h:247
@ CORPSE
Definition: Unit.h:250
@ DEAD
Definition: Unit.h:251
@ ALIVE
Definition: Unit.h:248
@ JUST_RESPAWNED
Definition: Unit.h:252
@ JUST_DIED
Definition: Unit.h:249
@ UNIT_STATE_DIED
Definition: Unit.h:257
@ UNIT_STATE_ATTACK_PLAYER
Definition: Unit.h:271
@ UNIT_STATE_ROOT
Definition: Unit.h:267
@ UNIT_STATE_IGNORE_PATHFINDING
Definition: Unit.h:285
@ UNIT_STATE_FLEEING
Definition: Unit.h:264
@ UNIT_STATE_FOCUSING
Definition: Unit.h:263
@ UNIT_STATE_ALL_ERASABLE
Definition: Unit.h:304
@ BASE_PCT
Definition: Unit.h:162
T RoundToInterval(T &num, T floor, T ceil)
Definition: Util.h:97
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition: Util.h:528
@ WORLD_DEL_SPAWNGROUP_MEMBER
Definition: WorldDatabase.h:84
@ WORLD_DEL_GAME_EVENT_MODEL_EQUIP
Definition: WorldDatabase.h:77
@ WORLD_DEL_CREATURE_ADDON
Definition: WorldDatabase.h:66
@ WORLD_DEL_LINKED_RESPAWN
Definition: WorldDatabase.h:31
@ WORLD_DEL_GAME_EVENT_CREATURE
Definition: WorldDatabase.h:76
@ WORLD_DEL_CREATURE
Definition: WorldDatabase.h:68
@ WORLD_DEL_LINKED_RESPAWN_MASTER
Definition: WorldDatabase.h:32
@ WORLD_INS_CREATURE
Definition: WorldDatabase.h:75
uint32 const Entry[5]
Unit & m_owner
Definition: Creature.h:627
ObjectGuid m_victim
Definition: Creature.h:625
bool Execute(uint64 e_time, uint32 p_time) override
Definition: Creature.cpp:282
void AddAssistant(ObjectGuid guid)
Definition: Creature.h:621
GuidList m_assistants
Definition: Creature.h:626
size_t wpos() const
Definition: ByteBuffer.h:438
void put(std::size_t pos, T value)
Definition: ByteBuffer.h:246
Unit * GetAnyTarget() const
virtual void EnterEvadeMode(EvadeReason why=EvadeReason::Other)
Definition: CreatureAI.cpp:218
virtual void JustAppeared()
Definition: CreatureAI.cpp:193
virtual bool CheckInRoom()
Definition: CreatureAI.cpp:452
Creature * GetLeader() const
void FormationReset(bool dismiss)
bool HasAliveMembers() const
bool IsFormed() const
void LeaderStartedMoving()
bool IsLeader(Creature const *creature) const
bool CanLeaderStartMoving() const
bool HasFlag(CreatureStaticFlags flag) const
Definition: CreatureData.h:302
EnumFlag< CreatureStaticFlags8 > GetFlags8() const
Definition: CreatureData.h:327
void ApplyFlag(CreatureStaticFlags flag, bool apply)
Definition: CreatureData.h:311
EnumFlag< CreatureStaticFlags2 > GetFlags2() const
Definition: CreatureData.h:321
EnumFlag< CreatureStaticFlags > GetFlags() const
Definition: CreatureData.h:320
EnumFlag< CreatureStaticFlags7 > GetFlags7() const
Definition: CreatureData.h:326
EnumFlag< CreatureStaticFlags5 > GetFlags5() const
Definition: CreatureData.h:324
EnumFlag< CreatureStaticFlags6 > GetFlags6() const
Definition: CreatureData.h:325
EnumFlag< CreatureStaticFlags4 > GetFlags4() const
Definition: CreatureData.h:323
EnumFlag< CreatureStaticFlags3 > GetFlags3() const
Definition: CreatureData.h:322
bool IsSwimPrevented() const
Definition: Creature.h:157
bool HasClassification(CreatureClassifications classification) const
Definition: Creature.h:167
bool LoadCreaturesAddon()
Definition: Creature.cpp:2754
void ClearUpdateMask(bool remove) override
Definition: Creature.cpp:3948
time_t _pickpocketLootRestore
Timers.
Definition: Creature.h:529
bool HasSpell(uint32 spellID) const override
Definition: Creature.cpp:2873
uint32 CalculateDamageForSparring(Unit *attacker, uint32 damage)
Definition: Creature.cpp:1754
bool AIM_Destroy()
Definition: Creature.cpp:1055
VendorItemCounts m_vendorItemCounts
Definition: Creature.h:521
void SetHomePosition(float x, float y, float z, float o)
Definition: Creature.h:386
float m_wanderDistance
Definition: Creature.h:535
void SetNoSearchAssistance(bool val)
Definition: Creature.h:340
void ReleaseSpellFocus(Spell const *focusSpell=nullptr, bool withDelay=true)
Definition: Creature.cpp:3591
CreatureDifficulty const * m_creatureDifficulty
Definition: Creature.h:561
void BuildValuesCreate(ByteBuffer *data, UF::UpdateFieldFlag flags, Player const *target) const override
Definition: Creature.cpp:3869
void SetCanMelee(bool canMelee, bool fleeFromMelee=false)
Definition: Creature.cpp:2832
bool IsTrigger() const
Definition: Creature.h:127
void Respawn(bool force=false)
Definition: Creature.cpp:2307
std::array< std::string const *, 3 > m_stringIds
Definition: Creature.h:562
void SetInteractionAllowedInCombat(bool interactionAllowed) override
Definition: Creature.cpp:3013
void ResetLootMode()
Definition: Creature.h:322
bool CanSwim() const override
Definition: Creature.cpp:2957
void RegenerateHealth()
Definition: Creature.cpp:998
CreatureTextRepeatGroup m_textRepeat
Definition: Creature.h:599
void GetRespawnPosition(float &x, float &y, float &z, float *ori=nullptr, float *dist=nullptr) const
Definition: Creature.cpp:2892
VendorItemData const * GetVendorItems() const
Definition: Creature.cpp:3220
void StartDefaultCombatMovement(Unit *victim, Optional< float > range={}, Optional< float > angle={})
Definition: Creature.cpp:2865
float _sparringHealthPct
Definition: Creature.h:612
bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const *data=nullptr, uint32 vehId=0)
Definition: Creature.cpp:1798
bool _regenerateHealth
Definition: Creature.h:606
CreatureStaticFlagsHolder _staticFlags
Definition: Creature.h:568
bool IsDungeonBoss() const
Definition: Creature.h:169
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool allowDuplicate)
Definition: Creature.cpp:1847
float GetHealthMultiplierForTarget(WorldObject const *target) const override
Definition: Creature.cpp:3067
bool isWorldBoss() const
Definition: Creature.cpp:2489
bool IsReturningHome() const
Definition: Creature.cpp:376
Optional< uint32 > m_lootId
Definition: Creature.h:565
void UpdateLevelDependantStats()
Definition: Creature.cpp:1612
int8 m_originalEquipmentId
Definition: Creature.h:544
void setDeathState(DeathState s) override
Definition: Creature.cpp:2194
static Creature * CreateCreatureFromDB(ObjectGuid::LowType spawnId, Map *map, b