feat: Adds configurable skill and stat gain (#1489)

### Summary

- Adds the following configurations:
```json
"stats.statMax": "125",
"stats.gainChanceMultiplier": 1.0,
"stats.primaryStatGainChance": 0.75,
"stats.gainDelay": "00:10:00",
"stats.petGainDelay": "00:05:00",
"stats.usePub45StatGain": "False"
```

- Adds Pub 45 stat gain rate for UOML+.
- Reduces the gain delay for legacy stat gain from 15 minutes to 10 minutes.
- Legacy gain no longer requires a gain in skill to gain in a stat.
This commit is contained in:
Vitalii Zurian 2023-10-14 21:02:36 +02:00 committed by GitHub
parent 29c7d839a7
commit f9517854d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 311 additions and 118 deletions

View file

@ -11,7 +11,9 @@
"DexGain": 0.5,
"IntGain": 0.5,
"GainFactor": 1,
"ProfessionSkillName": "Alchemy"
"ProfessionSkillName": "Alchemy",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 1,
@ -25,7 +27,9 @@
"DexGain": 0.15,
"IntGain": 0.7,
"GainFactor": 1,
"ProfessionSkillName": "Anatomy"
"ProfessionSkillName": "Anatomy",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 2,
@ -39,7 +43,9 @@
"DexGain": 0,
"IntGain": 1,
"GainFactor": 1,
"ProfessionSkillName": "AnimalLore"
"ProfessionSkillName": "AnimalLore",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 3,
@ -53,7 +59,9 @@
"DexGain": 0,
"IntGain": 1,
"GainFactor": 1,
"ProfessionSkillName": "ItemID"
"ProfessionSkillName": "ItemID",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 4,
@ -67,7 +75,9 @@
"DexGain": 0.15,
"IntGain": 0.1,
"GainFactor": 1,
"ProfessionSkillName": "ArmsLore"
"ProfessionSkillName": "ArmsLore",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 5,
@ -81,7 +91,9 @@
"DexGain": 0.25,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Parrying"
"ProfessionSkillName": "Parrying",
"PrimaryStat": "Dex",
"SecondaryStat": "Str"
},
{
"SkillID": 6,
@ -95,7 +107,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Begging"
"ProfessionSkillName": "Begging",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 7,
@ -109,7 +123,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Blacksmith"
"ProfessionSkillName": "Blacksmith",
"PrimaryStat": "Str",
"SecondaryStat": "Dex"
},
{
"SkillID": 8,
@ -123,7 +139,9 @@
"DexGain": 1.6,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Bowcraft"
"ProfessionSkillName": "Bowcraft",
"PrimaryStat": "Dex",
"SecondaryStat": "Str"
},
{
"SkillID": 9,
@ -137,7 +155,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Peacemaking"
"ProfessionSkillName": "Peacemaking",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 10,
@ -151,7 +171,9 @@
"DexGain": 1.5,
"IntGain": 1.5,
"GainFactor": 1,
"ProfessionSkillName": "Camping"
"ProfessionSkillName": "Camping",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 11,
@ -165,7 +187,9 @@
"DexGain": 0.5,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Carpentry"
"ProfessionSkillName": "Carpentry",
"PrimaryStat": "Str",
"SecondaryStat": "Dex"
},
{
"SkillID": 12,
@ -179,7 +203,9 @@
"DexGain": 0.75,
"IntGain": 0.75,
"GainFactor": 1,
"ProfessionSkillName": "Cartography"
"ProfessionSkillName": "Cartography",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 13,
@ -193,7 +219,9 @@
"DexGain": 2,
"IntGain": 3,
"GainFactor": 1,
"ProfessionSkillName": "Cooking"
"ProfessionSkillName": "Cooking",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 14,
@ -207,7 +235,9 @@
"DexGain": 0.4,
"IntGain": 0.6,
"GainFactor": 1,
"ProfessionSkillName": "DetectingHidden"
"ProfessionSkillName": "DetectingHidden",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 15,
@ -221,7 +251,9 @@
"DexGain": 0.25,
"IntGain": 0.25,
"GainFactor": 1,
"ProfessionSkillName": "Enticement"
"ProfessionSkillName": "Enticement",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 16,
@ -235,7 +267,9 @@
"DexGain": 0,
"IntGain": 1,
"GainFactor": 1,
"ProfessionSkillName": "EvaluateIntelligence"
"ProfessionSkillName": "EvaluateIntelligence",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 17,
@ -249,7 +283,9 @@
"DexGain": 0.6,
"IntGain": 0.8,
"GainFactor": 1,
"ProfessionSkillName": "Healing"
"ProfessionSkillName": "Healing",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 18,
@ -263,7 +299,9 @@
"DexGain": 0.5,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Fishing"
"ProfessionSkillName": "Fishing",
"PrimaryStat": "Dex",
"SecondaryStat": "Str"
},
{
"SkillID": 19,
@ -277,7 +315,9 @@
"DexGain": 0.2,
"IntGain": 0.8,
"GainFactor": 1,
"ProfessionSkillName": "ForensicEvaluation"
"ProfessionSkillName": "ForensicEvaluation",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 20,
@ -291,7 +331,9 @@
"DexGain": 0.625,
"IntGain": 0.25,
"GainFactor": 1,
"ProfessionSkillName": "Herding"
"ProfessionSkillName": "Herding",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 21,
@ -305,7 +347,9 @@
"DexGain": 0.8,
"IntGain": 0.2,
"GainFactor": 1,
"ProfessionSkillName": "Hiding"
"ProfessionSkillName": "Hiding",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 22,
@ -319,7 +363,9 @@
"DexGain": 0.45,
"IntGain": 0.05,
"GainFactor": 1,
"ProfessionSkillName": "Provocation"
"ProfessionSkillName": "Provocation",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 23,
@ -333,7 +379,9 @@
"DexGain": 0.2,
"IntGain": 0.8,
"GainFactor": 1,
"ProfessionSkillName": "Inscription"
"ProfessionSkillName": "Inscription",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 24,
@ -347,7 +395,9 @@
"DexGain": 2,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Lockpicking"
"ProfessionSkillName": "Lockpicking",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 25,
@ -361,7 +411,9 @@
"DexGain": 0,
"IntGain": 1.5,
"GainFactor": 1,
"ProfessionSkillName": "Magery"
"ProfessionSkillName": "Magery",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 26,
@ -375,7 +427,9 @@
"DexGain": 0.25,
"IntGain": 0.5,
"GainFactor": 1,
"ProfessionSkillName": "ResistingSpells"
"ProfessionSkillName": "ResistingSpells",
"PrimaryStat": "Str",
"SecondaryStat": "Dex"
},
{
"SkillID": 27,
@ -389,7 +443,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Tactics"
"ProfessionSkillName": "Tactics",
"PrimaryStat": "Str",
"SecondaryStat": "Dex"
},
{
"SkillID": 28,
@ -403,7 +459,9 @@
"DexGain": 2.5,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Snooping"
"ProfessionSkillName": "Snooping",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 29,
@ -417,7 +475,9 @@
"DexGain": 0.8,
"IntGain": 0.2,
"GainFactor": 1,
"ProfessionSkillName": "Musicianship"
"ProfessionSkillName": "Musicianship",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 30,
@ -431,7 +491,9 @@
"DexGain": 0.4,
"IntGain": 1.6,
"GainFactor": 1,
"ProfessionSkillName": "Poisoning"
"ProfessionSkillName": "Poisoning",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 31,
@ -445,7 +507,9 @@
"DexGain": 0.75,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Archery"
"ProfessionSkillName": "Archery",
"PrimaryStat": "Dex",
"SecondaryStat": "Str"
},
{
"SkillID": 32,
@ -459,7 +523,9 @@
"DexGain": 0,
"IntGain": 1,
"GainFactor": 1,
"ProfessionSkillName": "SpiritSpeak"
"ProfessionSkillName": "SpiritSpeak",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 33,
@ -473,7 +539,9 @@
"DexGain": 1,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Stealing"
"ProfessionSkillName": "Stealing",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 34,
@ -487,7 +555,9 @@
"DexGain": 1.63,
"IntGain": 0.5,
"GainFactor": 1,
"ProfessionSkillName": "Tailoring"
"ProfessionSkillName": "Tailoring",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 35,
@ -501,7 +571,9 @@
"DexGain": 0.2,
"IntGain": 0.4,
"GainFactor": 1,
"ProfessionSkillName": "AnimalTaming"
"ProfessionSkillName": "AnimalTaming",
"PrimaryStat": "Str",
"SecondaryStat": "Int"
},
{
"SkillID": 36,
@ -515,7 +587,9 @@
"DexGain": 0,
"IntGain": 0.8,
"GainFactor": 1,
"ProfessionSkillName": "TasteIdentification"
"ProfessionSkillName": "TasteIdentification",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 37,
@ -529,7 +603,9 @@
"DexGain": 0.2,
"IntGain": 0.3,
"GainFactor": 1,
"ProfessionSkillName": "Tinkering"
"ProfessionSkillName": "Tinkering",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 38,
@ -543,7 +619,9 @@
"DexGain": 1.25,
"IntGain": 1.25,
"GainFactor": 1,
"ProfessionSkillName": "Tracking"
"ProfessionSkillName": "Tracking",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 39,
@ -557,7 +635,9 @@
"DexGain": 0.4,
"IntGain": 0.8,
"GainFactor": 1,
"ProfessionSkillName": "Veterinary"
"ProfessionSkillName": "Veterinary",
"PrimaryStat": "Int",
"SecondaryStat": "Dex"
},
{
"SkillID": 40,
@ -571,7 +651,9 @@
"DexGain": 0.25,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Swordsmanship"
"ProfessionSkillName": "Swordsmanship",
"PrimaryStat": "Str",
"SecondaryStat": "Dex"
},
{
"SkillID": 41,
@ -585,7 +667,9 @@
"DexGain": 0.1,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "MaceFighting"
"ProfessionSkillName": "MaceFighting",
"PrimaryStat": "Str",
"SecondaryStat": "Dex"
},
{
"SkillID": 42,
@ -599,7 +683,9 @@
"DexGain": 0.55,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Fencing"
"ProfessionSkillName": "Fencing",
"PrimaryStat": "Dex",
"SecondaryStat": "Str"
},
{
"SkillID": 43,
@ -613,7 +699,9 @@
"DexGain": 0.1,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Wrestling"
"ProfessionSkillName": "Wrestling",
"PrimaryStat": "Str",
"SecondaryStat": "Dex"
},
{
"SkillID": 44,
@ -627,7 +715,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Lumberjacking"
"ProfessionSkillName": "Lumberjacking",
"PrimaryStat": "Str",
"SecondaryStat": "Dex"
},
{
"SkillID": 45,
@ -641,7 +731,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Mining"
"ProfessionSkillName": "Mining",
"PrimaryStat": "Str",
"SecondaryStat": "Dex"
},
{
"SkillID": 46,
@ -655,7 +747,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Meditation"
"ProfessionSkillName": "Meditation",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 47,
@ -669,7 +763,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Stealth"
"ProfessionSkillName": "Stealth",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 48,
@ -683,7 +779,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Disarm"
"ProfessionSkillName": "Disarm",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 49,
@ -697,7 +795,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Necromancy"
"ProfessionSkillName": "Necromancy",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 50,
@ -711,7 +811,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Focus"
"ProfessionSkillName": "Focus",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 51,
@ -725,7 +827,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Chivalry"
"ProfessionSkillName": "Chivalry",
"PrimaryStat": "Str",
"SecondaryStat": "Int"
},
{
"SkillID": 52,
@ -739,7 +843,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Bushido"
"ProfessionSkillName": "Bushido",
"PrimaryStat": "Str",
"SecondaryStat": "Int"
},
{
"SkillID": 53,
@ -753,7 +859,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Ninjitsu"
"ProfessionSkillName": "Ninjitsu",
"PrimaryStat": "Dex",
"SecondaryStat": "Int"
},
{
"SkillID": 54,
@ -767,7 +875,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Spellweaving"
"ProfessionSkillName": "Spellweaving",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 55,
@ -781,7 +891,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Mysticism"
"ProfessionSkillName": "Mysticism",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 56,
@ -795,7 +907,9 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Imbuing"
"ProfessionSkillName": "Imbuing",
"PrimaryStat": "Int",
"SecondaryStat": "Str"
},
{
"SkillID": 57,
@ -809,6 +923,8 @@
"DexGain": 0,
"IntGain": 0,
"GainFactor": 1,
"ProfessionSkillName": "Throwing"
"ProfessionSkillName": "Throwing",
"PrimaryStat": "Dex",
"SecondaryStat": "Str"
}
]
]

