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.SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?");
PrepareStatement(WorldStatements.DEL_CREATURE, "DELETE FROM creature WHERE guid = ?"); PrepareStatement(WorldStatements.DEL_CREATURE, "DELETE FROM creature WHERE guid = ?");
PrepareStatement(WorldStatements.SEL_COMMANDS, "SELECT name, help FROM command"); 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_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_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_"); 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; return false;
//experimental (unknown) flag not present //experimental (unknown) flag not present
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist)) if (!me.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist))
return false; return false;
//not a player //not a player
@@ -283,7 +283,7 @@ namespace Game.AI
return false; return false;
//experimental (unknown) flag not present //experimental (unknown) flag not present
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist)) if (!me.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist))
return false; return false;
if (!who.IsInAccessiblePlaceFor(me)) if (!who.IsInAccessiblePlaceFor(me))
+1 -1
View File
@@ -496,7 +496,7 @@ namespace Game.AI
return false; return false;
//experimental (unknown) flag not present //experimental (unknown) flag not present
if (!me.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CanAssist)) if (!me.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CanAssist))
return false; return false;
//not a player //not a player
+4 -2
View File
@@ -124,7 +124,9 @@ namespace Game.Chat
handler.SendSysMessage(CypherStrings.NpcinfoDynamicFlags, target.GetDynamicFlags()); handler.SendSysMessage(CypherStrings.NpcinfoDynamicFlags, target.GetDynamicFlags());
handler.SendSysMessage(CypherStrings.CommandRawpawntimes, defRespawnDelayStr, curRespawnDelayStr); 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()); handler.SendSysMessage(CypherStrings.NpcinfoDungeonId, target.GetInstanceId());
CreatureData data = Global.ObjectMgr.GetCreatureData(target.GetSpawnId()); CreatureData data = Global.ObjectMgr.GetCreatureData(target.GetSpawnId());
@@ -406,7 +408,7 @@ namespace Game.Chat
CreatureTemplate cInfo = creatureTarget.GetCreatureTemplate(); CreatureTemplate cInfo = creatureTarget.GetCreatureTemplate();
if (!cInfo.IsTameable(player.CanTameExoticPets())) if (!cInfo.IsTameable(player.CanTameExoticPets(), creatureTarget.GetCreatureDifficulty()))
{ {
handler.SendSysMessage(CypherStrings.CreatureNonTameable, cInfo.Entry); handler.SendSysMessage(CypherStrings.CreatureNonTameable, cInfo.Entry);
return false; return false;
@@ -12,6 +12,7 @@ namespace Game.Entities
{ {
CreatureTemplate m_creatureInfo; CreatureTemplate m_creatureInfo;
CreatureData m_creatureData; CreatureData m_creatureData;
CreatureDifficulty m_creatureDifficulty;
string[] m_stringIds = new string[3]; string[] m_stringIds = new string[3];
string m_scriptStringId; string m_scriptStringId;
+38 -58
View File
@@ -211,53 +211,30 @@ namespace Game.Entities
public bool InitEntry(uint entry, CreatureData data = null) public bool InitEntry(uint entry, CreatureData data = null)
{ {
CreatureTemplate normalInfo = Global.ObjectMgr.GetCreatureTemplate(entry); CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(entry);
if (normalInfo == null) if (creatureInfo == null)
{ {
Log.outError(LogFilter.Sql, "Creature.InitEntry creature entry {0} does not exist.", entry); Log.outError(LogFilter.Sql, "Creature.InitEntry creature entry {0} does not exist.", entry);
return false; return false;
} }
// get difficulty 1 mode entry m_creatureInfo = creatureInfo;
CreatureTemplate cInfo = null; SetEntry(entry);
DifficultyRecord difficultyEntry = CliDB.DifficultyStorage.LookupByKey(GetMap().GetDifficultyID()); m_creatureDifficulty = creatureInfo.GetDifficulty(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
// equal to player Race field, but creature does not have race // equal to player Race field, but creature does not have race
SetRace(0); SetRace(0);
SetClass((Class)cInfo.UnitClass); SetClass((Class)creatureInfo.UnitClass);
// Cancel load if no model defined // 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); Log.outError(LogFilter.Sql, "Creature (Entry: {0}) has no model defined in table `creature_template`, can't load. ", entry);
return false; return false;
} }
CreatureModel model = ObjectManager.ChooseDisplayId(cInfo, data); CreatureModel model = ObjectManager.ChooseDisplayId(creatureInfo, data);
CreatureModelInfo minfo = Global.ObjectMgr.GetCreatureModelRandomGender(ref model, cInfo); CreatureModelInfo minfo = Global.ObjectMgr.GetCreatureModelRandomGender(ref model, creatureInfo);
if (minfo == null) // Cancel load if no model defined 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); 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); LoadEquipment(data.equipmentId);
} }
SetName(normalInfo.Name); // at normal entry always SetName(creatureInfo.Name); // at normal entry always
SetModCastingSpeed(1.0f); SetModCastingSpeed(1.0f);
SetModSpellHaste(1.0f); SetModSpellHaste(1.0f);
@@ -286,25 +263,25 @@ namespace Game.Entities
SetModHasteRegen(1.0f); SetModHasteRegen(1.0f);
SetModTimeRate(1.0f); SetModTimeRate(1.0f);
SetSpeedRate(UnitMoveType.Walk, cInfo.SpeedWalk); SetSpeedRate(UnitMoveType.Walk, creatureInfo.SpeedWalk);
SetSpeedRate(UnitMoveType.Run, cInfo.SpeedRun); SetSpeedRate(UnitMoveType.Run, creatureInfo.SpeedRun);
SetSpeedRate(UnitMoveType.Swim, 1.0f); // using 1.0 rate SetSpeedRate(UnitMoveType.Swim, 1.0f); // using 1.0 rate
SetSpeedRate(UnitMoveType.Flight, 1.0f); // using 1.0 rate SetSpeedRate(UnitMoveType.Flight, 1.0f); // using 1.0 rate
SetObjectScale(GetNativeObjectScale()); SetObjectScale(GetNativeObjectScale());
SetCanDualWield(cInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.UseOffhandAttack)); SetCanDualWield(creatureInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.UseOffhandAttack));
// checked at loading // 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) if (m_wanderDistance == 0 && DefaultMovementType == MovementGeneratorType.Random)
DefaultMovementType = MovementGeneratorType.Idle; DefaultMovementType = MovementGeneratorType.Idle;
for (byte i = 0; i < SharedConst.MaxCreatureSpells; ++i) for (byte i = 0; i < SharedConst.MaxCreatureSpells; ++i)
m_spells[i] = GetCreatureTemplate().Spells[i]; m_spells[i] = GetCreatureTemplate().Spells[i];
_staticFlags.ApplyFlag(CreatureStaticFlags.NoXp, cInfo.CreatureType == CreatureType.Critter || IsPet() || IsTotem() || cInfo.FlagsExtra.HasFlag(CreatureFlagsExtra.NoXP)); _staticFlags.ApplyFlag(CreatureStaticFlags.NoXp, creatureInfo.CreatureType == CreatureType.Critter || IsPet() || IsTotem() || creatureInfo.FlagsExtra.HasFlag(CreatureFlagsExtra.NoXP));
_staticFlags.ApplyFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells, cInfo.TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit)); _staticFlags.ApplyFlag(CreatureStaticFlags4.TreatAsRaidUnitForHelpfulSpells, GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.TreatAsRaidUnit));
return true; return true;
} }
@@ -808,6 +785,8 @@ namespace Game.Entities
return false; return false;
} }
CreatureDifficulty creatureDifficulty = cinfo.GetDifficulty(GetMap().GetDifficultyID());
//! Relocate before CreateFromProto, to initialize coords and allow //! Relocate before CreateFromProto, to initialize coords and allow
//! returning correct zone id for selecting OutdoorPvP/Battlefield script //! returning correct zone id for selecting OutdoorPvP/Battlefield script
Relocate(pos); 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) // 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); m_serverSideVisibility.SetValue(ServerSideVisibilityType.Ghost, GhostVisibilityType.Alive | GhostVisibilityType.Ghost);
if (!CreateFromProto(guidlow, entry, data, vehId)) if (!CreateFromProto(guidlow, entry, data, vehId))
@@ -1050,7 +1029,7 @@ namespace Game.Entities
{ {
base.AtEngage(target); base.AtEngage(target);
if (!GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.AllowMountedCombat)) if (!GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.AllowMountedCombat))
Dismount(); Dismount();
RefreshCanSwimFlag(); RefreshCanSwimFlag();
@@ -1199,7 +1178,7 @@ namespace Game.Entities
if (_lootId.HasValue) if (_lootId.HasValue)
return _lootId.Value; return _lootId.Value;
return GetCreatureTemplate().LootId; return GetCreatureDifficulty().LootID;
} }
public void SetLootId(uint? lootId) public void SetLootId(uint? lootId)
@@ -1470,7 +1449,7 @@ namespace Game.Entities
// mana // mana
PowerType powerType = CalculateDisplayPowerType(); PowerType powerType = CalculateDisplayPowerType();
SetCreateMana(stats.BaseMana); 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); SetPowerType(powerType);
PowerTypeRecord powerTypeEntry = Global.DB2Mgr.GetPowerTypeEntry(powerType); PowerTypeRecord powerTypeEntry = Global.DB2Mgr.GetPowerTypeEntry(powerType);
@@ -2208,7 +2187,7 @@ namespace Game.Entities
if (IsPet()) if (IsPet())
return false; 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). // select nearest hostile unit within the given distance (regardless of threat list).
@@ -2462,7 +2441,7 @@ namespace Game.Entities
} }
// dependent from difficulty mode entry // dependent from difficulty mode entry
return Global.ObjectMgr.GetCreatureTemplateAddon(GetCreatureTemplate().Entry); return Global.ObjectMgr.GetCreatureTemplateAddon(GetEntry());
} }
public bool LoadCreaturesAddon() public bool LoadCreaturesAddon()
@@ -2716,29 +2695,29 @@ namespace Game.Entities
public void ApplyLevelScaling() public void ApplyLevelScaling()
{ {
CreatureLevelScaling scaling = GetCreatureTemplate().GetLevelScaling(GetMap().GetDifficultyID()); CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
var levels = Global.DB2Mgr.GetContentTuningData(scaling.ContentTuningID, 0); var levels = Global.DB2Mgr.GetContentTuningData(creatureDifficulty.ContentTuningID, 0);
if (levels.HasValue) 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.ScalingLevelMin), levels.Value.MinLevel);
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ScalingLevelMax), levels.Value.MaxLevel); SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.ScalingLevelMax), levels.Value.MaxLevel);
} }
int mindelta = Math.Min(scaling.DeltaLevelMax, scaling.DeltaLevelMin); int mindelta = Math.Min(creatureDifficulty.DeltaLevelMax, creatureDifficulty.DeltaLevelMin);
int maxdelta = Math.Max(scaling.DeltaLevelMax, scaling.DeltaLevelMin); int maxdelta = Math.Max(creatureDifficulty.DeltaLevelMax, creatureDifficulty.DeltaLevelMin);
int delta = mindelta == maxdelta ? mindelta : RandomHelper.IRand(mindelta, maxdelta); 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.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) ulong GetMaxHealthByLevel(uint level)
{ {
CreatureTemplate cInfo = GetCreatureTemplate(); CreatureTemplate cInfo = GetCreatureTemplate();
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID()); CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
float baseHealth = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass); 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) public override float GetHealthMultiplierForTarget(WorldObject target)
@@ -2756,8 +2735,8 @@ namespace Game.Entities
public float GetBaseDamageForLevel(uint level) public float GetBaseDamageForLevel(uint level)
{ {
CreatureTemplate cInfo = GetCreatureTemplate(); CreatureTemplate cInfo = GetCreatureTemplate();
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID()); CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
return Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureAutoAttackDps, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass); return Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureAutoAttackDps, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
} }
public override float GetDamageMultiplierForTarget(WorldObject target) public override float GetDamageMultiplierForTarget(WorldObject target)
@@ -2773,9 +2752,9 @@ namespace Game.Entities
float GetBaseArmorForLevel(uint level) float GetBaseArmorForLevel(uint level)
{ {
CreatureTemplate cInfo = GetCreatureTemplate(); CreatureTemplate cInfo = GetCreatureTemplate();
CreatureLevelScaling scaling = cInfo.GetLevelScaling(GetMap().GetDifficultyID()); CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
float baseArmor = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureArmor, level, cInfo.GetHealthScalingExpansion(), scaling.ContentTuningID, (Class)cInfo.UnitClass); float baseArmor = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureArmor, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
return baseArmor * cInfo.ModArmor; return baseArmor * creatureDifficulty.ArmorModifier;
} }
public override float GetArmorMultiplierForTarget(WorldObject target) public override float GetArmorMultiplierForTarget(WorldObject target)
@@ -3317,6 +3296,7 @@ namespace Game.Entities
public CreatureTemplate GetCreatureTemplate() { return m_creatureInfo; } public CreatureTemplate GetCreatureTemplate() { return m_creatureInfo; }
public CreatureData GetCreatureData() { return m_creatureData; } public CreatureData GetCreatureData() { return m_creatureData; }
public CreatureDifficulty GetCreatureDifficulty() { return m_creatureDifficulty; }
public override bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate) public override bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate)
{ {
+94 -90
View File
@@ -7,13 +7,13 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Game.Networking.Packets; using Game.Networking.Packets;
using Game.Maps; using Game.Maps;
using Game.DataStorage;
namespace Game.Entities namespace Game.Entities
{ {
public class CreatureTemplate public class CreatureTemplate
{ {
public uint Entry; public uint Entry;
public uint[] DifficultyEntry = new uint[SharedConst.MaxCreatureDifficulties];
public uint[] KillCredit = new uint[SharedConst.MaxCreatureKillCredit]; public uint[] KillCredit = new uint[SharedConst.MaxCreatureKillCredit];
public List<CreatureModel> Models = new(); public List<CreatureModel> Models = new();
public string Name; public string Name;
@@ -22,8 +22,7 @@ namespace Game.Entities
public string TitleAlt; public string TitleAlt;
public string IconName; public string IconName;
public List<uint> GossipMenuIds = new(); public List<uint> GossipMenuIds = new();
public Dictionary<Difficulty, CreatureLevelScaling> scalingStorage = new(); public Dictionary<Difficulty, CreatureDifficulty> difficultyStorage = new();
public int HealthScalingExpansion;
public uint RequiredExpansion; public uint RequiredExpansion;
public uint VignetteID; // @todo Read Vignette.db2 public uint VignetteID; // @todo Read Vignette.db2
public uint Faction; public uint Faction;
@@ -45,29 +44,15 @@ namespace Game.Entities
public CreatureFamily Family; public CreatureFamily Family;
public Class TrainerClass; public Class TrainerClass;
public CreatureType CreatureType; 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 int[] Resistance = new int[7];
public uint[] Spells = new uint[8]; public uint[] Spells = new uint[8];
public uint VehicleId; public uint VehicleId;
public uint MinGold;
public uint MaxGold;
public string AIName; public string AIName;
public uint MovementType; public uint MovementType;
public CreatureMovementData Movement = new(); 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 float ModExperience;
public bool RacialLeader; public bool RacialLeader;
public uint MovementId; public uint MovementId;
public int CreatureDifficultyID;
public int WidgetSetID; public int WidgetSetID;
public int WidgetSetUnitConditionID; public int WidgetSetUnitConditionID;
public bool RegenHealth; public bool RegenHealth;
@@ -77,7 +62,7 @@ namespace Game.Entities
public uint ScriptID; public uint ScriptID;
public string StringId; public string StringId;
public QueryCreatureResponse QueryData; public QueryCreatureResponse[] QueryData = new QueryCreatureResponse[(int)Locale.Total];
public CreatureModel GetModelByIdx(int idx) public CreatureModel GetModelByIdx(int idx)
{ {
@@ -142,74 +127,33 @@ namespace Game.Entities
return CreatureModel.DefaultVisibleModel; 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 bool IsTameable(bool canTameExotic, CreatureDifficulty creatureDifficulty)
public SkillType GetRequiredLootSkill()
{ {
if (TypeFlags.HasAnyFlag(CreatureTypeFlags.SkinWithHerbalism)) if (CreatureType != CreatureType.Beast || Family == CreatureFamily.None || !creatureDifficulty.TypeFlags.HasFlag(CreatureTypeFlags.Tameable))
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))
return false; return false;
// if can tame exotic then can tame any tameable // if can tame exotic then can tame any tameable
return canTameExotic || !IsExotic(); return canTameExotic || !IsExotic(creatureDifficulty);
}
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;
}
} }
public void InitializeQueryData() 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; public QueryCreatureResponse BuildQueryData(Locale locale, Difficulty difficulty)
QueryData.Allow = true; {
CreatureDifficulty creatureDifficulty = GetDifficulty(difficulty);
var queryTemp = new QueryCreatureResponse();
queryTemp.CreatureID = Entry;
queryTemp.Allow = true;
CreatureStats stats = new(); CreatureStats stats = new();
stats.Leader = RacialLeader; stats.Leader = RacialLeader;
@@ -217,8 +161,8 @@ namespace Game.Entities
stats.Name[0] = Name; stats.Name[0] = Name;
stats.NameAlt[0] = FemaleName; stats.NameAlt[0] = FemaleName;
stats.Flags[0] = (uint)TypeFlags; stats.Flags[0] = (uint)creatureDifficulty.TypeFlags;
stats.Flags[1] = TypeFlags2; stats.Flags[1] = creatureDifficulty.TypeFlags2;
stats.CreatureType = (int)CreatureType; stats.CreatureType = (int)CreatureType;
stats.CreatureFamily = (int)Family; stats.CreatureFamily = (int)Family;
@@ -233,15 +177,15 @@ namespace Game.Entities
stats.Display.CreatureDisplay.Add(new CreatureXDisplay(model.CreatureDisplayID, model.DisplayScale, model.Probability)); stats.Display.CreatureDisplay.Add(new CreatureXDisplay(model.CreatureDisplayID, model.DisplayScale, model.Probability));
} }
stats.HpMulti = ModHealth; stats.HpMulti = creatureDifficulty.HealthModifier;
stats.EnergyMulti = ModMana; stats.EnergyMulti = creatureDifficulty.ManaModifier;
stats.CreatureMovementInfoID = MovementId; stats.CreatureMovementInfoID = MovementId;
stats.RequiredExpansion = RequiredExpansion; stats.RequiredExpansion = RequiredExpansion;
stats.HealthScalingExpansion = HealthScalingExpansion; stats.HealthScalingExpansion = creatureDifficulty.HealthScalingExpansion;
stats.VignetteID = VignetteID; stats.VignetteID = VignetteID;
stats.Class = (int)UnitClass; stats.Class = (int)UnitClass;
stats.CreatureDifficultyID = CreatureDifficultyID; stats.CreatureDifficultyID = creatureDifficulty.CreatureDifficultyID;
stats.WidgetSetID = WidgetSetID; stats.WidgetSetID = WidgetSetID;
stats.WidgetSetUnitConditionID = WidgetSetUnitConditionID; stats.WidgetSetUnitConditionID = WidgetSetUnitConditionID;
@@ -249,20 +193,41 @@ namespace Game.Entities
stats.TitleAlt = TitleAlt; stats.TitleAlt = TitleAlt;
stats.CursorName = IconName; stats.CursorName = IconName;
var items = Global.ObjectMgr.GetCreatureQuestItemList(Entry); var items = Global.ObjectMgr.GetCreatureQuestItemList(Entry, difficulty);
if (items != null) if (items != null)
stats.QuestItems.AddRange(items); 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);
}
} }
public CreatureLevelScaling GetLevelScaling(Difficulty difficulty) queryTemp.Stats = stats;
{ return queryTemp;
var creatureLevelScaling = scalingStorage.LookupByKey(difficulty); }
if (creatureLevelScaling != null)
return creatureLevelScaling;
return new CreatureLevelScaling(); public CreatureDifficulty GetDifficulty(Difficulty difficulty)
{
var creatureDifficulty = difficultyStorage.LookupByKey(difficulty);
if (creatureDifficulty != null)
return creatureDifficulty;
// 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();
} }
} }
@@ -462,10 +427,49 @@ namespace Game.Entities
} }
} }
public class CreatureLevelScaling public class CreatureDifficulty
{ {
public short DeltaLevelMin; public short DeltaLevelMin;
public short DeltaLevelMax; public short DeltaLevelMax;
public uint ContentTuningID; 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(); Creature creatureTarget = target.ToCreature();
if (creatureTarget != null) 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) if (petInfo.Type == PetType.Hunter)
{ {
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(petInfo.CreatureId); CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(petInfo.CreatureId);
if (creatureInfo == null || !creatureInfo.IsTameable(owner.CanTameExoticPets())) if (creatureInfo == null || !creatureInfo.IsTameable(owner.CanTameExoticPets(), GetCreatureDifficulty()))
return false; return false;
} }
+2 -2
View File
@@ -5169,11 +5169,11 @@ namespace Game.Entities
return null; return null;
// Deathstate checks // Deathstate checks
if (!IsAlive() && !Convert.ToBoolean(creature.GetCreatureTemplate().TypeFlags & CreatureTypeFlags.VisibleToGhosts)) if (!IsAlive() && !creature.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.VisibleToGhosts))
return null; return null;
// alive or spirit healer // 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; return null;
// appropriate npc type // appropriate npc type
+1 -1
View File
@@ -2254,7 +2254,7 @@ namespace Game.Entities
float basePct = GetPctModifierValue(unitMod, UnitModifierPctType.Base) * attackSpeedMulti; float basePct = GetPctModifierValue(unitMod, UnitModifierPctType.Base) * attackSpeedMulti;
float totalValue = GetFlatModifierValue(unitMod, UnitModifierFlatType.Total); float totalValue = GetFlatModifierValue(unitMod, UnitModifierFlatType.Total);
float totalPct = addTotalPct ? GetPctModifierValue(unitMod, UnitModifierPctType.Total) : 1.0f; 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; minDamage = ((weaponMinDamage + baseValue) * dmgMultiplier * basePct + totalValue) * totalPct;
maxDamage = ((weaponMaxDamage + 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); CreatureBaseStats stats = Global.ObjectMgr.GetCreatureBaseStats(petlevel, cinfo.UnitClass);
ApplyLevelScaling(); 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); SetCreateMana(stats.BaseMana);
SetCreateStat(Stats.Strength, 22); SetCreateStat(Stats.Strength, 22);
+4 -4
View File
@@ -772,7 +772,7 @@ namespace Game.Entities
if (dungeonEncounter != null) if (dungeonEncounter != null)
{ {
creature.m_personalLoot = LootManager.GenerateDungeonEncounterPersonalLoot(dungeonEncounter.Id, creature.GetLootId(), 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); (ushort)creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext(), tappers);
} }
else if (!tappers.Empty()) 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()); loot.FillLoot(lootid, LootStorage.Creature, looter, dungeonEncounter != null, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
if (creature.GetLootMode() > 0) if (creature.GetLootMode() > 0)
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold); loot.GenerateMoneyLoot(creature.GetCreatureDifficulty().GoldMin, creature.GetCreatureDifficulty().GoldMax);
if (group) if (group)
loot.NotifyLootList(creature.GetMap()); loot.NotifyLootList(creature.GetMap());
@@ -814,7 +814,7 @@ namespace Game.Entities
loot.FillLoot(lootid, LootStorage.Creature, tapper, true, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext()); loot.FillLoot(lootid, LootStorage.Creature, tapper, true, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
if (creature.GetLootMode() > 0) 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; creature.m_personalLoot[tapper.GetGUID()] = loot;
} }
@@ -918,7 +918,7 @@ namespace Game.Entities
else else
creature.AllLootRemovedFromCorpse(); 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.SetDynamicFlag(UnitDynFlags.CanSkin);
creature.SetUnitFlag(UnitFlags.Skinnable); creature.SetUnitFlag(UnitFlags.Skinnable);
+181 -301
View File
@@ -25,14 +25,7 @@ namespace Game
{ {
public sealed class ObjectManager : Singleton<ObjectManager> public sealed class ObjectManager : Singleton<ObjectManager>
{ {
ObjectManager() ObjectManager() { }
{
for (var i = 0; i < SharedConst.MaxCreatureDifficulties; ++i)
{
difficultyEntries[i] = new List<uint>();
hasDifficultyEntries[i] = new List<uint>();
}
}
//Static Methods //Static Methods
public static bool NormalizePlayerName(ref string name) public static bool NormalizePlayerName(ref string name)
@@ -1792,44 +1785,35 @@ namespace Game
CreatureTemplate creature = new(); CreatureTemplate creature = new();
creature.Entry = entry; 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) 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.Name = fields.Read<string>(3);
creature.FemaleName = fields.Read<string>(7); creature.FemaleName = fields.Read<string>(4);
creature.SubName = fields.Read<string>(8); creature.SubName = fields.Read<string>(5);
creature.TitleAlt = fields.Read<string>(9); creature.TitleAlt = fields.Read<string>(6);
creature.IconName = fields.Read<string>(10); creature.IconName = fields.Read<string>(7);
creature.HealthScalingExpansion = fields.Read<int>(11); creature.RequiredExpansion = fields.Read<uint>(8);
creature.RequiredExpansion = fields.Read<uint>(12); creature.VignetteID = fields.Read<uint>(9);
creature.VignetteID = fields.Read<uint>(13); creature.Faction = fields.Read<uint>(10);
creature.Faction = fields.Read<uint>(14); creature.Npcflag = fields.Read<ulong>(11);
creature.Npcflag = fields.Read<ulong>(15); creature.SpeedWalk = fields.Read<float>(12);
creature.SpeedWalk = fields.Read<float>(16); creature.SpeedRun = fields.Read<float>(13);
creature.SpeedRun = fields.Read<float>(17); creature.Scale = fields.Read<float>(14);
creature.Scale = fields.Read<float>(18); creature.Rank = (CreatureEliteType)fields.Read<uint>(15);
creature.Rank = (CreatureEliteType)fields.Read<uint>(19); creature.DmgSchool = fields.Read<uint>(16);
creature.DmgSchool = fields.Read<uint>(20); creature.BaseAttackTime = fields.Read<uint>(17);
creature.BaseAttackTime = fields.Read<uint>(21); creature.RangeAttackTime = fields.Read<uint>(18);
creature.RangeAttackTime = fields.Read<uint>(22); creature.BaseVariance = fields.Read<float>(19);
creature.BaseVariance = fields.Read<float>(23); creature.RangeVariance = fields.Read<float>(20);
creature.RangeVariance = fields.Read<float>(24); creature.UnitClass = fields.Read<uint>(21);
creature.UnitClass = fields.Read<uint>(25); creature.UnitFlags = (UnitFlags)fields.Read<uint>(22);
creature.UnitFlags = (UnitFlags)fields.Read<uint>(26); creature.UnitFlags2 = fields.Read<uint>(23);
creature.UnitFlags2 = fields.Read<uint>(27); creature.UnitFlags3 = fields.Read<uint>(24);
creature.UnitFlags3 = fields.Read<uint>(28); creature.DynamicFlags = fields.Read<uint>(25);
creature.DynamicFlags = fields.Read<uint>(29); creature.Family = (CreatureFamily)fields.Read<uint>(26);
creature.Family = (CreatureFamily)fields.Read<uint>(30); creature.TrainerClass = (Class)fields.Read<byte>(27);
creature.TrainerClass = (Class)fields.Read<byte>(31); creature.CreatureType = (CreatureType)fields.Read<byte>(28);
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);
for (var i = (int)SpellSchools.Holy; i < (int)SpellSchools.Max; ++i) for (var i = (int)SpellSchools.Holy; i < (int)SpellSchools.Max; ++i)
creature.Resistance[i] = 0; creature.Resistance[i] = 0;
@@ -1837,51 +1821,42 @@ namespace Game
for (var i = 0; i < SharedConst.MaxCreatureSpells; ++i) for (var i = 0; i < SharedConst.MaxCreatureSpells; ++i)
creature.Spells[i] = 0; creature.Spells[i] = 0;
creature.VehicleId = fields.Read<uint>(38); creature.VehicleId = fields.Read<uint>(29);
creature.MinGold = fields.Read<uint>(39); creature.AIName = fields.Read<string>(30);
creature.MaxGold = fields.Read<uint>(40); creature.MovementType = fields.Read<uint>(31);
creature.AIName = fields.Read<string>(41);
creature.MovementType = fields.Read<uint>(42);
if (!fields.IsNull(43)) if (!fields.IsNull(32))
creature.Movement.Ground = (CreatureGroundMovementType)fields.Read<byte>(43); creature.Movement.Ground = (CreatureGroundMovementType)fields.Read<byte>(32);
if (!fields.IsNull(44)) if (!fields.IsNull(33))
creature.Movement.Swim = fields.Read<bool>(44); creature.Movement.Swim = fields.Read<bool>(33);
if (!fields.IsNull(45)) if (!fields.IsNull(34))
creature.Movement.Flight = (CreatureFlightMovementType)fields.Read<byte>(45); creature.Movement.Flight = (CreatureFlightMovementType)fields.Read<byte>(34);
if (!fields.IsNull(46)) if (!fields.IsNull(35))
creature.Movement.Rooted = fields.Read<bool>(46); creature.Movement.Rooted = fields.Read<bool>(35);
if (!fields.IsNull(47)) if (!fields.IsNull(36))
creature.Movement.Chase = (CreatureChaseMovementType)fields.Read<byte>(47); creature.Movement.Chase = (CreatureChaseMovementType)fields.Read<byte>(36);
if (!fields.IsNull(48)) if (!fields.IsNull(37))
creature.Movement.Random = (CreatureRandomMovementType)fields.Read<byte>(48); creature.Movement.Random = (CreatureRandomMovementType)fields.Read<byte>(37);
if (!fields.IsNull(49)) if (!fields.IsNull(38))
creature.Movement.InteractionPauseTimer = fields.Read<uint>(49); creature.Movement.InteractionPauseTimer = fields.Read<uint>(38);
creature.ModHealth = fields.Read<float>(50); creature.ModExperience = fields.Read<float>(39);
creature.ModHealthExtra = fields.Read<float>(51); creature.RacialLeader = fields.Read<bool>(40);
creature.ModMana = fields.Read<float>(52); creature.MovementId = fields.Read<uint>(41);
creature.ModManaExtra = fields.Read<float>(53); creature.WidgetSetID = fields.Read<int>(42);
creature.ModArmor = fields.Read<float>(54); creature.WidgetSetUnitConditionID = fields.Read<int>(43);
creature.ModDamage = fields.Read<float>(55); creature.RegenHealth = fields.Read<bool>(44);
creature.ModExperience = fields.Read<float>(56); creature.MechanicImmuneMask = fields.Read<ulong>(45);
creature.RacialLeader = fields.Read<bool>(57); creature.SpellSchoolImmuneMask = fields.Read<uint>(46);
creature.MovementId = fields.Read<uint>(58); creature.FlagsExtra = (CreatureFlagsExtra)fields.Read<uint>(47);
creature.CreatureDifficultyID = fields.Read<int>(59); creature.ScriptID = GetScriptId(fields.Read<string>(48));
creature.WidgetSetID = fields.Read<int>(60); creature.StringId = fields.Read<string>(49);
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);
creatureTemplateStorage[entry] = creature; creatureTemplateStorage[entry] = creature;
} }
@@ -2392,8 +2367,8 @@ namespace Game
{ {
uint oldMSTime = Time.GetMSTime(); uint oldMSTime = Time.GetMSTime();
// 0 1 2 // 0 1 2 3
SQLResult result = DB.World.Query("SELECT CreatureEntry, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC"); SQLResult result = DB.World.Query("SELECT CreatureEntry, DifficultyID, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
if (result.IsEmpty()) if (result.IsEmpty())
{ {
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature quest items. DB table `creature_questitem` is empty."); Log.outInfo(LogFilter.ServerLoading, "Loaded 0 creature quest items. DB table `creature_questitem` is empty.");
@@ -2404,22 +2379,23 @@ namespace Game
do do
{ {
uint entry = result.Read<uint>(0); uint entry = result.Read<uint>(0);
uint item = result.Read<uint>(1); Difficulty difficulty = (Difficulty)result.Read<byte>(1);
uint idx = result.Read<uint>(2); uint item = result.Read<uint>(2);
uint idx = result.Read<uint>(3);
if (!creatureTemplateStorage.ContainsKey(entry)) 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; continue;
} }
if (!CliDB.ItemStorage.ContainsKey(item)) 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; continue;
} }
creatureQuestItemStorage.Add(entry, item); creatureQuestItemStorage.Add((entry, difficulty), item);
++count; ++count;
} }
@@ -2709,15 +2685,15 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} creature template sparring rows in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); 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(); uint oldMSTime = Time.GetMSTime();
// 0 1 2 3 4 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
var result = DB.World.Query("SELECT Entry, DifficultyID, LevelScalingDeltaMin, LevelScalingDeltaMax, ContentTuningID FROM creature_template_scaling ORDER BY Entry"); 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()) 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; return;
} }
@@ -2730,196 +2706,55 @@ namespace Game
var template = creatureTemplateStorage.LookupByKey(entry); var template = creatureTemplateStorage.LookupByKey(entry);
if (template == null) 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; continue;
} }
CreatureLevelScaling creatureLevelScaling = new(); CreatureDifficulty creatureDifficulty = new();
creatureLevelScaling.DeltaLevelMin = result.Read<short>(2); creatureDifficulty.DeltaLevelMin = result.Read<short>(2);
creatureLevelScaling.DeltaLevelMax = result.Read<short>(3); creatureDifficulty.DeltaLevelMax = result.Read<short>(3);
creatureLevelScaling.ContentTuningID = result.Read<uint>(4); 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) if (creatureDifficulty.HealthScalingExpansion < (int)Expansion.LevelCurrent || creatureDifficulty.HealthScalingExpansion >= (int)Expansion.Max)
// TODO: Drop the use of creature difficulties
int difficultyIndex = CreatureTemplate.DifficultyIDToDifficultyEntryIndex((uint)difficulty);
if (difficultyIndex != -1)
{ {
uint difficultyEntry = template.DifficultyEntry[difficultyIndex]; Log.outError(LogFilter.Sql, $"Table `creature_template_difficulty` lists creature (ID: {entry}) with invalid `HealthScalingExpansion` {creatureDifficulty.HealthScalingExpansion}. Ignored and set to 0.");
if (difficultyEntry != 0) creatureDifficulty.HealthScalingExpansion = 0;
}
if (creatureDifficulty.GoldMin > creatureDifficulty.GoldMax)
{ {
var difficultyTemplate = creatureTemplateStorage.LookupByKey(difficultyEntry); 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}.");
if (difficultyTemplate != null) creatureDifficulty.GoldMax = creatureDifficulty.GoldMin;
difficultyTemplate.scalingStorage[difficulty] = creatureLevelScaling;
}
} }
template.difficultyStorage[difficulty] = creatureDifficulty;
++count; ++count;
} while (result.NextRow()); } 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) public void CheckCreatureTemplate(CreatureTemplate cInfo)
{ {
if (cInfo == null) if (cInfo == null)
return; 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)) 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); 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; 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) 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); 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.DynamicFlags = 0;
} }
cInfo.ModDamage *= Creature._GetDamageMod(cInfo.Rank);
if (!cInfo.GossipMenuIds.Empty() && !cInfo.Npcflag.HasAnyFlag((uint)NPCFlags.Gossip)) 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."); 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)) else if (cInfo.GossipMenuIds.Empty() && cInfo.Npcflag.HasAnyFlag((uint)NPCFlags.Gossip))
@@ -3694,18 +3521,6 @@ namespace Game
continue; 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, // -1 random, 0 no equipment,
if (data.equipmentId != 0) if (data.equipmentId != 0)
{ {
@@ -3934,39 +3749,43 @@ namespace Game
RemoveSpawnDataFromGrid(data); RemoveSpawnDataFromGrid(data);
} }
public List<uint> GetCreatureQuestItemList(uint id)
{
return creatureQuestItemStorage.LookupByKey(id);
}
public CreatureAddon GetCreatureAddon(ulong lowguid) public CreatureAddon GetCreatureAddon(ulong lowguid)
{ {
return creatureAddonStorage.LookupByKey(lowguid); return creatureAddonStorage.LookupByKey(lowguid);
} }
public CreatureTemplate GetCreatureTemplate(uint entry) public CreatureTemplate GetCreatureTemplate(uint entry)
{ {
return creatureTemplateStorage.LookupByKey(entry); return creatureTemplateStorage.LookupByKey(entry);
} }
public CreatureAddon GetCreatureTemplateAddon(uint entry) public CreatureAddon GetCreatureTemplateAddon(uint entry)
{ {
return creatureTemplateAddonStorage.LookupByKey(entry); return creatureTemplateAddonStorage.LookupByKey(entry);
} }
public uint GetCreatureDefaultTrainer(uint creatureId) public uint GetCreatureDefaultTrainer(uint creatureId)
{ {
return GetCreatureTrainerForGossipOption(creatureId, 0, 0); return GetCreatureTrainerForGossipOption(creatureId, 0, 0);
} }
public uint GetCreatureTrainerForGossipOption(uint creatureId, uint gossipMenuId, uint gossipOptionIndex) public uint GetCreatureTrainerForGossipOption(uint creatureId, uint gossipMenuId, uint gossipOptionIndex)
{ {
return _creatureDefaultTrainers.LookupByKey((creatureId, gossipMenuId, gossipOptionIndex)); return _creatureDefaultTrainers.LookupByKey((creatureId, gossipMenuId, gossipOptionIndex));
} }
public Dictionary<uint, CreatureTemplate> GetCreatureTemplates() public Dictionary<uint, CreatureTemplate> GetCreatureTemplates()
{ {
return creatureTemplateStorage; return creatureTemplateStorage;
} }
public Dictionary<ulong, CreatureData> GetAllCreatureData() { return creatureDataStorage; } public Dictionary<ulong, CreatureData> GetAllCreatureData() { return creatureDataStorage; }
public CreatureData GetCreatureData(ulong spawnId) public CreatureData GetCreatureData(ulong spawnId)
{ {
return creatureDataStorage.LookupByKey(spawnId); return creatureDataStorage.LookupByKey(spawnId);
} }
public ObjectGuid GetLinkedRespawnGuid(ObjectGuid spawnId) public ObjectGuid GetLinkedRespawnGuid(ObjectGuid spawnId)
{ {
var retGuid = linkedRespawnStorage.LookupByKey(spawnId); var retGuid = linkedRespawnStorage.LookupByKey(spawnId);
@@ -3974,6 +3793,7 @@ namespace Game
return ObjectGuid.Empty; return ObjectGuid.Empty;
return retGuid; return retGuid;
} }
public bool SetCreatureLinkedRespawn(ulong guidLow, ulong linkedGuidLow) public bool SetCreatureLinkedRespawn(ulong guidLow, ulong linkedGuidLow)
{ {
if (guidLow == 0) if (guidLow == 0)
@@ -4024,12 +3844,14 @@ namespace Game
DB.World.Execute(stmt); DB.World.Execute(stmt);
return true; return true;
} }
public CreatureData NewOrExistCreatureData(ulong spawnId) public CreatureData NewOrExistCreatureData(ulong spawnId)
{ {
if (!creatureDataStorage.ContainsKey(spawnId)) if (!creatureDataStorage.ContainsKey(spawnId))
creatureDataStorage[spawnId] = new CreatureData(); creatureDataStorage[spawnId] = new CreatureData();
return creatureDataStorage[spawnId]; return creatureDataStorage[spawnId];
} }
public void DeleteCreatureData(ulong spawnId) public void DeleteCreatureData(ulong spawnId)
{ {
CreatureData data = GetCreatureData(spawnId); CreatureData data = GetCreatureData(spawnId);
@@ -4041,6 +3863,7 @@ namespace Game
creatureDataStorage.Remove(spawnId); creatureDataStorage.Remove(spawnId);
} }
public CreatureBaseStats GetCreatureBaseStats(uint level, uint unitClass) public CreatureBaseStats GetCreatureBaseStats(uint level, uint unitClass)
{ {
var stats = creatureBaseStatsStorage.LookupByKey(MathFunctions.MakePair16(level, unitClass)); var stats = creatureBaseStatsStorage.LookupByKey(MathFunctions.MakePair16(level, unitClass));
@@ -4049,6 +3872,7 @@ namespace Game
return new DefaultCreatureBaseStats(); return new DefaultCreatureBaseStats();
} }
public CreatureModelInfo GetCreatureModelRandomGender(ref CreatureModel model, CreatureTemplate creatureTemplate) public CreatureModelInfo GetCreatureModelRandomGender(ref CreatureModel model, CreatureTemplate creatureTemplate)
{ {
CreatureModelInfo modelInfo = GetCreatureModelInfo(model.CreatureDisplayID); CreatureModelInfo modelInfo = GetCreatureModelInfo(model.CreatureDisplayID);
@@ -4081,14 +3905,17 @@ namespace Game
return modelInfo; return modelInfo;
} }
public CreatureModelInfo GetCreatureModelInfo(uint modelId) public CreatureModelInfo GetCreatureModelInfo(uint modelId)
{ {
return creatureModelStorage.LookupByKey(modelId); return creatureModelStorage.LookupByKey(modelId);
} }
public CreatureSummonedData GetCreatureSummonedData(uint entryId) public CreatureSummonedData GetCreatureSummonedData(uint entryId)
{ {
return creatureSummonedDataStorage.LookupByKey(entryId); return creatureSummonedDataStorage.LookupByKey(entryId);
} }
public NpcText GetNpcText(uint textId) public NpcText GetNpcText(uint textId)
{ {
return npcTextStorage.LookupByKey(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)); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} game object templates in {1} ms", _gameObjectTemplateStorage.Count, Time.GetMSTimeDiffToNow(time));
} }
} }
public void LoadGameObjectTemplateAddons() public void LoadGameObjectTemplateAddons()
{ {
uint oldMSTime = Time.GetMSTime(); 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)); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} game object template addons in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
} }
public void LoadGameObjectOverrides() public void LoadGameObjectOverrides()
{ {
uint oldMSTime = Time.GetMSTime(); 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"); Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} gameobject faction and flags overrides in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
} }
public void LoadGameObjects() public void LoadGameObjects()
{ {
var time = Time.GetMSTime(); var time = Time.GetMSTime();
@@ -4639,6 +4469,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} gameobjects in {1} ms", count, Time.GetMSTimeDiffToNow(time)); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} gameobjects in {1} ms", count, Time.GetMSTimeDiffToNow(time));
} }
public void LoadGameObjectAddons() public void LoadGameObjectAddons()
{ {
uint oldMSTime = Time.GetMSTime(); uint oldMSTime = Time.GetMSTime();
@@ -4710,6 +4541,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} gameobject addons in {Time.GetMSTimeDiffToNow(oldMSTime)} ms"); Log.outInfo(LogFilter.ServerLoading, $"Loaded {count} gameobject addons in {Time.GetMSTimeDiffToNow(oldMSTime)} ms");
} }
public void LoadGameObjectQuestItems() public void LoadGameObjectQuestItems()
{ {
uint oldMSTime = Time.GetMSTime(); 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)); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} gameobject quest items in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
} }
public void LoadGameObjectForQuests() public void LoadGameObjectForQuests()
{ {
uint oldMSTime = Time.GetMSTime(); 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)); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} GameObjects for quests in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
} }
public void AddGameObjectToGrid(GameObjectData data) public void AddGameObjectToGrid(GameObjectData data)
{ {
AddSpawnDataToGrid(data); AddSpawnDataToGrid(data);
} }
public void RemoveGameObjectFromGrid(GameObjectData data) public void RemoveGameObjectFromGrid(GameObjectData data)
{ {
RemoveSpawnDataFromGrid(data); RemoveSpawnDataFromGrid(data);
@@ -4827,16 +4662,21 @@ namespace Game
{ {
return _gameObjectAddonStorage.LookupByKey(lowguid); return _gameObjectAddonStorage.LookupByKey(lowguid);
} }
public List<uint> GetGameObjectQuestItemList(uint id) public List<uint> GetGameObjectQuestItemList(uint id)
{ {
return _gameObjectQuestItemStorage.LookupByKey(id); return _gameObjectQuestItemStorage.LookupByKey(id);
} }
MultiMap<uint, uint> GetGameObjectQuestItemMap() { return _gameObjectQuestItemStorage; } MultiMap<uint, uint> GetGameObjectQuestItemMap() { return _gameObjectQuestItemStorage; }
public Dictionary<ulong, GameObjectData> GetAllGameObjectData() { return gameObjectDataStorage; } public Dictionary<ulong, GameObjectData> GetAllGameObjectData() { return gameObjectDataStorage; }
public GameObjectData GetGameObjectData(ulong spawnId) public GameObjectData GetGameObjectData(ulong spawnId)
{ {
return gameObjectDataStorage.LookupByKey(spawnId); return gameObjectDataStorage.LookupByKey(spawnId);
} }
public void DeleteGameObjectData(ulong spawnId) public void DeleteGameObjectData(ulong spawnId)
{ {
GameObjectData data = GetGameObjectData(spawnId); GameObjectData data = GetGameObjectData(spawnId);
@@ -4848,6 +4688,7 @@ namespace Game
gameObjectDataStorage.Remove(spawnId); gameObjectDataStorage.Remove(spawnId);
} }
public GameObjectData NewOrExistGameObjectData(ulong spawnId) public GameObjectData NewOrExistGameObjectData(ulong spawnId)
{ {
if (!gameObjectDataStorage.ContainsKey(spawnId)) if (!gameObjectDataStorage.ContainsKey(spawnId))
@@ -4855,26 +4696,32 @@ namespace Game
return gameObjectDataStorage[spawnId]; return gameObjectDataStorage[spawnId];
} }
public GameObjectTemplate GetGameObjectTemplate(uint entry) public GameObjectTemplate GetGameObjectTemplate(uint entry)
{ {
return _gameObjectTemplateStorage.LookupByKey(entry); return _gameObjectTemplateStorage.LookupByKey(entry);
} }
public GameObjectTemplateAddon GetGameObjectTemplateAddon(uint entry) public GameObjectTemplateAddon GetGameObjectTemplateAddon(uint entry)
{ {
return _gameObjectTemplateAddonStorage.LookupByKey(entry); return _gameObjectTemplateAddonStorage.LookupByKey(entry);
} }
public GameObjectOverride GetGameObjectOverride(ulong spawnId) public GameObjectOverride GetGameObjectOverride(ulong spawnId)
{ {
return _gameObjectOverrideStorage.LookupByKey(spawnId); return _gameObjectOverrideStorage.LookupByKey(spawnId);
} }
public Dictionary<uint, GameObjectTemplate> GetGameObjectTemplates() public Dictionary<uint, GameObjectTemplate> GetGameObjectTemplates()
{ {
return _gameObjectTemplateStorage; return _gameObjectTemplateStorage;
} }
public bool IsGameObjectForQuests(uint entry) public bool IsGameObjectForQuests(uint entry)
{ {
return _gameObjectForQuestStorage.Contains(entry); return _gameObjectForQuestStorage.Contains(entry);
} }
void CheckGOLockId(GameObjectTemplate goInfo, uint dataN, uint N) void CheckGOLockId(GameObjectTemplate goInfo, uint dataN, uint N)
{ {
if (CliDB.LockStorage.ContainsKey(dataN)) 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); 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) void CheckGOLinkedTrapId(GameObjectTemplate goInfo, uint dataN, uint N)
{ {
GameObjectTemplate trapInfo = GetGameObjectTemplate(dataN); 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); 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) void CheckGOSpellId(GameObjectTemplate goInfo, uint dataN, uint N)
{ {
if (Global.SpellMgr.HasSpellInfo(dataN, Difficulty.None)) 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); 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) void CheckAndFixGOChairHeightId(GameObjectTemplate goInfo, ref uint dataN, uint N)
{ {
if (dataN <= (UnitStandStateType.SitHighChair - UnitStandStateType.SitLowChair)) if (dataN <= (UnitStandStateType.SitHighChair - UnitStandStateType.SitLowChair))
@@ -4908,6 +4758,7 @@ namespace Game
// prevent client and server unexpected work // prevent client and server unexpected work
dataN = 0; dataN = 0;
} }
void CheckGONoDamageImmuneId(GameObjectTemplate goTemplate, uint dataN, uint N) void CheckGONoDamageImmuneId(GameObjectTemplate goTemplate, uint dataN, uint N)
{ {
// 0/1 correct values // 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); 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) void CheckGOConsumable(GameObjectTemplate goInfo, uint dataN, uint N)
{ {
// 0/1 correct values // 0/1 correct values
@@ -5668,16 +5520,6 @@ namespace Game
continue; continue;
} }
creatureInfo.FlagsExtra |= CreatureFlagsExtra.DungeonBoss; 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; break;
} }
case EncounterCreditType.CastSpell: 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)); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quests definitions in {1} ms", _questTemplates.Count, Time.GetMSTimeDiffToNow(oldMSTime));
} }
public void LoadQuestStartersAndEnders() public void LoadQuestStartersAndEnders()
{ {
Log.outInfo(LogFilter.ServerLoading, "Loading GO Start Quest Data..."); Log.outInfo(LogFilter.ServerLoading, "Loading GO Start Quest Data...");
@@ -8098,6 +7941,7 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loading Creature End Quest Data..."); Log.outInfo(LogFilter.ServerLoading, "Loading Creature End Quest Data...");
LoadCreatureQuestEnders(); LoadCreatureQuestEnders();
} }
public void LoadGameobjectQuestStarters() public void LoadGameobjectQuestStarters()
{ {
LoadQuestRelationsHelper(_goQuestRelations, null, "gameobject_queststarter"); 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); 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() public void LoadGameobjectQuestEnders()
{ {
LoadQuestRelationsHelper(_goQuestInvolvedRelations, _goQuestInvolvedRelationsReverse, "gameobject_questender"); 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); 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() public void LoadCreatureQuestStarters()
{ {
LoadQuestRelationsHelper(_creatureQuestRelations, null, "creature_queststarter"); 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); 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() public void LoadCreatureQuestEnders()
{ {
LoadQuestRelationsHelper(_creatureQuestInvolvedRelations, _creatureQuestInvolvedRelationsReverse, "creature_questender"); 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); 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) void LoadQuestRelationsHelper(MultiMap<uint, uint> map, MultiMap<uint, uint> reverseMap, string table)
{ {
uint oldMSTime = Time.GetMSTime(); 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)); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest relations from {1} in {2} ms", count, table, Time.GetMSTimeDiffToNow(oldMSTime));
} }
public void LoadQuestPOI() public void LoadQuestPOI()
{ {
uint oldMSTime = Time.GetMSTime(); 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)); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest POI definitions in {1} ms", _questPOIStorage.Count, Time.GetMSTimeDiffToNow(oldMSTime));
} }
public void LoadQuestAreaTriggers() public void LoadQuestAreaTriggers()
{ {
uint oldMSTime = Time.GetMSTime(); 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)); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} quest trigger points in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
} }
public void LoadQuestGreetings() public void LoadQuestGreetings()
{ {
uint oldMSTime = Time.GetMSTime(); uint oldMSTime = Time.GetMSTime();
@@ -8381,34 +8232,48 @@ namespace Game
{ {
return _questTemplates.LookupByKey(questId); return _questTemplates.LookupByKey(questId);
} }
public Dictionary<uint, Quest> GetQuestTemplates() public Dictionary<uint, Quest> GetQuestTemplates()
{ {
return _questTemplates; return _questTemplates;
} }
public List<Quest> GetQuestTemplatesAutoPush() public List<Quest> GetQuestTemplatesAutoPush()
{ {
return _questTemplatesAutoPush; return _questTemplatesAutoPush;
} }
public MultiMap<uint, uint> GetGOQuestRelationMapHACK() { return _goQuestRelations; } public MultiMap<uint, uint> GetGOQuestRelationMapHACK() { return _goQuestRelations; }
public QuestRelationResult GetGOQuestRelations(uint entry) { return GetQuestRelationsFrom(_goQuestRelations, entry, true); } public QuestRelationResult GetGOQuestRelations(uint entry) { return GetQuestRelationsFrom(_goQuestRelations, entry, true); }
public QuestRelationResult GetGOQuestInvolvedRelations(uint entry) { return GetQuestRelationsFrom(_goQuestInvolvedRelations, entry, false); } public QuestRelationResult GetGOQuestInvolvedRelations(uint entry) { return GetQuestRelationsFrom(_goQuestInvolvedRelations, entry, false); }
public List<uint> GetGOQuestInvolvedRelationReverseBounds(uint questId) { return _goQuestInvolvedRelationsReverse.LookupByKey(questId); } public List<uint> GetGOQuestInvolvedRelationReverseBounds(uint questId) { return _goQuestInvolvedRelationsReverse.LookupByKey(questId); }
public MultiMap<uint, uint> GetCreatureQuestRelationMapHACK() { return _creatureQuestRelations; } public MultiMap<uint, uint> GetCreatureQuestRelationMapHACK() { return _creatureQuestRelations; }
public QuestRelationResult GetCreatureQuestRelations(uint entry) { return GetQuestRelationsFrom(_creatureQuestRelations, entry, true); } public QuestRelationResult GetCreatureQuestRelations(uint entry) { return GetQuestRelationsFrom(_creatureQuestRelations, entry, true); }
public QuestRelationResult GetCreatureQuestInvolvedRelations(uint entry) { return GetQuestRelationsFrom(_creatureQuestInvolvedRelations, entry, false); } public QuestRelationResult GetCreatureQuestInvolvedRelations(uint entry) { return GetQuestRelationsFrom(_creatureQuestInvolvedRelations, entry, false); }
public List<uint> GetCreatureQuestInvolvedRelationReverseBounds(uint questId) { return _creatureQuestInvolvedRelationsReverse.LookupByKey(questId); } public List<uint> GetCreatureQuestInvolvedRelationReverseBounds(uint questId) { return _creatureQuestInvolvedRelationsReverse.LookupByKey(questId); }
public QuestPOIData GetQuestPOIData(uint questId) public QuestPOIData GetQuestPOIData(uint questId)
{ {
return _questPOIStorage.LookupByKey(questId); return _questPOIStorage.LookupByKey(questId);
} }
public QuestObjective GetQuestObjective(uint questObjectiveId) public QuestObjective GetQuestObjective(uint questObjectiveId)
{ {
return _questObjectives.LookupByKey(questObjectiveId); return _questObjectives.LookupByKey(questObjectiveId);
} }
public List<uint> GetQuestsForAreaTrigger(uint triggerId) public List<uint> GetQuestsForAreaTrigger(uint triggerId)
{ {
return _questAreaTriggerStorage.LookupByKey(triggerId); return _questAreaTriggerStorage.LookupByKey(triggerId);
} }
public QuestGreeting GetQuestGreeting(TypeId type, uint id) public QuestGreeting GetQuestGreeting(TypeId type, uint id)
{ {
byte typeIndex; byte typeIndex;
@@ -8421,6 +8286,7 @@ namespace Game
return _questGreetingStorage[typeIndex].LookupByKey(id); return _questGreetingStorage[typeIndex].LookupByKey(id);
} }
public QuestGreetingLocale GetQuestGreetingLocale(TypeId type, uint id) public QuestGreetingLocale GetQuestGreetingLocale(TypeId type, uint id)
{ {
byte typeIndex; byte typeIndex;
@@ -8433,12 +8299,28 @@ namespace Game
return _questGreetingLocaleStorage[typeIndex].LookupByKey(id); return _questGreetingLocaleStorage[typeIndex].LookupByKey(id);
} }
public List<uint> GetExclusiveQuestGroupBounds(int exclusiveGroupId) public List<uint> GetExclusiveQuestGroupBounds(int exclusiveGroupId)
{ {
return _exclusiveQuestGroups.LookupByKey(exclusiveGroupId); return _exclusiveQuestGroups.LookupByKey(exclusiveGroupId);
} }
QuestRelationResult GetQuestRelationsFrom(MultiMap<uint, uint> map, uint key, bool onlyActive) { return new QuestRelationResult(map.LookupByKey(key), onlyActive); } 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 //Spells /Skills / Phases
public void LoadPhases() public void LoadPhases()
{ {
@@ -11039,7 +10921,7 @@ namespace Game
Dictionary<uint, CreatureSummonedData> creatureSummonedDataStorage = new(); Dictionary<uint, CreatureSummonedData> creatureSummonedDataStorage = new();
Dictionary<ulong, CreatureData> creatureDataStorage = new(); Dictionary<ulong, CreatureData> creatureDataStorage = new();
Dictionary<ulong, CreatureAddon> creatureAddonStorage = new(); Dictionary<ulong, CreatureAddon> creatureAddonStorage = new();
MultiMap<uint, uint> creatureQuestItemStorage = new(); MultiMap<(uint, Difficulty), uint> creatureQuestItemStorage = new();
Dictionary<uint, CreatureAddon> creatureTemplateAddonStorage = new(); Dictionary<uint, CreatureAddon> creatureTemplateAddonStorage = new();
MultiMap<uint, float> _creatureTemplateSparringStorage = new(); MultiMap<uint, float> _creatureTemplateSparringStorage = new();
Dictionary<ulong, CreatureMovementData> creatureMovementOverrides = new(); Dictionary<ulong, CreatureMovementData> creatureMovementOverrides = new();
@@ -11049,8 +10931,6 @@ namespace Game
Dictionary<uint, VendorItemData> cacheVendorItemStorage = new(); Dictionary<uint, VendorItemData> cacheVendorItemStorage = new();
Dictionary<uint, Trainer> trainers = new(); Dictionary<uint, Trainer> trainers = new();
Dictionary<(uint creatureId, uint gossipMenuId, uint gossipOptionIndex), uint> _creatureDefaultTrainers = 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(); Dictionary<uint, NpcText> npcTextStorage = new();
//GameObject //GameObject
+2 -2
View File
@@ -458,7 +458,7 @@ namespace Game
if (dstPet != null) if (dstPet != null)
{ {
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(dstPet.CreatureId); 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); SendPetStableResult(StableResult.CantControlExotic);
return; return;
@@ -484,7 +484,7 @@ namespace Game
} }
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(srcPet.CreatureId); 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); SendPetStableResult(StableResult.CantControlExotic);
return; return;
+7 -22
View File
@@ -99,32 +99,17 @@ namespace Game
CreatureTemplate ci = Global.ObjectMgr.GetCreatureTemplate(packet.CreatureID); CreatureTemplate ci = Global.ObjectMgr.GetCreatureTemplate(packet.CreatureID);
if (ci != null) if (ci != null)
{ {
if (!WorldConfig.GetBoolValue(WorldCfg.CacheDataQueries)) Difficulty difficulty = _player.GetMap().GetDifficultyID();
ci.InitializeQueryData();
QueryCreatureResponse queryCreatureResponse = ci.QueryData; // Cache only exists for difficulty base
if (!WorldConfig.GetBoolValue(WorldCfg.CacheDataQueries) && difficulty == Difficulty.None)
Locale loc = GetSessionDbLocaleIndex(); SendPacket(ci.QueryData[(int)GetSessionDbLocaleIndex()]);
if (loc != Locale.enUS) else
{ {
CreatureLocale creatureLocale = Global.ObjectMgr.GetCreatureLocale(ci.Entry); var response = ci.BuildQueryData(GetSessionDbLocaleIndex(), difficulty);
if (creatureLocale != null) SendPacket(response);
{
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;
} }
} }
SendPacket(queryCreatureResponse);
}
else else
{ {
Log.outDebug(LogFilter.Network, $"WORLD: CMSG_QUERY_CREATURE - NO CREATURE INFO! (ENTRY: {packet.CreatureID})"); Log.outDebug(LogFilter.Network, $"WORLD: CMSG_QUERY_CREATURE - NO CREATURE INFO! (ENTRY: {packet.CreatureID})");
+27 -21
View File
@@ -97,16 +97,18 @@ namespace Game.Loots
uint count = Creature.LoadAndCollectLootIds(out lootIdSet); uint count = Creature.LoadAndCollectLootIds(out lootIdSet);
// Remove real entries and check loot existence // Remove real entries and check loot existence
var ctc = Global.ObjectMgr.GetCreatureTemplates(); var templates = Global.ObjectMgr.GetCreatureTemplates();
foreach (var pair in ctc) foreach (var creatureTemplate in templates.Values)
{ {
uint lootid = pair.Value.LootId; foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
if (lootid != 0)
{ {
if (!lootIdSet.Contains(lootid)) if (creatureDifficulty.LootID != 0)
Creature.ReportNonExistingId(lootid, pair.Value.Entry); {
if (!lootIdSet.Contains(creatureDifficulty.LootID))
Creature.ReportNonExistingId(creatureDifficulty.LootID, creatureTemplate.Entry);
else else
lootIdSetUsed.Add(lootid); lootIdSetUsed.Add(creatureDifficulty.LootID);
}
} }
} }
@@ -289,16 +291,18 @@ namespace Game.Loots
uint count = Pickpocketing.LoadAndCollectLootIds(out lootIdSet); uint count = Pickpocketing.LoadAndCollectLootIds(out lootIdSet);
// Remove real entries and check loot existence // Remove real entries and check loot existence
var ctc = Global.ObjectMgr.GetCreatureTemplates(); var templates = Global.ObjectMgr.GetCreatureTemplates();
foreach (var pair in ctc) foreach (var creatureTemplate in templates.Values)
{ {
uint lootid = pair.Value.PickPocketId; foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
if (lootid != 0)
{ {
if (!lootIdSet.Contains(lootid)) if (creatureDifficulty.PickPocketLootID != 0)
Pickpocketing.ReportNonExistingId(lootid, pair.Value.Entry); {
if (!lootIdSet.Contains(creatureDifficulty.PickPocketLootID))
Pickpocketing.ReportNonExistingId(creatureDifficulty.PickPocketLootID, creatureTemplate.Entry);
else else
lootIdSetUsed.Add(lootid); lootIdSetUsed.Add(creatureDifficulty.PickPocketLootID);
}
} }
} }
@@ -377,16 +381,18 @@ namespace Game.Loots
uint count = Skinning.LoadAndCollectLootIds(out lootIdSet); uint count = Skinning.LoadAndCollectLootIds(out lootIdSet);
// remove real entries and check existence loot // remove real entries and check existence loot
var ctc = Global.ObjectMgr.GetCreatureTemplates(); var templates = Global.ObjectMgr.GetCreatureTemplates();
foreach (var pair in ctc) foreach (var creatureTemplate in templates.Values)
{ {
uint lootid = pair.Value.SkinLootId; foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
if (lootid != 0)
{ {
if (!lootIdSet.Contains(lootid)) if (creatureDifficulty.SkinLootID != 0)
Skinning.ReportNonExistingId(lootid, pair.Value.Entry); {
if (!lootIdSet.Contains(creatureDifficulty.SkinLootID))
Skinning.ReportNonExistingId(creatureDifficulty.SkinLootID, creatureTemplate.Entry);
else else
lootIdSetUsed.Add(lootid); lootIdSetUsed.Add(creatureDifficulty.SkinLootID);
}
} }
} }
+1 -1
View File
@@ -147,7 +147,7 @@ namespace Game
if (gain != 0 && creature) if (gain != 0 && creature)
{ {
// Players get only 10% xp for killing creatures of lower expansion levels than himself // 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); gain = (uint)Math.Round(gain / 10.0f);
if (creature.IsElite()) if (creature.IsElite())
@@ -1245,32 +1245,32 @@ namespace Game.Networking.Packets
bool GenerateDataCreatureToPlayer(Creature attacker, Player target) bool GenerateDataCreatureToPlayer(Creature attacker, Player target)
{ {
CreatureTemplate creatureTemplate = attacker.GetCreatureTemplate(); CreatureTemplate creatureTemplate = attacker.GetCreatureTemplate();
CreatureLevelScaling creatureScaling = creatureTemplate.GetLevelScaling(attacker.GetMap().GetDifficultyID()); CreatureDifficulty creatureDifficulty = creatureTemplate.GetDifficulty(attacker.GetMap().GetDifficultyID());
TuningType = ContentTuningType.CreatureToPlayerDamage; TuningType = ContentTuningType.CreatureToPlayerDamage;
PlayerLevelDelta = (short)target.m_activePlayerData.ScalingPlayerLevelDelta; PlayerLevelDelta = (short)target.m_activePlayerData.ScalingPlayerLevelDelta;
PlayerItemLevel = (ushort)target.GetAverageItemLevel(); PlayerItemLevel = (ushort)target.GetAverageItemLevel();
ScalingHealthItemLevelCurveID = (ushort)target.m_unitData.ScalingHealthItemLevelCurveID; ScalingHealthItemLevelCurveID = (ushort)target.m_unitData.ScalingHealthItemLevelCurveID;
TargetLevel = (byte)target.GetLevel(); TargetLevel = (byte)target.GetLevel();
Expansion = (byte)creatureTemplate.HealthScalingExpansion; Expansion = (byte)creatureDifficulty.HealthScalingExpansion;
TargetScalingLevelDelta = (sbyte)attacker.m_unitData.ScalingLevelDelta; TargetScalingLevelDelta = (sbyte)attacker.m_unitData.ScalingLevelDelta;
TargetContentTuningID = creatureScaling.ContentTuningID; TargetContentTuningID = creatureDifficulty.ContentTuningID;
return true; return true;
} }
bool GenerateDataPlayerToCreature(Player attacker, Creature target) bool GenerateDataPlayerToCreature(Player attacker, Creature target)
{ {
CreatureTemplate creatureTemplate = target.GetCreatureTemplate(); CreatureTemplate creatureTemplate = target.GetCreatureTemplate();
CreatureLevelScaling creatureScaling = creatureTemplate.GetLevelScaling(target.GetMap().GetDifficultyID()); CreatureDifficulty creatureDifficulty = creatureTemplate.GetDifficulty(target.GetMap().GetDifficultyID());
TuningType = ContentTuningType.PlayerToCreatureDamage; TuningType = ContentTuningType.PlayerToCreatureDamage;
PlayerLevelDelta = (short)attacker.m_activePlayerData.ScalingPlayerLevelDelta; PlayerLevelDelta = (short)attacker.m_activePlayerData.ScalingPlayerLevelDelta;
PlayerItemLevel = (ushort)attacker.GetAverageItemLevel(); PlayerItemLevel = (ushort)attacker.GetAverageItemLevel();
ScalingHealthItemLevelCurveID = (ushort)target.m_unitData.ScalingHealthItemLevelCurveID; ScalingHealthItemLevelCurveID = (ushort)target.m_unitData.ScalingHealthItemLevelCurveID;
TargetLevel = (byte)target.GetLevel(); TargetLevel = (byte)target.GetLevel();
Expansion = (byte)creatureTemplate.HealthScalingExpansion; Expansion = (byte)creatureDifficulty.HealthScalingExpansion;
TargetScalingLevelDelta = (sbyte)target.m_unitData.ScalingLevelDelta; TargetScalingLevelDelta = (sbyte)target.m_unitData.ScalingLevelDelta;
TargetContentTuningID = creatureScaling.ContentTuningID; TargetContentTuningID = creatureDifficulty.ContentTuningID;
return true; return true;
} }
@@ -1278,15 +1278,15 @@ namespace Game.Networking.Packets
{ {
Creature accessor = target.HasScalableLevels() ? target : attacker; Creature accessor = target.HasScalableLevels() ? target : attacker;
CreatureTemplate creatureTemplate = accessor.GetCreatureTemplate(); CreatureTemplate creatureTemplate = accessor.GetCreatureTemplate();
CreatureLevelScaling creatureScaling = creatureTemplate.GetLevelScaling(accessor.GetMap().GetDifficultyID()); CreatureDifficulty creatureDifficulty = creatureTemplate.GetDifficulty(accessor.GetMap().GetDifficultyID());
TuningType = ContentTuningType.CreatureToCreatureDamage; TuningType = ContentTuningType.CreatureToCreatureDamage;
PlayerLevelDelta = 0; PlayerLevelDelta = 0;
PlayerItemLevel = 0; PlayerItemLevel = 0;
TargetLevel = (byte)target.GetLevel(); TargetLevel = (byte)target.GetLevel();
Expansion = (byte)creatureTemplate.HealthScalingExpansion; Expansion = (byte)creatureDifficulty.HealthScalingExpansion;
TargetScalingLevelDelta = (sbyte)accessor.m_unitData.ScalingLevelDelta; TargetScalingLevelDelta = (sbyte)accessor.m_unitData.ScalingLevelDelta;
TargetContentTuningID = creatureScaling.ContentTuningID; TargetContentTuningID = creatureDifficulty.ContentTuningID;
return true; return true;
} }
+4 -3
View File
@@ -1402,7 +1402,7 @@ namespace Game.Spells
Creature creatureTarget = unitTarget.ToCreature(); Creature creatureTarget = unitTarget.ToCreature();
if (creatureTarget) if (creatureTarget)
{ {
if (!creatureTarget.GetCreatureTemplate().TypeFlags.HasAnyFlag(CreatureTypeFlags.CollideWithMissiles)) if (!creatureTarget.GetCreatureDifficulty().TypeFlags.HasFlag(CreatureTypeFlags.CollideWithMissiles))
continue; continue;
} }
} }
@@ -5405,10 +5405,11 @@ namespace Game.Spells
if (info.Item1.Type == PetType.Hunter) if (info.Item1.Type == PetType.Hunter)
{ {
CreatureTemplate creatureInfo = Global.ObjectMgr.GetCreatureTemplate(info.Item1.CreatureId); 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 problem in exotic pet
if (creatureInfo != null && creatureInfo.IsTameable(true)) if (creatureInfo != null && creatureInfo.IsTameable(true, creatureDifficulty))
playerCaster.SendTameFailure(PetTameResult.CantControlExotic); playerCaster.SendTameFailure(PetTameResult.CantControlExotic);
else else
playerCaster.SendTameFailure(PetTameResult.NoPetAvailable); playerCaster.SendTameFailure(PetTameResult.NoPetAvailable);
+3 -3
View File
@@ -1819,7 +1819,7 @@ namespace Game.Spells
creature.StartPickPocketRefillTimer(); creature.StartPickPocketRefillTimer();
creature._loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Pickpocketing, null); creature._loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Pickpocketing, null);
uint lootid = creature.GetCreatureTemplate().PickPocketId; uint lootid = creature.GetCreatureDifficulty().PickPocketLootID;
if (lootid != 0) if (lootid != 0)
creature._loot.FillLoot(lootid, LootStorage.Pickpocketing, player, true); creature._loot.FillLoot(lootid, LootStorage.Pickpocketing, player, true);
@@ -3442,13 +3442,13 @@ namespace Game.Spells
Creature creature = unitTarget.ToCreature(); Creature creature = unitTarget.ToCreature();
int targetLevel = (int)creature.GetLevelForTarget(m_caster); int targetLevel = (int)creature.GetLevelForTarget(m_caster);
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill(); SkillType skill = creature.GetCreatureDifficulty().GetRequiredLootSkill();
creature.SetUnitFlag3(UnitFlags3.AlreadySkinned); creature.SetUnitFlag3(UnitFlags3.AlreadySkinned);
creature.SetDynamicFlag(UnitDynFlags.Lootable); creature.SetDynamicFlag(UnitDynFlags.Lootable);
Loot loot = new(creature.GetMap(), creature.GetGUID(), LootType.Skinning, null); Loot loot = new(creature.GetMap(), creature.GetGUID(), LootType.Skinning, null);
creature.m_personalLoot[player.GetGUID()] = loot; 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); player.SendLoot(loot);
if (!Global.SpellMgr.IsPartOfSkillLine(skill, m_spellInfo.Id)) if (!Global.SpellMgr.IsPartOfSkillLine(skill, m_spellInfo.Id))
+1 -1
View File
@@ -1032,7 +1032,7 @@ namespace Game.Spells
if (targetCreature == null) if (targetCreature == null)
return SpellCastResult.BadTargets; 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; return SpellCastResult.TargetNoPockets;
} }
+3 -3
View File
@@ -606,12 +606,12 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template addons..."); Log.outInfo(LogFilter.ServerLoading, "Loading Creature template addons...");
Global.ObjectMgr.LoadCreatureTemplateAddons(); Global.ObjectMgr.LoadCreatureTemplateAddons();
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template difficulty...");
Global.ObjectMgr.LoadCreatureTemplateDifficulty();
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template sparring..."); Log.outInfo(LogFilter.ServerLoading, "Loading Creature template sparring...");
Global.ObjectMgr.LoadCreatureTemplateSparring(); Global.ObjectMgr.LoadCreatureTemplateSparring();
Log.outInfo(LogFilter.ServerLoading, "Loading Creature template scaling...");
Global.ObjectMgr.LoadCreatureScalingData();
Log.outInfo(LogFilter.ServerLoading, "Loading Reputation Reward Rates..."); Log.outInfo(LogFilter.ServerLoading, "Loading Reputation Reward Rates...");
Global.ObjectMgr.LoadReputationRewardRate(); Global.ObjectMgr.LoadReputationRewardRate();
+1 -1
View File
@@ -454,7 +454,7 @@ namespace Scripts.Spells.Hunter
return SpellCastResult.Highlevel; return SpellCastResult.Highlevel;
// use SMSG_PET_TAME_FAILURE? // use SMSG_PET_TAME_FAILURE?
if (!target.GetCreatureTemplate().IsTameable(caster.CanTameExoticPets())) if (!target.GetCreatureTemplate().IsTameable(caster.CanTameExoticPets(), target.GetCreatureDifficulty()))
return SpellCastResult.BadTargets; return SpellCastResult.BadTargets;
PetStable petStable = caster.GetPetStable(); PetStable petStable = caster.GetPetStable();
+3 -3
View File
@@ -7,7 +7,6 @@ using Game.BattleGrounds;
using Game.DataStorage; using Game.DataStorage;
using Game.Entities; using Game.Entities;
using Game.Loots; using Game.Loots;
using Game.Networking.Packets;
using Game.Scripting; using Game.Scripting;
using Game.Spells; using Game.Spells;
using System; using System;
@@ -1322,11 +1321,12 @@ namespace Scripts.Spells.Items
{ {
Player player = GetCaster().ToPlayer(); Player player = GetCaster().ToPlayer();
Creature creature = GetTarget().ToCreature(); Creature creature = GetTarget().ToCreature();
CreatureDifficulty creatureDifficulty = creature.GetCreatureDifficulty();
// missing lootid has been reported on startup - just return // missing lootid has been reported on startup - just return
if (creature.GetCreatureTemplate().SkinLootId == 0) if (creatureDifficulty.SkinLootID == 0)
return; return;
player.AutoStoreLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, ItemContext.None, true); player.AutoStoreLoot(creatureDifficulty.SkinLootID, LootStorage.Skinning, ItemContext.None, true);
creature.DespawnOrUnsummon(); creature.DespawnOrUnsummon();
} }
} }