Decompiled source of CarryWeightSkill v1.0.1

plugins/CarrySkill.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CarrySkill.Patches;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Managers;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Carry Skill")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Carry Skill")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e6b72c5a-9fe8-49d7-855e-8165656a6777")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CarrySkill
{
	[BepInPlugin("shadymods.carryskill.carry_weight", "Carry Weight Skill", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class CarrySkillPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "shadymods.carryskill.carry_weight";

		public const string PluginName = "Carry Weight Skill";

		public const string PluginVersion = "1.0.1";

		public const string SkillIdentifier = "com.shadymods.carryskill.carry_weight_v1";

		public static SkillType CarrySkillType { get; private set; }

		internal static ManualLogSource CarrySkillLog { get; private set; }

		internal static ConfigEntry<bool> LogXpGain { get; private set; }

		internal static ConfigEntry<float> XpLogIntervalSeconds { get; private set; }

		private void Awake()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			//IL_00c6: Expected O, but got Unknown
			CarrySkillLog = ((BaseUnityPlugin)this).Logger;
			LogXpGain = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "LogCarryXp", false, "When true, logs carry skill XP gains (accumulator progress), current level, and progress to the next level.");
			XpLogIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Logging", "XpLogIntervalSeconds", 1f, "Seconds between XP log lines while training. Use 0 for every physics frame (LogDebug, very verbose).");
			CarrySkillType = SkillManager.Instance.AddSkill(new SkillConfig
			{
				Identifier = "com.shadymods.carryskill.carry_weight_v1",
				Name = "Carry Weight",
				Description = "Gain skill while sprinting or swimming with a heavy load (50%+ of max weight). No XP while over-encumbered. Heavier loads train faster. Each level adds +3 carry weight (up to +300 at level 100).",
				IncreaseStep = 1f
			});
			Harmony val = new Harmony("shadymods.carryskill.carry_weight");
			val.PatchAll();
			Game.isModded = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Carry Weight Skill loaded.");
			TryPatchGetMaxCarryWeight(val);
			TryPatchUpdateWalking(val);
			TryPatchSwimming(val);
		}

		private static void TryPatchSwimming(Harmony harmony)
		{
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Expected O, but got Unknown
			Type[] array = new Type[2]
			{
				typeof(Vector3),
				typeof(float)
			};
			MethodBase methodBase = AccessTools.DeclaredMethod(typeof(Player), "OnSwimming", array, (Type[])null) ?? AccessTools.DeclaredMethod(typeof(Character), "OnSwimming", array, (Type[])null) ?? AccessTools.DeclaredMethod(typeof(Humanoid), "OnSwimming", array, (Type[])null);
			if (methodBase != null)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(CarrySkillPatches), "OnSwimming_Postfix", new Type[3]
				{
					typeof(Character),
					typeof(Vector3),
					typeof(float)
				}, (Type[])null);
				if (methodInfo == null)
				{
					CarrySkillLog.LogError((object)"CarrySkill: Could not resolve OnSwimming_Postfix — swim carry XP will not apply.");
					return;
				}
				try
				{
					harmony.Patch(methodBase, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					return;
				}
				catch (Exception ex)
				{
					CarrySkillLog.LogError((object)("CarrySkill: Manual OnSwimming patch failed: " + ex));
					return;
				}
			}
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(Character), "UpdateSwimming", new Type[1] { typeof(float) }, (Type[])null);
			if (methodInfo2 == null)
			{
				CarrySkillLog.LogError((object)"CarrySkill: Could not resolve Player/Character.OnSwimming or Character.UpdateSwimming — swim carry XP will not apply.");
				return;
			}
			MethodInfo methodInfo3 = AccessTools.Method(typeof(CarrySkillPatches), "UpdateSwimming_Postfix", new Type[2]
			{
				typeof(Character),
				typeof(float)
			}, (Type[])null);
			if (methodInfo3 == null)
			{
				CarrySkillLog.LogError((object)"CarrySkill: Could not resolve UpdateSwimming_Postfix — swim carry XP will not apply.");
				return;
			}
			try
			{
				harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex2)
			{
				CarrySkillLog.LogError((object)("CarrySkill: Manual UpdateSwimming patch failed: " + ex2));
			}
		}

		private static void TryPatchGetMaxCarryWeight(Harmony harmony)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Player), "GetMaxCarryWeight", Type.EmptyTypes, (Type[])null);
			if (methodInfo == null)
			{
				CarrySkillLog.LogError((object)"CarrySkill: Could not resolve Player.GetMaxCarryWeight() — carry weight bonus will not apply.");
				return;
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(CarrySkillPatches), "GetMaxCarryWeight_Postfix", new Type[2]
			{
				typeof(Player),
				typeof(float).MakeByRefType()
			}, (Type[])null);
			if (methodInfo2 == null)
			{
				CarrySkillLog.LogError((object)"CarrySkill: Could not resolve GetMaxCarryWeight_Postfix — carry weight bonus will not apply.");
				return;
			}
			try
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				CarrySkillLog.LogError((object)("CarrySkill: Manual GetMaxCarryWeight patch failed: " + ex));
			}
		}

		private static void TryPatchUpdateWalking(Harmony harmony)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Character), "UpdateWalking", new Type[1] { typeof(float) }, (Type[])null);
			if (methodInfo == null)
			{
				CarrySkillLog.LogError((object)"CarrySkill: Could not resolve Character.UpdateWalking(float) — run/sprint carry XP will not apply.");
				return;
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(CarrySkillPatches), "UpdateWalking_Postfix", new Type[2]
			{
				typeof(Character),
				typeof(float)
			}, (Type[])null);
			if (methodInfo2 == null)
			{
				CarrySkillLog.LogError((object)"CarrySkill: Could not resolve UpdateWalking_Postfix — run/sprint carry XP will not apply.");
				return;
			}
			try
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				CarrySkillLog.LogError((object)("CarrySkill: Manual UpdateWalking patch failed: " + ex));
			}
		}
	}
}
namespace CarrySkill.Patches
{
	public static class CarrySkillPatches
	{
		private const float MinFill = 0.5f;

