Core/Creatures: Move creature difficulty specific data from creature_template table to creature_template_difficulty

Port From (https://github.com/TrinityCore/TrinityCore/commit/06d0b16f158e8793860d9edd11b990f20b1d0dac)
This commit is contained in:
hondacrx
2023-05-30 08:16:40 -04:00
parent 77febfdee5
commit 068ccd990f
26 changed files with 399 additions and 539 deletions
@@ -60,7 +60,7 @@ namespace Framework.Database
PrepareStatement(WorldStatements.SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?");
PrepareStatement(WorldStatements.DEL_CREATURE, "DELETE FROM creature WHERE guid = ?");
PrepareStatement(WorldStatements.SEL_COMMANDS, "SELECT name, help FROM command");
PrepareStatement(WorldStatements.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 = ?");
PrepareStatement(WorldStatements.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 = ?");
PrepareStatement(WorldStatements.SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?");
PrepareStatement(WorldStatements.SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?");
PrepareStatement(WorldStatements.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_");
@@ -40,7 +40,7 @@ namespace Game.AI
return false;
//experimental (unknown) flag not present
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist))
if (!me.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist))
return false;
//not a player
@@ -283,7 +283,7 @@ namespace Game.AI
return false;
//experimental (unknown) flag not present
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist))
if (!me.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist))
return false;
if (!who.IsInAccessiblePlaceFor(me))
+1 -1
View File
@@ -496,7 +496,7 @@ namespace Game.AI
return false;
//experimental (unknown) flag not present
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist))
if (!me.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist))
return false;
//not a player
+4 -2
View File
@@ -124,7 +124,9 @@ namespace Game.Chat
handler.SendSysMessage(CypherStrings.NpcinfoDynamicFlags, target.GetDynamicFlags());
handler.SendSysMessage(CypherStrings.CommandRawpawntimes, defRespawnDelayStr, curRespawnDelayStr);
handler.SendSysMessage(CypherStrings.NpcinfoLoot, cInfo.LootId, cInfo.PickPocketId, cInfo.SkinLootId);
CreatureDifficulty creatureDifficulty = target.GetCreatureDifficulty();
handler.SendSysMessage(CypherStrings.NpcinfoLoot, creatureDifficulty.LootID, creatureDifficulty.PickPocketLootID, creatureDifficulty.SkinLootID);
handler.SendSysMessage(CypherStrings.NpcinfoDungeonId, target.GetInstanceId());
CreatureData data = Global.ObjectMgr.GetCreatureData(target.GetSpawnId());
@@ -406,7 +408,7 @@ namespace Game.Chat
CreatureTemplate cInfo = creatureTarget.GetCreatureTemplate();
if (!cInfo.IsTameable(player.CanTameExoticPets()))
if (!cInfo.IsTameable(player.CanTameExoticPets(), creatureTarget.GetCreatureDifficulty()))
{
handler.SendSysMessage(CypherStrings.CreatureNonTameable, cInfo.Entry);
return false;
@@ -12,6 +12,7 @@ namespace Game.Entities
{
CreatureTemplate m_creatureInfo;
CreatureData m_creatureData;
CreatureDifficulty m_creatureDifficulty;
string[] m_stringIds = new string[3];
string m_scriptStringId;
+39 -59
View File
@@ -211,53 +211,30 @@ namespace Game.Entities
public bool InitEntry(uint entry, CreatureData data = null)
{
CreatureTemplate normalInfo = Global.ObjectMgr.GetCreatureTemplate(entry);
if (normalInfo == null)
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(entry);
if (creatureInfo == null)
{
Log.outError(LogFilter.Sql, "Creature.InitEntry creature entry {0} does not exist.", entry);
return false;
}
// get difficulty 1 mode entry
CreatureTemplate cInfo = null;
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(GetMap().GetDifficultyID());
while (cInfo == null && difficultyEntry != null)
{
int idx = CreatureTemplate.DifficultyIDToDifficultyEntryIndex(difficultyEntry.Id);
if (idx == -1)
break;
if (normalInfo.DifficultyEntry[idx] != 0)
{
cInfo = Global.ObjectMgr.GetCreatureTemplate(normalInfo.DifficultyEntry[idx]);
break;
}
if (difficultyEntry.FallbackDifficultyID == 0)
break;
difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficultyEntry.FallbackDifficultyID);
}
if (cInfo == null)
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(0);
SetClass((Class)cInfo.UnitClass);
SetClass((Class)creatureInfo.UnitClass);
// Cancel load if no model defined
if (cInfo.GetFirstValidModel() == null)
if (creatureInfo.GetFirstValidModel() == null)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has no model defined in table `creature_template`, can't load. ", entry);
return false;
}
CreatureModel model = ObjectManager.ChooseDisplayId(cInfo, data);
CreatureModelInfo minfo = Global.ObjectMgr.GetCreatureModelRandomGender(ref model, cInfo);
CreatureModel model = ObjectManager.ChooseDisplayId(creatureInfo, data);
CreatureModelInfo minfo = Global.ObjectMgr.GetCreatureModelRandomGender(ref model, creatureInfo);
if (minfo == null) // Cancel load if no model defined
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has invalid model {1} defined in table `creature_template`, can't load.", entry, model.CreatureDisplayID);
@@ -277,7 +254,7 @@ namespace Game.Entities
LoadEquipment(data.equipmentId);
}
SetName(normalInfo.Name); // at normal entry always
SetName(creatureInfo.Name); // at normal entry always
SetModCastingSpeed(1.0f);
SetModSpellHaste(1.0f);
@@ -286,25 +263,25 @@ namespace Game.Entities
SetModHasteRegen(1.0f);
SetModTimeRate(1.0f);
SetSpeedRate(UnitMoveType.Walk, cInfo.SpeedWalk);
SetSpeedRate(UnitMoveType.Run, cInfo.SpeedRun);
SetSpeedRate(UnitMoveType.Walk, creatureInfo.SpeedWalk);
SetSpeedRate(UnitMoveType.Run, creatureInfo.SpeedRun);
SetSpeedRate(UnitMoveType.Swim, 1.0f); // using 1.0 rate
SetSpeedRate(UnitMoveType.Flight, 1.0f); // using 1.0 rate
SetObjectScale(GetNativeObjectScale());
SetCanDualWield(cInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.UseOffhandAttack));
SetCanDualWield(creatureInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.UseOffhandAttack));
// checked at loading
DefaultMovementType = (MovementGeneratorType)(data != null ? data.movementType : cInfo.MovementType);
DefaultMovementType = (MovementGeneratorType)(data != null ? data.movementType : creatureInfo.MovementType);
if (m_wanderDistance == 0 && DefaultMovementType == MovementGeneratorType.Random)
DefaultMovementType = MovementGeneratorType.Idle;
for (byte i = 0; i < SharedConst.MaxCreatureSpells; ++i)
m_spells[i] = GetCreatureTemplate().Spells[i];
_staticFlags.ApplyFlag(CreatureStaticFlags.NoXp, cInfo.CreatureType == CreatureType.Critter || IsPet() || IsTotem() || cInfo.FlagsExtra.HasFlag(CreatureFlagsExtra.NoXP));
_staticFlags.ApplyFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells, cInfo.TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit));
_staticFlags.ApplyFlag(CreatureStaticFlags.NoXp, creatureInfo.CreatureType == CreatureType.Critter || IsPet() || IsTotem() || creatureInfo.FlagsExtra.HasFlag(CreatureFlagsExtra.NoXP));
_staticFlags.ApplyFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells, GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit));
return true;
}
@@ -808,6 +785,8 @@ namespace Game.Entities
return false;
}
CreatureDifficulty creatureDifficulty = cinfo.GetDifficulty(GetMap().GetDifficultyID());
//! Relocate before CreateFromProto, to initialize coords and allow
//! returning correct zone id for selecting OutdoorPvP/Battlefield script
Relocate(pos);
@@ -828,7 +807,7 @@ namespace Game.Entities
}
// Allow players to see those units while dead, do it here (mayby altered by addon auras)
if (cinfo.TypeFlags.HasAnyFlag(CreatureTypeFlags.VisibleToGhosts))
if (creatureDifficulty.TypeFlags.HasAnyFlag(CreatureTypeFlags.VisibleToGhosts))
m_serverSideVisibility.SetValue(ServerSideVisibilityType.Ghost, GhostVisibilityType.Alive | GhostVisibilityType.Ghost);
if (!CreateFromProto(guidlow, entry, data, vehId))
@@ -1050,7 +1029,7 @@ namespace Game.Entities
{
base.AtEngage(target);
if (!GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.AllowMountedCombat))
if (!GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.AllowMountedCombat))
Dismount();
RefreshCanSwimFlag();
@@ -1199,7 +1178,7 @@ namespace Game.Entities
if (_lootId.HasValue)
return _lootId.Value;
return GetCreatureTemplate().LootId;
return GetCreatureDifficulty().LootID;
}
public void SetLootId(uint? lootId)
@@ -1470,7 +1449,7 @@ namespace Game.Entities
// mana
PowerType powerType = CalculateDisplayPowerType();
SetCreateMana(stats.BaseMana);
SetStatPctModifier(UnitMods.PowerStart + (int)powerType, UnitModifierPctType.Base, cInfo.ModMana * cInfo.ModManaExtra);
SetStatPctModifier(UnitMods.PowerStart + (int)powerType, UnitModifierPctType.Base, GetCreatureDifficulty().ManaModifier);
SetPowerType(powerType);
PowerTypeRecord powerTypeEntry = Global.DB2Mgr.GetPowerTypeEntry(powerType);
@@ -2208,7 +2187,7 @@ namespace Game.Entities
if (IsPet())
return false;
return Convert.ToBoolean(GetCreatureTemplate().TypeFlags & CreatureTypeFlags.BossMob);
return GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.BossMob);
}
// select nearest hostile unit within the given distance (regardless of threat list).
@@ -2462,7 +2441,7 @@ namespace Game.Entities
}
// dependent from difficulty mode entry
return Global.ObjectMgr.GetCreatureTemplateAddon(GetCreatureTemplate().Entry);
return Global.ObjectMgr.GetCreatureTemplateAddon(GetEntry());
}
public bool LoadCreaturesAddon()
@@ -2716,29 +2695,29 @@ namespace Game.Entities
public void ApplyLevelScaling()
{
CreatureLevelScaling scaling = GetCreatureTemplate().GetLevelScaling(GetMap().GetDifficultyID());
var levels = Global.DB2Mgr.GetContentTuningData(scaling.ContentTuningID, 0);
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
var levels = Global.DB2Mgr.GetContentTuningData(creatureDifficulty.ContentTuningID, 0);
if (levels.HasValue)
{
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ScalingLevelMin), levels.Value.MinLevel);
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ScalingLevelMax), levels.Value.MaxLevel);
}
int mindelta = Math.Min(scaling.DeltaLevelMax, scaling.DeltaLevelMin);
int maxdelta = Math.Max(scaling.DeltaLevelMax, scaling.DeltaLevelMin);
int mindelta = Math.Min(creatureDifficulty.DeltaLevelMax, creatureDifficulty.DeltaLevelMin);
int maxdelta = Math.Max(creatureDifficulty.DeltaLevelMax, creatureDifficulty.DeltaLevelMin);
int delta = mindelta == maxdelta ? mindelta : RandomHelper.IRand(mindelta, maxdelta);
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ScalingLevelDelta), delta);
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ContentTuningID), scaling.ContentTuningID);
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ContentTuningID), creatureDifficulty.ContentTuningID);
}
ulong GetMaxHealthByLevel(uint level)
{
CreatureTemplate cInfo = GetCreatureTemplate();
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID());
float baseHealth = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass);
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
float baseHealth = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
return (ulong)Math.Max(baseHealth * cInfo.ModHealth * cInfo.ModHealthExtra, 1.0f);
return (ulong)Math.Max(baseHealth * creatureDifficulty.HealthModifier, 1.0f);
}
public override float GetHealthMultiplierForTarget(WorldObject target)
@@ -2756,8 +2735,8 @@ namespace Game.Entities
public float GetBaseDamageForLevel(uint level)
{
CreatureTemplate cInfo = GetCreatureTemplate();
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID());
return Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureAutoAttackDps, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass);
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
return Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureAutoAttackDps, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
}
public override float GetDamageMultiplierForTarget(WorldObject target)
@@ -2773,9 +2752,9 @@ namespace Game.Entities
float GetBaseArmorForLevel(uint level)
{
CreatureTemplate cInfo = GetCreatureTemplate();
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID());
float baseArmor = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureArmor, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass);
return baseArmor * cInfo.ModArmor;
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
float baseArmor = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureArmor, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
return baseArmor * creatureDifficulty.ArmorModifier;
}
public override float GetArmorMultiplierForTarget(WorldObject target)
@@ -3317,7 +3296,8 @@ namespace Game.Entities
public CreatureTemplate GetCreatureTemplate() { return m_creatureInfo; }
public CreatureData GetCreatureData() { return m_creatureData; }
public CreatureDifficulty GetCreatureDifficulty() { return m_creatureDifficulty; }
public override bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate)
{
if (!allowDuplicate)
+94 -90
View File
@@ -7,13 +7,13 @@ using System;
using System.Collections.Generic;
using Game.Networking.Packets;
using Game.Maps;
using Game.DataStorage;
namespace Game.Entities
{
public class CreatureTemplate
{
public uint Entry;
public uint[] DifficultyEntry = new uint[SharedConst.MaxCreatureDifficulties];
public uint[] KillCredit = new uint[SharedConst.MaxCreatureKillCredit];
public List<CreatureModel> Models = new();
public string Name;
@@ -22,8 +22,7 @@ namespace Game.Entities
public string TitleAlt;
public string IconName;
public List<uint> GossipMenuIds = new();
public Dictionary<Difficulty, CreatureLevelScaling> scalingStorage = new();
public int HealthScalingExpansion;
public Dictionary<Difficulty, CreatureDifficulty> difficultyStorage = new();
public uint RequiredExpansion;
public uint VignetteID; // @todo Read Vignette.db2
public uint Faction;
@@ -45,29 +44,15 @@ namespace Game.Entities
public CreatureFamily Family;
public Class TrainerClass;
public CreatureType CreatureType;
public CreatureTypeFlags TypeFlags;
public uint TypeFlags2;
public uint LootId;
public uint PickPocketId;
public uint SkinLootId;
public int[] Resistance = new int[7];
public uint[] Spells = new uint[8];
public uint VehicleId;
public uint MinGold;
public uint MaxGold;
public string AIName;
public uint MovementType;
public CreatureMovementData Movement = new();
public float ModHealth;
public float ModHealthExtra;
public float ModMana;
public float ModManaExtra;
public float ModArmor;
public float ModDamage;
public float ModExperience;
public bool RacialLeader;
public uint MovementId;
public int CreatureDifficultyID;
public int WidgetSetID;
public int WidgetSetUnitConditionID;
public bool RegenHealth;
@@ -77,7 +62,7 @@ namespace Game.Entities
public uint ScriptID;
public string StringId;
public QueryCreatureResponse QueryData;
public QueryCreatureResponse[] QueryData = new QueryCreatureResponse[(int)Locale.Total];
public CreatureModel GetModelByIdx(int idx)
{
@@ -142,74 +127,33 @@ namespace Game.Entities
return CreatureModel.DefaultVisibleModel;
}
public int GetHealthScalingExpansion()
public bool IsExotic(CreatureDifficulty creatureDifficulty)
{
return HealthScalingExpansion == (int)Expansion.LevelCurrent ? (int)Expansion.WarlordsOfDraenor : HealthScalingExpansion;
return creatureDifficulty.TypeFlags.HasFlag(CreatureTypeFlags.TameableExotic);
}
public SkillType GetRequiredLootSkill()
public bool IsTameable(bool canTameExotic, CreatureDifficulty creatureDifficulty)
{
if (TypeFlags.HasAnyFlag(CreatureTypeFlags.SkinWithHerbalism))
return SkillType.Herbalism;
else if (TypeFlags.HasAnyFlag(CreatureTypeFlags.SkinWithMining))
return SkillType.Mining;
else if (TypeFlags.HasAnyFlag(CreatureTypeFlags.SkinWithEngineering))
return SkillType.Engineering;
else
return SkillType.Skinning; // normal case
}
public bool IsExotic()
{
return (TypeFlags & CreatureTypeFlags.TameableExotic) != 0;
}
public bool IsTameable(bool canTameExotic)
{
if (CreatureType != CreatureType.Beast || Family == CreatureFamily.None || !TypeFlags.HasAnyFlag(CreatureTypeFlags.Tameable))
if (CreatureType != CreatureType.Beast || Family == CreatureFamily.None || !creatureDifficulty.TypeFlags.HasFlag(CreatureTypeFlags.Tameable))
return false;
// if can tame exotic then can tame any tameable
return canTameExotic || !IsExotic();
}
public static int DifficultyIDToDifficultyEntryIndex(uint difficulty)
{
switch ((Difficulty)difficulty)
{
case Difficulty.None:
case Difficulty.Normal:
case Difficulty.Raid10N:
case Difficulty.Raid40:
case Difficulty.Scenario3ManN:
case Difficulty.NormalRaid:
return -1;
case Difficulty.Heroic:
case Difficulty.Raid25N:
case Difficulty.Scenario3ManHC:
case Difficulty.HeroicRaid:
return 0;
case Difficulty.Raid10HC:
case Difficulty.MythicKeystone:
case Difficulty.MythicRaid:
return 1;
case Difficulty.Raid25HC:
return 2;
case Difficulty.LFR:
case Difficulty.LFRNew:
case Difficulty.EventRaid:
case Difficulty.EventDungeon:
case Difficulty.EventScenario:
default:
return -1;
}
return canTameExotic || !IsExotic(creatureDifficulty);
}
public void InitializeQueryData()
{
QueryData = new QueryCreatureResponse();
for (var loc = Locale.enUS; loc < Locale.Total; ++loc)
QueryData[(int)loc] = BuildQueryData(loc, Difficulty.None);
}
QueryData.CreatureID = Entry;
QueryData.Allow = true;
public QueryCreatureResponse BuildQueryData(Locale locale, Difficulty difficulty)
{
CreatureDifficulty creatureDifficulty = GetDifficulty(difficulty);
var queryTemp = new QueryCreatureResponse();
queryTemp.CreatureID = Entry;
queryTemp.Allow = true;
CreatureStats stats = new();
stats.Leader = RacialLeader;
@@ -217,8 +161,8 @@ namespace Game.Entities
stats.Name[0] = Name;
stats.NameAlt[0] = FemaleName;
stats.Flags[0] = (uint)TypeFlags;
stats.Flags[1] = TypeFlags2;
stats.Flags[0] = (uint)creatureDifficulty.TypeFlags;
stats.Flags[1] = creatureDifficulty.TypeFlags2;
stats.CreatureType = (int)CreatureType;
stats.CreatureFamily = (int)Family;
@@ -233,15 +177,15 @@ namespace Game.Entities
stats.Display.CreatureDisplay.Add(new CreatureXDisplay(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 = (int)UnitClass;
stats.CreatureDifficultyID = CreatureDifficultyID;
stats.CreatureDifficultyID = creatureDifficulty.CreatureDifficultyID;
stats.WidgetSetID = WidgetSetID;
stats.WidgetSetUnitConditionID = WidgetSetUnitConditionID;
@@ -249,20 +193,41 @@ namespace Game.Entities
stats.TitleAlt = TitleAlt;
stats.CursorName = IconName;
var items = Global.ObjectMgr.GetCreatureQuestItemList(Entry);
var items = Global.ObjectMgr.GetCreatureQuestItemList(Entry, difficulty);
if (items != null)
stats.QuestItems.AddRange(items);
QueryData.Stats = stats;
if (locale != Locale.enUS)
{
CreatureLocale creatureLocale = Global.ObjectMgr.GetCreatureLocale(Entry);
if (creatureLocale != null)
{
string name = stats.Name[0];
string nameAlt = stats.NameAlt[0];
ObjectManager.GetLocaleString(creatureLocale.Name, locale, ref name);
ObjectManager.GetLocaleString(creatureLocale.NameAlt, locale, ref nameAlt);
ObjectManager.GetLocaleString(creatureLocale.Title, locale, ref stats.Title);
ObjectManager.GetLocaleString(creatureLocale.TitleAlt, locale, ref stats.TitleAlt);
}
}
queryTemp.Stats = stats;
return queryTemp;
}
public CreatureLevelScaling GetLevelScaling(Difficulty difficulty)
public CreatureDifficulty GetDifficulty(Difficulty difficulty)
{
var creatureLevelScaling = scalingStorage.LookupByKey(difficulty);
if (creatureLevelScaling != null)
return creatureLevelScaling;
var creatureDifficulty = difficultyStorage.LookupByKey(difficulty);
if (creatureDifficulty != null)
return creatureDifficulty;
return new CreatureLevelScaling();
// If there is no data for the difficulty, try to get data for the fallback difficulty
var difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficulty);
if (difficultyEntry != null)
return GetDifficulty((Difficulty)difficultyEntry.FallbackDifficultyID);
return new CreatureDifficulty();
}
}
@@ -347,7 +312,7 @@ namespace Game.Entities
return $"Ground: {Ground}, Swim: {Swim}, Flight: {Flight} {(Rooted ? ", Rooted" : "")}, Chase: {Chase}, Random: {Random}, InteractionPauseTimer: {InteractionPauseTimer}";
}
}
public class CreatureModelInfo
{
public float BoundingRadius;
@@ -462,10 +427,49 @@ namespace Game.Entities
}
}
public class CreatureLevelScaling
public class CreatureDifficulty
{
public short DeltaLevelMin;
public short DeltaLevelMax;
public uint ContentTuningID;
public int HealthScalingExpansion;
public float HealthModifier;
public float ManaModifier;
public float ArmorModifier;
public float DamageModifier;
public int CreatureDifficultyID;
public CreatureTypeFlags TypeFlags;
public uint TypeFlags2;
public uint LootID;
public uint PickPocketLootID;
public uint SkinLootID;
public uint GoldMin;
public uint GoldMax;
public CreatureDifficulty()
{
HealthModifier = 1.0f;
ManaModifier = 1.0f;
ArmorModifier = 1.0f;
DamageModifier = 1.0f;
}
// Helpers
public int GetHealthScalingExpansion()
{
return HealthScalingExpansion == (int)Expansion.LevelCurrent ? (int)PlayerConst.CurrentExpansion : HealthScalingExpansion;
}
public SkillType GetRequiredLootSkill()
{
if (TypeFlags.HasFlag(CreatureTypeFlags.SkinWithHerbalism))
return SkillType.Herbalism;
else if (TypeFlags.HasFlag(CreatureTypeFlags.SkinWithMining))
return SkillType.Mining;
else if (TypeFlags.HasFlag(CreatureTypeFlags.SkinWithEngineering))
return SkillType.Engineering;
else
return SkillType.Skinning; // Default case
}
}
}
+1 -1
View File
@@ -2773,7 +2773,7 @@ namespace Game.Entities
{
Creature creatureTarget = target.ToCreature();
if (creatureTarget != null)
return creatureTarget.HasFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells) || creatureTarget.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist);
return creatureTarget.HasFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells) || creatureTarget.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist);
}
}
}
+1 -1
View File
@@ -162,7 +162,7 @@ namespace Game.Entities
if (petInfo.Type == PetType.Hunter)
{
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(petInfo.CreatureId);
if (creatureInfo == null || !creatureInfo.IsTameable(owner.CanTameExoticPets()))
if (creatureInfo == null || !creatureInfo.IsTameable(owner.CanTameExoticPets(), GetCreatureDifficulty()))
return false;
}
+2 -2
View File
@@ -5169,11 +5169,11 @@ namespace Game.Entities
return null;
// Deathstate checks
if (!IsAlive() && !Convert.ToBoolean(creature.GetCreatureTemplate().TypeFlags & CreatureTypeFlags.VisibleToGhosts))
if (!IsAlive() && !creature.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.VisibleToGhosts))
return null;
// alive or spirit healer
if (!creature.IsAlive() && !Convert.ToBoolean(creature.GetCreatureTemplate().TypeFlags & CreatureTypeFlags.InteractWhileDead))
if (!creature.IsAlive() && !creature.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.InteractWhileDead))
return null;
// appropriate npc type
+1 -1
View File
@@ -2254,7 +2254,7 @@ namespace Game.Entities
float basePct = GetPctModifierValue(unitMod, UnitModifierPctType.Base) * attackSpeedMulti;
float totalValue = GetFlatModifierValue(unitMod, UnitModifierFlatType.Total);
float totalPct = addTotalPct ? GetPctModifierValue(unitMod, UnitModifierPctType.Total) : 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;
+2 -1
View File
@@ -584,7 +584,8 @@ namespace Game.Entities
CreatureBaseStats stats = Global.ObjectMgr.GetCreatureBaseStats(petlevel, cinfo.UnitClass);
ApplyLevelScaling();
SetCreateHealth((uint)Math.Max(Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, petlevel, cinfo.GetHealthScalingExpansion(), m_unitData.ContentTuningID, (Class)cinfo.UnitClass) * cinfo.ModHealth * cinfo.ModHealthExtra * GetHealthMod(cinfo.Rank), 1.0f));
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
SetCreateHealth((uint)Math.Max(Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, petlevel, creatureDifficulty.GetHealthScalingExpansion(), m_unitData.ContentTuningID, (Class)cinfo.UnitClass) * creatureDifficulty.HealthModifier * GetHealthMod(cinfo.Rank), 1.0f));
SetCreateMana(stats.BaseMana);
SetCreateStat(Stats.Strength, 22);
+4 -4
View File
@@ -772,7 +772,7 @@ namespace Game.Entities
if (dungeonEncounter != null)
{
creature.m_personalLoot = LootManager.GenerateDungeonEncounterPersonalLoot(dungeonEncounter.Id, creature.GetLootId(),
LootStorage.Creature, LootType.Corpse, creature, creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold,
LootStorage.Creature, LootType.Corpse, creature, creature.GetCreatureDifficulty().GoldMin, creature.GetCreatureDifficulty().GoldMax,
(ushort)creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext(), tappers);
}
else if (!tappers.Empty())
@@ -787,7 +787,7 @@ namespace Game.Entities
loot.FillLoot(lootid, LootStorage.Creature, looter, dungeonEncounter != null, 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());
@@ -814,7 +814,7 @@ namespace Game.Entities
loot.FillLoot(lootid, LootStorage.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()] = loot;
}
@@ -918,7 +918,7 @@ namespace Game.Entities
else
creature.AllLootRemovedFromCorpse();
if (creature.CanHaveLoot() && LootStorage.Skinning.HaveLootFor(creature.GetCreatureTemplate().SkinLootId))
if (creature.CanHaveLoot() && LootStorage.Skinning.HaveLootFor(creature.GetCreatureDifficulty().SkinLootID))
{
creature.SetDynamicFlag(UnitDynFlags.CanSkin);
creature.SetUnitFlag(UnitFlags.Skinnable);
+182 -302
View File
@@ -25,14 +25,7 @@ namespace Game
{
public sealed class ObjectManager : Singleton<ObjectManager>
{
ObjectManager()
{
for (var i = 0; i < SharedConst.MaxCreatureDifficulties; ++i)
{
difficultyEntries[i] = new List<uint>();
hasDifficultyEntries[i] = new List<uint>();
}
}
ObjectManager() { }
//Static Methods
public static bool NormalizePlayerName(ref string name)
@@ -1792,44 +1785,35 @@ namespace Game
CreatureTemplate creature = new();
creature.Entry = entry;
for (var i = 0; i < SharedConst.MaxCreatureDifficulties; ++i)
creature.DifficultyEntry[i] = fields.Read<uint>(1 + i);
for (var i = 0; i < 2; ++i)
creature.KillCredit[i] = fields.Read<uint>(4 + i);
creature.KillCredit[i] = fields.Read<uint>(1 + i);
creature.Name = fields.Read<string>(6);
creature.FemaleName = fields.Read<string>(7);
creature.SubName = fields.Read<string>(8);
creature.TitleAlt = fields.Read<string>(9);
creature.IconName = fields.Read<string>(10);
creature.HealthScalingExpansion = fields.Read<int>(11);
creature.RequiredExpansion = fields.Read<uint>(12);
creature.VignetteID = fields.Read<uint>(13);
creature.Faction = fields.Read<uint>(14);
creature.Npcflag = fields.Read<ulong>(15);
creature.SpeedWalk = fields.Read<float>(16);
creature.SpeedRun = fields.Read<float>(17);
creature.Scale = fields.Read<float>(18);
creature.Rank = (CreatureEliteType)fields.Read<uint>(19);
creature.DmgSchool = fields.Read<uint>(20);
creature.BaseAttackTime = fields.Read<uint>(21);
creature.RangeAttackTime = fields.Read<uint>(22);
creature.BaseVariance = fields.Read<float>(23);
creature.RangeVariance = fields.Read<float>(24);
creature.UnitClass = fields.Read<uint>(25);
creature.UnitFlags = (UnitFlags)fields.Read<uint>(26);
creature.UnitFlags2 = fields.Read<uint>(27);
creature.UnitFlags3 = fields.Read<uint>(28);
creature.DynamicFlags = fields.Read<uint>(29);
creature.Family = (CreatureFamily)fields.Read<uint>(30);
creature.TrainerClass = (Class)fields.Read<byte>(31);
creature.CreatureType = (CreatureType)fields.Read<byte>(32);
creature.TypeFlags = (CreatureTypeFlags)fields.Read<uint>(33);
creature.TypeFlags2 = fields.Read<uint>(34);
creature.LootId = fields.Read<uint>(35);
creature.PickPocketId = fields.Read<uint>(36);
creature.SkinLootId = fields.Read<uint>(37);
creature.Name = fields.Read<string>(3);
creature.FemaleName = fields.Read<string>(4);
creature.SubName = fields.Read<string>(5);
creature.TitleAlt = fields.Read<string>(6);
creature.IconName = fields.Read<string>(7);
creature.RequiredExpansion = fields.Read<uint>(8);
creature.VignetteID = fields.Read<uint>(9);
creature.Faction = fields.Read<uint>(10);
creature.Npcflag = fields.Read<ulong>(11);
creature.SpeedWalk = fields.Read<float>(12);
creature.SpeedRun = fields.Read<float>(13);
creature.Scale = fields.Read<float>(14);
creature.Rank = (CreatureEliteType)fields.Read<uint>(15);
creature.DmgSchool = fields.Read<uint>(16);
creature.BaseAttackTime = fields.Read<uint>(17);
creature.RangeAttackTime = fields.Read<uint>(18);
creature.BaseVariance = fields.Read<float>(19);
creature.RangeVariance = fields.Read<float>(20);
creature.UnitClass = fields.Read<uint>(21);
creature.UnitFlags = (UnitFlags)fields.Read<uint>(22);
creature.UnitFlags2 = fields.Read<uint>(23);
creature.UnitFlags3 = fields.Read<uint>(24);
creature.DynamicFlags = fields.Read<uint>(25);
creature.Family = (CreatureFamily)fields.Read<uint>(26);
creature.TrainerClass = (Class)fields.Read<byte>(27);
creature.CreatureType = (CreatureType)fields.Read<byte>(28);
for (var i = (int)SpellSchools.Holy; i < (int)SpellSchools.Max; ++i)
creature.Resistance[i] = 0;
@@ -1837,51 +1821,42 @@ namespace Game
for (var i = 0; i < SharedConst.MaxCreatureSpells; ++i)
creature.Spells[i] = 0;
creature.VehicleId = fields.Read<uint>(38);
creature.MinGold = fields.Read<uint>(39);
creature.MaxGold = fields.Read<uint>(40);
creature.AIName = fields.Read<string>(41);
creature.MovementType = fields.Read<uint>(42);
creature.VehicleId = fields.Read<uint>(29);
creature.AIName = fields.Read<string>(30);
creature.MovementType = fields.Read<uint>(31);
if (!fields.IsNull(43))
creature.Movement.Ground = (CreatureGroundMovementType)fields.Read<byte>(43);
if (!fields.IsNull(32))
creature.Movement.Ground = (CreatureGroundMovementType)fields.Read<byte>(32);
if (!fields.IsNull(44))
creature.Movement.Swim = fields.Read<bool>(44);
if (!fields.IsNull(33))
creature.Movement.Swim = fields.Read<bool>(33);
if (!fields.IsNull(45))
creature.Movement.Flight = (CreatureFlightMovementType)fields.Read<byte>(45);
if (!fields.IsNull(34))
creature.Movement.Flight = (CreatureFlightMovementType)fields.Read<byte>(34);
if (!fields.IsNull(46))
creature.Movement.Rooted = fields.Read<bool>(46);
if (!fields.IsNull(35))
creature.Movement.Rooted = fields.Read<bool>(35);
if (!fields.IsNull(47))
creature.Movement.Chase = (CreatureChaseMovementType)fields.Read<byte>(47);
if (!fields.IsNull(36))
creature.Movement.Chase = (CreatureChaseMovementType)fields.Read<byte>(36);
if (!fields.IsNull(48))
creature.Movement.Random = (CreatureRandomMovementType)fields.Read<byte>(48);
if (!fields.IsNull(37))
creature.Movement.Random = (CreatureRandomMovementType)fields.Read<byte>(37);
if (!fields.IsNull(49))
creature.Movement.InteractionPauseTimer = fields.Read<uint>(49);
if (!fields.IsNull(38))
creature.Movement.InteractionPauseTimer = fields.Read<uint>(38);
creature.ModHealth = fields.Read<float>(50);
creature.ModHealthExtra = fields.Read<float>(51);
creature.ModMana = fields.Read<float>(52);
creature.ModManaExtra = fields.Read<float>(53);
creature.ModArmor = fields.Read<float>(54);
creature.ModDamage = fields.Read<float>(55);
creature.ModExperience = fields.Read<float>(56);
creature.RacialLeader = fields.Read<bool>(57);
creature.MovementId = fields.Read<uint>(58);
creature.CreatureDifficultyID = fields.Read<int>(59);
creature.WidgetSetID = fields.Read<int>(60);
creature.WidgetSetUnitConditionID = fields.Read<int>(61);
creature.RegenHealth = fields.Read<bool>(62);
creature.MechanicImmuneMask = fields.Read<ulong>(63);
creature.SpellSchoolImmuneMask = fields.Read<uint>(64);
creature.FlagsExtra = (CreatureFlagsExtra)fields.Read<uint>(65);
creature.ScriptID = GetScriptId(fields.Read<string>(66));
creature.StringId = fields.Read<string>(67);
creature.ModExperience = fields.Read<float>(39);
creature.RacialLeader = fields.Read<bool>(40);
creature.MovementId = fields.Read<uint>(41);
creature.WidgetSetID = fields.Read<int>(42);
creature.WidgetSetUnitConditionID = fields.Read<int>(43);
creature.RegenHealth = fields.Read<bool>(44);
creature.MechanicImmuneMask = fields.Read<ulong>(45);
creature.SpellSchoolImmuneMask = fields.Read<uint>(46);
creature.FlagsExtra = (CreatureFlagsExtra)fields.Read<uint>(47);
creature.ScriptID = GetScriptId(fields.Read<string>(48));
creature.StringId = fields.Read<string>(49);
creatureTemplateStorage[entry] = creature;
}
@@ -2392,8 +2367,8 @@ namespace Game
{
uint oldMSTime = Time.GetMSTime();
// 0 1 2
SQLResult result = DB.World.Query("SELECT CreatureEntry, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
// 0 1 2 3
SQLResult result = DB.World.Query("SELECT CreatureEntry, DifficultyID, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature quest items. DB table `creature_questitem` is empty.");
@@ -2404,22 +2379,23 @@ namespace Game
do
{
uint entry = result.Read<uint>(0);
uint item = result.Read<uint>(1);
uint idx = result.Read<uint>(2);
Difficulty difficulty = (Difficulty)result.Read<byte>(1);
uint item = result.Read<uint>(2);
uint idx = result.Read<uint>(3);
if (!creatureTemplateStorage.ContainsKey(entry))
{
Log.outError(LogFilter.Sql, "Table `creature_questitem` has data for nonexistent creature (entry: {0}, idx: {1}), skipped", entry, idx);
Log.outError(LogFilter.Sql, $"Table `creature_questitem` has data for nonexistent creature (entry: {entry}, difficulty: {difficulty} idx: {idx}), skipped");
continue;
}
if (!CliDB.ItemStorage.ContainsKey(item))
{
Log.outError(LogFilter.Sql, "Table `creature_questitem` has nonexistent item (ID: {0}) in creature (entry: {1}, idx: {2}), skipped", item, entry, idx);
Log.outError(LogFilter.Sql, $"Table `creature_questitem` has nonexistent item (ID: {item}) in creature (entry: {entry}, difficulty: {difficulty} idx: {idx}), skipped");
continue;
}
creatureQuestItemStorage.Add(entry, item);
creatureQuestItemStorage.Add((entry, difficulty), item);
++count;
}
@@ -2709,15 +2685,15 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} creature template sparring rows in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
}
public void LoadCreatureScalingData()
public void LoadCreatureTemplateDifficulty()
{
uint oldMSTime = Time.GetMSTime();
// 0 1 2 3 4
var result = DB.World.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
SQLResult result = DB.World.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.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature template scaling definitions. DB table `creature_template_scaling` is empty.");
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature template difficulty definitions. DB table `creature_template_difficulty` is empty.");
return;
}
@@ -2730,196 +2706,55 @@ namespace Game
var template = creatureTemplateStorage.LookupByKey(entry);
if (template == null)
{
Log.outError(LogFilter.Sql, $"Creature template (Entry: {entry}) does not exist but has a record in `creature_template_scaling`");
Log.outError(LogFilter.Sql, $"Creature template (Entry: {entry}) does not exist but has a record in `creature_template_difficulty`");
continue;
}
CreatureLevelScaling creatureLevelScaling = new();
creatureLevelScaling.DeltaLevelMin = result.Read<short>(2);
creatureLevelScaling.DeltaLevelMax = result.Read<short>(3);
creatureLevelScaling.ContentTuningID = result.Read<uint>(4);
CreatureDifficulty creatureDifficulty = new();
creatureDifficulty.DeltaLevelMin = result.Read<short>(2);
creatureDifficulty.DeltaLevelMax = result.Read<short>(3);
creatureDifficulty.ContentTuningID = result.Read<uint>(4);
creatureDifficulty.HealthScalingExpansion = result.Read<int>(5);
creatureDifficulty.HealthModifier = result.Read<float>(6);
creatureDifficulty.ManaModifier = result.Read<float>(7);
creatureDifficulty.ArmorModifier = result.Read<float>(8);
creatureDifficulty.DamageModifier = result.Read<float>(9);
creatureDifficulty.CreatureDifficultyID = result.Read<int>(10);
creatureDifficulty.TypeFlags = (CreatureTypeFlags)result.Read<uint>(11);
creatureDifficulty.TypeFlags2 = result.Read<uint>(12);
creatureDifficulty.LootID = result.Read<uint>(13);
creatureDifficulty.PickPocketLootID = result.Read<uint>(14);
creatureDifficulty.SkinLootID = result.Read<uint>(15);
creatureDifficulty.GoldMin = result.Read<uint>(16);
creatureDifficulty.GoldMax = result.Read<uint>(17);
template.scalingStorage[difficulty] = creatureLevelScaling;
// TODO: Check if this still applies
creatureDifficulty.DamageModifier *= Creature._GetDamageMod(template.Rank);
// Assign creature level scaling to creature difficulty entry (if any)
// TODO: Drop the use of creature difficulties
int difficultyIndex = CreatureTemplate.DifficultyIDToDifficultyEntryIndex((uint)difficulty);
if (difficultyIndex != -1)
if (creatureDifficulty.HealthScalingExpansion < (int)Expansion.LevelCurrent || creatureDifficulty.HealthScalingExpansion >= (int)Expansion.Max)
{
uint difficultyEntry = template.DifficultyEntry[difficultyIndex];
if (difficultyEntry != 0)
{
var difficultyTemplate = creatureTemplateStorage.LookupByKey(difficultyEntry);
if (difficultyTemplate != null)
difficultyTemplate.scalingStorage[difficulty] = creatureLevelScaling;
}
Log.outError(LogFilter.Sql, $"Table `creature_template_difficulty` lists creature (ID: {entry}) with invalid `HealthScalingExpansion` {creatureDifficulty.HealthScalingExpansion}. Ignored and set to 0.");
creatureDifficulty.HealthScalingExpansion = 0;
}
if (creatureDifficulty.GoldMin > creatureDifficulty.GoldMax)
{
Log.outError(LogFilter.Sql, $"Table `creature_template_difficulty` lists creature (ID: {entry}) with `GoldMin` {creatureDifficulty.GoldMin} greater than `GoldMax` {creatureDifficulty.GoldMax}, setting `GoldMax` to {creatureDifficulty.GoldMin}.");
creatureDifficulty.GoldMax = creatureDifficulty.GoldMin;
}
template.difficultyStorage[difficulty] = creatureDifficulty;
++count;
} while (result.NextRow());
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} creature template scaling data in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} creature template difficulty data in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
}
public void CheckCreatureTemplate(CreatureTemplate cInfo)
{
if (cInfo == null)
return;
bool ok = true; // bool to allow continue outside this loop
for (uint diff = 0; diff < SharedConst.MaxCreatureDifficulties && ok; ++diff)
{
if (cInfo.DifficultyEntry[diff] == 0)
continue;
ok = false; // will be set to true at the end of this loop again
CreatureTemplate difficultyInfo = GetCreatureTemplate(cInfo.DifficultyEntry[diff]);
if (difficultyInfo == null)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has `difficulty_entry_{1}`={2} but creature entry {3} does not exist.",
cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff], cInfo.DifficultyEntry[diff]);
continue;
}
bool ok2 = true;
for (uint diff2 = 0; diff2 < SharedConst.MaxCreatureDifficulties && ok2; ++diff2)
{
ok2 = false;
if (difficultyEntries[diff2].Contains(cInfo.Entry))
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) is listed as `difficulty_entry_{1}` of another creature, but itself lists {2} in `difficulty_entry_{3}`.",
cInfo.Entry, diff2 + 1, cInfo.DifficultyEntry[diff], diff + 1);
continue;
}
if (difficultyEntries[diff2].Contains(cInfo.DifficultyEntry[diff]))
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) already listed as `difficulty_entry_{1}` for another entry.", cInfo.DifficultyEntry[diff], diff2 + 1);
continue;
}
if (hasDifficultyEntries[diff2].Contains(cInfo.DifficultyEntry[diff]))
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has `difficulty_entry_{1}`={2} but creature entry {3} has itself a value in `difficulty_entry_{4}`.",
cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff], cInfo.DifficultyEntry[diff], diff2 + 1);
continue;
}
ok2 = true;
}
if (!ok2)
continue;
if (cInfo.HealthScalingExpansion > difficultyInfo.HealthScalingExpansion)
{
Log.outError(LogFilter.Sql, "Creature (Id: {0}, Expansion {1}) has different `HealthScalingExpansion` in difficulty {2} mode (Id: {3}, Expansion: {4}).",
cInfo.Entry, cInfo.HealthScalingExpansion, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.HealthScalingExpansion);
}
if (cInfo.Faction != difficultyInfo.Faction)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, faction: {1}) has different `faction` in difficulty {2} mode (Entry: {3}, faction: {4}).",
cInfo.Entry, cInfo.Faction, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.Faction);
}
if (cInfo.UnitClass != difficultyInfo.UnitClass)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, class: {1}) has different `unit_class` in difficulty {2} mode (Entry: {3}, class: {4}).",
cInfo.Entry, cInfo.UnitClass, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.UnitClass);
continue;
}
if (cInfo.Npcflag != difficultyInfo.Npcflag)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has different `npcflag` in difficulty {1} mode (Entry: {2}).", cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff]);
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `npcflag`=`npcflag`^{0} WHERE `entry`={1};", cInfo.Npcflag ^ difficultyInfo.Npcflag, cInfo.DifficultyEntry[diff]);
continue;
}
if (cInfo.DmgSchool != difficultyInfo.DmgSchool)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, `dmgschool`: {1}) has different `dmgschool` in difficulty {2} mode (Entry: {3}, `dmgschool`: {4}).",
cInfo.Entry, cInfo.DmgSchool, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.DmgSchool);
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `dmgschool`={0} WHERE `entry`={1};", cInfo.DmgSchool, cInfo.DifficultyEntry[diff]);
}
if (cInfo.UnitFlags2 != difficultyInfo.UnitFlags2)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, `unit_flags2`: {1}) has different `unit_flags2` in difficulty {2} mode (Entry: {3}, `unit_flags2`: {4}).",
cInfo.Entry, cInfo.UnitFlags2, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.UnitFlags2);
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `unit_flags2`=`unit_flags2`^{0} WHERE `entry`={1};", cInfo.UnitFlags2 ^ difficultyInfo.UnitFlags2, cInfo.DifficultyEntry[diff]);
}
if (cInfo.Family != difficultyInfo.Family)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, family: {1}) has different `family` in difficulty {2} mode (Entry: {3}, family: {4}).",
cInfo.Entry, cInfo.Family, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.Family);
}
if (cInfo.TrainerClass != difficultyInfo.TrainerClass)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has different `trainer_class` in difficulty {1} mode (Entry: {2}).", cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff]);
continue;
}
if (cInfo.CreatureType != difficultyInfo.CreatureType)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, type: {1}) has different `type` in difficulty {2} mode (Entry: {3}, type: {4}).",
cInfo.Entry, cInfo.CreatureType, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.CreatureType);
}
if (cInfo.VehicleId == 0 && difficultyInfo.VehicleId != 0)
{
Log.outError(LogFilter.Sql, "Non-vehicle Creature (Entry: {0}, VehicleId: {1}) has `VehicleId` set in difficulty {2} mode (Entry: {3}, VehicleId: {4}).",
cInfo.Entry, cInfo.VehicleId, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.VehicleId);
}
if (cInfo.RegenHealth != difficultyInfo.RegenHealth)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, RegenHealth: {1}) has different `RegenHealth` in difficulty {2} mode (Entry: {3}, RegenHealth: {4}).",
cInfo.Entry, cInfo.RegenHealth, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.RegenHealth);
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `RegenHealth`={0} WHERE `entry`={1};", cInfo.RegenHealth, cInfo.DifficultyEntry[diff]);
}
ulong differenceMask = cInfo.MechanicImmuneMask & (~difficultyInfo.MechanicImmuneMask);
if (differenceMask != 0)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, mechanic_immune_mask: {1}) has weaker immunities in difficulty {2} mode (Entry: {3}, mechanic_immune_mask: {4}).",
cInfo.Entry, cInfo.MechanicImmuneMask, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.MechanicImmuneMask);
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `mechanic_immune_mask`=`mechanic_immune_mask`|{0} WHERE `entry`={1};", differenceMask, cInfo.DifficultyEntry[diff]);
}
differenceMask = (uint)((cInfo.FlagsExtra ^ difficultyInfo.FlagsExtra) & (~CreatureFlagsExtra.InstanceBind));
if (differenceMask != 0)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, flags_extra: {1}) has different `flags_extra` in difficulty {2} mode (Entry: {3}, flags_extra: {4}).",
cInfo.Entry, cInfo.FlagsExtra, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.FlagsExtra);
Log.outError(LogFilter.Sql, "Possible FIX: UPDATE `creature_template` SET `flags_extra`=`flags_extra`^{0} WHERE `entry`={1};", differenceMask, cInfo.DifficultyEntry[diff]);
}
if (difficultyInfo.AIName.IsEmpty())
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) lists difficulty {1} mode entry {2} 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 != 0)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) lists difficulty {1} mode entry {2} with `ScriptName` filled in. `ScriptName` of difficulty 0 mode creature is always used instead.",
cInfo.Entry, diff + 1, cInfo.DifficultyEntry[diff]);
continue;
}
hasDifficultyEntries[diff].Add(cInfo.Entry);
difficultyEntries[diff].Add(cInfo.DifficultyEntry[diff]);
ok = true;
}
if (cInfo.MinGold > cInfo.MaxGold)
{
Log.outError(LogFilter.Sql, $"Creature (Entry: {cInfo.Entry}) has `mingold` {cInfo.MinGold} which is greater than `maxgold` {cInfo.MaxGold}, setting `maxgold` to {cInfo.MinGold}.");
cInfo.MaxGold = cInfo.MinGold;
}
if (!CliDB.FactionTemplateStorage.ContainsKey(cInfo.Faction))
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has non-existing faction template ({1}). This can lead to crashes, set to faction 35", cInfo.Entry, cInfo.Faction);
@@ -3009,12 +2844,6 @@ namespace Game
cInfo.MovementType = (uint)MovementGeneratorType.Idle;
}
if (cInfo.HealthScalingExpansion < (int)Expansion.LevelCurrent || cInfo.HealthScalingExpansion >= (int)Expansion.Max)
{
Log.outError(LogFilter.Sql, "Table `creature_template` lists creature (Id: {0}) with invalid `HealthScalingExpansion` {1}. Ignored and set to 0.", cInfo.Entry, cInfo.HealthScalingExpansion);
cInfo.HealthScalingExpansion = 0;
}
if (cInfo.RequiredExpansion > (int)Expansion.Max)
{
Log.outError(LogFilter.Sql, "Table `creature_template` lists creature (Entry: {0}) with `RequiredExpansion` {1}. Ignored and set to 0.", cInfo.Entry, cInfo.RequiredExpansion);
@@ -3055,8 +2884,6 @@ namespace Game
cInfo.DynamicFlags = 0;
}
cInfo.ModDamage *= Creature._GetDamageMod(cInfo.Rank);
if (!cInfo.GossipMenuIds.Empty() && !cInfo.Npcflag.HasAnyFlag((uint)NPCFlags.Gossip))
Log.outInfo(LogFilter.Sql, $"Creature (Entry: {cInfo.Entry}) has assigned gossip menu, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.");
else if (cInfo.GossipMenuIds.Empty() && cInfo.Npcflag.HasAnyFlag((uint)NPCFlags.Gossip))
@@ -3694,18 +3521,6 @@ namespace Game
continue;
}
bool ok = true;
for (uint diff = 0; diff < SharedConst.MaxCreatureDifficulties && ok; ++diff)
{
if (difficultyEntries[diff].Contains(data.Id))
{
Log.outError(LogFilter.Sql, "Table `creature` have creature (GUID: {0}) that listed as difficulty {1} template (entry: {2}) in `creaturetemplate`, skipped.", guid, diff + 1, data.Id);
ok = false;
}
}
if (!ok)
continue;
// -1 random, 0 no equipment,
if (data.equipmentId != 0)
{
@@ -3934,39 +3749,43 @@ namespace Game
RemoveSpawnDataFromGrid(data);
}
public List<uint> GetCreatureQuestItemList(uint id)
{
return creatureQuestItemStorage.LookupByKey(id);
}
public CreatureAddon GetCreatureAddon(ulong lowguid)
{
return creatureAddonStorage.LookupByKey(lowguid);
}
public CreatureTemplate GetCreatureTemplate(uint entry)
{
return creatureTemplateStorage.LookupByKey(entry);
}
public CreatureAddon GetCreatureTemplateAddon(uint entry)
{
return creatureTemplateAddonStorage.LookupByKey(entry);
}
public uint GetCreatureDefaultTrainer(uint creatureId)
{
return GetCreatureTrainerForGossipOption(creatureId, 0, 0);
}
public uint GetCreatureTrainerForGossipOption(uint creatureId, uint gossipMenuId, uint gossipOptionIndex)
{
return _creatureDefaultTrainers.LookupByKey((creatureId, gossipMenuId, gossipOptionIndex));
}
public Dictionary<uint, CreatureTemplate> GetCreatureTemplates()
{
return creatureTemplateStorage;
}
public Dictionary<ulong, CreatureData> GetAllCreatureData() { return creatureDataStorage; }
public CreatureData GetCreatureData(ulong spawnId)
{
return creatureDataStorage.LookupByKey(spawnId);
}
public ObjectGuid GetLinkedRespawnGuid(ObjectGuid spawnId)
{
var retGuid = linkedRespawnStorage.LookupByKey(spawnId);
@@ -3974,6 +3793,7 @@ namespace Game
return ObjectGuid.Empty;
return retGuid;
}
public bool SetCreatureLinkedRespawn(ulong guidLow, ulong linkedGuidLow)
{
if (guidLow == 0)
@@ -4024,12 +3844,14 @@ namespace Game
DB.World.Execute(stmt);
return true;
}
public CreatureData NewOrExistCreatureData(ulong spawnId)
{
if (!creatureDataStorage.ContainsKey(spawnId))
creatureDataStorage[spawnId] = new CreatureData();
return creatureDataStorage[spawnId];
}
public void DeleteCreatureData(ulong spawnId)
{
CreatureData data = GetCreatureData(spawnId);
@@ -4041,6 +3863,7 @@ namespace Game
creatureDataStorage.Remove(spawnId);
}
public CreatureBaseStats GetCreatureBaseStats(uint level, uint unitClass)
{
var stats = creatureBaseStatsStorage.LookupByKey(MathFunctions.MakePair16(level, unitClass));
@@ -4049,6 +3872,7 @@ namespace Game
return new DefaultCreatureBaseStats();
}
public CreatureModelInfo GetCreatureModelRandomGender(ref CreatureModel model, CreatureTemplate creatureTemplate)
{
CreatureModelInfo modelInfo = GetCreatureModelInfo(model.CreatureDisplayID);
@@ -4081,14 +3905,17 @@ namespace Game
return modelInfo;
}
public CreatureModelInfo GetCreatureModelInfo(uint modelId)
{
return creatureModelStorage.LookupByKey(modelId);
}
public CreatureSummonedData GetCreatureSummonedData(uint entryId)
{
return creatureSummonedDataStorage.LookupByKey(entryId);
}
public NpcText GetNpcText(uint textId)
{
return npcTextStorage.LookupByKey(textId);
@@ -4289,6 +4116,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} game object templates in {1} ms", _gameObjectTemplateStorage.Count, Time.GetMSTimeDiffToNow(time));
}
}
public void LoadGameObjectTemplateAddons()
{
uint oldMSTime = Time.GetMSTime();
@@ -4372,6 +4200,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} game object template addons in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadGameObjectOverrides()
{
uint oldMSTime = Time.GetMSTime();
@@ -4409,6 +4238,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} gameobject faction and flags overrides in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
}
public void LoadGameObjects()
{
var time = Time.GetMSTime();
@@ -4639,6 +4469,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} gameobjects in {1} ms", count, Time.GetMSTimeDiffToNow(time));
}
public void LoadGameObjectAddons()
{
uint oldMSTime = Time.GetMSTime();
@@ -4710,6 +4541,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} gameobject addons in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
}
public void LoadGameObjectQuestItems()
{
uint oldMSTime = Time.GetMSTime();
@@ -4749,6 +4581,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} gameobject quest items in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadGameObjectForQuests()
{
uint oldMSTime = Time.GetMSTime();
@@ -4814,10 +4647,12 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} GameObjects for quests in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void AddGameObjectToGrid(GameObjectData data)
{
AddSpawnDataToGrid(data);
}
public void RemoveGameObjectFromGrid(GameObjectData data)
{
RemoveSpawnDataFromGrid(data);
@@ -4827,16 +4662,21 @@ namespace Game
{
return _gameObjectAddonStorage.LookupByKey(lowguid);
}
public List<uint> GetGameObjectQuestItemList(uint id)
{
return _gameObjectQuestItemStorage.LookupByKey(id);
}
MultiMap<uint, uint> GetGameObjectQuestItemMap() { return _gameObjectQuestItemStorage; }
public Dictionary<ulong, GameObjectData> GetAllGameObjectData() { return gameObjectDataStorage; }
public GameObjectData GetGameObjectData(ulong spawnId)
{
return gameObjectDataStorage.LookupByKey(spawnId);
}
public void DeleteGameObjectData(ulong spawnId)
{
GameObjectData data = GetGameObjectData(spawnId);
@@ -4848,6 +4688,7 @@ namespace Game
gameObjectDataStorage.Remove(spawnId);
}
public GameObjectData NewOrExistGameObjectData(ulong spawnId)
{
if (!gameObjectDataStorage.ContainsKey(spawnId))
@@ -4855,26 +4696,32 @@ namespace Game
return gameObjectDataStorage[spawnId];
}
public GameObjectTemplate GetGameObjectTemplate(uint entry)
{
return _gameObjectTemplateStorage.LookupByKey(entry);
}
public GameObjectTemplateAddon GetGameObjectTemplateAddon(uint entry)
{
return _gameObjectTemplateAddonStorage.LookupByKey(entry);
}
public GameObjectOverride GetGameObjectOverride(ulong spawnId)
{
return _gameObjectOverrideStorage.LookupByKey(spawnId);
}
public Dictionary<uint, GameObjectTemplate> GetGameObjectTemplates()
{
return _gameObjectTemplateStorage;
}
public bool IsGameObjectForQuests(uint entry)
{
return _gameObjectForQuestStorage.Contains(entry);
}
void CheckGOLockId(GameObjectTemplate goInfo, uint dataN, uint N)
{
if (CliDB.LockStorage.ContainsKey(dataN))
@@ -4882,6 +4729,7 @@ namespace Game
Log.outError(LogFilter.Sql, "Gameobject (Entry: {0} GoType: {1}) have data{2}={3} but lock (Id: {4}) not found.", goInfo.entry, goInfo.type, N, goInfo.Door.open, goInfo.Door.open);
}
void CheckGOLinkedTrapId(GameObjectTemplate goInfo, uint dataN, uint N)
{
GameObjectTemplate trapInfo = GetGameObjectTemplate(dataN);
@@ -4891,6 +4739,7 @@ namespace Game
Log.outError(LogFilter.Sql, "Gameobject (Entry: {0} GoType: {1}) have data{2}={3} but GO (Entry {4}) have not GAMEOBJECT_TYPE_TRAP type.", goInfo.entry, goInfo.type, N, dataN, dataN);
}
}
void CheckGOSpellId(GameObjectTemplate goInfo, uint dataN, uint N)
{
if (Global.SpellMgr.HasSpellInfo(dataN, Difficulty.None))
@@ -4898,6 +4747,7 @@ namespace Game
Log.outError(LogFilter.Sql, "Gameobject (Entry: {0} GoType: {1}) have data{2}={3} but Spell (Entry {4}) not exist.", goInfo.entry, goInfo.type, N, dataN, dataN);
}
void CheckAndFixGOChairHeightId(GameObjectTemplate goInfo, ref uint dataN, uint N)
{
if (dataN <= (UnitStandStateType.SitHighChair - UnitStandStateType.SitLowChair))
@@ -4908,6 +4758,7 @@ namespace Game
// prevent client and server unexpected work
dataN = 0;
}
void CheckGONoDamageImmuneId(GameObjectTemplate goTemplate, uint dataN, uint N)
{
// 0/1 correct values
@@ -4916,6 +4767,7 @@ namespace Game
Log.outError(LogFilter.Sql, "Gameobject (Entry: {0} GoType: {1}) have data{2}={3} but expected boolean (0/1) noDamageImmune field value.", goTemplate.entry, goTemplate.type, N, dataN);
}
void CheckGOConsumable(GameObjectTemplate goInfo, uint dataN, uint N)
{
// 0/1 correct values
@@ -5668,16 +5520,6 @@ namespace Game
continue;
}
creatureInfo.FlagsExtra |= CreatureFlagsExtra.DungeonBoss;
for (byte diff = 0; diff < SharedConst.MaxCreatureDifficulties; ++diff)
{
uint diffEntry = creatureInfo.DifficultyEntry[diff];
if (diffEntry != 0)
{
CreatureTemplate diffInfo = GetCreatureTemplate(diffEntry);
if (diffInfo != null)
diffInfo.FlagsExtra |= CreatureFlagsExtra.DungeonBoss;
}
}
break;
}
case EncounterCreditType.CastSpell:
@@ -8087,6 +7929,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quests definitions in {1} ms", _questTemplates.Count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadQuestStartersAndEnders()
{
Log.outInfo(LogFilter.ServerLoading, "Loading GO Start Quest Data...");
@@ -8098,6 +7941,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loading Creature End Quest Data...");
LoadCreatureQuestEnders();
}
public void LoadGameobjectQuestStarters()
{
LoadQuestRelationsHelper(_goQuestRelations, null, "gameobject_queststarter");
@@ -8111,6 +7955,7 @@ namespace Game
Log.outError(LogFilter.Sql, "Table `gameobject_queststarter` have data gameobject entry ({0}) for quest {1}, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", pair.Key, pair.Value);
}
}
public void LoadGameobjectQuestEnders()
{
LoadQuestRelationsHelper(_goQuestInvolvedRelations, _goQuestInvolvedRelationsReverse, "gameobject_questender");
@@ -8124,6 +7969,7 @@ namespace Game
Log.outError(LogFilter.Sql, "Table `gameobject_questender` have data gameobject entry ({0}) for quest {1}, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", pair.Key, pair.Value);
}
}
public void LoadCreatureQuestStarters()
{
LoadQuestRelationsHelper(_creatureQuestRelations, null, "creature_queststarter");
@@ -8137,6 +7983,7 @@ namespace Game
Log.outError(LogFilter.Sql, "Table `creature_queststarter` has creature entry ({0}) for quest {1}, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", pair.Key, pair.Value);
}
}
public void LoadCreatureQuestEnders()
{
LoadQuestRelationsHelper(_creatureQuestInvolvedRelations, _creatureQuestInvolvedRelationsReverse, "creature_questender");
@@ -8150,6 +7997,7 @@ namespace Game
Log.outError(LogFilter.Sql, "Table `creature_questender` has creature entry ({0}) for quest {1}, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", pair.Key, pair.Value);
}
}
void LoadQuestRelationsHelper(MultiMap<uint, uint> map, MultiMap<uint, uint> reverseMap, string table)
{
uint oldMSTime = Time.GetMSTime();
@@ -8186,6 +8034,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest relations from {1} in {2} ms", count, table, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadQuestPOI()
{
uint oldMSTime = Time.GetMSTime();
@@ -8264,6 +8113,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest POI definitions in {1} ms", _questPOIStorage.Count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadQuestAreaTriggers()
{
uint oldMSTime = Time.GetMSTime();
@@ -8324,6 +8174,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest trigger points in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public void LoadQuestGreetings()
{
uint oldMSTime = Time.GetMSTime();
@@ -8381,34 +8232,48 @@ namespace Game
{
return _questTemplates.LookupByKey(questId);
}
public Dictionary<uint, Quest> GetQuestTemplates()
{
return _questTemplates;
}
public List<Quest> GetQuestTemplatesAutoPush()
{
return _questTemplatesAutoPush;
}
public MultiMap<uint, uint> GetGOQuestRelationMapHACK() { return _goQuestRelations; }
public QuestRelationResult GetGOQuestRelations(uint entry) { return GetQuestRelationsFrom(_goQuestRelations, entry, true); }
public QuestRelationResult GetGOQuestInvolvedRelations(uint entry) { return GetQuestRelationsFrom(_goQuestInvolvedRelations, entry, false); }
public List<uint> GetGOQuestInvolvedRelationReverseBounds(uint questId) { return _goQuestInvolvedRelationsReverse.LookupByKey(questId); }
public MultiMap<uint, uint> GetCreatureQuestRelationMapHACK() { return _creatureQuestRelations; }
public QuestRelationResult GetCreatureQuestRelations(uint entry) { return GetQuestRelationsFrom(_creatureQuestRelations, entry, true); }
public QuestRelationResult GetCreatureQuestInvolvedRelations(uint entry) { return GetQuestRelationsFrom(_creatureQuestInvolvedRelations, entry, false); }
public List<uint> GetCreatureQuestInvolvedRelationReverseBounds(uint questId) { return _creatureQuestInvolvedRelationsReverse.LookupByKey(questId); }
public QuestPOIData GetQuestPOIData(uint questId)
{
return _questPOIStorage.LookupByKey(questId);
}
public QuestObjective GetQuestObjective(uint questObjectiveId)
{
return _questObjectives.LookupByKey(questObjectiveId);
}
public List<uint> GetQuestsForAreaTrigger(uint triggerId)
{
return _questAreaTriggerStorage.LookupByKey(triggerId);
}
public QuestGreeting GetQuestGreeting(TypeId type, uint id)
{
byte typeIndex;
@@ -8421,6 +8286,7 @@ namespace Game
return _questGreetingStorage[typeIndex].LookupByKey(id);
}
public QuestGreetingLocale GetQuestGreetingLocale(TypeId type, uint id)
{
byte typeIndex;
@@ -8433,12 +8299,28 @@ namespace Game
return _questGreetingLocaleStorage[typeIndex].LookupByKey(id);
}
public List<uint> GetExclusiveQuestGroupBounds(int exclusiveGroupId)
{
return _exclusiveQuestGroups.LookupByKey(exclusiveGroupId);
}
QuestRelationResult GetQuestRelationsFrom(MultiMap<uint, uint> map, uint key, bool onlyActive) { return new QuestRelationResult(map.LookupByKey(key), onlyActive); }
public List<uint> GetCreatureQuestItemList(uint creatureEntry, Difficulty difficulty)
{
var itr = creatureQuestItemStorage.LookupByKey((creatureEntry, difficulty));
if (itr != null)
return itr;
// If there is no data for the difficulty, try to get data for the fallback difficulty
var difficultyEntry = CliDB.DifficultyStorage.LookupByKey(difficulty);
if (difficultyEntry != null)
return GetCreatureQuestItemList(creatureEntry, (Difficulty)difficultyEntry.FallbackDifficultyID);
return null;
}
//Spells /Skills / Phases
public void LoadPhases()
{
@@ -11039,7 +10921,7 @@ namespace Game
Dictionary<uint, CreatureSummonedData> creatureSummonedDataStorage = new();
Dictionary<ulong, CreatureData> creatureDataStorage = new();
Dictionary<ulong, CreatureAddon> creatureAddonStorage = new();
MultiMap<uint, uint> creatureQuestItemStorage = new();
MultiMap<(uint, Difficulty), uint> creatureQuestItemStorage = new();
Dictionary<uint, CreatureAddon> creatureTemplateAddonStorage = new();
MultiMap<uint, float> _creatureTemplateSparringStorage = new();
Dictionary<ulong, CreatureMovementData> creatureMovementOverrides = new();
@@ -11049,8 +10931,6 @@ namespace Game
Dictionary<uint, VendorItemData> cacheVendorItemStorage = new();
Dictionary<uint, Trainer> trainers = new();
Dictionary<(uint creatureId, uint gossipMenuId, uint gossipOptionIndex), uint> _creatureDefaultTrainers = new();
List<uint>[] difficultyEntries = new List<uint>[SharedConst.MaxCreatureDifficulties]; // already loaded difficulty 1 value in creatures, used in CheckCreatureTemplate
List<uint>[] hasDifficultyEntries = new List<uint>[SharedConst.MaxCreatureDifficulties]; // already loaded creatures with difficulty 1 values, used in CheckCreatureTemplate
Dictionary<uint, NpcText> npcTextStorage = new();
//GameObject
+2 -2
View File
@@ -458,7 +458,7 @@ namespace Game
if (dstPet != null)
{
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(dstPet.CreatureId);
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets()))
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets(), creatureInfo.GetDifficulty(Difficulty.None)))
{
SendPetStableResult(StableResult.CantControlExotic);
return;
@@ -484,7 +484,7 @@ namespace Game
}
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(srcPet.CreatureId);
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets()))
if (creatureInfo == null || !creatureInfo.IsTameable(_player.CanTameExoticPets(), creatureInfo.GetDifficulty(Difficulty.None)))
{
SendPetStableResult(StableResult.CantControlExotic);
return;
+7 -22
View File
@@ -99,31 +99,16 @@ namespace Game
CreatureTemplate ci = Global.ObjectMgr.GetCreatureTemplate(packet.CreatureID);
if (ci != null)
{
if (!WorldConfig.GetBoolValue(WorldCfg.CacheDataQueries))
ci.InitializeQueryData();
Difficulty difficulty = _player.GetMap().GetDifficultyID();
QueryCreatureResponse queryCreatureResponse = ci.QueryData;
Locale loc = GetSessionDbLocaleIndex();
if (loc != Locale.enUS)
// Cache only exists for difficulty base
if (!WorldConfig.GetBoolValue(WorldCfg.CacheDataQueries) && difficulty == Difficulty.None)
SendPacket(ci.QueryData[(int)GetSessionDbLocaleIndex()]);
else
{
CreatureLocale creatureLocale = Global.ObjectMgr.GetCreatureLocale(ci.Entry);
if (creatureLocale != null)
{
string name = queryCreatureResponse.Stats.Name[0];
string nameAlt = queryCreatureResponse.Stats.NameAlt[0];
ObjectManager.GetLocaleString(creatureLocale.Name, loc, ref name);
ObjectManager.GetLocaleString(creatureLocale.NameAlt, loc, ref nameAlt);
ObjectManager.GetLocaleString(creatureLocale.Title, loc, ref queryCreatureResponse.Stats.Title);
ObjectManager.GetLocaleString(creatureLocale.TitleAlt, loc, ref queryCreatureResponse.Stats.TitleAlt);
queryCreatureResponse.Stats.Name[0] = name;
queryCreatureResponse.Stats.NameAlt[0] = nameAlt;
}
var response = ci.BuildQueryData(GetSessionDbLocaleIndex(), difficulty);
SendPacket(response);
}
SendPacket(queryCreatureResponse);
}
else
{
+30 -24
View File
@@ -97,16 +97,18 @@ namespace Game.Loots
uint count = Creature.LoadAndCollectLootIds(out lootIdSet);
// Remove real entries and check loot existence
var ctc = Global.ObjectMgr.GetCreatureTemplates();
foreach (var pair in ctc)
var templates = Global.ObjectMgr.GetCreatureTemplates();
foreach (var creatureTemplate in templates.Values)
{
uint lootid = pair.Value.LootId;
if (lootid != 0)
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
{
if (!lootIdSet.Contains(lootid))
Creature.ReportNonExistingId(lootid, pair.Value.Entry);
else
lootIdSetUsed.Add(lootid);
if (creatureDifficulty.LootID != 0)
{
if (!lootIdSet.Contains(creatureDifficulty.LootID))
Creature.ReportNonExistingId(creatureDifficulty.LootID, creatureTemplate.Entry);
else
lootIdSetUsed.Add(creatureDifficulty.LootID);
}
}
}
@@ -289,16 +291,18 @@ namespace Game.Loots
uint count = Pickpocketing.LoadAndCollectLootIds(out lootIdSet);
// Remove real entries and check loot existence
var ctc = Global.ObjectMgr.GetCreatureTemplates();
foreach (var pair in ctc)
var templates = Global.ObjectMgr.GetCreatureTemplates();
foreach (var creatureTemplate in templates.Values)
{
uint lootid = pair.Value.PickPocketId;
if (lootid != 0)
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
{
if (!lootIdSet.Contains(lootid))
Pickpocketing.ReportNonExistingId(lootid, pair.Value.Entry);
else
lootIdSetUsed.Add(lootid);
if (creatureDifficulty.PickPocketLootID != 0)
{
if (!lootIdSet.Contains(creatureDifficulty.PickPocketLootID))
Pickpocketing.ReportNonExistingId(creatureDifficulty.PickPocketLootID, creatureTemplate.Entry);
else
lootIdSetUsed.Add(creatureDifficulty.PickPocketLootID);
}
}
}
@@ -377,16 +381,18 @@ namespace Game.Loots
uint count = Skinning.LoadAndCollectLootIds(out lootIdSet);
// remove real entries and check existence loot
var ctc = Global.ObjectMgr.GetCreatureTemplates();
foreach (var pair in ctc)
var templates = Global.ObjectMgr.GetCreatureTemplates();
foreach (var creatureTemplate in templates.Values)
{
uint lootid = pair.Value.SkinLootId;
if (lootid != 0)
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
{
if (!lootIdSet.Contains(lootid))
Skinning.ReportNonExistingId(lootid, pair.Value.Entry);
else
lootIdSetUsed.Add(lootid);
if (creatureDifficulty.SkinLootID != 0)
{
if (!lootIdSet.Contains(creatureDifficulty.SkinLootID))
Skinning.ReportNonExistingId(creatureDifficulty.SkinLootID, creatureTemplate.Entry);
else
lootIdSetUsed.Add(creatureDifficulty.SkinLootID);
}
}
}
+1 -1
View File
@@ -147,7 +147,7 @@ namespace Game
if (gain != 0 && creature)
{
// Players get only 10% xp for killing creatures of lower expansion levels than himself
if ((creature.GetCreatureTemplate().GetHealthScalingExpansion() < (int)GetExpansionForLevel(player.GetLevel())))
if ((creature.GetCreatureDifficulty().GetHealthScalingExpansion() < (int)GetExpansionForLevel(player.GetLevel())))
gain = (uint)Math.Round(gain / 10.0f);
if (creature.IsElite())
@@ -1245,32 +1245,32 @@ namespace Game.Networking.Packets
bool GenerateDataCreatureToPlayer(Creature attacker, Player target)
{
CreatureTemplate creatureTemplate = attacker.GetCreatureTemplate();
CreatureLevelScaling creatureScaling = creatureTemplate.GetLevelScaling(attacker.GetMap().GetDifficultyID());
CreatureDifficulty creatureDifficulty = creatureTemplate.GetDifficulty(attacker.GetMap().GetDifficultyID());
TuningType = ContentTuningType.CreatureToPlayerDamage;
PlayerLevelDelta = (short)target.m_activePlayerData.ScalingPlayerLevelDelta;
PlayerItemLevel = (ushort)target.GetAverageItemLevel();
ScalingHealthItemLevelCurveID = (ushort)target.m_unitData.ScalingHealthItemLevelCurveID;
TargetLevel = (byte)target.GetLevel();
Expansion = (byte)creatureTemplate.HealthScalingExpansion;
Expansion = (byte)creatureDifficulty.HealthScalingExpansion;
TargetScalingLevelDelta = (sbyte)attacker.m_unitData.ScalingLevelDelta;
TargetContentTuningID = creatureScaling.ContentTuningID;
TargetContentTuningID = creatureDifficulty.ContentTuningID;
return true;
}
bool GenerateDataPlayerToCreature(Player attacker, Creature target)
{
CreatureTemplate creatureTemplate = target.GetCreatureTemplate();
CreatureLevelScaling creatureScaling = creatureTemplate.GetLevelScaling(target.GetMap().GetDifficultyID());
CreatureDifficulty creatureDifficulty = creatureTemplate.GetDifficulty(target.GetMap().GetDifficultyID());
TuningType = ContentTuningType.PlayerToCreatureDamage;
PlayerLevelDelta = (short)attacker.m_activePlayerData.ScalingPlayerLevelDelta;
PlayerItemLevel = (ushort)attacker.GetAverageItemLevel();
ScalingHealthItemLevelCurveID = (ushort)target.m_unitData.ScalingHealthItemLevelCurveID;
TargetLevel = (byte)target.GetLevel();
Expansion = (byte)creatureTemplate.HealthScalingExpansion;
Expansion = (byte)creatureDifficulty.HealthScalingExpansion;
TargetScalingLevelDelta = (sbyte)target.m_unitData.ScalingLevelDelta;
TargetContentTuningID = creatureScaling.ContentTuningID;
TargetContentTuningID = creatureDifficulty.ContentTuningID;
return true;
}
@@ -1278,15 +1278,15 @@ namespace Game.Networking.Packets
{
Creature accessor = target.HasScalableLevels() ? target : attacker;
CreatureTemplate creatureTemplate = accessor.GetCreatureTemplate();
CreatureLevelScaling creatureScaling = creatureTemplate.GetLevelScaling(accessor.GetMap().GetDifficultyID());
CreatureDifficulty creatureDifficulty = creatureTemplate.GetDifficulty(accessor.GetMap().GetDifficultyID());
TuningType = ContentTuningType.CreatureToCreatureDamage;
PlayerLevelDelta = 0;
PlayerItemLevel = 0;
TargetLevel = (byte)target.GetLevel();
Expansion = (byte)creatureTemplate.HealthScalingExpansion;
Expansion = (byte)creatureDifficulty.HealthScalingExpansion;
TargetScalingLevelDelta = (sbyte)accessor.m_unitData.ScalingLevelDelta;
TargetContentTuningID = creatureScaling.ContentTuningID;
TargetContentTuningID = creatureDifficulty.ContentTuningID;
return true;
}
+4 -3
View File
@@ -1402,7 +1402,7 @@ namespace Game.Spells
Creature creatureTarget = unitTarget.ToCreature();
if (creatureTarget)
{
if (!creatureTarget.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CollideWithMissiles))
if (!creatureTarget.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CollideWithMissiles))
continue;
}
}
@@ -5405,10 +5405,11 @@ namespace Game.Spells
if (info.Item1.Type == PetType.Hunter)
{
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(info.Item1.CreatureId);
if (creatureInfo == null || !creatureInfo.IsTameable(playerCaster.CanTameExoticPets()))
CreatureDifficulty creatureDifficulty = creatureInfo.GetDifficulty(Difficulty.None);
if (creatureInfo == null || !creatureInfo.IsTameable(playerCaster.CanTameExoticPets(), creatureDifficulty))
{
// if problem in exotic pet
if (creatureInfo != null && creatureInfo.IsTameable(true))
if (creatureInfo != null && creatureInfo.IsTameable(true, creatureDifficulty))
playerCaster.SendTameFailure(PetTameResult.CantControlExotic);
else
playerCaster.SendTameFailure(PetTameResult.NoPetAvailable);
+3 -3
View File
@@ -1819,7 +1819,7 @@ namespace Game.Spells
creature.StartPickPocketRefillTimer();
creature._loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Pickpocketing, null);
uint lootid = creature.GetCreatureTemplate().PickPocketId;
uint lootid = creature.GetCreatureDifficulty().PickPocketLootID;
if (lootid != 0)
creature._loot.FillLoot(lootid, LootStorage.Pickpocketing, player, true);
@@ -3442,13 +3442,13 @@ namespace Game.Spells
Creature creature = unitTarget.ToCreature();
int targetLevel = (int)creature.GetLevelForTarget(m_caster);
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
SkillType skill = creature.GetCreatureDifficulty().GetRequiredLootSkill();
creature.SetUnitFlag3(UnitFlags3.AlreadySkinned);
creature.SetDynamicFlag(UnitDynFlags.Lootable);
Loot loot = new(creature.GetMap(), creature.GetGUID(), LootType.Skinning, null);
creature.m_personalLoot[player.GetGUID()] = loot;
loot.FillLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, player, true);
loot.FillLoot(creature.GetCreatureDifficulty().SkinLootID, LootStorage.Skinning, player, true);
player.SendLoot(loot);
if (!Global.SpellMgr.IsPartOfSkillLine(skill, m_spellInfo.Id))
+1 -1
View File
@@ -1032,7 +1032,7 @@ namespace Game.Spells
if (targetCreature == null)
return SpellCastResult.BadTargets;
if (!targetCreature.CanHaveLoot() || !Loots.LootStorage.Pickpocketing.HaveLootFor(targetCreature.GetCreatureTemplate().PickPocketId))
if (!targetCreature.CanHaveLoot() || !Loots.LootStorage.Pickpocketing.HaveLootFor(targetCreature.GetCreatureDifficulty().PickPocketLootID))
return SpellCastResult.TargetNoPockets;
}
+3 -3
View File
@@ -606,12 +606,12 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template addons...");
Global.ObjectMgr.LoadCreatureTemplateAddons();
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template difficulty...");
Global.ObjectMgr.LoadCreatureTemplateDifficulty();
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template sparring...");
Global.ObjectMgr.LoadCreatureTemplateSparring();
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template scaling...");
Global.ObjectMgr.LoadCreatureScalingData();
Log.outInfo(LogFilter.ServerLoading, "Loading Reputation Reward Rates...");
Global.ObjectMgr.LoadReputationRewardRate();
+1 -1
View File
@@ -454,7 +454,7 @@ namespace Scripts.Spells.Hunter
return SpellCastResult.Highlevel;
// use SMSG_PET_TAME_FAILURE?
if (!target.GetCreatureTemplate().IsTameable(caster.CanTameExoticPets()))
if (!target.GetCreatureTemplate().IsTameable(caster.CanTameExoticPets(), target.GetCreatureDifficulty()))
return SpellCastResult.BadTargets;
PetStable petStable = caster.GetPetStable();
+3 -3
View File
@@ -7,7 +7,6 @@ using Game.BattleGrounds;
using Game.DataStorage;
using Game.Entities;
using Game.Loots;
using Game.Networking.Packets;
using Game.Scripting;
using Game.Spells;
using System;
@@ -1322,11 +1321,12 @@ namespace Scripts.Spells.Items
{
Player player = GetCaster().ToPlayer();
Creature creature = GetTarget().ToCreature();
CreatureDifficulty creatureDifficulty = creature.GetCreatureDifficulty();
// missing lootid has been reported on startup - just return
if (creature.GetCreatureTemplate().SkinLootId == 0)
if (creatureDifficulty.SkinLootID == 0)
return;
player.AutoStoreLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, ItemContext.None, true);
player.AutoStoreLoot(creatureDifficulty.SkinLootID, LootStorage.Skinning, ItemContext.None, true);
creature.DespawnOrUnsummon();
}
}