using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using Valgrind.Configuration; using Valgrind.Logic; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 Valgrind { [BepInPlugin("com.bigai.valgrind", "Valgrind", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class ValgrindPlugin : BaseUnityPlugin { public const string PluginGUID = "com.bigai.valgrind"; public const string PluginName = "Valgrind"; public const string PluginVersion = "1.0.1"; private Harmony? _harmony; public static ValgrindPlugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; try { Log.LogInfo((object)"══════════════════════════════════════════"); Log.LogInfo((object)" Valgrind v1.0.1 loading..."); Log.LogInfo((object)"══════════════════════════════════════════"); ModConfig.Initialize(((BaseUnityPlugin)this).Config); _harmony = new Harmony("com.bigai.valgrind"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); Log.LogInfo((object)"[Valgrind] All Harmony patches applied successfully."); Log.LogInfo((object)"[Valgrind] Ready. Dynamic death penalty active with Jötunn ServerSync."); } catch (Exception arg) { Log.LogError((object)string.Format("[{0}] Failed to initialize: {1}", "Valgrind", arg)); } } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Log.LogInfo((object)"[Valgrind] Unloaded."); } } } namespace Valgrind.Patches { [HarmonyPatch(typeof(Skills), "OnDeath")] public static class SkillsDeathPatch { [HarmonyPrefix] public static bool Prefix(Skills __instance) { if ((Object)(object)__instance == (Object)null) { return true; } try { SkillLossCalculator.ApplyDeathPenalty(__instance); return false; } catch (Exception arg) { ValgrindPlugin.Log.LogError((object)$"[Valgrind] Error applying dynamic death penalty: {arg}"); return true; } } } } namespace Valgrind.Logic { public static class SkillLossCalculator { public static void ApplyDeathPenalty(Skills skills) { if ((Object)(object)skills == (Object)null) { return; } List skillList = skills.GetSkillList(); if (skillList != null && skillList.Count != 0) { CalculationMode value = ModConfig.CalculationModeEntry.Value; bool value2 = ModConfig.ResetAccumulatorOnDeath.Value; bool value3 = ModConfig.EnableDebugLogging.Value; if (value == CalculationMode.PerSkill) { ApplyPerSkillDeathPenalty(skillList, value2, value3); } else { ApplyPlayerWideDeathPenalty(skillList, value, value2, value3); } } } private static void ApplyPlayerWideDeathPenalty(List skillList, CalculationMode mode, bool resetAccumulator, bool debug) { float num = CalculateAverageSkillLevel(skillList); float num2 = ((mode != CalculationMode.ContinuousCurve) ? GetTierLossPercent(num) : GetCurveLossPercent(num)); float num3 = Mathf.Clamp01(1f - num2 / 100f); if (debug) { ValgrindPlugin.Log.LogInfo((object)$"[SkillLossCalculator] Mode={mode}, AvgLevel={num:F2}, LossPercent={num2:F2}%, Multiplier={num3:F4}, ResetAccumulator={resetAccumulator}"); } foreach (Skill skill in skillList) { if (skill != null && skill.m_level > 0f) { float level = skill.m_level; skill.m_level = Mathf.Max(0f, skill.m_level * num3); if (resetAccumulator) { skill.m_accumulator = 0f; } if (debug) { ValgrindPlugin.Log.LogInfo((object)string.Format("[SkillLossCalculator] Skill {0}: {1:F2} -> {2:F2}", ((object)Unsafe.As(ref skill.m_info?.m_skill)/*cast due to .constrained prefix*/).ToString() ?? "Unknown", level, skill.m_level)); } } } } private static void ApplyPerSkillDeathPenalty(List skillList, bool resetAccumulator, bool debug) { if (debug) { ValgrindPlugin.Log.LogInfo((object)$"[SkillLossCalculator] Applying Per-Skill death penalty. ResetAccumulator={resetAccumulator}"); } foreach (Skill skill in skillList) { if (skill != null && skill.m_level > 0f) { float tierLossPercent = GetTierLossPercent(skill.m_level); float num = Mathf.Clamp01(1f - tierLossPercent / 100f); float level = skill.m_level; skill.m_level = Mathf.Max(0f, skill.m_level * num); if (resetAccumulator) { skill.m_accumulator = 0f; } if (debug) { ValgrindPlugin.Log.LogInfo((object)string.Format("[SkillLossCalculator] Skill {0} (Level {1:F2}): Loss {2:F2}% -> New Level {3:F2}", ((object)Unsafe.As(ref skill.m_info?.m_skill)/*cast due to .constrained prefix*/).ToString() ?? "Unknown", level, tierLossPercent, skill.m_level)); } } } } public static float CalculateAverageSkillLevel(List skillList) { if (skillList == null || skillList.Count == 0) { return 0f; } List list = new List(); foreach (Skill skill in skillList) { if (skill != null && skill.m_level > 0f) { list.Add(skill.m_level); } } if (list.Count == 0) { return 0f; } if (ModConfig.UseTopNSkillsOnly.Value) { int count = Mathf.Clamp(ModConfig.TopNSkillsCount.Value, 1, list.Count); return list.OrderByDescending((float l) => l).Take(count).Average(); } return list.Average(); } public static float GetTierLossPercent(float level) { if (level < 25f) { return Mathf.Clamp(ModConfig.EarlyGameLossPercent.Value, 0f, 100f); } if (level < 50f) { return Mathf.Clamp(ModConfig.MidGameLossPercent.Value, 0f, 100f); } if (level <= 75f) { return Mathf.Clamp(ModConfig.LateGameLossPercent.Value, 0f, 100f); } return Mathf.Clamp(ModConfig.EndgameLossPercent.Value, 0f, 100f); } public static float GetCurveLossPercent(float level) { float num = Mathf.Clamp(ModConfig.CurveMaxLossPercent.Value, 0f, 100f); float num2 = Mathf.Clamp(ModConfig.CurveMinLossPercent.Value, 0f, 100f); float num3 = Mathf.Clamp01(level / 100f); return Mathf.Lerp(num, num2, num3); } } } namespace Valgrind.Configuration { public enum CalculationMode { TieredBrackets, ContinuousCurve, PerSkill } public static class ModConfig { public static ConfigEntry CalculationModeEntry { get; private set; } public static ConfigEntry UseTopNSkillsOnly { get; private set; } public static ConfigEntry TopNSkillsCount { get; private set; } public static ConfigEntry ResetAccumulatorOnDeath { get; private set; } public static ConfigEntry EnableDebugLogging { get; private set; } public static ConfigEntry EarlyGameLossPercent { get; private set; } public static ConfigEntry MidGameLossPercent { get; private set; } public static ConfigEntry LateGameLossPercent { get; private set; } public static ConfigEntry EndgameLossPercent { get; private set; } public static ConfigEntry CurveMaxLossPercent { get; private set; } public static ConfigEntry CurveMinLossPercent { get; private set; } public static void Initialize(ConfigFile config) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Expected O, but got Unknown //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Expected O, but got Unknown //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Expected O, but got Unknown //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Expected O, but got Unknown //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Expected O, but got Unknown //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Expected O, but got Unknown //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Expected O, but got Unknown //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Expected O, but got Unknown //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Expected O, but got Unknown //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Expected O, but got Unknown CalculationModeEntry = config.Bind("1 - General", "CalculationMode", CalculationMode.TieredBrackets, new ConfigDescription("Method used to calculate dynamic skill loss on death:\n- TieredBrackets: Discrete loss % based on overall average skill level.\n- ContinuousCurve: Smooth linear/curved scaling between Max and Min loss % based on average skill.\n- PerSkill: Evaluates each skill independently against the Tiered Brackets based on its own level (instead of character average).", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); UseTopNSkillsOnly = config.Bind("1 - General", "UseTopNSkillsOnly", false, new ConfigDescription("If true, average skill level is computed using only the player's top N highest skills (reflecting primary build). If false, averages all discovered skills (level > 0).", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); TopNSkillsCount = config.Bind("1 - General", "TopNSkillsCount", 5, new ConfigDescription("Number of top skills to factor into the average when UseTopNSkillsOnly is enabled.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 20), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); ResetAccumulatorOnDeath = config.Bind("1 - General", "ResetAccumulatorOnDeath", true, new ConfigDescription("If true, partial XP progress toward the next skill level is wiped to 0 on death (vanilla behavior). If false, progress toward the next level is preserved.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); EnableDebugLogging = config.Bind("1 - General", "EnableDebugLogging", false, new ConfigDescription("Enable verbose logging of skill loss calculations to the BepInEx console.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = false } })); EarlyGameLossPercent = config.Bind("2 - Tiered Brackets", "EarlyGameLossPercent", 8f, new ConfigDescription("Skill loss % for skill/average levels < 25. (Vanilla baseline is 5.0%)", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); MidGameLossPercent = config.Bind("2 - Tiered Brackets", "MidGameLossPercent", 5f, new ConfigDescription("Skill loss % for skill/average levels between 25 and 50. (Vanilla baseline is 5.0%)", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); LateGameLossPercent = config.Bind("2 - Tiered Brackets", "LateGameLossPercent", 2.5f, new ConfigDescription("Skill loss % for skill/average levels between 50 and 75.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); EndgameLossPercent = config.Bind("2 - Tiered Brackets", "EndgameLossPercent", 1f, new ConfigDescription("Skill loss % for skill/average levels > 75.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); CurveMaxLossPercent = config.Bind("3 - Continuous Curve", "CurveMaxLossPercent", 8f, new ConfigDescription("Maximum skill loss percentage (applied at skill level 0).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); CurveMinLossPercent = config.Bind("3 - Continuous Curve", "CurveMinLossPercent", 1f, new ConfigDescription("Minimum skill loss percentage (applied at skill level 100).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 100f), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); } } }