		private const float MaxLerpFill = 1f;

		private const float SwimVelThreshold = 0.1f;

		private const float SwimVelThresholdHeavy = 0.001f;

		private static float _xpAccumForLog;

		private static float _xpLogWindowStart = -1f;

		private static float _lastZeroGainWarningTime = -1000f;

		private static bool _loggedMissingSkillsGetSkill;

		private static readonly MethodInfo SkillsGetSkill = AccessTools.Method(typeof(Skills), "GetSkill", new Type[1] { typeof(SkillType) }, (Type[])null);

		internal static void GetMaxCarryWeight_Postfix(Player __instance, ref float __result)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && ((Character)__instance).IsPlayer())
			{
				Skills skills = ((Character)__instance).GetSkills();
				if (!((Object)(object)skills == (Object)null))
				{
					float skillLevel = skills.GetSkillLevel(CarrySkillPlugin.CarrySkillType);
					__result += skillLevel * 3f;
				}
			}
		}

		internal static void UpdateWalking_Postfix(Character __instance, float dt)
		{
			if (!(dt <= 0f))
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !((Character)val).IsDead() && !((Character)val).IsSwimming() && !((Character)val).IsEncumbered() && Traverse.Create((object)__instance).Field("m_running").GetValue<bool>())
				{
					TryApplyRunCarryXp(val, dt, "Run");
				}
			}
		}

		internal static void OnSwimming_Postfix(Character __instance, Vector3 targetVel, float dt)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !((Character)val).IsDead() && !((Character)val).IsEncumbered() && TryGetFill(val, out var fill))
			{
				float num = ((fill >= 0.5f) ? 0.001f : 0.1f);
				bool flag = ((Vector3)(ref targetVel)).magnitude > num;
				if (!flag && fill >= 0.5f && TryGetSwimMoveSpeed(val, out var horizontalSpeed))
				{
					flag = horizontalSpeed > num;
				}
				if (flag)
				{
					TryApplySwimCarryXp(val, dt);
				}
			}
		}

		internal static void UpdateSwimming_Postfix(Character __instance, float dt)
		{
			if (dt <= 0f)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !((Character)val).IsDead() && ((Character)val).IsSwimming() && !((Character)val).IsEncumbered() && TryGetSwimMoveSpeed(val, out var horizontalSpeed))
			{
				float fill;
				float num = ((TryGetFill(val, out fill) && fill >= 0.5f) ? 0.001f : 0.1f);
				if (!(horizontalSpeed <= num))
				{
					TryApplySwimCarryXp(val, dt);
				}
			}
		}

		private static bool TryGetSwimMoveSpeed(Player player, out float horizontalSpeed)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			horizontalSpeed = 0f;
			Rigidbody value = Traverse.Create((object)player).Field("m_body").GetValue<Rigidbody>();
			if ((Object)(object)value == (Object)null)
			{
				return false;
			}
			Vector3 linearVelocity = value.linearVelocity;
			linearVelocity.y = 0f;
			horizontalSpeed = ((Vector3)(ref linearVelocity)).magnitude;
			return true;
		}

		private static void TryApplyRunCarryXp(Player player, float dt, string sourceLabel)
		{
			Skills skills = ((Character)player).GetSkills();
			if (!((Object)(object)skills == (Object)null) && !(dt <= 0f) && TryGetFill(player, out var fill) && !(fill < 0.5f))
			{
				float num = WeightFillMultiplier(fill);
				float raiseFactor = GetIncreaseStep(skills, (SkillType)102) * num * dt;
				ApplyCarrySkillXp(player, skills, raiseFactor, sourceLabel);
			}
		}

		private static void TryApplySwimCarryXp(Player player, float dt)
		{
			Skills skills = ((Character)player).GetSkills();
			if (!((Object)(object)skills == (Object)null) && !(dt <= 0f) && TryGetFill(player, out var fill) && !(fill < 0.5f))
			{
				float num = WeightFillMultiplier(fill);
				float raiseFactor = GetIncreaseStep(skills, (SkillType)103) * num * dt;
				ApplyCarrySkillXp(player, skills, raiseFactor, "Swim");
			}
		}

		private static bool TryGetFill(Player player, out float fill)
		{
			fill = 0f;
			float maxCarryWeight = player.GetMaxCarryWeight();
			if (maxCarryWeight <= 0f)
			{
				return false;
			}
			float totalWeight = ((Humanoid)player).GetInventory().GetTotalWeight();
			fill = Mathf.Clamp01(totalWeight / maxCarryWeight);
			return true;
		}

		private static void ApplyCarrySkillXp(Player player, Skills skills, float raiseFactor, string sourceLabel)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			Skill orCreateCarrySkill = GetOrCreateCarrySkill(skills);
			if (orCreateCarrySkill == null)
			{
				if (!_loggedMissingSkillsGetSkill && CarrySkillPlugin.CarrySkillLog != null)
				{
					_loggedMissingSkillsGetSkill = true;
					CarrySkillPlugin.CarrySkillLog.LogError((object)"CarrySkill: Skills.GetSkill reflection failed — carry XP logging may be incomplete.");
				}
				((Character)player).RaiseSkill(CarrySkillPlugin.CarrySkillType, raiseFactor);
				return;
			}
			float nextLevelRequirement = GetNextLevelRequirement(orCreateCarrySkill);
			float accumulator = orCreateCarrySkill.m_accumulator;
			float level = orCreateCarrySkill.m_level;
			((Character)player).RaiseSkill(CarrySkillPlugin.CarrySkillType, raiseFactor);
			float num = ComputeAccumulatorGain(accumulator, level, nextLevelRequirement, orCreateCarrySkill.m_accumulator, orCreateCarrySkill.m_level);
			if (raiseFactor > 1E-08f && num <= 0f && CarrySkillPlugin.CarrySkillLog != null && Time.time - _lastZeroGainWarningTime > 30f)
			{
				_lastZeroGainWarningTime = Time.time;
				CarrySkillPlugin.CarrySkillLog.LogWarning((object)($"CarryWeight: RaiseSkill had no accumulator gain (factor={raiseFactor:E3}). " + "At skill 100, or if a status blocks skill XP, this is normal."));
			}
			LogCarryXpIfEnabled(num, orCreateCarrySkill, sourceLabel);
		}

		private static Skill GetOrCreateCarrySkill(Skills skills)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Skill val = TryGetCarrySkillInstance(skills);
			if (val != null)
			{
				return val;
			}
			if (SkillsGetSkill == null)
			{
				return null;
			}
			object? obj = SkillsGetSkill.Invoke(skills, new object[1] { CarrySkillPlugin.CarrySkillType });
			return (Skill)((obj is Skill) ? obj : null);
		}

		private static Skill TryGetCarrySkillInstance(Skills skills)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected I4, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<SkillType, Skill> value = Traverse.Create((object)skills).Field("m_skillData").GetValue<Dictionary<SkillType, Skill>>();
			if (value == null)
			{
				return null;
			}
			SkillType carrySkillType = CarrySkillPlugin.CarrySkillType;
			if (value.TryGetValue(carrySkillType, out var value2))
			{
				return value2;
			}
			int num = (int)carrySkillType;
			if (num != 0 && num != int.MinValue)
			{
				SkillType val = (SkillType)Math.Abs(num);
				if (val != carrySkillType && value.TryGetValue(val, out value2))
				{
					return value2;
				}
				SkillType val2 = (SkillType)(-num);
				if (val2 != carrySkillType && value.TryGetValue(val2, out value2))
				{
					return value2;
				}
			}
			return null;
		}

		private static float GetNextLevelRequirement(Skill skill)
		{
			return Traverse.Create((object)skill).Method("GetNextLevelRequirement", Array.Empty<object>()).GetValue<float>();
		}

		private static float ComputeAccumulatorGain(float accBefore, float levelBefore, float nextReqBefore, float accAfter, float levelAfter)
		{
			if (levelAfter > levelBefore)
			{
				return nextReqBefore - accBefore + accAfter;
			}
			return accAfter - accBefore;
		}

		private static void LogCarryXpIfEnabled(float accumulatorGain, Skill skill, string sourceLabel)
		{
			if (!CarrySkillPlugin.LogXpGain.Value || CarrySkillPlugin.CarrySkillLog == null || accumulatorGain <= 0f)
			{
				return;
			}
			float value = CarrySkillPlugin.XpLogIntervalSeconds.Value;
			float time = Time.time;
			if (value <= 0f)
			{
				WriteXpLogLine(accumulatorGain, skill, sourceLabel, singleFrame: true);
				return;
			}
			if (_xpLogWindowStart < 0f)
			{
				_xpLogWindowStart = time;
			}
			_xpAccumForLog += accumulatorGain;
			if (!(time - _xpLogWindowStart < value))
			{
				WriteXpLogLine(_xpAccumForLog, skill, sourceLabel, singleFrame: false);
				_xpAccumForLog = 0f;
				_xpLogWindowStart = time;
			}
		}

		private static void WriteXpLogLine(float accumulatorDelta, Skill skill, string sourceLabel, bool singleFrame)
		{
			float level = skill.m_level;
			float nextLevelRequirement = GetNextLevelRequirement(skill);
			float accumulator = skill.m_accumulator;
			float num = ((level >= 99.999f) ? 0f : Mathf.Max(0f, nextLevelRequirement - accumulator));
			string text = string.Format("{0}: +{1:F4} skill XP (accumulator){2} | ", sourceLabel, accumulatorDelta, singleFrame ? "" : " [sum over interval]") + $"level {level:F2} | {num:F2} until next level";
			if (singleFrame)
			{
				CarrySkillPlugin.CarrySkillLog.LogDebug((object)text);
			}
			else
			{
				CarrySkillPlugin.CarrySkillLog.LogInfo((object)text);
			}
		}

		private static float WeightFillMultiplier(float fillClamped01)
		{
			if (fillClamped01 >= 1f)
			{
				return 2f;
			}
			float num = (fillClamped01 - 0.5f) / 0.5f;
			return Mathf.Lerp(1f, 2f, num);
		}

		private static float GetIncreaseStep(Skills skills, SkillType type)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (skills?.m_skills == null)
			{
				return 1f;
			}
			foreach (SkillDef skill in skills.m_skills)
			{
				if (skill.m_skill == type)
				{
					return skill.m_increseStep;
				}
			}
			return 1f;
		}
	}
}