Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of IronShoulders v0.1.0
IronShoulders.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using IronShoulders.Core; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("Kastante")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+2d7713dd322538148341ef632b5e28d42bf116a6")] [assembly: AssemblyProduct("IronShoulders")] [assembly: AssemblyTitle("IronShoulders")] [assembly: AssemblyVersion("0.1.0.0")] namespace IronShoulders { internal static class GameState { internal static readonly FieldRef<Skills, Dictionary<SkillType, Skill>> Data = AccessTools.FieldRefAccess<Skills, Dictionary<SkillType, Skill>>("m_skillData"); internal static void EnsureSkill(Player player) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)player) || (Object)(object)Plugin.Instance == (Object)null) { return; } Skills skills = ((Character)player).GetSkills(); if (!Object.op_Implicit((Object)(object)skills)) { return; } Plugin.Instance.UpdateIcon(skills); Dictionary<SkillType, Skill> dictionary = Data.Invoke(skills); if (!dictionary.ContainsKey(Plugin.Carrying)) { SkillDef val = skills.m_skills.Find((SkillDef def) => def.m_skill == Plugin.Carrying); if (val != null) { dictionary.Add(Plugin.Carrying, new Skill(val)); } } } internal static float LearnedLevel(Player player) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)((Character)player).GetSkills())) { return 0f; } if (!Data.Invoke(((Character)player).GetSkills()).TryGetValue(Plugin.Carrying, out var value)) { return 0f; } return value.m_level; } } [HarmonyPatch(typeof(Skill), "Raise")] internal static class CarryingProgression { private static bool Prefix(Skill __instance, float factor, ref bool __result) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected I4, but got Unknown //IL_0063: Expected I4, but got Unknown if (!Progression.TryRaise((int)__instance.m_info.m_skill, (int)Plugin.Carrying, ref __instance.m_level, ref __instance.m_accumulator, factor, __instance.m_info.m_increseStep, Plugin.Instance.Tracker.Awarding ? Plugin.Instance.Tracker.AwardWorldMultiplier : Game.m_skillGainRate, out var leveled, Plugin.Instance.Active)) { return true; } __result = leveled; return false; } } [HarmonyPatch(typeof(SEMan), "ModifyRaiseSkill")] internal static class IntervalSkillEffects { private static void Postfix(Character ___m_character, SkillType skill, ref float multiplier) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) MovementTracker tracker = Plugin.Instance.Tracker; if (tracker.Awarding && skill == Plugin.Carrying && (Object)(object)___m_character == (Object)(object)Player.m_localPlayer) { multiplier = tracker.AwardSkillMultiplier; } } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class PlayerDied { private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { Plugin.Instance.Tracker.Reset(); } } } [HarmonyPatch(typeof(Player), "TeleportTo")] internal static class PlayerTeleporting { private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { Plugin.Instance.Tracker.Reset(); } } } [HarmonyPatch(typeof(SEMan), "ModifyMaxCarryWeight")] internal static class CarryReward { private static void Postfix(Character ___m_character, ref float limit) { Plugin instance = Plugin.Instance; Player val = (Player)(object)((___m_character is Player) ? ___m_character : null); if (!((Object)(object)instance == (Object)null) && instance.Active && Object.op_Implicit((Object)(object)val)) { double num = (double)limit + instance.Settings.Current.CarryBonus(GameState.LearnedLevel(val)); if (Balance.Finite(num) && num <= 3.4028234663852886E+38) { limit = (float)num; } } } } [HarmonyPatch(typeof(Player), "SetLocalPlayer")] internal static class LocalPlayerReady { private static void Postfix(Player __instance) { Plugin.Instance.Tracker.Reset(); GameState.EnsureSkill(__instance); } } [HarmonyPatch(typeof(Skills), "Load")] internal static class SkillsLoaded { private static void Postfix(Skills __instance) { Player component = ((Component)__instance).GetComponent<Player>(); if ((Object)(object)component == (Object)(object)Player.m_localPlayer) { Plugin.Instance.Tracker.Reset(); } GameState.EnsureSkill(component); } } [HarmonyPatch(typeof(SkillsDialog), "Setup")] internal static class SkillListReady { private static void Prefix(Player player) { GameState.EnsureSkill(player); } } internal sealed class MovementTracker { private static readonly FieldRef<Character, Rigidbody> Body = AccessTools.FieldRefAccess<Character, Rigidbody>("m_body"); private static readonly FieldRef<Character, Rigidbody> Ground = AccessTools.FieldRefAccess<Character, Rigidbody>("m_lastGroundBody"); private static readonly FieldRef<Character, bool> Sliding = AccessTools.FieldRefAccess<Character, bool>("m_sliding"); private static readonly FieldRef<Character, bool> Slipping = AccessTools.FieldRefAccess<Character, bool>("m_slipping"); private static readonly FieldRef<Character, bool> IceShoes = AccessTools.FieldRefAccess<Character, bool>("m_iceShoes"); internal readonly RunStaminaMeter Stamina = new RunStaminaMeter(); private Player owner; private bool sampled; private bool inStep; private Vector3 position; private Vector3 supportLocalPosition; private Rigidbody support; private float seconds; private float weight; private float capacity; private double expectedDistance; private double spent; private bool running; private bool hadSupport; private MotionBlock block; private float skillMultiplier; private float worldMultiplier; internal bool Awarding { get; private set; } internal float AwardSkillMultiplier => skillMultiplier; internal float AwardWorldMultiplier => worldMultiplier; internal static bool LocalOwner(Player player) { if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return false; } ZNetView component = ((Component)player).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid()) { return component.IsOwner(); } return false; } private static MotionBlock Block(Player player) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) MotionBlock motionBlock = MotionBlock.None; if (!LocalOwner(player)) { motionBlock |= MotionBlock.NotLocalOwner; } Vector3 moveDir = ((Character)player).GetMoveDir(); if (((Vector3)(ref moveDir)).magnitude <= 0.1f) { motionBlock |= MotionBlock.NoInput; } if (!((Character)player).IsOnGround()) { motionBlock |= MotionBlock.Airborne; } if (((Character)player).IsEncumbered()) { motionBlock |= MotionBlock.Overloaded; } if (((Character)player).IsSwimming()) { motionBlock |= MotionBlock.Swimming; } if (((Character)player).IsFlying()) { motionBlock |= MotionBlock.Flying; } if (((Character)player).IsRiding()) { motionBlock |= MotionBlock.Riding; } if (((Character)player).IsAttached()) { motionBlock |= MotionBlock.Attached; } if (Object.op_Implicit((Object)(object)((Character)player).GetStandingOnShip()) || ((Character)player).IsAttachedToShip()) { motionBlock |= MotionBlock.Ship; } if (((Character)player).IsKnockedBack() || ((Character)player).IsStaggering()) { motionBlock |= MotionBlock.Knockback; } if (((Character)player).InDodge()) { motionBlock |= MotionBlock.Dodge; } if (Sliding.Invoke((Character)(object)player) || (Slipping.Invoke((Character)(object)player) && !IceShoes.Invoke((Character)(object)player))) { motionBlock |= MotionBlock.Sliding; } if (((Character)player).IsDead() || ((Character)player).InIntro() || ((Character)player).InCutscene() || ((Character)player).IsTeleporting()) { motionBlock |= MotionBlock.Unavailable; } if (((Character)player).InAttack()) { motionBlock |= MotionBlock.Attack; } return motionBlock; } private static Point Point(Vector3 vector) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) return new Point(vector.x, vector.y, vector.z); } internal void Begin(Player player) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Instance.Active || !LocalOwner(player)) { Reset(); return; } if ((Object)(object)owner != (Object)(object)player) { Reset(); owner = player; } MotionBlock end = Block(player); if (sampled) { Vector3 vector = (Object.op_Implicit((Object)(object)support) ? (support.position + support.rotation * supportLocalPosition) : position); double distance = Motion.OwnDistance(Point(Body.Invoke((Character)(object)player).position), Point(vector), expectedDistance, block, end, (Object)(object)support == (Object)(object)Ground.Invoke((Character)(object)player) && (!hadSupport || Object.op_Implicit((Object)(object)support))); double num = Plugin.Instance.Settings.Current.Experience(seconds, distance, weight, capacity, running, spent); if (num > 0.0) { Awarding = true; try { ((Character)player).RaiseSkill(Plugin.Carrying, (float)num); } finally { Awarding = false; } } } sampled = false; inStep = true; Stamina.BeginStep(player); } internal void End(Player player, float dt) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) if (!inStep || (Object)(object)owner != (Object)(object)player || !Plugin.Instance.Active || !LocalOwner(player)) { Reset(); return; } spent = Stamina.EndStep(); inStep = false; block = Block(player); if (block != MotionBlock.None || !Balance.Finite(dt) || dt <= 0f) { sampled = false; return; } position = Body.Invoke((Character)(object)player).position; support = Ground.Invoke((Character)(object)player); hadSupport = Object.op_Implicit((Object)(object)support); supportLocalPosition = (Object.op_Implicit((Object)(object)support) ? (Quaternion.Inverse(support.rotation) * (position - support.position)) : Vector3.zero); Vector3 val = (Object.op_Implicit((Object)(object)support) ? support.GetPointVelocity(position) : Vector3.zero); Vector3 val2 = Body.Invoke((Character)(object)player).linearVelocity - val; expectedDistance = ((Vector3)(ref val2)).magnitude * dt; seconds = dt; weight = ((Humanoid)player).GetInventory().GetTotalWeight(); capacity = player.GetMaxCarryWeight(); running = ((Character)player).IsRunning(); if (!running) { spent = 0.0; } skillMultiplier = 1f; ((Character)player).GetSEMan().ModifyRaiseSkill(Plugin.Carrying, ref skillMultiplier); worldMultiplier = Game.m_skillGainRate; sampled = true; } internal void Reset() { owner = null; support = null; sampled = (inStep = false); Awarding = false; Stamina.Reset(); } } [HarmonyPatch(typeof(Character), "CustomFixedUpdate")] internal static class PhysicsStep { private static void Prefix(Character __instance) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (Object.op_Implicit((Object)(object)val) && (Object)(object)val == (Object)(object)Player.m_localPlayer) { Plugin.Instance.Tracker.Begin(val); } } private static void Postfix(Character __instance, float dt) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (Object.op_Implicit((Object)(object)val) && (Object)(object)val == (Object)(object)Player.m_localPlayer) { Plugin.Instance.Tracker.End(val, dt); } } private static Exception Finalizer(Character __instance, Exception __exception) { if (__exception != null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { Plugin.Instance.Tracker.Reset(); } return __exception; } } [HarmonyPatch(typeof(Player), "CheckRun")] internal static class RunningContext { private static void Prefix(Player __instance, out object __state) { __state = Plugin.Instance.Tracker.Stamina.EnterRun(__instance); } private static Exception Finalizer(object __state, Exception __exception) { Plugin.Instance.Tracker.Stamina.ExitRun(__state); return __exception; } } [HarmonyPatch(typeof(Player), "RPC_UseStamina")] internal static class StaminaUsed { private static void Prefix(float ___m_stamina, out float __state) { __state = ___m_stamina; } private static void Postfix(Player __instance, float ___m_stamina, float __state) { Plugin.Instance.Tracker.Stamina.Record(__instance, __state, ___m_stamina); } } [BepInPlugin("kastante.IronShoulders", "IronShoulders", "0.1.0")] [BepInDependency("com.jotunn.jotunn", "2.30.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [SynchronizationMode(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "kastante.IronShoulders"; public const string Version = "0.1.0"; public const string SkillIdentifier = "kastante.IronShoulders.carrying"; internal static Plugin Instance; internal static SkillType Carrying; internal readonly ConfigurationState Settings = new ConfigurationState(); internal readonly MovementTracker Tracker = new MovementTracker(); private readonly List<ConfigEntry<double>> balanceEntries = new List<ConfigEntry<double>>(); private ConfigEntry<bool> modEnabled; private bool dirty; private bool synchronizing; private Harmony harmony; private SkillConfig skillConfig; internal bool Active { get { if (modEnabled.Value && Settings.Enabled && !dirty) { return !synchronizing; } return false; } } private void Awake() { //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Expected I4, but got Unknown //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Expected I4, but got Unknown //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Expected O, but got Unknown //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Expected I4, but got Unknown Instance = this; modEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, Description("Enable training and carry bonus; retain saved skill when disabled.")); Bind("Progression", "WalkTrainingHours", 35.0, "Active walking hours from level 0 to 100 at full load, without Rested."); Bind("Progression", "RunTrainingHours", 25.0, "Active running hours with full stamina bonus, without Rested."); Bind("Progression", "MinLoadRatio", 0.3, "No training at or below this fraction of current maximum carry weight."); Bind("Progression", "FullLoadRatio", 0.6, "Full training coefficient at this fraction of current maximum carry weight."); Bind("Progression", "MovementSpeedForFullXp", 1.0, "Own ground movement speed (m/s) needed to count all active time."); Bind("Progression", "StaminaBonusMax", 0.15, "Maximum relative bonus for stamina actually spent running."); Bind("Progression", "StaminaPerMeterReference", 1.0, "Running stamina spent per metre for the full stamina bonus."); Bind("Reward", "MaxCarryBonus", 150.0, "Additional carry weight at learned level 100, before the world multiplier."); ((BaseUnityPlugin)this).Config.SettingChanged += SettingsChanged; SynchronizationManager.OnSyncingConfiguration += SyncStarting; SynchronizationManager.OnConfigurationSynchronized += SyncFinished; ApplySettings(); skillConfig = new SkillConfig { Identifier = "kastante.IronShoulders.carrying", Name = "$ironshoulders_carrying", Description = "$ironshoulders_carrying_description", IncreaseStep = 1f }; Carrying = SkillManager.Instance.AddSkill(skillConfig); if ((int)Carrying == 0) { throw new InvalidOperationException("Cannot register carrying skill"); } CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "English"; localization.AddTranslation(ref text, new Dictionary<string, string> { { "ironshoulders_carrying", "Carrying" }, { "skill_" + (int)Carrying, "Carrying" }, { "ironshoulders_carrying_description", "Increase carry weight by travelling on foot with a load." } }); text = "Russian"; localization.AddTranslation(ref text, new Dictionary<string, string> { { "ironshoulders_carrying", "Переноска" }, { "skill_" + (int)Carrying, "Переноска" }, { "ironshoulders_carrying_description", "Увеличивает грузоподъёмность при передвижении пешком с грузом." } }); harmony = new Harmony("kastante.IronShoulders"); harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)("IronShoulders 0.1.0; skill=" + (int)Carrying + ". Game acceptance pending.")); } private static ConfigDescription Description(string text) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return new ConfigDescription(text, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } }); } private void Bind(string section, string key, double value, string description) { balanceEntries.Add(((BaseUnityPlugin)this).Config.Bind<double>(section, key, value, Description(description))); } private void SettingsChanged(object sender, SettingChangedEventArgs args) { dirty = true; Tracker.Reset(); } private void SyncStarting(object sender, SyncingConfigurationEventArgs args) { synchronizing = true; Tracker.Reset(); } private void SyncFinished(object sender, ConfigurationSynchronizationEventArgs args) { synchronizing = false; dirty = true; Tracker.Reset(); } private void ApplySettings() { BalanceInput input = new BalanceInput { WalkTrainingHours = balanceEntries[0].Value, RunTrainingHours = balanceEntries[1].Value, MinLoadRatio = balanceEntries[2].Value, FullLoadRatio = balanceEntries[3].Value, MovementSpeedForFullXp = balanceEntries[4].Value, StaminaBonusMax = balanceEntries[5].Value, StaminaPerMeterReference = balanceEntries[6].Value, MaxCarryBonus = balanceEntries[7].Value }; if (!Settings.Apply(modEnabled.Value, input, out var error)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Rejected balance settings; retaining the last valid snapshot (defaults on first load): " + error)); } dirty = false; Tracker.Reset(); } private void Update() { if (dirty && !synchronizing) { ApplySettings(); } if (!Active || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { Tracker.Reset(); } } internal void UpdateIcon(Skills skills) { //IL_0102: Unknown result type (might be due to invalid IL or missing references) Sprite val = null; if (Object.op_Implicit((Object)(object)ObjectDB.instance)) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("BeltStrength"); ItemDrop val2 = (Object.op_Implicit((Object)(object)itemPrefab) ? itemPrefab.GetComponent<ItemDrop>() : null); if (Object.op_Implicit((Object)(object)val2) && val2.m_itemData.m_shared.m_icons != null && val2.m_itemData.m_shared.m_icons.Length != 0) { val = val2.m_itemData.m_shared.m_icons[0]; } } if (!Object.op_Implicit((Object)(object)val)) { SkillDef val3 = skills.m_skills.Find((SkillDef def) => (int)def.m_skill == 102); if (val3 != null) { val = val3.m_icon; } } skillConfig.Icon = val; SkillDef val4 = skills.m_skills.Find((SkillDef def) => def.m_skill == Carrying); if (val4 != null) { val4.m_icon = val; } if (GameState.Data.Invoke(skills).TryGetValue(Carrying, out var value)) { value.m_info.m_icon = val; } } private void OnDestroy() { ((BaseUnityPlugin)this).Config.SettingChanged -= SettingsChanged; SynchronizationManager.OnSyncingConfiguration -= SyncStarting; SynchronizationManager.OnConfigurationSynchronized -= SyncFinished; Tracker.Reset(); Harmony obj = harmony; if (obj != null) { obj.UnpatchAll("kastante.IronShoulders"); } if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } } namespace IronShoulders.Core { public sealed class BalanceInput { public double WalkTrainingHours = 35.0; public double RunTrainingHours = 25.0; public double MinLoadRatio = 0.3; public double FullLoadRatio = 0.6; public double MovementSpeedForFullXp = 1.0; public double StaminaBonusMax = 0.15; public double StaminaPerMeterReference = 1.0; public double MaxCarryBonus = 150.0; } public sealed class Balance { public readonly double WalkTrainingHours; public readonly double RunTrainingHours; public readonly double MinLoadRatio; public readonly double FullLoadRatio; public readonly double MovementSpeedForFullXp; public readonly double StaminaBonusMax; public readonly double StaminaPerMeterReference; public readonly double MaxCarryBonus; public readonly double BaseXpPerSecond; public readonly double RunMultiplier; public static Balance Default => new Balance(new BalanceInput()); private Balance(BalanceInput input) { WalkTrainingHours = input.WalkTrainingHours; RunTrainingHours = input.RunTrainingHours; MinLoadRatio = input.MinLoadRatio; FullLoadRatio = input.FullLoadRatio; MovementSpeedForFullXp = input.MovementSpeedForFullXp; StaminaBonusMax = input.StaminaBonusMax; StaminaPerMeterReference = input.StaminaPerMeterReference; MaxCarryBonus = input.MaxCarryBonus; BaseXpPerSecond = Progression.TotalCost / (WalkTrainingHours * 3600.0); RunMultiplier = WalkTrainingHours / (RunTrainingHours * (1.0 + StaminaBonusMax)); } public static bool Finite(double value) { if (!double.IsNaN(value)) { return !double.IsInfinity(value); } return false; } private static bool Positive(double value) { if (Finite(value)) { return value > 0.0; } return false; } public static bool TryCreate(BalanceInput input, out Balance result, out string error) { result = null; error = null; if (input == null) { error = "Missing balance settings"; } else if (!Positive(input.WalkTrainingHours * 3600.0) || !Positive(input.RunTrainingHours * 3600.0)) { error = "Training hours must be finite and positive"; } else if (!Finite(input.MinLoadRatio) || !Finite(input.FullLoadRatio) || input.MinLoadRatio < 0.0 || input.MinLoadRatio >= input.FullLoadRatio || input.FullLoadRatio > 1.0) { error = "Load ratios must satisfy 0 <= MinLoadRatio < FullLoadRatio <= 1"; } else if (!Positive(input.MovementSpeedForFullXp) || !Positive(input.StaminaPerMeterReference)) { error = "Movement speed and stamina reference must be finite and positive"; } else if (!Finite(input.StaminaBonusMax) || input.StaminaBonusMax < 0.0 || !Finite(input.MaxCarryBonus) || input.MaxCarryBonus < 0.0 || input.MaxCarryBonus > 1.7014117331926443E+38) { error = "Bonuses must be finite, nonnegative and representable by the game"; } else if (!Positive(input.RunTrainingHours * (1.0 + input.StaminaBonusMax)) || input.WalkTrainingHours < input.RunTrainingHours * (1.0 + input.StaminaBonusMax)) { error = "WalkTrainingHours must be >= RunTrainingHours * (1 + StaminaBonusMax)"; } if (error != null) { return false; } Balance balance = new Balance(input); if (!Positive(balance.BaseXpPerSecond) || !Positive(balance.RunMultiplier) || balance.BaseXpPerSecond * balance.RunMultiplier * (1.0 + balance.StaminaBonusMax) > 3.4028234663852886E+38 || balance.BaseXpPerSecond > 3.4028234663852886E+38) { error = "Derived experience rate is outside the supported range"; return false; } result = balance; return true; } public double LoadFactor(double weight, double capacity) { if (!Finite(weight) || !Positive(capacity) || weight < 0.0 || weight > capacity) { return 0.0; } return Clamp01((weight / capacity - MinLoadRatio) / (FullLoadRatio - MinLoadRatio)); } public double Experience(double seconds, double distance, double weight, double capacity, bool running, double stamina) { if (!Positive(seconds) || !Positive(distance)) { return 0.0; } double num = Math.Min(seconds, distance / MovementSpeedForFullXp); double num2 = ((running && Finite(stamina) && stamina > 0.0) ? (1.0 + StaminaBonusMax * Clamp01(stamina / distance / StaminaPerMeterReference)) : 1.0); double num3 = BaseXpPerSecond * num * LoadFactor(weight, capacity) * (running ? RunMultiplier : 1.0) * num2; if (!Finite(num3) || !(num3 > 0.0) || !(num3 <= 3.4028234663852886E+38)) { return 0.0; } return num3; } public double CarryBonus(double learnedLevel) { if (!Finite(learnedLevel)) { return 0.0; } return MaxCarryBonus * Clamp01(learnedLevel / 100.0); } public static double Clamp01(double value) { return Math.Max(0.0, Math.Min(1.0, value)); } } public sealed class ConfigurationState { public Balance Current { get; private set; } = Balance.Default; public bool Enabled { get; private set; } = true; public bool Apply(bool enabled, BalanceInput input, out string error) { Enabled = enabled; if (!Balance.TryCreate(input, out var result, out error)) { return false; } Current = result; return true; } } [Flags] public enum MotionBlock { None = 0, NotLocalOwner = 1, NoInput = 2, Airborne = 4, Overloaded = 8, Swimming = 0x10, Flying = 0x20, Riding = 0x40, Attached = 0x80, Ship = 0x100, Knockback = 0x200, Dodge = 0x400, Sliding = 0x800, Unavailable = 0x1000, Attack = 0x2000 } public struct Point { public readonly double X; public readonly double Y; public readonly double Z; public Point(double x, double y, double z) { X = x; Y = y; Z = z; } public static double Distance(Point a, Point b) { double num = a.X - b.X; double num2 = a.Y - b.Y; double num3 = a.Z - b.Z; return Math.Sqrt(num * num + num2 * num2 + num3 * num3); } } public static class Motion { public static double OwnDistance(Point current, Point carriedStart, double expectedDistance, MotionBlock start, MotionBlock end, bool sameSupport) { if (start != MotionBlock.None || end != MotionBlock.None || !sameSupport || !Balance.Finite(expectedDistance) || expectedDistance < 0.0) { return 0.0; } double num = Point.Distance(current, carriedStart); double num2 = Math.Max(0.25, expectedDistance * 3.0); if (!Balance.Finite(num) || !(num <= num2)) { return 0.0; } return num; } } public sealed class RunStaminaMeter { private object actor; private object context; private bool active; private double spent; public void BeginStep(object owner) { Reset(); actor = owner; active = true; } public object EnterRun(object owner) { object result = context; context = owner; return result; } public void ExitRun(object previous) { context = previous; } public void Record(object owner, double before, double after) { if (active && actor == owner && context == owner && Balance.Finite(before) && Balance.Finite(after) && before > after) { spent += Math.Max(0.0, before) - Math.Max(0.0, after); } } public double EndStep() { double result = spent; Reset(); return result; } public void Reset() { active = false; actor = (context = null); spent = 0.0; } } public static class Progression { public static readonly double TotalCost = SumCost(); public static double Cost(double level) { return 0.5 * Math.Pow(Math.Floor(level + 1.0), 1.5) + 0.5; } private static double SumCost() { double num = 0.0; for (int i = 0; i < 100; i++) { num += Cost(i); } return num; } public static bool TryRaise(int skill, int carryingSkill, ref float level, ref float accumulator, double factor, double increaseStep, double worldMultiplier, out bool leveled, bool enabled = true) { leveled = false; if (skill != carryingSkill) { return false; } if (!enabled) { return true; } double num = factor * increaseStep * worldMultiplier; if (!Balance.Finite(num) || num <= 0.0) { return true; } if (!Balance.Finite(level) || level < 0f) { level = 0f; } if (level >= 100f) { level = 100f; accumulator = 0f; return true; } double num2 = ((Balance.Finite(accumulator) && accumulator > 0f) ? accumulator : 0f); num2 += num; while (level < 100f && num2 >= Cost(level)) { num2 -= Cost(level); level = Math.Min(100f, level + 1f); leveled = true; } accumulator = ((level >= 100f) ? 0f : ((float)num2)); return true; } } }