From 8828e944823b3161f492930d7788892c348db46b Mon Sep 17 00:00:00 2001 From: hondacrx Date: Fri, 21 Apr 2023 18:49:58 -0400 Subject: [PATCH] Core/Creatures: Compute the level from ContentTuning Port From (https://github.com/TrinityCore/TrinityCore/commit/562f65f3f8f271fc52cc428c71a562af22829dc0) --- Source/Framework/Constants/SharedConst.cs | 1 + .../Database/Databases/WorldDatabase.cs | 2 +- Source/Game/Entities/Creature/Creature.cs | 13 +- Source/Game/Entities/Creature/CreatureData.cs | 11 -- Source/Game/Globals/ObjectManager.cs | 174 ++++++++---------- 5 files changed, 87 insertions(+), 114 deletions(-) diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index d64c0c588..222af0166 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -31,6 +31,7 @@ namespace Framework.Constants public const int MaxAzeriteEssenceSlot = 4; public const int MaxAzeriteEssenceRank = 4; public const int AchivementCategoryPetBattles = 15117; + public const int MaxUnitClasses = 4; /// /// BattlePets Const diff --git a/Source/Framework/Database/Databases/WorldDatabase.cs b/Source/Framework/Database/Databases/WorldDatabase.cs index 4d0a6c995..2262c1134 100644 --- a/Source/Framework/Database/Databases/WorldDatabase.cs +++ b/Source/Framework/Database/Databases/WorldDatabase.cs @@ -61,7 +61,7 @@ namespace Framework.Database PrepareStatement(WorldStatements.SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?"); PrepareStatement(WorldStatements.DEL_CREATURE, "DELETE FROM creature WHERE guid = ?"); PrepareStatement(WorldStatements.SEL_COMMANDS, "SELECT name, help FROM command"); - PrepareStatement(WorldStatements.SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, 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_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_"); diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index e10d634e6..0f0f87d28 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -1425,16 +1425,11 @@ namespace Game.Entities public void SelectLevel() { - CreatureTemplate cInfo = GetCreatureTemplate(); - - // 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); - + // Level ApplyLevelScaling(); + int levelWithDelta = m_unitData.ScalingLevelMax + m_unitData.ScalingLevelDelta; + byte level = (byte)MathFunctions.RoundToInterval(ref levelWithDelta, 1, SharedConst.StrongMaxLevel); + SetLevel(level); UpdateLevelDependantStats(); } diff --git a/Source/Game/Entities/Creature/CreatureData.cs b/Source/Game/Entities/Creature/CreatureData.cs index cbb7ddd61..a39dcc651 100644 --- a/Source/Game/Entities/Creature/CreatureData.cs +++ b/Source/Game/Entities/Creature/CreatureData.cs @@ -22,9 +22,7 @@ namespace Game.Entities public string TitleAlt; public string IconName; public uint GossipMenuId; - public short Minlevel; public Dictionary scalingStorage = new(); - public short Maxlevel; public int HealthScalingExpansion; public uint RequiredExpansion; public uint VignetteID; // @todo Read Vignette.db2 @@ -145,15 +143,6 @@ namespace Game.Entities 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() { return HealthScalingExpansion == (int)Expansion.LevelCurrent ? (int)Expansion.WarlordsOfDraenor : HealthScalingExpansion; diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index e85ef2764..aa8dc2745 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -1823,35 +1823,33 @@ namespace Game creature.TitleAlt = fields.Read(9); creature.IconName = fields.Read(10); creature.GossipMenuId = fields.Read(11); - creature.Minlevel = fields.Read(12); - creature.Maxlevel = fields.Read(13); - creature.HealthScalingExpansion = fields.Read(14); - creature.RequiredExpansion = fields.Read(15); - creature.VignetteID = fields.Read(16); - creature.Faction = fields.Read(17); - creature.Npcflag = fields.Read(18); - creature.SpeedWalk = fields.Read(19); - creature.SpeedRun = fields.Read(20); - creature.Scale = fields.Read(21); - creature.Rank = (CreatureEliteType)fields.Read(22); - creature.DmgSchool = fields.Read(23); - creature.BaseAttackTime = fields.Read(24); - creature.RangeAttackTime = fields.Read(25); - creature.BaseVariance = fields.Read(26); - creature.RangeVariance = fields.Read(27); - creature.UnitClass = fields.Read(28); - creature.UnitFlags = (UnitFlags)fields.Read(29); - creature.UnitFlags2 = fields.Read(30); - creature.UnitFlags3 = fields.Read(31); - creature.DynamicFlags = fields.Read(32); - creature.Family = (CreatureFamily)fields.Read(33); - creature.TrainerClass = (Class)fields.Read(34); - creature.CreatureType = (CreatureType)fields.Read(35); - creature.TypeFlags = (CreatureTypeFlags)fields.Read(36); - creature.TypeFlags2 = fields.Read(37); - creature.LootId = fields.Read(38); - creature.PickPocketId = fields.Read(39); - creature.SkinLootId = fields.Read(40); + creature.HealthScalingExpansion = fields.Read(12); + creature.RequiredExpansion = fields.Read(13); + creature.VignetteID = fields.Read(14); + creature.Faction = fields.Read(15); + creature.Npcflag = fields.Read(16); + creature.SpeedWalk = fields.Read(17); + creature.SpeedRun = fields.Read(18); + creature.Scale = fields.Read(19); + creature.Rank = (CreatureEliteType)fields.Read(20); + creature.DmgSchool = fields.Read(21); + creature.BaseAttackTime = fields.Read(22); + creature.RangeAttackTime = fields.Read(23); + creature.BaseVariance = fields.Read(24); + creature.RangeVariance = fields.Read(25); + creature.UnitClass = fields.Read(26); + creature.UnitFlags = (UnitFlags)fields.Read(27); + creature.UnitFlags2 = fields.Read(28); + creature.UnitFlags3 = fields.Read(29); + creature.DynamicFlags = fields.Read(30); + creature.Family = (CreatureFamily)fields.Read(31); + creature.TrainerClass = (Class)fields.Read(32); + creature.CreatureType = (CreatureType)fields.Read(33); + creature.TypeFlags = (CreatureTypeFlags)fields.Read(34); + creature.TypeFlags2 = fields.Read(35); + creature.LootId = fields.Read(36); + creature.PickPocketId = fields.Read(37); + creature.SkinLootId = fields.Read(38); for (var i = (int)SpellSchools.Holy; i < (int)SpellSchools.Max; ++i) creature.Resistance[i] = 0; @@ -1859,52 +1857,52 @@ namespace Game for (var i = 0; i < SharedConst.MaxCreatureSpells; ++i) creature.Spells[i] = 0; - creature.VehicleId = fields.Read(41); - creature.MinGold = fields.Read(42); - creature.MaxGold = fields.Read(43); - creature.AIName = fields.Read(44); - creature.MovementType = fields.Read(45); + creature.VehicleId = fields.Read(39); + creature.MinGold = fields.Read(40); + creature.MaxGold = fields.Read(41); + creature.AIName = fields.Read(42); + creature.MovementType = fields.Read(43); + + if (!fields.IsNull(44)) + creature.Movement.Ground = (CreatureGroundMovementType)fields.Read(44); + + if (!fields.IsNull(45)) + creature.Movement.Swim = fields.Read(45); if (!fields.IsNull(46)) - creature.Movement.Ground = (CreatureGroundMovementType)fields.Read(46); + creature.Movement.Flight = (CreatureFlightMovementType)fields.Read(46); if (!fields.IsNull(47)) - creature.Movement.Swim = fields.Read(47); + creature.Movement.Rooted = fields.Read(47); if (!fields.IsNull(48)) - creature.Movement.Flight = (CreatureFlightMovementType)fields.Read(48); + creature.Movement.Chase = (CreatureChaseMovementType)fields.Read(48); if (!fields.IsNull(49)) - creature.Movement.Rooted = fields.Read(49); + creature.Movement.Random = (CreatureRandomMovementType)fields.Read(49); if (!fields.IsNull(50)) - creature.Movement.Chase = (CreatureChaseMovementType)fields.Read(50); + creature.Movement.InteractionPauseTimer = fields.Read(50); - if (!fields.IsNull(51)) - creature.Movement.Random = (CreatureRandomMovementType)fields.Read(51); - - if (!fields.IsNull(52)) - creature.Movement.InteractionPauseTimer = fields.Read(52); - - creature.HoverHeight = fields.Read(53); - creature.ModHealth = fields.Read(54); - creature.ModHealthExtra = fields.Read(55); - creature.ModMana = fields.Read(56); - creature.ModManaExtra = fields.Read(57); - creature.ModArmor = fields.Read(58); - creature.ModDamage = fields.Read(59); - creature.ModExperience = fields.Read(60); - creature.RacialLeader = fields.Read(61); - creature.MovementId = fields.Read(62); - creature.CreatureDifficultyID = fields.Read(63); - creature.WidgetSetID = fields.Read(64); - creature.WidgetSetUnitConditionID = fields.Read(65); - creature.RegenHealth = fields.Read(66); - creature.MechanicImmuneMask = fields.Read(67); - creature.SpellSchoolImmuneMask = fields.Read(68); - creature.FlagsExtra = (CreatureFlagsExtra)fields.Read(69); - creature.ScriptID = GetScriptId(fields.Read(70)); - creature.StringId = fields.Read(71); + creature.HoverHeight = fields.Read(51); + creature.ModHealth = fields.Read(52); + creature.ModHealthExtra = fields.Read(53); + creature.ModMana = fields.Read(54); + creature.ModManaExtra = fields.Read(55); + creature.ModArmor = fields.Read(56); + creature.ModDamage = fields.Read(57); + creature.ModExperience = fields.Read(58); + creature.RacialLeader = fields.Read(59); + creature.MovementId = fields.Read(60); + creature.CreatureDifficultyID = fields.Read(61); + creature.WidgetSetID = fields.Read(62); + creature.WidgetSetUnitConditionID = fields.Read(63); + creature.RegenHealth = fields.Read(64); + creature.MechanicImmuneMask = fields.Read(65); + creature.SpellSchoolImmuneMask = fields.Read(66); + creature.FlagsExtra = (CreatureFlagsExtra)fields.Read(67); + creature.ScriptID = GetScriptId(fields.Read(68)); + creature.StringId = fields.Read(69); creatureTemplateStorage[entry] = creature; } @@ -2575,12 +2573,13 @@ namespace Game ++count; } 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) - Log.outError(LogFilter.Sql, "Missing base stats for creature class {0} level {1}", creatureTemplate.UnitClass, lvl); + uint unitClassMask = 1u << (unitClass - 1); + 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; + // 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; } while (result.NextRow()); @@ -2742,18 +2755,6 @@ namespace Game 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) { 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; } - 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); if (cInfo.GossipMenuId != 0 && !cInfo.Npcflag.HasAnyFlag((ulong)NPCFlags.Gossip))