aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp158
-rw-r--r--src/server/game/Entities/Creature/Creature.h4
-rw-r--r--src/server/game/Entities/Creature/CreatureData.h113
-rw-r--r--src/server/game/Entities/Object/Object.cpp2
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp5
-rw-r--r--src/server/game/Entities/Player/Player.cpp4
-rw-r--r--src/server/game/Entities/Unit/StatSystem.cpp2
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp8
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp466
-rw-r--r--src/server/game/Globals/ObjectMgr.h15
-rw-r--r--src/server/game/Handlers/NPCHandler.cpp4
-rw-r--r--src/server/game/Handlers/QueryHandler.cpp9
-rw-r--r--src/server/game/Loot/LootMgr.cpp39
-rw-r--r--src/server/game/Miscellaneous/Formulas.h2
-rw-r--r--src/server/game/Server/Packets/CombatLogPacketsCommon.cpp18
-rw-r--r--src/server/game/Spells/Spell.cpp7
-rw-r--r--src/server/game/Spells/SpellEffects.cpp6
-rw-r--r--src/server/game/Spells/SpellInfo.cpp2
-rw-r--r--src/server/game/World/World.cpp6
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp7
-rw-r--r--src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp6
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp33
-rw-r--r--src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp8
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp2
-rw-r--r--src/server/scripts/Spells/spell_item.cpp5
29 files changed, 382 insertions, 557 deletions
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp
index f5af5860cc4..ec9f91a548c 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.cpp
+++ b/src/server/database/Database/Implementation/WorldDatabase.cpp
@@ -76,7 +76,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, help FROM command", CONNECTION_SYNCH);
- PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, HealthScalingExpansion, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, VehicleId, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH);
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index 572ab846ef2..c12c1b8c1f2 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -389,7 +389,7 @@ bool EscortAI::AssistPlayerInCombatAgainst(Unit* who)
return false;
// experimental (unknown) flag not present
- if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST))
+ if (!(me->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_CAN_ASSIST))
return false;
// not a player
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
index 04057317e97..f12b7aaf9b6 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
@@ -268,7 +268,7 @@ bool FollowerAI::ShouldAssistPlayerInCombatAgainst(Unit* who) const
return false;
// experimental (unknown) flag not present
- if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST))
+ if (!(me->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_CAN_ASSIST))
return false;
if (!who->isInAccessiblePlaceFor(me))
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 36917a73ea3..0b83f83643d 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -464,7 +464,7 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who)
return false;
// experimental (unknown) flag not present
- if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST))
+ if (!(me->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_CAN_ASSIST))
return false;
// not a player
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 60d0319b9ce..522314d8687 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -164,19 +164,16 @@ CreatureModel const* CreatureTemplate::GetFirstVisibleModel() const
return &CreatureModel::DefaultVisibleModel;
}
-int32 CreatureTemplate::GetHealthScalingExpansion() const
-{
- return HealthScalingExpansion == EXPANSION_LEVEL_CURRENT ? CURRENT_EXPANSION : HealthScalingExpansion;
-}
-
void CreatureTemplate::InitializeQueryData()
{
for (uint8 loc = LOCALE_enUS; loc < TOTAL_LOCALES; ++loc)
- QueryData[loc] = BuildQueryData(static_cast<LocaleConstant>(loc));
+ QueryData[loc] = BuildQueryData(static_cast<LocaleConstant>(loc), DIFFICULTY_NONE);
}
-WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const
+WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc, Difficulty difficulty) const
{
+ CreatureDifficulty const* creatureDifficulty = GetDifficulty(difficulty);
+
WorldPackets::Query::QueryCreatureResponse queryTemp;
queryTemp.CreatureID = Entry;
@@ -190,8 +187,8 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const
stats.Name[0] = Name;
stats.NameAlt[0] = FemaleName;
- stats.Flags[0] = type_flags;
- stats.Flags[1] = type_flags2;
+ stats.Flags[0] = creatureDifficulty->TypeFlags;
+ stats.Flags[1] = creatureDifficulty->TypeFlags2;
stats.CreatureType = type;
stats.CreatureFamily = family;
@@ -207,15 +204,15 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const
return { model.CreatureDisplayID, model.DisplayScale, model.Probability };
});
- stats.HpMulti = ModHealth;
- stats.EnergyMulti = ModMana;
+ stats.HpMulti = creatureDifficulty->HealthModifier;
+ stats.EnergyMulti = creatureDifficulty->ManaModifier;
stats.CreatureMovementInfoID = movementId;
stats.RequiredExpansion = RequiredExpansion;
- stats.HealthScalingExpansion = HealthScalingExpansion;
+ stats.HealthScalingExpansion = creatureDifficulty->HealthScalingExpansion;
stats.VignetteID = VignetteID;
stats.Class = unit_class;
- stats.CreatureDifficultyID = CreatureDifficultyID;
+ stats.CreatureDifficultyID = creatureDifficulty->CreatureDifficultyID;
stats.WidgetSetID = WidgetSetID;
stats.WidgetSetUnitConditionID = WidgetSetUnitConditionID;
@@ -223,7 +220,7 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const
stats.TitleAlt = TitleAlt;
stats.CursorName = IconName;
- if (std::vector<uint32> const* items = sObjectMgr->GetCreatureQuestItemList(Entry))
+ if (std::vector<uint32> const* items = sObjectMgr->GetCreatureQuestItemList(Entry, difficulty))
stats.QuestItems.insert(stats.QuestItems.begin(), items->begin(), items->end());
if (loc != LOCALE_enUS)
@@ -240,23 +237,42 @@ WorldPacket CreatureTemplate::BuildQueryData(LocaleConstant loc) const
return queryTemp.Move();
}
-CreatureLevelScaling const* CreatureTemplate::GetLevelScaling(Difficulty difficulty) const
+CreatureDifficulty const* CreatureTemplate::GetDifficulty(Difficulty difficulty) const
{
- auto it = scalingStore.find(difficulty);
- if (it != scalingStore.end())
+ auto it = difficultyStore.find(difficulty);
+ if (it != difficultyStore.end())
return &it->second;
- struct DefaultCreatureLevelScaling : public CreatureLevelScaling
+ // If there is no data for the difficulty, try to get data for the fallback difficulty
+ DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty);
+ if (difficultyEntry)
+ return GetDifficulty(Difficulty(difficultyEntry->FallbackDifficultyID));
+
+ // No data for DIFFICULTY_NONE (0)
+ struct DefaultCreatureDifficulty : public CreatureDifficulty
{
- DefaultCreatureLevelScaling()
+ DefaultCreatureDifficulty()
{
DeltaLevelMin = 0;
DeltaLevelMax = 0;
ContentTuningID = 0;
+ HealthScalingExpansion = 0;
+ HealthModifier = 1.f;
+ ManaModifier = 1.f;
+ ArmorModifier = 1.f;
+ DamageModifier = 1.f;
+ CreatureDifficultyID = 0;
+ TypeFlags = 0;
+ TypeFlags2 = 0;
+ LootID = 0;
+ PickPocketLootID = 0;
+ SkinLootID = 0;
+ GoldMin = 0;
+ GoldMax = 0;
}
};
- static const DefaultCreatureLevelScaling defScaling;
- return &defScaling;
+ static const DefaultCreatureDifficulty defDifficulty;
+ return &defDifficulty;
}
bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
@@ -292,7 +308,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
Creature::Creature(bool isWorldObject) : Unit(isWorldObject), MapObject(), m_PlayerDamageReq(0), m_dontClearTapListOnEvade(false), _pickpocketLootRestore(0),
m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_ignoreCorpseDecayRatio(false), m_wanderDistance(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0),
- m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0),
+ m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_creatureDifficulty(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0),
m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0),
_regenerateHealth(true), _isMissingCanSwimFlagOutOfCombat(false), _gossipMenuId(0), _sparringHealthPct(0)
{
@@ -472,55 +488,32 @@ void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers)
*/
bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
{
- CreatureTemplate const* normalInfo = sObjectMgr->GetCreatureTemplate(entry);
- if (!normalInfo)
+ CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(entry);
+ if (!creatureInfo)
{
TC_LOG_ERROR("sql.sql", "Creature::InitEntry creature entry {} does not exist.", entry);
return false;
}
- // get difficulty 1 mode entry
- CreatureTemplate const* cinfo = nullptr;
- DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(GetMap()->GetDifficultyID());
- while (!cinfo && difficultyEntry)
- {
- int32 idx = CreatureTemplate::DifficultyIDToDifficultyEntryIndex(difficultyEntry->ID);
- if (idx == -1)
- break;
-
- if (normalInfo->DifficultyEntry[idx])
- {
- cinfo = sObjectMgr->GetCreatureTemplate(normalInfo->DifficultyEntry[idx]);
- break;
- }
-
- if (!difficultyEntry->FallbackDifficultyID)
- break;
-
- difficultyEntry = sDifficultyStore.LookupEntry(difficultyEntry->FallbackDifficultyID);
- }
-
- if (!cinfo)
- cinfo = normalInfo;
-
- SetEntry(entry); // normal entry always
- m_creatureInfo = cinfo; // map mode related always
+ m_creatureInfo = creatureInfo;
+ SetEntry(entry);
+ m_creatureDifficulty = creatureInfo->GetDifficulty(GetMap()->GetDifficultyID());
// equal to player Race field, but creature does not have race
SetRace(RACE_NONE);
// known valid are: CLASS_WARRIOR, CLASS_PALADIN, CLASS_ROGUE, CLASS_MAGE
- SetClass(uint8(cinfo->unit_class));
+ SetClass(uint8(creatureInfo->unit_class));
// Cancel load if no model defined
- if (!(cinfo->GetFirstValidModel()))
+ if (!(creatureInfo->GetFirstValidModel()))
{
TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has no model defined in table `creature_template`, can't load. ", entry);
return false;
}
- CreatureModel model = *ObjectMgr::ChooseDisplayId(cinfo, data);
- CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&model, cinfo);
+ CreatureModel model = *ObjectMgr::ChooseDisplayId(creatureInfo, data);
+ CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&model, creatureInfo);
if (!minfo) // Cancel load if no model defined
{
TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid model {} defined in table `creature_template`, can't load.", entry, model.CreatureDisplayID);
@@ -540,7 +533,7 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
LoadEquipment(data->equipmentId);
}
- SetName(normalInfo->Name); // at normal entry always
+ SetName(creatureInfo->Name); // at normal entry always
SetModCastingSpeed(1.0f);
SetModSpellHaste(1.0f);
@@ -549,30 +542,30 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetModHasteRegen(1.0f);
SetModTimeRate(1.0f);
- SetSpeedRate(MOVE_WALK, cinfo->speed_walk);
- SetSpeedRate(MOVE_RUN, cinfo->speed_run);
+ SetSpeedRate(MOVE_WALK, creatureInfo->speed_walk);
+ SetSpeedRate(MOVE_RUN, creatureInfo->speed_run);
SetSpeedRate(MOVE_SWIM, 1.0f); // using 1.0 rate
SetSpeedRate(MOVE_FLIGHT, 1.0f); // using 1.0 rate
// Will set UNIT_FIELD_BOUNDINGRADIUS, UNIT_FIELD_COMBATREACH and UNIT_FIELD_DISPLAYSCALE
SetObjectScale(GetNativeObjectScale());
- SetCanDualWield(cinfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK);
+ SetCanDualWield(creatureInfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK);
// checked at loading
- m_defaultMovementType = MovementGeneratorType(data ? data->movementType : cinfo->MovementType);
+ m_defaultMovementType = MovementGeneratorType(data ? data->movementType : creatureInfo->MovementType);
if (!m_wanderDistance && m_defaultMovementType == RANDOM_MOTION_TYPE)
m_defaultMovementType = IDLE_MOTION_TYPE;
for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
m_spells[i] = GetCreatureTemplate()->spells[i];
- _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_NO_XP, cinfo->type == CREATURE_TYPE_CRITTER
+ _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_NO_XP, creatureInfo->type == CREATURE_TYPE_CRITTER
|| IsPet()
|| IsTotem()
- || cinfo->flags_extra & CREATURE_FLAG_EXTRA_NO_XP);
+ || creatureInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_XP);
- _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS, (cinfo->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) != 0);
+ _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS, (GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) != 0);
return true;
}
@@ -1072,6 +1065,8 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, Posit
return false;
}
+ CreatureDifficulty const* creatureDifficulty = cinfo->GetDifficulty(GetMap()->GetDifficultyID());
+
//! Relocate before CreateFromProto, to initialize coords and allow
//! returning correct zone id for selecting OutdoorPvP/Battlefield script
Relocate(pos);
@@ -1091,7 +1086,7 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 entry, Posit
}
// Allow players to see those units while dead, do it here (mayby altered by addon auras)
- if (cinfo->type_flags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS)
+ if (creatureDifficulty->TypeFlags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS)
m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST);
if (!CreateFromProto(guidlow, entry, data, vehId))
@@ -1302,7 +1297,7 @@ uint32 Creature::GetLootId() const
if (m_lootId)
return *m_lootId;
- return GetCreatureTemplate()->lootid;
+ return GetCreatureDifficulty()->LootID;
}
void Creature::SetLootId(Optional<uint32> lootId)
@@ -1568,7 +1563,7 @@ void Creature::UpdateLevelDependantStats()
// mana
Powers powerType = CalculateDisplayPowerType();
SetCreateMana(stats->BaseMana);
- SetStatPctModifier(UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(powerType)), BASE_PCT, cInfo->ModMana * cInfo->ModManaExtra);
+ SetStatPctModifier(UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(powerType)), BASE_PCT, GetCreatureDifficulty()->ManaModifier);
SetPowerType(powerType);
if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType))
@@ -2432,7 +2427,7 @@ bool Creature::isWorldBoss() const
if (IsPet())
return false;
- return (GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_BOSS_MOB) != 0;
+ return (GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_BOSS_MOB) != 0;
}
// select nearest hostile unit within the given distance (regardless of threat list).
@@ -2688,8 +2683,7 @@ CreatureAddon const* Creature::GetCreatureAddon() const
return addon;
}
- // dependent from difficulty mode entry
- return sObjectMgr->GetCreatureTemplateAddon(GetCreatureTemplate()->Entry);
+ return sObjectMgr->GetCreatureTemplateAddon(GetEntry());
}
//creature_addon table
@@ -2939,28 +2933,28 @@ bool Creature::HasScalableLevels() const
void Creature::ApplyLevelScaling()
{
- CreatureLevelScaling const* scaling = GetCreatureTemplate()->GetLevelScaling(GetMap()->GetDifficultyID());
+ CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty();
- if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(scaling->ContentTuningID, 0))
+ if (Optional<ContentTuningLevels> levels = sDB2Manager.GetContentTuningData(creatureDifficulty->ContentTuningID, 0))
{
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ScalingLevelMin), levels->MinLevel);
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ScalingLevelMax), levels->MaxLevel);
}
- int32 mindelta = std::min(scaling->DeltaLevelMax, scaling->DeltaLevelMin);
- int32 maxdelta = std::max(scaling->DeltaLevelMax, scaling->DeltaLevelMin);
+ int32 mindelta = std::min(creatureDifficulty->DeltaLevelMax, creatureDifficulty->DeltaLevelMin);
+ int32 maxdelta = std::max(creatureDifficulty->DeltaLevelMax, creatureDifficulty->DeltaLevelMin);
int32 delta = mindelta == maxdelta ? mindelta : irand(mindelta, maxdelta);
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ScalingLevelDelta), delta);
- SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ContentTuningID), scaling->ContentTuningID);
+ SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ContentTuningID), creatureDifficulty->ContentTuningID);
}
uint64 Creature::GetMaxHealthByLevel(uint8 level) const
{
CreatureTemplate const* cInfo = GetCreatureTemplate();
- CreatureLevelScaling const* scaling = cInfo->GetLevelScaling(GetMap()->GetDifficultyID());
- float baseHealth = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, level, cInfo->GetHealthScalingExpansion(), scaling->ContentTuningID, Classes(cInfo->unit_class));
- return std::max(baseHealth * cInfo->ModHealth * cInfo->ModHealthExtra, 1.0f);
+ CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty();
+ float baseHealth = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class));
+ return std::max(baseHealth * creatureDifficulty->HealthModifier, 1.0f);
}
float Creature::GetHealthMultiplierForTarget(WorldObject const* target) const
@@ -2978,8 +2972,8 @@ float Creature::GetHealthMultiplierForTarget(WorldObject const* target) const
float Creature::GetBaseDamageForLevel(uint8 level) const
{
CreatureTemplate const* cInfo = GetCreatureTemplate();
- CreatureLevelScaling const* scaling = cInfo->GetLevelScaling(GetMap()->GetDifficultyID());
- return sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureAutoAttackDps, level, cInfo->GetHealthScalingExpansion(), scaling->ContentTuningID, Classes(cInfo->unit_class));
+ CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty();
+ return sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureAutoAttackDps, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class));
}
float Creature::GetDamageMultiplierForTarget(WorldObject const* target) const
@@ -2995,9 +2989,9 @@ float Creature::GetDamageMultiplierForTarget(WorldObject const* target) const
float Creature::GetBaseArmorForLevel(uint8 level) const
{
CreatureTemplate const* cInfo = GetCreatureTemplate();
- CreatureLevelScaling const* scaling = cInfo->GetLevelScaling(GetMap()->GetDifficultyID());
- float baseArmor = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureArmor, level, cInfo->GetHealthScalingExpansion(), scaling->ContentTuningID, Classes(cInfo->unit_class));
- return baseArmor * cInfo->ModArmor;
+ CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty();
+ float baseArmor = sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureArmor, level, creatureDifficulty->GetHealthScalingExpansion(), creatureDifficulty->ContentTuningID, Classes(cInfo->unit_class));
+ return baseArmor * creatureDifficulty->ArmorModifier;
}
float Creature::GetArmorMultiplierForTarget(WorldObject const* target) const
@@ -3505,7 +3499,7 @@ void Creature::AtEngage(Unit* target)
{
Unit::AtEngage(target);
- if (!(GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_ALLOW_MOUNTED_COMBAT))
+ if (!(GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_ALLOW_MOUNTED_COMBAT))
Dismount();
RefreshCanSwimFlag();
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index a60701e8f26..1876b77f664 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -215,6 +215,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
CreatureTemplate const* GetCreatureTemplate() const { return m_creatureInfo; }
CreatureData const* GetCreatureData() const { return m_creatureData; }
+ CreatureDifficulty const* GetCreatureDifficulty() const { return m_creatureDifficulty; }
CreatureAddon const* GetCreatureAddon() const;
std::string const& GetAIName() const;
@@ -472,8 +473,9 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool DisableReputationGain;
- CreatureTemplate const* m_creatureInfo; // Can differ from sObjectMgr->GetCreatureTemplate(GetEntry()) in difficulty mode > 0
+ CreatureTemplate const* m_creatureInfo;
CreatureData const* m_creatureData;
+ CreatureDifficulty const* m_creatureDifficulty;
std::array<std::string_view, 3> m_stringIds;
Optional<std::string> m_scriptStringId;
diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h
index 010afc75434..e84c1541749 100644
--- a/src/server/game/Entities/Creature/CreatureData.h
+++ b/src/server/game/Entities/Creature/CreatureData.h
@@ -441,7 +441,6 @@ const uint8 MAX_KILL_CREDIT = 2;
const uint32 MAX_CREATURE_MODELS = 4;
const uint32 MAX_CREATURE_NAMES = 4;
const uint32 MAX_CREATURE_SPELLS = 8;
-const uint32 MAX_CREATURE_DIFFICULTIES = 3;
struct CreatureModel
{
@@ -459,18 +458,48 @@ struct CreatureModel
float Probability;
};
-struct CreatureLevelScaling
+struct CreatureDifficulty
{
int16 DeltaLevelMin;
int16 DeltaLevelMax;
int32 ContentTuningID;
+ int32 HealthScalingExpansion;
+ float HealthModifier;
+ float ManaModifier;
+ float ArmorModifier;
+ float DamageModifier;
+ int32 CreatureDifficultyID;
+ uint32 TypeFlags;
+ uint32 TypeFlags2;
+ uint32 LootID;
+ uint32 PickPocketLootID;
+ uint32 SkinLootID;
+ uint32 GoldMin;
+ uint32 GoldMax;
+
+ // Helpers
+ int32 GetHealthScalingExpansion() const
+ {
+ return HealthScalingExpansion == EXPANSION_LEVEL_CURRENT ? CURRENT_EXPANSION : HealthScalingExpansion;
+ }
+
+ SkillType GetRequiredLootSkill() const
+ {
+ if (TypeFlags & CREATURE_TYPE_FLAG_SKIN_WITH_HERBALISM)
+ return SKILL_HERBALISM;
+ else if (TypeFlags & CREATURE_TYPE_FLAG_SKIN_WITH_MINING)
+ return SKILL_MINING;
+ else if (TypeFlags & CREATURE_TYPE_FLAG_SKIN_WITH_ENGINEERING)
+ return SKILL_ENGINEERING;
+ else
+ return SKILL_SKINNING; // Default case
+ }
};
// from `creature_template` table
struct TC_GAME_API CreatureTemplate
{
uint32 Entry;
- uint32 DifficultyEntry[MAX_CREATURE_DIFFICULTIES];
uint32 KillCredit[MAX_KILL_CREDIT];
std::vector<CreatureModel> Models;
std::string Name;
@@ -479,8 +508,7 @@ struct TC_GAME_API CreatureTemplate
std::string TitleAlt;
std::string IconName;
std::vector<uint32> GossipMenuIds;
- std::unordered_map<Difficulty, CreatureLevelScaling> scalingStore;
- int32 HealthScalingExpansion;
+ std::unordered_map<Difficulty, CreatureDifficulty> difficultyStore;
uint32 RequiredExpansion;
uint32 VignetteID; /// @todo Read Vignette.db2
uint32 faction;
@@ -502,29 +530,15 @@ struct TC_GAME_API CreatureTemplate
CreatureFamily family; // enum CreatureFamily values (optional)
uint32 trainer_class;
uint32 type; // enum CreatureType values
- uint32 type_flags; // enum CreatureTypeFlags mask values
- uint32 type_flags2; // unknown enum, only set for 4 creatures (with value 1)
- uint32 lootid;
- uint32 pickpocketLootId;
- uint32 SkinLootId;
int32 resistance[MAX_SPELL_SCHOOL];
uint32 spells[MAX_CREATURE_SPELLS];
uint32 VehicleId;
- uint32 mingold;
- uint32 maxgold;
std::string AIName;
uint32 MovementType;
CreatureMovementData Movement;
- float ModHealth;
- float ModHealthExtra;
- float ModMana;
- float ModManaExtra; // Added in 4.x, this value is usually 2 for a small group of creatures with double mana
- float ModArmor;
- float ModDamage;
float ModExperience;
bool RacialLeader;
uint32 movementId;
- int32 CreatureDifficultyID;
int32 WidgetSetID;
int32 WidgetSetUnitConditionID;
bool RegenHealth;
@@ -540,70 +554,25 @@ struct TC_GAME_API CreatureTemplate
CreatureModel const* GetModelWithDisplayId(uint32 displayId) const;
CreatureModel const* GetFirstInvisibleModel() const;
CreatureModel const* GetFirstVisibleModel() const;
- int32 GetHealthScalingExpansion() const;
- CreatureLevelScaling const* GetLevelScaling(Difficulty difficulty) const;
+ CreatureDifficulty const* GetDifficulty(Difficulty difficulty) const;
- // helpers
- SkillType GetRequiredLootSkill() const
- {
- if (type_flags & CREATURE_TYPE_FLAG_SKIN_WITH_HERBALISM)
- return SKILL_HERBALISM;
- else if (type_flags & CREATURE_TYPE_FLAG_SKIN_WITH_MINING)
- return SKILL_MINING;
- else if (type_flags & CREATURE_TYPE_FLAG_SKIN_WITH_ENGINEERING)
- return SKILL_ENGINEERING;
- else
- return SKILL_SKINNING; // normal case
- }
-
- bool IsExotic() const
+ // Helpers
+ bool IsExotic(CreatureDifficulty const* creatureDifficulty) const
{
- return (type_flags & CREATURE_TYPE_FLAG_TAMEABLE_EXOTIC) != 0;
+ return (creatureDifficulty->TypeFlags & CREATURE_TYPE_FLAG_TAMEABLE_EXOTIC) != 0;
}
- bool IsTameable(bool canTameExotic) const
+ bool IsTameable(bool canTameExotic, CreatureDifficulty const* creatureDifficulty) const
{
- if (type != CREATURE_TYPE_BEAST || family == CREATURE_FAMILY_NONE || (type_flags & CREATURE_TYPE_FLAG_TAMEABLE) == 0)
+ if (type != CREATURE_TYPE_BEAST || family == CREATURE_FAMILY_NONE || (creatureDifficulty->TypeFlags & CREATURE_TYPE_FLAG_TAMEABLE) == 0)
return false;
// if can tame exotic then can tame any tameable
- return canTameExotic || !IsExotic();
+ return canTameExotic || !IsExotic(creatureDifficulty);
}
void InitializeQueryData();
- WorldPacket BuildQueryData(LocaleConstant loc) const;
-
- static int32 DifficultyIDToDifficultyEntryIndex(uint32 difficulty)
- {
- switch (difficulty)
- {
- case DIFFICULTY_NONE:
- case DIFFICULTY_NORMAL:
- case DIFFICULTY_10_N:
- case DIFFICULTY_40:
- case DIFFICULTY_3_MAN_SCENARIO_N:
- case DIFFICULTY_NORMAL_RAID:
- return -1;
- case DIFFICULTY_HEROIC:
- case DIFFICULTY_25_N:
- case DIFFICULTY_3_MAN_SCENARIO_HC:
- case DIFFICULTY_HEROIC_RAID:
- return 0;
- case DIFFICULTY_10_HC:
- case DIFFICULTY_MYTHIC_KEYSTONE:
- case DIFFICULTY_MYTHIC_RAID:
- return 1;
- case DIFFICULTY_25_HC:
- return 2;
- case DIFFICULTY_LFR:
- case DIFFICULTY_LFR_NEW:
- case DIFFICULTY_EVENT_RAID:
- case DIFFICULTY_EVENT_DUNGEON:
- case DIFFICULTY_EVENT_SCENARIO:
- default:
- return -1;
- }
- }
+ WorldPacket BuildQueryData(LocaleConstant loc, Difficulty difficulty) const;
};
#pragma pack(push, 1)
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 96bbbd0c0a9..7b80617b528 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -3191,7 +3191,7 @@ bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const
if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_ASSIST_IMMUNE_PC))
if (unitTarget && !unitTarget->IsPvP())
if (Creature const* creatureTarget = target->ToCreature())
- return creatureTarget->HasFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS) || (creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST);
+ return creatureTarget->HasFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS) || (creatureTarget->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_CAN_ASSIST);
}
return true;
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 27102506008..7b7ad04704b 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -230,7 +230,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
if (petInfo->Type == HUNTER_PET)
{
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(petInfo->CreatureId);
- if (!creatureInfo || !creatureInfo->IsTameable(owner->CanTameExoticPets()))
+ if (!creatureInfo || !creatureInfo->IsTameable(owner->CanTameExoticPets(), GetCreatureDifficulty()))
return false;
}
@@ -919,7 +919,8 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(petlevel, cinfo->unit_class);
ApplyLevelScaling();
- SetCreateHealth(std::max(sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, petlevel, cinfo->GetHealthScalingExpansion(), m_unitData->ContentTuningID, Classes(cinfo->unit_class)) * cinfo->ModHealth * cinfo->ModHealthExtra * _GetHealthMod(cinfo->rank), 1.0f));
+ CreatureDifficulty const* creatureDifficulty = GetCreatureDifficulty();
+ SetCreateHealth(std::max(sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, petlevel, creatureDifficulty->GetHealthScalingExpansion(), m_unitData->ContentTuningID, Classes(cinfo->unit_class)) * creatureDifficulty->HealthModifier * _GetHealthMod(cinfo->rank), 1.0f));
SetCreateMana(stats->BaseMana);
SetCreateStat(STAT_STRENGTH, 22);
SetCreateStat(STAT_AGILITY, 22);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index a2f4245b5bf..6d28af5cd21 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -2030,11 +2030,11 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, NPCFlags npcFl
return nullptr;
// Deathstate checks
- if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS))
+ if (!IsAlive() && !(creature->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS))
return nullptr;
// alive or spirit healer
- if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_INTERACT_WHILE_DEAD))
+ if (!creature->IsAlive() && !(creature->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_INTERACT_WHILE_DEAD))
return nullptr;
// appropriate npc type
diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp
index 2e1f248ecc7..b408ebe5403 100644
--- a/src/server/game/Entities/Unit/StatSystem.cpp
+++ b/src/server/game/Entities/Unit/StatSystem.cpp
@@ -1034,7 +1034,7 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized,
float basePct = GetPctModifierValue(unitMod, BASE_PCT) * attackSpeedMulti;
float totalValue = GetFlatModifierValue(unitMod, TOTAL_VALUE);
float totalPct = addTotalPct ? GetPctModifierValue(unitMod, TOTAL_PCT) : 1.0f;
- float dmgMultiplier = GetCreatureTemplate()->ModDamage; // = ModDamage * _GetDamageMod(rank);
+ float dmgMultiplier = GetCreatureDifficulty()->DamageModifier; // = DamageModifier * _GetDamageMod(rank);
minDamage = ((weaponMinDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct;
maxDamage = ((weaponMaxDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index f6acfa09d7b..7de61fa8ed8 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -10747,7 +10747,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
if (dungeonEncounter)
{
creature->m_personalLoot = GenerateDungeonEncounterPersonalLoot(dungeonEncounter->ID, creature->GetLootId(),
- LootTemplates_Creature, LOOT_CORPSE, creature, creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold,
+ LootTemplates_Creature, LOOT_CORPSE, creature, creature->GetCreatureDifficulty()->GoldMin, creature->GetCreatureDifficulty()->GoldMax,
creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext(), tappers);
}
else if (!tappers.empty())
@@ -10761,7 +10761,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
loot->FillLoot(lootid, LootTemplates_Creature, looter, dungeonEncounter != nullptr, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext());
if (creature->GetLootMode() > 0)
- loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold);
+ loot->generateMoneyLoot(creature->GetCreatureDifficulty()->GoldMin, creature->GetCreatureDifficulty()->GoldMax);
if (group)
loot->NotifyLootList(creature->GetMap());
@@ -10787,7 +10787,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
loot->FillLoot(lootid, LootTemplates_Creature, tapper, true, false, creature->GetLootMode(), creature->GetMap()->GetDifficultyLootItemContext());
if (creature->GetLootMode() > 0)
- loot->generateMoneyLoot(creature->GetCreatureTemplate()->mingold, creature->GetCreatureTemplate()->maxgold);
+ loot->generateMoneyLoot(creature->GetCreatureDifficulty()->GoldMin, creature->GetCreatureDifficulty()->GoldMax);
creature->m_personalLoot[tapper->GetGUID()].reset(loot);
}
@@ -10887,7 +10887,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
else
creature->AllLootRemovedFromCorpse();
- if (creature->CanHaveLoot() && LootTemplates_Skinning.HaveLootFor(creature->GetCreatureTemplate()->SkinLootId))
+ if (creature->CanHaveLoot() && LootTemplates_Skinning.HaveLootFor(creature->GetCreatureDifficulty()->SkinLootID))
{
creature->SetDynamicFlag(UNIT_DYNFLAG_CAN_SKIN);
creature->SetUnitFlag(UNIT_FLAG_SKINNABLE);
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index a0ec6d3c411..78179a5534c 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -353,23 +353,23 @@ void ObjectMgr::LoadCreatureTemplates()
{
uint32 oldMSTime = getMSTime();
- // 0 1 2 3 4 5 6 7 8
- // "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, "
- // 9 10 11 12 13
- // "TitleAlt, IconName, HealthScalingExpansion, RequiredExpansion, VignetteID, "
- // 14 15 16 17 18 19 20 21 22 23 24
+ // 0 1 2 3 4 5
+ // "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, "
+ // 6 7 8 9
+ // "TitleAlt, IconName, RequiredExpansion, VignetteID, "
+ // 10 11 12 13 14 15 16 17 18 19 20
// "faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, "
- // 25 26 27 28 29 30 31 32
+ // 21 22 23 24 25 26 27 28
// "unit_class, unit_flags, unit_flags2, unit_flags3, dynamicflags, family, trainer_class, type, "
- // 33 34 35 36 37 38 39 40 41 42
- // "type_flags, type_flags2, lootid, pickpocketloot, skinloot, VehicleId, mingold, maxgold, AIName, MovementType, "
- // 43 44 45 46 47 48 49 50 51 52 53 54 55 56
- // "ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, "
- // 57 58 59 60 61 62
- // "RacialLeader, movementId, CreatureDifficultyID, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, "
- // 63 64 65
+ // 29 30 31
+ // "VehicleId, AIName, MovementType, "
+ // 32 33 34 35 36 38 38 39
+ // "ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, "
+ // 40 41 42 43 44
+ // "RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, "
+ // 45 46 47
// "mechanic_immune_mask, spell_school_immune_mask, flags_extra, "
- // 66 67
+ // 48 49
// "ScriptName, StringId FROM creature_template WHERE entry = ? OR 1 = ?");
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEMPLATE);
@@ -412,44 +412,35 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields)
creatureTemplate.Entry = entry;
- for (uint32 i = 0; i < MAX_CREATURE_DIFFICULTIES; ++i)
- creatureTemplate.DifficultyEntry[i] = fields[1 + i].GetUInt32();
-
for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
- creatureTemplate.KillCredit[i] = fields[4 + i].GetUInt32();
-
- creatureTemplate.Name = fields[6].GetString();
- creatureTemplate.FemaleName = fields[7].GetString();
- creatureTemplate.SubName = fields[8].GetString();
- creatureTemplate.TitleAlt = fields[9].GetString();
- creatureTemplate.IconName = fields[10].GetString();
- creatureTemplate.HealthScalingExpansion = fields[11].GetInt32();
- creatureTemplate.RequiredExpansion = fields[12].GetUInt32();
- creatureTemplate.VignetteID = fields[13].GetUInt32();
- creatureTemplate.faction = fields[14].GetUInt16();
- creatureTemplate.npcflag = fields[15].GetUInt64();
- creatureTemplate.speed_walk = fields[16].GetFloat();
- creatureTemplate.speed_run = fields[17].GetFloat();
- creatureTemplate.scale = fields[18].GetFloat();
- creatureTemplate.rank = uint32(fields[19].GetUInt8());
- creatureTemplate.dmgschool = uint32(fields[20].GetInt8());
- creatureTemplate.BaseAttackTime = fields[21].GetUInt32();
- creatureTemplate.RangeAttackTime = fields[22].GetUInt32();
- creatureTemplate.BaseVariance = fields[23].GetFloat();
- creatureTemplate.RangeVariance = fields[24].GetFloat();
- creatureTemplate.unit_class = uint32(fields[25].GetUInt8());
- creatureTemplate.unit_flags = fields[26].GetUInt32();
- creatureTemplate.unit_flags2 = fields[27].GetUInt32();
- creatureTemplate.unit_flags3 = fields[28].GetUInt32();
- creatureTemplate.dynamicflags = fields[29].GetUInt32();
- creatureTemplate.family = CreatureFamily(fields[30].GetInt32());
- creatureTemplate.trainer_class = uint32(fields[31].GetUInt8());
- creatureTemplate.type = uint32(fields[32].GetUInt8());
- creatureTemplate.type_flags = fields[33].GetUInt32();
- creatureTemplate.type_flags2 = fields[34].GetUInt32();
- creatureTemplate.lootid = fields[35].GetUInt32();
- creatureTemplate.pickpocketLootId = fields[36].GetUInt32();
- creatureTemplate.SkinLootId = fields[37].GetUInt32();
+ creatureTemplate.KillCredit[i] = fields[1 + i].GetUInt32();
+
+ creatureTemplate.Name = fields[3].GetString();
+ creatureTemplate.FemaleName = fields[4].GetString();
+ creatureTemplate.SubName = fields[5].GetString();
+ creatureTemplate.TitleAlt = fields[6].GetString();
+ creatureTemplate.IconName = fields[7].GetString();
+ creatureTemplate.RequiredExpansion = fields[8].GetUInt32();
+ creatureTemplate.VignetteID = fields[9].GetUInt32();
+ creatureTemplate.faction = fields[10].GetUInt16();
+ creatureTemplate.npcflag = fields[11].GetUInt64();
+ creatureTemplate.speed_walk = fields[12].GetFloat();
+ creatureTemplate.speed_run = fields[13].GetFloat();
+ creatureTemplate.scale = fields[14].GetFloat();
+ creatureTemplate.rank = uint32(fields[15].GetUInt8());
+ creatureTemplate.dmgschool = uint32(fields[16].GetInt8());
+ creatureTemplate.BaseAttackTime = fields[17].GetUInt32();
+ creatureTemplate.RangeAttackTime = fields[18].GetUInt32();
+ creatureTemplate.BaseVariance = fields[19].GetFloat();
+ creatureTemplate.RangeVariance = fields[20].GetFloat();
+ creatureTemplate.unit_class = uint32(fields[21].GetUInt8());
+ creatureTemplate.unit_flags = fields[22].GetUInt32();
+ creatureTemplate.unit_flags2 = fields[23].GetUInt32();
+ creatureTemplate.unit_flags3 = fields[24].GetUInt32();
+ creatureTemplate.dynamicflags = fields[25].GetUInt32();
+ creatureTemplate.family = CreatureFamily(fields[26].GetInt32());
+ creatureTemplate.trainer_class = uint32(fields[27].GetUInt8());
+ creatureTemplate.type = uint32(fields[28].GetUInt8());
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
creatureTemplate.resistance[i] = 0;
@@ -457,50 +448,41 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields)
for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
creatureTemplate.spells[i] = 0;
- creatureTemplate.VehicleId = fields[38].GetUInt32();
- creatureTemplate.mingold = fields[39].GetUInt32();
- creatureTemplate.maxgold = fields[40].GetUInt32();
- creatureTemplate.AIName = fields[41].GetString();
- creatureTemplate.MovementType = uint32(fields[42].GetUInt8());
- if (!fields[43].IsNull())
- creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[43].GetUInt8());
-
- if (!fields[44].IsNull())
- creatureTemplate.Movement.Swim = fields[44].GetBool();
-
- if (!fields[45].IsNull())
- creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[45].GetUInt8());
-
- if (!fields[46].IsNull())
- creatureTemplate.Movement.Rooted = fields[46].GetBool();
-
- if (!fields[47].IsNull())
- creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[47].GetUInt8());
-
- if (!fields[48].IsNull())
- creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[48].GetUInt8());
-
- if (!fields[49].IsNull())
- creatureTemplate.Movement.InteractionPauseTimer = fields[49].GetUInt32();
-
- creatureTemplate.ModHealth = fields[50].GetFloat();
- creatureTemplate.ModHealthExtra = fields[51].GetFloat();
- creatureTemplate.ModMana = fields[52].GetFloat();
- creatureTemplate.ModManaExtra = fields[53].GetFloat();
- creatureTemplate.ModArmor = fields[54].GetFloat();
- creatureTemplate.ModDamage = fields[55].GetFloat();
- creatureTemplate.ModExperience = fields[56].GetFloat();
- creatureTemplate.RacialLeader = fields[57].GetBool();
- creatureTemplate.movementId = fields[58].GetUInt32();
- creatureTemplate.CreatureDifficultyID = fields[59].GetInt32();
- creatureTemplate.WidgetSetID = fields[60].GetInt32();
- creatureTemplate.WidgetSetUnitConditionID = fields[61].GetInt32();
- creatureTemplate.RegenHealth = fields[62].GetBool();
- creatureTemplate.MechanicImmuneMask = fields[63].GetUInt64();
- creatureTemplate.SpellSchoolImmuneMask = fields[64].GetUInt32();
- creatureTemplate.flags_extra = fields[65].GetUInt32();
- creatureTemplate.ScriptID = GetScriptId(fields[66].GetString());
- creatureTemplate.StringId = fields[67].GetString();
+ creatureTemplate.VehicleId = fields[29].GetUInt32();
+ creatureTemplate.AIName = fields[30].GetString();
+ creatureTemplate.MovementType = uint32(fields[31].GetUInt8());
+ if (!fields[32].IsNull())
+ creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[32].GetUInt8());
+
+ if (!fields[33].IsNull())
+ creatureTemplate.Movement.Swim = fields[33].GetBool();
+
+ if (!fields[34].IsNull())
+ creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[34].GetUInt8());
+
+ if (!fields[35].IsNull())
+ creatureTemplate.Movement.Rooted = fields[35].GetBool();
+
+ if (!fields[36].IsNull())
+ creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[36].GetUInt8());
+
+ if (!fields[37].IsNull())
+ creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[37].GetUInt8());
+
+ if (!fields[38].IsNull())
+ creatureTemplate.Movement.InteractionPauseTimer = fields[38].GetUInt32();
+
+ creatureTemplate.ModExperience = fields[39].GetFloat();
+ creatureTemplate.RacialLeader = fields[40].GetBool();
+ creatureTemplate.movementId = fields[41].GetUInt32();
+ creatureTemplate.WidgetSetID = fields[42].GetInt32();
+ creatureTemplate.WidgetSetUnitConditionID = fields[43].GetInt32();
+ creatureTemplate.RegenHealth = fields[44].GetBool();
+ creatureTemplate.MechanicImmuneMask = fields[45].GetUInt64();
+ creatureTemplate.SpellSchoolImmuneMask = fields[46].GetUInt32();
+ creatureTemplate.flags_extra = fields[47].GetUInt32();
+ creatureTemplate.ScriptID = GetScriptId(fields[48].GetString());
+ creatureTemplate.StringId = fields[49].GetString();
}
void ObjectMgr::LoadCreatureTemplateGossip()
@@ -935,16 +917,16 @@ void ObjectMgr::LoadCreatureTemplateSparring()
TC_LOG_INFO("server.loading", ">> Loaded %u creature template sparring rows in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
-void ObjectMgr::LoadCreatureScalingData()
+void ObjectMgr::LoadCreatureTemplateDifficulty()
{
uint32 oldMSTime = getMSTime();
- // 0 1 2 3 4
- QueryResult result = WorldDatabase.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID FROM creature_template_scaling ORDER BY Entry");
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
+ QueryResult result = WorldDatabase.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID, HealthScalingExpansion, HealthModifier, ManaModifier, ArmorModifier, DamageModifier, CreatureDifficultyID, TypeFlags, TypeFlags2, LootID, PickPocketLootID, SkinLootID, GoldMin, GoldMax FROM creature_template_difficulty ORDER BY Entry");
if (!result)
{
- TC_LOG_INFO("server.loading", ">> Loaded 0 creature template scaling definitions. DB table `creature_template_scaling` is empty.");
+ TC_LOG_INFO("server.loading", ">> Loaded 0 creature template difficulty definitions. DB table `creature_template_difficulty` is empty.");
return;
}
@@ -959,34 +941,51 @@ void ObjectMgr::LoadCreatureScalingData()
auto itr = _creatureTemplateStore.find(entry);
if (itr == _creatureTemplateStore.end())
{
- TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_scaling`", entry);
+ TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_difficulty`", entry);
continue;
}
- CreatureLevelScaling creatureLevelScaling;
- creatureLevelScaling.DeltaLevelMin = fields[2].GetInt16();
- creatureLevelScaling.DeltaLevelMax = fields[3].GetInt16();
- creatureLevelScaling.ContentTuningID = fields[4].GetInt32();
+ CreatureDifficulty creatureDifficulty;
+ creatureDifficulty.DeltaLevelMin = fields[2].GetInt16();
+ creatureDifficulty.DeltaLevelMax = fields[3].GetInt16();
+ creatureDifficulty.ContentTuningID = fields[4].GetInt32();
+ creatureDifficulty.HealthScalingExpansion = fields[5].GetInt32();
+ creatureDifficulty.HealthModifier = fields[6].GetFloat();
+ creatureDifficulty.ManaModifier = fields[7].GetFloat();
+ creatureDifficulty.ArmorModifier = fields[8].GetFloat();
+ creatureDifficulty.DamageModifier = fields[9].GetFloat();
+ creatureDifficulty.CreatureDifficultyID = fields[10].GetInt32();
+ creatureDifficulty.TypeFlags = fields[11].GetUInt32();
+ creatureDifficulty.TypeFlags2 = fields[12].GetUInt32();
+ creatureDifficulty.LootID = fields[13].GetUInt32();
+ creatureDifficulty.PickPocketLootID = fields[14].GetUInt32();
+ creatureDifficulty.SkinLootID = fields[15].GetUInt32();
+ creatureDifficulty.GoldMin = fields[16].GetUInt32();
+ creatureDifficulty.GoldMax = fields[17].GetUInt32();
- itr->second.scalingStore[difficulty] = creatureLevelScaling;
+ // TODO: Check if this still applies
+ creatureDifficulty.DamageModifier *= Creature::_GetDamageMod(itr->second.rank);
- // Assign creature level scaling to creature difficulty entry (if any)
- // TODO: Drop the use of creature difficulties
- int32 difficultyIndex = CreatureTemplate::DifficultyIDToDifficultyEntryIndex(difficulty);
- if (difficultyIndex != -1)
+ if (creatureDifficulty.HealthScalingExpansion < EXPANSION_LEVEL_CURRENT || creatureDifficulty.HealthScalingExpansion >= MAX_EXPANSIONS)
{
- if (uint32 difficultyEntry = itr->second.DifficultyEntry[difficultyIndex])
- {
- auto itr2 = _creatureTemplateStore.find(difficultyEntry);
- if (itr2 != _creatureTemplateStore.end())
- itr2->second.scalingStore[difficulty] = creatureLevelScaling;
- }
+ TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with invalid `HealthScalingExpansion` {}. Ignored and set to 0.",
+ entry, creatureDifficulty.HealthScalingExpansion);
+ creatureDifficulty.HealthScalingExpansion = 0;
}
+ if (creatureDifficulty.GoldMin > creatureDifficulty.GoldMax)
+ {
+ TC_LOG_ERROR("sql.sql", "Table `creature_template_difficulty` lists creature (ID: {}) with `GoldMin` {} greater than `GoldMax` {}, setting `GoldMax` to {}.",
+ entry, creatureDifficulty.GoldMin, creatureDifficulty.GoldMax, creatureDifficulty.GoldMin);
+ creatureDifficulty.GoldMax = creatureDifficulty.GoldMin;
+ }
+
+ itr->second.difficultyStore[difficulty] = creatureDifficulty;
+
++count;
} while (result->NextRow());
- TC_LOG_INFO("server.loading", ">> Loaded {} creature template scaling data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded {} creature template difficulty data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
}
void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
@@ -994,183 +993,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
if (!cInfo)
return;
- bool ok = true; // bool to allow continue outside this loop
- for (uint32 diff = 0; diff < MAX_CREATURE_DIFFICULTIES && ok; ++diff)
- {
- if (!cInfo->DifficultyEntry[diff])
- continue;
- ok = false; // will be set to true at the end of this loop again
-
- CreatureTemplate const* difficultyInfo = GetCreatureTemplate(cInfo->DifficultyEntry[diff]);
- if (!difficultyInfo)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has `difficulty_entry_{}`={} but creature entry {} does not exist.",
- cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff]);
- continue;
- }
-
- bool ok2 = true;
- for (uint32 diff2 = 0; diff2 < MAX_CREATURE_DIFFICULTIES && ok2; ++diff2)
- {
- ok2 = false;
- if (_difficultyEntries[diff2].find(cInfo->Entry) != _difficultyEntries[diff2].end())
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) is listed as `difficulty_entry_{}` of another creature, but itself lists {} in `difficulty_entry_{}`.",
- cInfo->Entry, diff2 + 1, cInfo->DifficultyEntry[diff], diff + 1);
- continue;
- }
-
- if (_difficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _difficultyEntries[diff2].end())
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) already listed as `difficulty_entry_{}` for another entry.", cInfo->DifficultyEntry[diff], diff2 + 1);
- continue;
- }
-
- if (_hasDifficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _hasDifficultyEntries[diff2].end())
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has `difficulty_entry_{}`={} but creature entry {} has itself a value in `difficulty_entry_{}`.",
- cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff], diff2 + 1);
- continue;
- }
- ok2 = true;
- }
-
- if (!ok2)
- continue;
-
- if (cInfo->HealthScalingExpansion > difficultyInfo->HealthScalingExpansion)
- {
- TC_LOG_ERROR("sql.sql", "Creature (ID: {}, Expansion: {}) has different `HealthScalingExpansion` in difficulty {} mode (ID: {}, Expansion: {}).",
- cInfo->Entry, cInfo->HealthScalingExpansion, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->HealthScalingExpansion);
- }
-
- if (cInfo->faction != difficultyInfo->faction)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, faction: {}) has different `faction` in difficulty {} mode (Entry: {}, faction: {}).",
- cInfo->Entry, cInfo->faction, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->faction);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `faction`={} WHERE `entry`={};",
- cInfo->faction, cInfo->DifficultyEntry[diff]);
- }
-
- if (cInfo->unit_class != difficultyInfo->unit_class)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, class: {}) has different `unit_class` in difficulty {} mode (Entry: {}, class: {}).",
- cInfo->Entry, cInfo->unit_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_class);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `unit_class`={} WHERE `entry`={};",
- cInfo->unit_class, cInfo->DifficultyEntry[diff]);
- continue;
- }
-
- uint32 differenceMask = cInfo->npcflag ^ difficultyInfo->npcflag;
- if (cInfo->npcflag != difficultyInfo->npcflag)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, `npcflag`: {}) has different `npcflag` in difficulty {} mode (Entry: {}, `npcflag`: {}).",
- cInfo->Entry, cInfo->npcflag, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->npcflag);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `npcflag`=`npcflag`^{} WHERE `entry`={};",
- differenceMask, cInfo->DifficultyEntry[diff]);
- continue;
- }
-
- if (cInfo->dmgschool != difficultyInfo->dmgschool)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, `dmgschool`: {}) has different `dmgschool` in difficulty {} mode (Entry: {}, `dmgschool`: {}).",
- cInfo->Entry, cInfo->dmgschool, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->dmgschool);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `dmgschool`={} WHERE `entry`={};",
- cInfo->dmgschool, cInfo->DifficultyEntry[diff]);
- }
-
- differenceMask = cInfo->unit_flags2 ^ difficultyInfo->unit_flags2;
- if (cInfo->unit_flags2 != difficultyInfo->unit_flags2)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, `unit_flags2`: {}) has different `unit_flags2` in difficulty {} mode (Entry: {}, `unit_flags2`: {}).",
- cInfo->Entry, cInfo->unit_flags2, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_flags2);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `unit_flags2`=`unit_flags2`^{} WHERE `entry`={};",
- differenceMask, cInfo->DifficultyEntry[diff]);
- }
-
- if (cInfo->family != difficultyInfo->family)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, family: {}) has different `family` in difficulty {} mode (Entry: {}, family: {}).",
- cInfo->Entry, cInfo->family, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->family);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `family`={} WHERE `entry`={};",
- cInfo->family, cInfo->DifficultyEntry[diff]);
- }
-
- if (cInfo->trainer_class != difficultyInfo->trainer_class)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, trainer_class: {}) has different `trainer_class` in difficulty {} mode (Entry: {}, trainer_class: {}).",
- cInfo->Entry, cInfo->trainer_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->trainer_class);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `trainer_class`={} WHERE `entry`={};",
- cInfo->trainer_class, cInfo->DifficultyEntry[diff]);
- continue;
- }
-
- if (cInfo->type != difficultyInfo->type)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, type: {}) has different `type` in difficulty {} mode (Entry: {}, type: {}).",
- cInfo->Entry, cInfo->type, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->type);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `type`={} WHERE `entry`={};",
- cInfo->type, cInfo->DifficultyEntry[diff]);
- }
-
- if (!cInfo->VehicleId && difficultyInfo->VehicleId)
- {
- TC_LOG_ERROR("sql.sql", "Non-vehicle Creature (Entry: {}, VehicleId: {}) has `VehicleId` set in difficulty {} mode (Entry: {}, VehicleId: {}).",
- cInfo->Entry, cInfo->VehicleId, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->VehicleId);
- }
-
- if (cInfo->RegenHealth != difficultyInfo->RegenHealth)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, RegenHealth: {}) has different `RegenHealth` in difficulty {} mode (Entry: {}, RegenHealth: {}).",
- cInfo->Entry, cInfo->RegenHealth, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->RegenHealth);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `RegenHealth`={} WHERE `entry`={};",
- cInfo->RegenHealth, cInfo->DifficultyEntry[diff]);
- }
-
- differenceMask = cInfo->MechanicImmuneMask & (~difficultyInfo->MechanicImmuneMask);
- if (differenceMask)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, mechanic_immune_mask: {}) has weaker immunities in difficulty {} mode (Entry: {}, mechanic_immune_mask: {}).",
- cInfo->Entry, cInfo->MechanicImmuneMask, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->MechanicImmuneMask);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `mechanic_immune_mask`=`mechanic_immune_mask`|{} WHERE `entry`={};",
- differenceMask, cInfo->DifficultyEntry[diff]);
- }
-
- differenceMask = (cInfo->flags_extra ^ difficultyInfo->flags_extra) & (~CREATURE_FLAG_EXTRA_INSTANCE_BIND);
- if (differenceMask)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, flags_extra: {}) has different `flags_extra` in difficulty {} mode (Entry: {}, flags_extra: {}).",
- cInfo->Entry, cInfo->flags_extra, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->flags_extra);
- TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `flags_extra`=`flags_extra`^{} WHERE `entry`={};",
- differenceMask, cInfo->DifficultyEntry[diff]);
- }
-
- if (!difficultyInfo->AIName.empty())
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists difficulty {} mode entry {} with `AIName` filled in. `AIName` of difficulty 0 mode creature is always used instead.",
- cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
- continue;
- }
-
- if (difficultyInfo->ScriptID)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists difficulty {} mode entry {} with `ScriptName` filled in. `ScriptName` of difficulty 0 mode creature is always used instead.",
- cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
- continue;
- }
-
- _hasDifficultyEntries[diff].insert(cInfo->Entry);
- _difficultyEntries[diff].insert(cInfo->DifficultyEntry[diff]);
- ok = true;
- }
-
- if (cInfo->mingold > cInfo->maxgold)
- {
- TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has `mingold` {} which is greater than `maxgold` {}, setting `maxgold` to {}.",
- cInfo->Entry, cInfo->mingold, cInfo->maxgold, cInfo->mingold);
- const_cast<CreatureTemplate*>(cInfo)->maxgold = cInfo->mingold;
- }
-
if (!cInfo->AIName.empty())
{
auto registryItem = sCreatureAIRegistry->GetRegistryItem(cInfo->AIName);
@@ -1281,12 +1103,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
}
- if (cInfo->HealthScalingExpansion < EXPANSION_LEVEL_CURRENT || cInfo->HealthScalingExpansion >= MAX_EXPANSIONS)
- {
- TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (ID: {}) with invalid `HealthScalingExpansion` {}. Ignored and set to 0.", cInfo->Entry, cInfo->HealthScalingExpansion);
- const_cast<CreatureTemplate*>(cInfo)->HealthScalingExpansion = 0;
- }
-
if (cInfo->RequiredExpansion >= MAX_EXPANSIONS)
{
TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with `RequiredExpansion` {}. Ignored and set to 0.", cInfo->Entry, cInfo->RequiredExpansion);
@@ -1323,8 +1139,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
const_cast<CreatureTemplate*>(cInfo)->dynamicflags = 0;
}
- const_cast<CreatureTemplate*>(cInfo)->ModDamage *= Creature::_GetDamageMod(cInfo->rank);
-
if (!cInfo->GossipMenuIds.empty() && !(cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP))
TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has assigned gossip menu, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.", cInfo->Entry);
else if (cInfo->GossipMenuIds.empty() && cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP)
@@ -2412,19 +2226,6 @@ void ObjectMgr::LoadCreatures()
continue;
}
- bool ok = true;
- for (uint32 diff = 0; diff < MAX_CREATURE_DIFFICULTIES && ok; ++diff)
- {
- if (_difficultyEntries[diff].find(data.id) != _difficultyEntries[diff].end())
- {
- TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is listed as difficulty {} template (entry: {}) in `creature_template`, skipped.",
- guid, diff + 1, data.id);
- ok = false;
- }
- }
- if (!ok)
- continue;
-
// -1 random, 0 no equipment
if (data.equipmentId != 0)
{
@@ -4635,6 +4436,20 @@ void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, Play
}
}
+std::vector<uint32> const* ObjectMgr::GetCreatureQuestItemList(uint32 creatureEntry, Difficulty difficulty) const
+{
+ CreatureQuestItemMap::const_iterator itr = _creatureQuestItemStore.find(std::make_pair(creatureEntry, difficulty));
+ if (itr != _creatureQuestItemStore.end())
+ return &itr->second;
+
+ // If there is no data for the difficulty, try to get data for the fallback difficulty
+ DifficultyEntry const* difficultyEntry = sDifficultyStore.LookupEntry(difficulty);
+ if (difficultyEntry)
+ return GetCreatureQuestItemList(creatureEntry, Difficulty(difficultyEntry->FallbackDifficultyID));
+
+ return nullptr;
+}
+
void ObjectMgr::LoadQuests()
{
uint32 oldMSTime = getMSTime();
@@ -6442,14 +6257,6 @@ void ObjectMgr::LoadInstanceEncounters()
continue;
}
const_cast<CreatureTemplate*>(creatureInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
- for (uint8 diff = 0; diff < MAX_CREATURE_DIFFICULTIES; ++diff)
- {
- if (uint32 diffEntry = creatureInfo->DifficultyEntry[diff])
- {
- if (CreatureTemplate const* diffInfo = GetCreatureTemplate(diffEntry))
- const_cast<CreatureTemplate*>(diffInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
- }
- }
break;
}
case ENCOUNTER_CREDIT_CAST_SPELL:
@@ -10993,8 +10800,8 @@ void ObjectMgr::LoadCreatureQuestItems()
{
uint32 oldMSTime = getMSTime();
- // 0 1 2
- QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
+ // 0 1 2 3
+ QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, DifficultyID, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
if (!result)
{
@@ -11008,24 +10815,25 @@ void ObjectMgr::LoadCreatureQuestItems()
Field* fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
- uint32 item = fields[1].GetUInt32();
- uint32 idx = fields[2].GetUInt32();
+ Difficulty difficulty = Difficulty(fields[1].GetUInt8());
+ uint32 item = fields[2].GetUInt32();
+ uint32 idx = fields[3].GetUInt32();
CreatureTemplate const* creatureInfo = GetCreatureTemplate(entry);
if (!creatureInfo)
{
- TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, idx: {}), skipped", entry, idx);
+ TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, difficulty: {}, idx: {}), skipped", entry, difficulty, idx);
continue;
};
ItemEntry const* db2Data = sItemStore.LookupEntry(item);
if (!db2Data)
{
- TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, idx: {}), skipped", item, entry, idx);
+ TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, difficulty: {}, idx: {}), skipped", item, entry, difficulty, idx);
continue;
};
- _creatureQuestItemStore[entry].push_back(item);
+ _creatureQuestItemStore[std::make_pair(entry, difficulty)].push_back(item);
++count;
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index c0a2f5b228c..939f5bcc217 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -502,7 +502,7 @@ typedef std::unordered_map<uint16, CreatureBaseStats> CreatureBaseStatsContainer
typedef std::unordered_map<uint8, EquipmentInfo> EquipmentInfoContainerInternal;
typedef std::unordered_map<uint32, EquipmentInfoContainerInternal> EquipmentInfoContainer;
typedef std::unordered_map<uint32, CreatureModelInfo> CreatureModelContainer;
-typedef std::unordered_map<uint32, std::vector<uint32>> CreatureQuestItemMap;
+typedef std::unordered_map<std::pair<uint32, Difficulty>, std::vector<uint32>> CreatureQuestItemMap;
typedef std::unordered_map<uint32, GameObjectTemplate> GameObjectTemplateContainer;
typedef std::unordered_map<uint32, GameObjectTemplateAddon> GameObjectTemplateAddonContainer;
typedef std::unordered_map<ObjectGuid::LowType, GameObjectOverride> GameObjectOverrideContainer;
@@ -1181,13 +1181,7 @@ class TC_GAME_API ObjectMgr
}
GameObjectQuestItemMap const* GetGameObjectQuestItemMap() const { return &_gameObjectQuestItemStore; }
- std::vector<uint32> const* GetCreatureQuestItemList(uint32 id) const
- {
- CreatureQuestItemMap::const_iterator itr = _creatureQuestItemStore.find(id);
- if (itr != _creatureQuestItemStore.end())
- return &itr->second;
- return nullptr;
- }
+ std::vector<uint32> const* GetCreatureQuestItemList(uint32 creatureEntry, Difficulty difficulty) const;
CreatureQuestItemMap const* GetCreatureQuestItemMap() const { return &_creatureQuestItemStore; }
uint32 GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team);
@@ -1326,12 +1320,12 @@ class TC_GAME_API ObjectMgr
void LoadCreatureTemplateAddons();
void LoadCreatureTemplateSparring();
void LoadCreatureTemplate(Field* fields);
+ void LoadCreatureTemplateDifficulty();
void LoadCreatureTemplateGossip();
void LoadCreatureTemplateResistances();
void LoadCreatureTemplateSpells();
void LoadCreatureTemplateModels();
void LoadCreatureSummonedData();
- void LoadCreatureScalingData();
void CheckCreatureTemplate(CreatureTemplate const* cInfo);
void CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement);
void LoadGameObjectQuestItems();
@@ -1953,9 +1947,6 @@ class TC_GAME_API ObjectMgr
std::unordered_map<uint32, Trainer::Trainer> _trainers;
std::map<std::tuple<uint32, uint32, uint32>, uint32> _creatureDefaultTrainers;
- std::set<uint32> _difficultyEntries[MAX_CREATURE_DIFFICULTIES]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate
- std::set<uint32> _hasDifficultyEntries[MAX_CREATURE_DIFFICULTIES]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate
-
std::unordered_map<uint8, RaceUnlockRequirement> _raceUnlockRequirementStore;
std::vector<RaceClassAvailability> _classExpansionRequirementStore;
RealmNameContainer _realmNameStore;
diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp
index c8a94c3f206..372816cc758 100644
--- a/src/server/game/Handlers/NPCHandler.cpp
+++ b/src/server/game/Handlers/NPCHandler.cpp
@@ -481,7 +481,7 @@ void WorldSession::HandleSetPetSlot(WorldPackets::NPC::SetPetSlot& setPetSlot)
if (dstPet)
{
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(dstPet->CreatureId);
- if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
+ if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets(), creatureInfo->GetDifficulty(DIFFICULTY_NONE)))
{
SendPetStableResult(StableResult::CantControlExotic);
return;
@@ -507,7 +507,7 @@ void WorldSession::HandleSetPetSlot(WorldPackets::NPC::SetPetSlot& setPetSlot)
}
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(srcPet->CreatureId);
- if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets()))
+ if (!creatureInfo || !creatureInfo->IsTameable(_player->CanTameExoticPets(), creatureInfo->GetDifficulty(DIFFICULTY_NONE)))
{
SendPetStableResult(StableResult::CantControlExotic);
return;
diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp
index 1dddefe38b7..74548ba34e2 100644
--- a/src/server/game/Handlers/QueryHandler.cpp
+++ b/src/server/game/Handlers/QueryHandler.cpp
@@ -23,6 +23,7 @@
#include "GameTime.h"
#include "Item.h"
#include "Log.h"
+#include "Map.h"
#include "NPCHandler.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
@@ -72,11 +73,15 @@ void WorldSession::HandleCreatureQuery(WorldPackets::Query::QueryCreature& packe
if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(packet.CreatureID))
{
TC_LOG_DEBUG("network", "WORLD: CMSG_QUERY_CREATURE '{}' - Entry: {}.", ci->Name, packet.CreatureID);
- if (sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES))
+
+ Difficulty difficulty = _player->GetMap()->GetDifficultyID();
+
+ // Cache only exists for difficulty base
+ if (sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES) && difficulty == DIFFICULTY_NONE)
SendPacket(&ci->QueryData[static_cast<uint32>(GetSessionDbLocaleIndex())]);
else
{
- WorldPacket response = ci->BuildQueryData(GetSessionDbLocaleIndex());
+ WorldPacket response = ci->BuildQueryData(GetSessionDbLocaleIndex(), difficulty);
SendPacket(&response);
}
TC_LOG_DEBUG("network", "WORLD: Sent SMSG_QUERY_CREATURE_RESPONSE");
diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp
index 9475a5c906b..00959ce3a0b 100644
--- a/src/server/game/Loot/LootMgr.cpp
+++ b/src/server/game/Loot/LootMgr.cpp
@@ -993,12 +993,15 @@ void LoadLootTemplates_Creature()
CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
for (auto const& creatureTemplatePair : ctc)
{
- if (uint32 lootid = creatureTemplatePair.second.lootid)
+ for (auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore)
{
- if (!lootIdSet.count(lootid))
- LootTemplates_Creature.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first);
- else
- lootIdSetUsed.insert(lootid);
+ if (uint32 lootid = creatureDifficulty.LootID)
+ {
+ if (!lootIdSet.count(lootid))
+ LootTemplates_Creature.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first);
+ else
+ lootIdSetUsed.insert(lootid);
+ }
}
}
@@ -1182,12 +1185,15 @@ void LoadLootTemplates_Pickpocketing()
CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
for (auto const& creatureTemplatePair : ctc)
{
- if (uint32 lootid = creatureTemplatePair.second.pickpocketLootId)
+ for (auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore)
{
- if (!lootIdSet.count(lootid))
- LootTemplates_Pickpocketing.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first);
- else
- lootIdSetUsed.insert(lootid);
+ if (uint32 lootid = creatureDifficulty.PickPocketLootID)
+ {
+ if (!lootIdSet.count(lootid))
+ LootTemplates_Pickpocketing.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first);
+ else
+ lootIdSetUsed.insert(lootid);
+ }
}
}
@@ -1269,12 +1275,15 @@ void LoadLootTemplates_Skinning()
CreatureTemplateContainer const& ctc = sObjectMgr->GetCreatureTemplates();
for (auto const& creatureTemplatePair : ctc)
{
- if (uint32 lootid = creatureTemplatePair.second.SkinLootId)
+ for (auto const& [difficulty, creatureDifficulty] : creatureTemplatePair.second.difficultyStore)
{
- if (!lootIdSet.count(lootid))
- LootTemplates_Skinning.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first);
- else
- lootIdSetUsed.insert(lootid);
+ if (uint32 lootid = creatureDifficulty.SkinLootID)
+ {
+ if (!lootIdSet.count(lootid))
+ LootTemplates_Skinning.ReportNonExistingId(lootid, "Creature", creatureTemplatePair.first);
+ else
+ lootIdSetUsed.insert(lootid);
+ }
}
}
diff --git a/src/server/game/Miscellaneous/Formulas.h b/src/server/game/Miscellaneous/Formulas.h
index 77f0c64f93a..6630602b026 100644
--- a/src/server/game/Miscellaneous/Formulas.h
+++ b/src/server/game/Miscellaneous/Formulas.h
@@ -188,7 +188,7 @@ namespace Trinity
if (gain && creature)
{
// Players get only 10% xp for killing creatures of lower expansion levels than himself
- if ((uint32(creature->GetCreatureTemplate()->GetHealthScalingExpansion()) < GetExpansionForLevel(player->GetLevel())))
+ if ((uint32(creature->GetCreatureDifficulty()->GetHealthScalingExpansion()) < GetExpansionForLevel(player->GetLevel())))
gain = uint32(round(gain / 10.0f));
if (creature->isElite())
diff --git a/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp b/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp
index acaeacb9b9f..962fe622201 100644
--- a/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp
+++ b/src/server/game/Server/Packets/CombatLogPacketsCommon.cpp
@@ -68,7 +68,7 @@ template<>
bool ContentTuningParams::GenerateDataForUnits<Creature, Player>(Creature* attacker, Player* target)
{
CreatureTemplate const* creatureTemplate = attacker->GetCreatureTemplate();
- CreatureLevelScaling const* creatureScaling = creatureTemplate->GetLevelScaling(attacker->GetMap()->GetDifficultyID());
+ CreatureDifficulty const* creatureDifficulty = creatureTemplate->GetDifficulty(attacker->GetMap()->GetDifficultyID());
Type = TYPE_CREATURE_TO_PLAYER_DAMAGE;
PlayerLevelDelta = target->m_activePlayerData->ScalingPlayerLevelDelta;
@@ -76,9 +76,9 @@ bool ContentTuningParams::GenerateDataForUnits<Creature, Player>(Creature* attac
TargetItemLevel = 0;
ScalingHealthItemLevelCurveID = target->m_unitData->ScalingHealthItemLevelCurveID;
TargetLevel = target->GetLevel();
- Expansion = creatureTemplate->HealthScalingExpansion;
+ Expansion = creatureDifficulty->HealthScalingExpansion;
TargetScalingLevelDelta = int8(attacker->m_unitData->ScalingLevelDelta);
- TargetContentTuningID = creatureScaling->ContentTuningID;
+ TargetContentTuningID = creatureDifficulty->ContentTuningID;
return true;
}
@@ -86,7 +86,7 @@ template<>
bool ContentTuningParams::GenerateDataForUnits<Player, Creature>(Player* attacker, Creature* target)
{
CreatureTemplate const* creatureTemplate = target->GetCreatureTemplate();
- CreatureLevelScaling const* creatureScaling = creatureTemplate->GetLevelScaling(target->GetMap()->GetDifficultyID());
+ CreatureDifficulty const* creatureDifficulty = creatureTemplate->GetDifficulty(target->GetMap()->GetDifficultyID());
Type = TYPE_PLAYER_TO_CREATURE_DAMAGE;
PlayerLevelDelta = attacker->m_activePlayerData->ScalingPlayerLevelDelta;
@@ -94,9 +94,9 @@ bool ContentTuningParams::GenerateDataForUnits<Player, Creature>(Player* attacke
TargetItemLevel = 0;
ScalingHealthItemLevelCurveID = target->m_unitData->ScalingHealthItemLevelCurveID;
TargetLevel = target->GetLevel();
- Expansion = creatureTemplate->HealthScalingExpansion;
+ Expansion = creatureDifficulty->HealthScalingExpansion;
TargetScalingLevelDelta = int8(target->m_unitData->ScalingLevelDelta);
- TargetContentTuningID = creatureScaling->ContentTuningID;
+ TargetContentTuningID = creatureDifficulty->ContentTuningID;
return true;
}
@@ -105,15 +105,15 @@ bool ContentTuningParams::GenerateDataForUnits<Creature, Creature>(Creature* att
{
Creature* accessor = target->HasScalableLevels() ? target : attacker;
CreatureTemplate const* creatureTemplate = accessor->GetCreatureTemplate();
- CreatureLevelScaling const* creatureScaling = creatureTemplate->GetLevelScaling(accessor->GetMap()->GetDifficultyID());
+ CreatureDifficulty const* creatureDifficulty = creatureTemplate->GetDifficulty(accessor->GetMap()->GetDifficultyID());
Type = TYPE_CREATURE_TO_CREATURE_DAMAGE;
PlayerLevelDelta = 0;
PlayerItemLevel = 0;
TargetLevel = target->GetLevel();
- Expansion = creatureTemplate->HealthScalingExpansion;
+ Expansion = creatureDifficulty->HealthScalingExpansion;
TargetScalingLevelDelta = int8(accessor->m_unitData->ScalingLevelDelta);
- TargetContentTuningID = creatureScaling->ContentTuningID;
+ TargetContentTuningID = creatureDifficulty->ContentTuningID;
return true;
}
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 2b58cdca83f..e46565887ac 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -1875,7 +1875,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffectInfo const& spellEffectInfo, Sp
if (Creature* creatureTarget = unit->ToCreature())
{
- if (!(creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_COLLIDE_WITH_MISSILES))
+ if (!(creatureTarget->GetCreatureDifficulty()->TypeFlags & CREATURE_TYPE_FLAG_COLLIDE_WITH_MISSILES))
continue;
}
}
@@ -6252,10 +6252,11 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32
if (info.first->Type == HUNTER_PET)
{
CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(info.first->CreatureId);
- if (!creatureInfo || !creatureInfo->IsTameable(playerCaster->CanTameExoticPets()))
+ CreatureDifficulty const* creatureDifficulty = creatureInfo->GetDifficulty(DIFFICULTY_NONE);
+ if (!creatureInfo || !creatureInfo->IsTameable(playerCaster->CanTameExoticPets(), creatureDifficulty))
{
// if problem in exotic pet
- if (creatureInfo && creatureInfo->IsTameable(true))
+ if (creatureInfo && creatureInfo->IsTameable(true, creatureDifficulty))
playerCaster->SendTameFailure(PetTameResult::CantControlExotic);
else
playerCaster->SendTameFailure(PetTameResult::NoPetAvailable);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index d90f7cd7342..1e548610341 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -2177,7 +2177,7 @@ void Spell::EffectPickPocket()
creature->StartPickPocketRefillTimer();
creature->m_loot.reset(new Loot(creature->GetMap(), creature->GetGUID(), LOOT_PICKPOCKETING, nullptr));
- if (uint32 lootid = creature->GetCreatureTemplate()->pickpocketLootId)
+ if (uint32 lootid = creature->GetCreatureDifficulty()->PickPocketLootID)
creature->m_loot->FillLoot(lootid, LootTemplates_Pickpocketing, player, true);
// Generate extra money for pick pocket loot
@@ -3725,13 +3725,13 @@ void Spell::EffectSkinning()
Creature* creature = unitTarget->ToCreature();
int32 targetLevel = creature->GetLevelForTarget(m_caster);
- uint32 skill = creature->GetCreatureTemplate()->GetRequiredLootSkill();
+ uint32 skill = creature->GetCreatureDifficulty()->GetRequiredLootSkill();
creature->SetUnitFlag3(UNIT_FLAG3_ALREADY_SKINNED);
creature->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
Loot* loot = new Loot(creature->GetMap(), creature->GetGUID(), LOOT_SKINNING, nullptr);
creature->m_personalLoot[player->GetGUID()].reset(loot);
- loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, player, true);
+ loot->FillLoot(creature->GetCreatureDifficulty()->SkinLootID, LootTemplates_Skinning, player, true);
player->SendLoot(*loot);
if (!IsPartOfSkillLine(skill, m_spellInfo->Id))
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index cab8349095b..f835b85de2e 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -2184,7 +2184,7 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co
if (!targetCreature)
return SPELL_FAILED_BAD_TARGETS;
- if (!targetCreature->CanHaveLoot() || !LootTemplates_Pickpocketing.HaveLootFor(targetCreature->GetCreatureTemplate()->pickpocketLootId))
+ if (!targetCreature->CanHaveLoot() || !LootTemplates_Pickpocketing.HaveLootFor(targetCreature->GetCreatureDifficulty()->PickPocketLootID))
return SPELL_FAILED_TARGET_NO_POCKETS;
}
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 3bf7c0a6c58..67d8cc0d280 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1981,12 +1981,12 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading Creature template addons...");
sObjectMgr->LoadCreatureTemplateAddons();
+ TC_LOG_INFO("server.loading", "Loading Creature template difficulty...");
+ sObjectMgr->LoadCreatureTemplateDifficulty();
+
TC_LOG_INFO("server.loading", "Loading Creature template sparring...");
sObjectMgr->LoadCreatureTemplateSparring();
- TC_LOG_INFO("server.loading", "Loading Creature template scaling...");
- sObjectMgr->LoadCreatureScalingData();
-
TC_LOG_INFO("server.loading", "Loading Reputation Reward Rates...");
sObjectMgr->LoadReputationRewardRate();
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 75e622d8d88..96de137197b 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -523,7 +523,10 @@ public:
handler->PSendSysMessage(LANG_NPCINFO_DYNAMIC_FLAGS, target->GetDynamicFlags());
handler->PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(), curRespawnDelayStr.c_str());
- handler->PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid, cInfo->pickpocketLootId, cInfo->SkinLootId);
+
+ CreatureDifficulty const* creatureDifficulty = target->GetCreatureDifficulty();
+ handler->PSendSysMessage(LANG_NPCINFO_LOOT, creatureDifficulty->LootID, creatureDifficulty->PickPocketLootID, creatureDifficulty->SkinLootID);
+
handler->PSendSysMessage(LANG_NPCINFO_DUNGEON_ID, target->GetInstanceId());
if (CreatureData const* data = sObjectMgr->GetCreatureData(target->GetSpawnId()))
@@ -1092,7 +1095,7 @@ public:
CreatureTemplate const* cInfo = creatureTarget->GetCreatureTemplate();
- if (!cInfo->IsTameable (player->CanTameExoticPets()))
+ if (!cInfo->IsTameable (player->CanTameExoticPets(), creatureTarget->GetCreatureDifficulty()))
{
handler->PSendSysMessage (LANG_CREATURE_NON_TAMEABLE, cInfo->Entry);
handler->SetSentErrorMessage (true);
diff --git a/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp b/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp
index 17c6645df02..12b9ac830dc 100644
--- a/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp
+++ b/src/server/scripts/Northrend/Gundrak/boss_drakkari_colossus.cpp
@@ -45,8 +45,12 @@ enum Spells
SPELL_FREEZE_ANIM = 16245,
SPELL_MOJO_PUDDLE = 55627,
SPELL_MOJO_WAVE = 55626,
+ SPELL_MORTAL_STRIKES_NORMAL = 54715,
+ SPELL_MORTAL_STRIKES_HEROIC = 59454
};
+#define SPELL_MORTAL_STRIKES DUNGEON_MODE<uint32>(SPELL_MORTAL_STRIKES_NORMAL, SPELL_MORTAL_STRIKES_HEROIC)
+
enum ColossusEvents
{
EVENT_MIGHTY_BLOW = 1,
@@ -110,6 +114,8 @@ struct boss_drakkari_colossus : public BossAI
events.ScheduleEvent(EVENT_MIGHTY_BLOW, 10s, 30s);
Initialize();
+
+ DoCastSelf(SPELL_MORTAL_STRIKES, true);
}
void JustEngagedWith(Unit* who) override
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp
index 68e8279c164..0d2cd019ef4 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp
@@ -76,6 +76,7 @@ enum Spells
SPELL_TELEPORT_LIVE = 28026
};
+#define SPELL_UNHOLY_AURA RAID_MODE<uint32>(55606, 55608)
#define SPELL_DEATH_PLAGUE RAID_MODE<uint32>(55604, 55645)
#define SPELL_SHADOW_BOLT_VOLLEY RAID_MODE<uint32>(27831, 55638)
#define SPELL_ARCANE_EXPLOSION RAID_MODE<uint32>(27989, 56407)
@@ -663,7 +664,19 @@ struct npc_gothik_minion_livingrider : public npc_gothik_minion_baseAI
{
npc_gothik_minion_livingrider(Creature* creature) : npc_gothik_minion_baseAI(creature, SPELL_ANCHOR_1_RIDER), _boltVolleyTimer(urandms(5,10)) { }
- void _UpdateAI(uint32 diff)
+ void JustAppeared() override
+ {
+ npc_gothik_minion_baseAI::JustAppeared();
+ DoCastSelf(SPELL_UNHOLY_AURA, true);
+ }
+
+ void JustReachedHome() override
+ {
+ npc_gothik_minion_baseAI::JustReachedHome();
+ DoCastSelf(SPELL_UNHOLY_AURA, true);
+ }
+
+ void _UpdateAI(uint32 diff) override
{
if (diff < _boltVolleyTimer)
_boltVolleyTimer -= diff;
@@ -674,6 +687,8 @@ struct npc_gothik_minion_livingrider : public npc_gothik_minion_baseAI
}
if (!me->HasUnitState(UNIT_STATE_CASTING))
DoMeleeAttackIfReady();
+
+ npc_gothik_minion_baseAI::_UpdateAI(diff);
}
uint32 _boltVolleyTimer;
};
@@ -718,7 +733,19 @@ struct npc_gothik_minion_spectralrider : public npc_gothik_minion_baseAI
{
npc_gothik_minion_spectralrider(Creature* creature) : npc_gothik_minion_baseAI(creature), _frenzyTimer(urandms(2,5)), _drainTimer(urandms(8,12)) { }
- void _UpdateAI(uint32 diff)
+ void JustAppeared() override
+ {
+ npc_gothik_minion_baseAI::JustAppeared();
+ DoCastSelf(SPELL_UNHOLY_AURA, true);
+ }
+
+ void JustReachedHome() override
+ {
+ npc_gothik_minion_baseAI::JustReachedHome();
+ DoCastSelf(SPELL_UNHOLY_AURA, true);
+ }
+
+ void _UpdateAI(uint32 diff) override
{
if (diff < _frenzyTimer)
_frenzyTimer -= diff;
@@ -764,6 +791,8 @@ struct npc_gothik_minion_spectralrider : public npc_gothik_minion_baseAI
if (!me->HasUnitState(UNIT_STATE_CASTING))
DoMeleeAttackIfReady();
+
+ npc_gothik_minion_baseAI::_UpdateAI(diff);
}
uint32 _frenzyTimer, _drainTimer;
};
diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp
index 0af123ae289..ab0c7473639 100644
--- a/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp
+++ b/src/server/scripts/Outland/TempestKeep/arcatraz/boss_wrath_scryer_soccothrates.cpp
@@ -56,9 +56,13 @@ enum Spells
SPELL_KNOCK_AWAY = 36512,
SPELL_FELFIRE_LINE_UP = 35770,
SPELL_CHARGE_TARGETING = 36038,
- SPELL_CHARGE = 35754
+ SPELL_CHARGE = 35754,
+ SPELL_FEL_IMMOLATION_NORMAL = 36051,
+ SPELL_FEL_IMMOLATION_HEROIC = 39007
};
+#define SPELL_FEL_IMMOLATION DUNGEON_MODE<uint32>(SPELL_FEL_IMMOLATION_NORMAL, SPELL_FEL_IMMOLATION_HEROIC)
+
enum Events
{
EVENT_FELFIRE_SHOCK = 1,
@@ -92,6 +96,8 @@ struct boss_wrath_scryer_soccothrates : public BossAI
preFight = false;
dalliahTaunt = false;
dalliahDeath = false;
+
+ DoCastSelf(SPELL_FEL_IMMOLATION, true);
}
void JustDied(Unit* /*killer*/) override
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index 2da3be2e4b0..bd24a8fbf5c 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -548,7 +548,7 @@ class spell_hun_tame_beast : public SpellScript
return SPELL_FAILED_HIGHLEVEL;
// use SMSG_PET_TAME_FAILURE?
- if (!target->GetCreatureTemplate()->IsTameable(caster->CanTameExoticPets()))
+ if (!target->GetCreatureTemplate()->IsTameable(caster->CanTameExoticPets(), target->GetCreatureDifficulty()))
return SPELL_FAILED_BAD_TARGETS;
if (PetStable const* petStable = caster->GetPetStable())
diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp
index 6c16d72b086..5a305ef0102 100644
--- a/src/server/scripts/Spells/spell_item.cpp
+++ b/src/server/scripts/Spells/spell_item.cpp
@@ -1074,11 +1074,12 @@ class spell_item_extract_gas : public AuraScript
{
Player* player = GetCaster()->ToPlayer();
Creature* creature = GetTarget()->ToCreature();
+ CreatureDifficulty const* creatureDifficulty = creature->GetCreatureDifficulty();
// missing lootid has been reported on startup - just return
- if (!creature->GetCreatureTemplate()->SkinLootId)
+ if (!creatureDifficulty->SkinLootID)
return;
- player->AutoStoreLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, ItemContext::NONE, true);
+ player->AutoStoreLoot(creatureDifficulty->SkinLootID, LootTemplates_Skinning, ItemContext::NONE, true);
creature->DespawnOrUnsummon();
}
}