View file

@ -64,6 +64,12 @@ public static class ServerConfiguration
return bool.TryParse(strValue, out var value) ? value : defaultValue;
}
public static TimeSpan GetSetting(string key, TimeSpan defaultValue)
{
_settings.Settings.TryGetValue(key, out var strValue);
return TimeSpan.TryParse(strValue, out var value) ? value : defaultValue;
}
public static T GetSetting<T>(string key, T defaultValue) where T : struct, Enum
{
_settings.Settings.TryGetValue(key, out var strValue);

View file

@ -76,6 +76,13 @@ public enum SkillName
Throwing = 57
}
public enum Stat
{
Str,
Dex,
Int
}
[PropertyObject]
public class Skill
{
@ -135,7 +142,7 @@ public class Skill
}
}
if (Lock is < SkillLock.Up or > SkillLock.Locked)
if (Lock > SkillLock.Locked)
{
Lock = SkillLock.Up;
}
@ -245,25 +252,29 @@ public class Skill
get
{
var baseValue = Base;
var inv = 100.0 - baseValue;
if (inv < 0.0)
{
inv = 0.0;
}
inv /= 100.0;
var statsOffset = Owner.Owner.RawStr * Info.StrScale +
Owner.Owner.RawDex * Info.DexScale +
Owner.Owner.RawInt * Info.IntScale;
var statTotal = Info.StatTotal * inv;
statsOffset *= inv;
var inv = 100.0 - baseValue;
if (statsOffset > statTotal)
if (inv <= 0.0)
{
statsOffset = statTotal;
statsOffset = 0.0;
}
else
{
statsOffset *= inv;
if (Info.StatTotal > 0)
{
var statTotal = Info.StatTotal * inv;
if (statsOffset > statTotal)
{
statsOffset = statTotal;
}
}
}
var value = baseValue + statsOffset;
@ -323,7 +334,7 @@ public class Skill
public void SetLockNoRelay(SkillLock skillLock)
{
if (skillLock is < SkillLock.Up or > SkillLock.Locked)
if (skillLock > SkillLock.Locked)
{
return;
}
@ -387,7 +398,7 @@ public class SkillInfo
public SkillInfo(
int skillID, string name, double strScale, double dexScale, double intScale, string title,
SkillUseCallback callback, double strGain, double dexGain, double intGain, double gainFactor,
string professionSkillName = null
string professionSkillName, Stat primaryStat, Stat secondaryStat
)
{
Name = name;
@ -403,6 +414,8 @@ public class SkillInfo
GainFactor = gainFactor;
ProfessionSkillName = professionSkillName ?? Name.RemoveOrdinal(" ");
StatTotal = strScale + dexScale + intScale;
PrimaryStat = primaryStat;
SecondaryStat = secondaryStat;
}
public SkillUseCallback Callback { get; set; }
@ -431,6 +444,10 @@ public class SkillInfo
public string ProfessionSkillName { get; set; }
public Stat PrimaryStat { get; set; }
public Stat SecondaryStat { get; set; }
public static SkillInfo[] Table { get; set; } = Array.Empty<SkillInfo>();
}

View file

@ -1,25 +1,35 @@
using System;
using System.Runtime.CompilerServices;
using Server.Factions;
using Server.Mobiles;
namespace Server.Misc;
// TODO: Make this entirely configurable
public static class SkillCheck
{
public enum Stat
private static int _statMax;
private static double _statGainChanceMultiplier;
private static bool _usePub45StatGain;
private static double _primaryStatGainChance;
private static TimeSpan _statGainDelay;
private static TimeSpan _petStatGainDelay;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool RollStatIncreaseChance(double statGain) =>
statGain / 33.3 * _statGainChanceMultiplier > Utility.RandomDouble();
public static void Configure()
{
Str,
Dex,
Int
_statMax = ServerConfiguration.GetOrUpdateSetting("stats.statMax", Core.LBR ? 125 : 100);
_statGainChanceMultiplier = ServerConfiguration.GetOrUpdateSetting("stats.gainChanceMultiplier", 1.0);
_primaryStatGainChance = ServerConfiguration.GetSetting("stats.primaryStatGainChance", 0.75);
_statGainDelay = ServerConfiguration.GetSetting("stats.gainDelay", TimeSpan.FromMinutes(Core.ML ? 0.05 : 10));
_petStatGainDelay = ServerConfiguration.GetSetting("stats.petGainDelay", TimeSpan.FromMinutes(5.0));
// Publish 45 - Preparation for UOKR
_usePub45StatGain = ServerConfiguration.GetSetting("stats.usePub45StatGain", Core.ML);
}
// Publish 16 changed max stats from 100 to 125
private static int StatMax = Core.LBR ? 125 : 100;
private static readonly TimeSpan m_StatGainDelay = TimeSpan.FromMinutes(Core.ML ? 0.05 : 15);
private static readonly TimeSpan m_PetStatGainDelay = TimeSpan.FromMinutes(5.0);
public static void Initialize()
{
Mobile.SkillCheckLocationHandler = Mobile_SkillCheckLocation;
@ -123,6 +133,12 @@ public static class SkillCheck
Gain(from, skill);
}
}
// Gain one stat per 10 minutes, regardless of whether you gain in the skill
if (!_usePub45StatGain && success)
{
LegacyGain(from, skill.Info);
}
}
return success;
@ -220,43 +236,80 @@ public static class SkillCheck
if (toLower != skill && toLower.Lock == SkillLock.Down && toLower.BaseFixedPoint >= toGain)
{
toLower.BaseFixedPoint -= toGain;
toLower.BaseFixedPoint = Math.Max(toLower.BaseFixedPoint - toGain, 0);
break;
}
}
}
if (from is PlayerMobile pm && skill.SkillName == pm.AcceleratedSkill &&
pm.AcceleratedStart > Core.Now)
if (from is PlayerMobile pm && skill.SkillName == pm.AcceleratedSkill && pm.AcceleratedStart > Core.Now)
{
toGain *= Utility.RandomMinMax(2, 5);
}
if (!from.Player || skills.Total + toGain <= skills.Cap)
if (!from.Player || skills.Total < skills.Cap)
{
skill.BaseFixedPoint += toGain;
skill.BaseFixedPoint = Math.Min(toGain, skill.CapFixedPoint - skill.BaseFixedPoint);
}
}
if (skill.Lock == SkillLock.Up)
if (_usePub45StatGain && skill.Lock == SkillLock.Up)
{
var info = skill.Info;
if (from.StrLock == StatLockType.Up && info.StrGain / 33.3 > Utility.RandomDouble())
var primaryStat = info.PrimaryStat;
var secondaryStat = info.SecondaryStat;
var primaryStatLock = primaryStat.ToLock(from);
var secondaryStatLock = secondaryStat.ToLock(from);
if (primaryStatLock != StatLockType.Up && secondaryStatLock != StatLockType.Up)
{
GainStat(from, Stat.Str);
return;
}
else if (from.DexLock == StatLockType.Up && info.DexGain / 33.3 > Utility.RandomDouble())
// Flat 1 in 20 chance to gain anything
if (0.05 * _statGainChanceMultiplier > Utility.RandomDouble())
{
GainStat(from, Stat.Dex);
}
else if (from.IntLock == StatLockType.Up && info.IntGain / 33.3 > Utility.RandomDouble())
{
GainStat(from, Stat.Int);
// 75% for primary, 25% for secondary - Unless primary is not set to gain.
var statToGain = primaryStatLock is StatLockType.Up && _primaryStatGainChance > Utility.RandomDouble()
? primaryStat
: secondaryStat;
GainStat(from, statToGain);
}
}
}
public static void LegacyGain(Mobile from, SkillInfo info)
{
if (info.StrGain > 0 && from.StrLock == StatLockType.Up && RollStatIncreaseChance(info.StrGain))
{
GainStat(from, Stat.Str);
}
if (info.DexGain > 0 && from.DexLock == StatLockType.Up && RollStatIncreaseChance(info.DexGain))
{
GainStat(from, Stat.Dex);
}
if (info.IntGain > 0 && from.IntLock == StatLockType.Up && RollStatIncreaseChance(info.IntGain))
{
GainStat(from, Stat.Int);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static StatLockType ToLock(this Stat stat, Mobile from) =>
stat switch
{
Stat.Str => from.StrLock,
Stat.Dex => from.DexLock,
Stat.Int => from.IntLock,
_ => StatLockType.Up
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CanLower(Mobile from, Stat stat)
{
return stat switch
@ -268,6 +321,7 @@ public static class SkillCheck
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CanRaise(Mobile from, Stat stat)
{
if (!(from is BaseCreature creature && creature.Controlled))
@ -280,9 +334,9 @@ public static class SkillCheck
return stat switch
{
Stat.Str => from.StrLock == StatLockType.Up && from.RawStr < StatMax,
Stat.Dex => from.DexLock == StatLockType.Up && from.RawDex < StatMax,
Stat.Int => from.IntLock == StatLockType.Up && from.RawInt < StatMax,
Stat.Str => from.StrLock == StatLockType.Up && from.RawStr < _statMax,
Stat.Dex => from.DexLock == StatLockType.Up && from.RawDex < _statMax,
Stat.Int => from.IntLock == StatLockType.Up && from.RawInt < _statMax,
_ => false
};
}
@ -299,17 +353,17 @@ public static class SkillCheck
{
if (CanLower(from, Stat.Dex) && (from.RawDex < from.RawInt || !CanLower(from, Stat.Int)))
{
--from.RawDex;
from.RawDex -= 1;
}
else if (CanLower(from, Stat.Int))
{
--from.RawInt;
from.RawInt -= 1;
}
}
if (CanRaise(from, Stat.Str))
{
++from.RawStr;
from.RawStr = Math.Min(from.RawStr + 1, _statMax);
}
break;
@ -320,17 +374,17 @@ public static class SkillCheck
{
if (CanLower(from, Stat.Str) && (from.RawStr < from.RawInt || !CanLower(from, Stat.Int)))
{
--from.RawStr;
from.RawStr -= 1;
}
else if (CanLower(from, Stat.Int))
{
--from.RawInt;
from.RawInt -= 1;
}
}
if (CanRaise(from, Stat.Dex))
{
++from.RawDex;
from.RawDex = Math.Min(from.RawDex + 1, _statMax);
}
break;
@ -341,17 +395,17 @@ public static class SkillCheck
{
if (CanLower(from, Stat.Str) && (from.RawStr < from.RawDex || !CanLower(from, Stat.Dex)))
{
--from.RawStr;
from.RawStr -= 1;
}
else if (CanLower(from, Stat.Dex))
{
--from.RawDex;
from.RawDex -= 1;
}
}
if (CanRaise(from, Stat.Int))
{
++from.RawInt;
from.RawInt = Math.Min(from.RawInt + 1, _statMax);
}
break;
@ -367,12 +421,12 @@ public static class SkillCheck
{
if (from is BaseCreature creature && creature.Controlled)
{
if (creature.LastStrGain + m_PetStatGainDelay >= Core.Now)
if (creature.LastStrGain + _petStatGainDelay >= Core.Now)
{
return;
}
}
else if (from.LastStrGain + m_StatGainDelay >= Core.Now)
else if (from.LastStrGain + _statGainDelay >= Core.Now)
{
return;
}
@ -384,12 +438,12 @@ public static class SkillCheck
{
if (from is BaseCreature creature && creature.Controlled)
{
if (creature.LastDexGain + m_PetStatGainDelay >= Core.Now)
if (creature.LastDexGain + _petStatGainDelay >= Core.Now)
{
return;
}
}
else if (from.LastDexGain + m_StatGainDelay >= Core.Now)
else if (from.LastDexGain + _statGainDelay >= Core.Now)
{
return;
}
@ -401,12 +455,12 @@ public static class SkillCheck
{
if (from is BaseCreature creature && creature.Controlled)
{
if (creature.LastIntGain + m_PetStatGainDelay >= Core.Now)
if (creature.LastIntGain + _petStatGainDelay >= Core.Now)
{
return;
}
}
else if (from.LastIntGain + m_StatGainDelay >= Core.Now)
else if (from.LastIntGain + _statGainDelay >= Core.Now)
{
return;
}