Core/Creatures: Compute the level from ContentTuning

Port From (https://github.com/TrinityCore/TrinityCore/commit/562f65f3f8f271fc52cc428c71a562af22829dc0)
This commit is contained in:
hondacrx
2023-04-21 18:49:58 -04:00
parent b7a8a88a76
commit 8828e94482
5 changed files with 87 additions and 114 deletions
@@ -31,6 +31,7 @@ namespace Framework.Constants
public const int MaxAzeriteEssenceSlot = 4; public const int MaxAzeriteEssenceSlot = 4;
public const int MaxAzeriteEssenceRank = 4; public const int MaxAzeriteEssenceRank = 4;
public const int AchivementCategoryPetBattles = 15117; public const int AchivementCategoryPetBattles = 15117;
public const int MaxUnitClasses = 4;
/// <summary> /// <summary>
/// BattlePets Const /// BattlePets Const
@@ -61,7 +61,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, gossip_menu_id, minlevel, maxlevel, 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, HoverHeight, 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, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, gossip_menu_id, 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, HoverHeight, 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_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_");
+4 -9
View File
@@ -1425,16 +1425,11 @@ namespace Game.Entities
public void SelectLevel() public void SelectLevel()
{ {
CreatureTemplate cInfo = GetCreatureTemplate(); // Level
// level
var minMaxLevels = cInfo.GetMinMaxLevel();
int minlevel = Math.Min(minMaxLevels[0], minMaxLevels[1]);
int maxlevel = Math.Max(minMaxLevels[0], minMaxLevels[1]);
int level = (minlevel == maxlevel ? minlevel : RandomHelper.IRand(minlevel, maxlevel));
SetLevel((uint)level);
ApplyLevelScaling(); ApplyLevelScaling();
int levelWithDelta = m_unitData.ScalingLevelMax + m_unitData.ScalingLevelDelta;
byte level = (byte)MathFunctions.RoundToInterval(ref levelWithDelta, 1, SharedConst.StrongMaxLevel);
SetLevel(level);
UpdateLevelDependantStats(); UpdateLevelDependantStats();
} }
@@ -22,9 +22,7 @@ namespace Game.Entities
public string TitleAlt; public string TitleAlt;
public string IconName; public string IconName;
public uint GossipMenuId; public uint GossipMenuId;
public short Minlevel;
public Dictionary<Difficulty, CreatureLevelScaling> scalingStorage = new(); public Dictionary<Difficulty, CreatureLevelScaling> scalingStorage = new();
public short Maxlevel;
public int HealthScalingExpansion; public int HealthScalingExpansion;
public uint RequiredExpansion; public uint RequiredExpansion;
public uint VignetteID; // @todo Read Vignette.db2 public uint VignetteID; // @todo Read Vignette.db2
@@ -145,15 +143,6 @@ namespace Game.Entities
return CreatureModel.DefaultVisibleModel; return CreatureModel.DefaultVisibleModel;
} }
public int[] GetMinMaxLevel()
{
return new[]
{
HealthScalingExpansion != (int)Expansion.LevelCurrent ? Minlevel : Minlevel + SharedConst.MaxLevel,
HealthScalingExpansion != (int)Expansion.LevelCurrent ? Maxlevel : Maxlevel + SharedConst.MaxLevel
};
}
public int GetHealthScalingExpansion() public int GetHealthScalingExpansion()
{ {
return HealthScalingExpansion == (int)Expansion.LevelCurrent ? (int)Expansion.WarlordsOfDraenor : HealthScalingExpansion; return HealthScalingExpansion == (int)Expansion.LevelCurrent ? (int)Expansion.WarlordsOfDraenor : HealthScalingExpansion;
+81 -93
View File
@@ -1823,35 +1823,33 @@ namespace Game
creature.TitleAlt = fields.Read<string>(9); creature.TitleAlt = fields.Read<string>(9);
creature.IconName = fields.Read<string>(10); creature.IconName = fields.Read<string>(10);
creature.GossipMenuId = fields.Read<uint>(11); creature.GossipMenuId = fields.Read<uint>(11);
creature.Minlevel = fields.Read<short>(12); creature.HealthScalingExpansion = fields.Read<int>(12);
creature.Maxlevel = fields.Read<short>(13); creature.RequiredExpansion = fields.Read<uint>(13);
creature.HealthScalingExpansion = fields.Read<int>(14); creature.VignetteID = fields.Read<uint>(14);
creature.RequiredExpansion = fields.Read<uint>(15); creature.Faction = fields.Read<uint>(15);
creature.VignetteID = fields.Read<uint>(16); creature.Npcflag = fields.Read<ulong>(16);
creature.Faction = fields.Read<uint>(17); creature.SpeedWalk = fields.Read<float>(17);
creature.Npcflag = fields.Read<ulong>(18); creature.SpeedRun = fields.Read<float>(18);
creature.SpeedWalk = fields.Read<float>(19); creature.Scale = fields.Read<float>(19);
creature.SpeedRun = fields.Read<float>(20); creature.Rank = (CreatureEliteType)fields.Read<uint>(20);
creature.Scale = fields.Read<float>(21); creature.DmgSchool = fields.Read<uint>(21);
creature.Rank = (CreatureEliteType)fields.Read<uint>(22); creature.BaseAttackTime = fields.Read<uint>(22);
creature.DmgSchool = fields.Read<uint>(23); creature.RangeAttackTime = fields.Read<uint>(23);
creature.BaseAttackTime = fields.Read<uint>(24); creature.BaseVariance = fields.Read<float>(24);
creature.RangeAttackTime = fields.Read<uint>(25); creature.RangeVariance = fields.Read<float>(25);
creature.BaseVariance = fields.Read<float>(26); creature.UnitClass = fields.Read<uint>(26);
creature.RangeVariance = fields.Read<float>(27); creature.UnitFlags = (UnitFlags)fields.Read<uint>(27);
creature.UnitClass = fields.Read<uint>(28); creature.UnitFlags2 = fields.Read<uint>(28);
creature.UnitFlags = (UnitFlags)fields.Read<uint>(29); creature.UnitFlags3 = fields.Read<uint>(29);
creature.UnitFlags2 = fields.Read<uint>(30); creature.DynamicFlags = fields.Read<uint>(30);
creature.UnitFlags3 = fields.Read<uint>(31); creature.Family = (CreatureFamily)fields.Read<uint>(31);
creature.DynamicFlags = fields.Read<uint>(32); creature.TrainerClass = (Class)fields.Read<byte>(32);
creature.Family = (CreatureFamily)fields.Read<uint>(33); creature.CreatureType = (CreatureType)fields.Read<byte>(33);
creature.TrainerClass = (Class)fields.Read<byte>(34); creature.TypeFlags = (CreatureTypeFlags)fields.Read<uint>(34);
creature.CreatureType = (CreatureType)fields.Read<byte>(35); creature.TypeFlags2 = fields.Read<uint>(35);
creature.TypeFlags = (CreatureTypeFlags)fields.Read<uint>(36); creature.LootId = fields.Read<uint>(36);
creature.TypeFlags2 = fields.Read<uint>(37); creature.PickPocketId = fields.Read<uint>(37);
creature.LootId = fields.Read<uint>(38); creature.SkinLootId = fields.Read<uint>(38);
creature.PickPocketId = fields.Read<uint>(39);
creature.SkinLootId = fields.Read<uint>(40);
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;
@@ -1859,52 +1857,52 @@ 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>(41); creature.VehicleId = fields.Read<uint>(39);
creature.MinGold = fields.Read<uint>(42); creature.MinGold = fields.Read<uint>(40);
creature.MaxGold = fields.Read<uint>(43); creature.MaxGold = fields.Read<uint>(41);
creature.AIName = fields.Read<string>(44); creature.AIName = fields.Read<string>(42);
creature.MovementType = fields.Read<uint>(45); creature.MovementType = fields.Read<uint>(43);
if (!fields.IsNull(44))
creature.Movement.Ground = (CreatureGroundMovementType)fields.Read<byte>(44);
if (!fields.IsNull(45))
creature.Movement.Swim = fields.Read<bool>(45);
if (!fields.IsNull(46)) if (!fields.IsNull(46))
creature.Movement.Ground = (CreatureGroundMovementType)fields.Read<byte>(46); creature.Movement.Flight = (CreatureFlightMovementType)fields.Read<byte>(46);
if (!fields.IsNull(47)) if (!fields.IsNull(47))
creature.Movement.Swim = fields.Read<bool>(47); creature.Movement.Rooted = fields.Read<bool>(47);
if (!fields.IsNull(48)) if (!fields.IsNull(48))
creature.Movement.Flight = (CreatureFlightMovementType)fields.Read<byte>(48); creature.Movement.Chase = (CreatureChaseMovementType)fields.Read<byte>(48);
if (!fields.IsNull(49)) if (!fields.IsNull(49))
creature.Movement.Rooted = fields.Read<bool>(49); creature.Movement.Random = (CreatureRandomMovementType)fields.Read<byte>(49);
if (!fields.IsNull(50)) if (!fields.IsNull(50))
creature.Movement.Chase = (CreatureChaseMovementType)fields.Read<byte>(50); creature.Movement.InteractionPauseTimer = fields.Read<uint>(50);
if (!fields.IsNull(51)) creature.HoverHeight = fields.Read<float>(51);
creature.Movement.Random = (CreatureRandomMovementType)fields.Read<byte>(51); creature.ModHealth = fields.Read<float>(52);
creature.ModHealthExtra = fields.Read<float>(53);
if (!fields.IsNull(52)) creature.ModMana = fields.Read<float>(54);
creature.Movement.InteractionPauseTimer = fields.Read<uint>(52); creature.ModManaExtra = fields.Read<float>(55);
creature.ModArmor = fields.Read<float>(56);
creature.HoverHeight = fields.Read<float>(53); creature.ModDamage = fields.Read<float>(57);
creature.ModHealth = fields.Read<float>(54); creature.ModExperience = fields.Read<float>(58);
creature.ModHealthExtra = fields.Read<float>(55); creature.RacialLeader = fields.Read<bool>(59);
creature.ModMana = fields.Read<float>(56); creature.MovementId = fields.Read<uint>(60);
creature.ModManaExtra = fields.Read<float>(57); creature.CreatureDifficultyID = fields.Read<int>(61);
creature.ModArmor = fields.Read<float>(58); creature.WidgetSetID = fields.Read<int>(62);
creature.ModDamage = fields.Read<float>(59); creature.WidgetSetUnitConditionID = fields.Read<int>(63);
creature.ModExperience = fields.Read<float>(60); creature.RegenHealth = fields.Read<bool>(64);
creature.RacialLeader = fields.Read<bool>(61); creature.MechanicImmuneMask = fields.Read<ulong>(65);
creature.MovementId = fields.Read<uint>(62); creature.SpellSchoolImmuneMask = fields.Read<uint>(66);
creature.CreatureDifficultyID = fields.Read<int>(63); creature.FlagsExtra = (CreatureFlagsExtra)fields.Read<uint>(67);
creature.WidgetSetID = fields.Read<int>(64); creature.ScriptID = GetScriptId(fields.Read<string>(68));
creature.WidgetSetUnitConditionID = fields.Read<int>(65); creature.StringId = fields.Read<string>(69);
creature.RegenHealth = fields.Read<bool>(66);
creature.MechanicImmuneMask = fields.Read<ulong>(67);
creature.SpellSchoolImmuneMask = fields.Read<uint>(68);
creature.FlagsExtra = (CreatureFlagsExtra)fields.Read<uint>(69);
creature.ScriptID = GetScriptId(fields.Read<string>(70));
creature.StringId = fields.Read<string>(71);
creatureTemplateStorage[entry] = creature; creatureTemplateStorage[entry] = creature;
} }
@@ -2575,12 +2573,13 @@ namespace Game
++count; ++count;
} while (result.NextRow()); } while (result.NextRow());
foreach (var creatureTemplate in creatureTemplateStorage.Values) for (byte unitLevel = 1; unitLevel <= SharedConst.DefaultMaxLevel + 3; ++unitLevel)
{ {
for (short lvl = creatureTemplate.Minlevel; lvl <= creatureTemplate.Maxlevel; ++lvl) for (byte unitClass = 1; unitClass <= SharedConst.MaxUnitClasses; ++unitClass)
{ {
if (creatureBaseStatsStorage.LookupByKey(MathFunctions.MakePair16((uint)lvl, creatureTemplate.UnitClass)) == null) uint unitClassMask = 1u << (unitClass - 1);
Log.outError(LogFilter.Sql, "Missing base stats for creature class {0} level {1}", creatureTemplate.UnitClass, lvl); if (!creatureBaseStatsStorage.ContainsKey(MathFunctions.MakePair16(unitLevel, unitClassMask)))
Log.outError(LogFilter.Sql, $"Missing base stats for creature class {unitClassMask} level {unitLevel}");
} }
} }
@@ -2683,6 +2682,20 @@ namespace Game
template.scalingStorage[difficulty] = creatureLevelScaling; template.scalingStorage[difficulty] = creatureLevelScaling;
// Assign creature level scaling to creature difficulty entry (if any)
// TODO: Drop the use of creature difficulties
int difficultyIndex = CreatureTemplate.DifficultyIDToDifficultyEntryIndex((uint)difficulty);
if (difficultyIndex != -1)
{
uint difficultyEntry = template.DifficultyEntry[difficultyIndex];
if (difficultyEntry != 0)
{
var difficultyTemplate = creatureTemplateStorage.LookupByKey(difficultyEntry);
if (difficultyTemplate != null)
difficultyTemplate.scalingStorage[difficulty] = creatureLevelScaling;
}
}
++count; ++count;
} while (result.NextRow()); } while (result.NextRow());
@@ -2742,18 +2755,6 @@ namespace Game
cInfo.Entry, cInfo.HealthScalingExpansion, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.HealthScalingExpansion); cInfo.Entry, cInfo.HealthScalingExpansion, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.HealthScalingExpansion);
} }
if (cInfo.Minlevel > difficultyInfo.Minlevel)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, minlevel: {1}) has lower `minlevel` in difficulty {2} mode (Entry: {3}, minlevel: {4}).",
cInfo.Entry, cInfo.Minlevel, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.Minlevel);
}
if (cInfo.Maxlevel > difficultyInfo.Maxlevel)
{
Log.outError(LogFilter.Sql, "Creature (Entry: {0}, maxlevel: {1}) has lower `maxlevel` in difficulty {2} mode (Entry: {3}, maxlevel: {4}).",
cInfo.Entry, cInfo.Maxlevel, diff + 1, cInfo.DifficultyEntry[diff], difficultyInfo.Maxlevel);
}
if (cInfo.Faction != difficultyInfo.Faction) 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}).", Log.outError(LogFilter.Sql, "Creature (Entry: {0}, faction: {1}) has different `faction` in difficulty {2} mode (Entry: {3}, faction: {4}).",
@@ -3001,19 +3002,6 @@ namespace Game
cInfo.DynamicFlags = 0; cInfo.DynamicFlags = 0;
} }
var levels = cInfo.GetMinMaxLevel();
if (levels[0] < 1 || levels[0] > SharedConst.StrongMaxLevel)
{
Log.outError(LogFilter.Sql, $"Creature (ID: {cInfo.Entry}): Calculated minLevel {cInfo.Minlevel} is not within [1, 255], value has been set to {(cInfo.HealthScalingExpansion == (int)Expansion.LevelCurrent ? SharedConst.MaxLevel : 1)}.");
cInfo.Minlevel = (short)(cInfo.HealthScalingExpansion == (int)Expansion.LevelCurrent ? 0 : 1);
}
if (levels[1] < 1 || levels[1] > SharedConst.StrongMaxLevel)
{
Log.outError(LogFilter.Sql, $"Creature (ID: {cInfo.Entry}): Calculated maxLevel {cInfo.Maxlevel} is not within [1, 255], value has been set to {(cInfo.HealthScalingExpansion == (int)Expansion.LevelCurrent ? SharedConst.MaxLevel : 1)}.");
cInfo.Maxlevel = (short)(cInfo.HealthScalingExpansion == (int)Expansion.LevelCurrent ? 0 : 1);
}
cInfo.ModDamage *= Creature._GetDamageMod(cInfo.Rank); cInfo.ModDamage *= Creature._GetDamageMod(cInfo.Rank);
if (cInfo.GossipMenuId != 0 && !cInfo.Npcflag.HasAnyFlag((ulong)NPCFlags.Gossip)) if (cInfo.GossipMenuId != 0 && !cInfo.Npcflag.HasAnyFlag((ulong)NPCFlags.Gossip))