Decompiled source of Elins PEAK v0.4.7
BepInEx/plugins/Elins_PEAK/PEAKUsageSkills.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using PEAKLib.UI; using PEAKLib.UI.Elements; using PEAKUsageSkills.Config; using PEAKUsageSkills.Core; using PEAKUsageSkills.Diagnostics; using PEAKUsageSkills.Effects; using PEAKUsageSkills.GameAdapters; using PEAKUsageSkills.GameAdapters.Patches; using PEAKUsageSkills.Localization; using PEAKUsageSkills.Persistence; using PEAKUsageSkills.Tracking; using PEAKUsageSkills.UI; using Peak.Afflictions; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.SceneManagement; using UnityEngine.UI; using Zorro.Core.Serizalization; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PEAKUsageSkills")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.4.7.0")] [assembly: AssemblyInformationalVersion("0.4.7+ff2a3c774b0d6323303de590055096131c3b177a")] [assembly: AssemblyProduct("PEAKUsageSkills")] [assembly: AssemblyTitle("PEAKUsageSkills")] [assembly: AssemblyVersion("0.4.7.0")] [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 PEAKUsageSkills { [BepInPlugin("com.chiseled.peak.usageskills", "Elin's PEAK", "0.4.7")] [BepInProcess("PEAK.exe")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.chiseled.peak.usageskills"; public const string PluginName = "Elin's PEAK"; public const string PluginVersion = "0.4.7"; private Harmony? harmony; internal static UsageSkillsConfig Settings { get; private set; } internal static RunStateAdapter RunState { get; private set; } internal static ProgressionService Progression { get; private set; } internal static EffectService Effects { get; private set; } internal static DiagnosticHub? Diagnostics { get; private set; } internal static ManualLogSource ModLog { get; private set; } private void Awake() { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown ModLog = ((BaseUnityPlugin)this).Logger; Settings = new UsageSkillsConfig(((BaseUnityPlugin)this).Config); LocalizationService.Initialize(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? Paths.PluginPath); RunState = new RunStateAdapter(Settings); SaveStore store = new SaveStore(((BaseUnityPlugin)this).Logger); Progression = new ProgressionService(Settings, store, ((BaseUnityPlugin)this).Logger, RunState); Effects = new EffectService(Settings, Progression, RunState); Diagnostics = new DiagnosticHub(((BaseUnityPlugin)this).Logger, Settings, RunState, Progression, Effects); Progression.LevelChanged += OnSkillLevelChanged; harmony = new Harmony("com.chiseled.peak.usageskills"); PatchAllFailSoft(Assembly.GetExecutingAssembly()); ((Component)this).gameObject.AddComponent<ActivitySampler>(); ((Component)this).gameObject.AddComponent<InventorySkillController>(); ((Component)this).gameObject.AddComponent<DebugOverlay>(); PauseMenuIntegration.Register(); SceneManager.sceneLoaded += OnSceneLoaded; InspectPatchHealth(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Elin's PEAK 0.4.7 loaded. Save levels are player-owned; Airport XP is disabled and anti-farming policy is deferred for the first runtime pass."); } private void Update() { Progression.Tick(); Diagnostics?.Tick(); } private void OnApplicationQuit() { Progression.Flush(); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; Progression.LevelChanged -= OnSkillLevelChanged; PauseMenuIntegration.Unregister(); LocalizationService.Shutdown(); Progression.Flush(); Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) Progression.Flush(); ModLog.LogInfo((object)$"[UsageSkills:Scene] loaded={((Scene)(ref scene)).name} mode={mode} xpEligible={RunState.IsExperienceEligible}"); } private static void OnSkillLevelChanged(SkillId skillId, int level) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (skillId == SkillId.Strength && Settings.DebugAllSkillLevelOverride.Value < 0) { WeightPatch.RefreshLocalWeight($"StrengthLevel:{level}"); InventorySkillService.EnsureBackpackCapacity(InventorySkillService.TryGetEquippedBackpackData(Character.localCharacter, out var backpackType), backpackType, $"StrengthLevel:{level}"); } } private static void InspectPatchHealth() { RecordHook("StaminaCapacity", typeof(Character), "GetMaxStamina"); RecordHook("PassOutThreshold", typeof(CharacterAfflictions), "get_shouldPassOut"); RecordHook("PassOutRecovery", typeof(Character), "HandlePassedOut"); RecordHook("StaminaCost", typeof(Character), "UseStamina"); RecordHook("StaminaRegen", typeof(Character), "UpdateVariablesFixed"); RecordHook("StaminaHud", typeof(StaminaBar), "Update"); RecordHook("Weight", typeof(CharacterAfflictions), "UpdateWeight"); RecordHook("WallClimbing", typeof(CharacterClimbing), "GetRequestedPostition"); RecordHook("RopeClimbing", typeof(CharacterRopeHandling), "Update"); RecordHook("VineClimbing", typeof(CharacterVineClimbing), "FixedUpdate"); RecordHook("Athletics", typeof(CharacterMovement), "GetMovementForce"); RecordHook("Agility", typeof(CharacterMovement), "JumpRpc"); RecordHook("NormalFalls", typeof(CharacterMovement), "CheckFallDamage"); RecordHook("WallFalls", typeof(CharacterClimbing), "CheckFallDamage"); RecordHook("Afflictions", typeof(CharacterAfflictions), "AddStatus"); RecordHook("ConditionRecovery", typeof(CharacterAfflictions), "SubtractStatus"); RecordHook("Petrification", typeof(CharacterAfflictions), "AddPetrify", new Type[1] { typeof(int) }); RecordHook("BackpackInventory", typeof(BackpackData), "DeserializeValue"); RecordHook("BackpackWheel", typeof(BackpackWheel), "InitWheel"); RecordHook("WetGrip", typeof(WindChillZone), "ApplyStatus"); } private void PatchAllFailSoft(Assembly assembly) { Type[] types = assembly.GetTypes(); Array.Sort(types, (Type left, Type right) => string.CompareOrdinal(left.FullName, right.FullName)); Type[] array = types; foreach (Type type in array) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0) { try { harmony.CreateClassProcessor(type).Patch(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"[UsageSkills:Patch] Failed patch class {type.FullName}; its adapter will remain disabled. {arg}"); } } } } private static void RecordHook(string adapter, Type type, string methodName, Type[]? parameterTypes = null) { MethodInfo methodInfo = ((parameterTypes == null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : AccessTools.Method(type, methodName, parameterTypes, (Type[])null)); int num; if (methodInfo != null) { Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); num = ((patchInfo != null && patchInfo.Owners.Contains("com.chiseled.peak.usageskills")) ? 1 : 0); } else { num = 0; } bool healthy = (byte)num != 0; string hook = ((parameterTypes == null) ? (type.FullName + "." + methodName) : (type.FullName + "." + methodName + "(" + string.Join(",", Array.ConvertAll(parameterTypes, (Type value) => value.Name)) + ")")); Diagnostics?.RecordPatchHealth(adapter, healthy, hook); } } } namespace PEAKUsageSkills.UI { internal sealed class DebugOverlay : MonoBehaviour { private GUIStyle? labelStyle; private void OnGUI() { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_0064: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings.EnableMod.Value && Plugin.Settings.ShowDebugOverlay.Value && Plugin.Diagnostics != null) { if (labelStyle == null) { labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 14, wordWrap = false, richText = false, alignment = (TextAnchor)0 }; labelStyle.normal.textColor = Color.white; } Rect val = default(Rect); ((Rect)(ref val))..ctor(18f, 18f, 820f, 420f); GUI.Box(val, GUIContent.none); GUILayout.BeginArea(new Rect(((Rect)(ref val)).x + 12f, ((Rect)(ref val)).y + 10f, ((Rect)(ref val)).width - 24f, ((Rect)(ref val)).height - 20f)); GUILayout.Label(Plugin.Diagnostics.BuildOverlayText(), labelStyle, Array.Empty<GUILayoutOption>()); GUILayout.EndArea(); } } } internal static class PauseMenuIntegration { [CompilerGenerated] private static class <>O { public static Action <0>__OnLanguageChanged; public static BuilderDelegate <1>__BuildPauseMenu; } private const string RootObjectName = "UI_PEAKUsageSkills_ReleasePanels"; private const float PanelWidth = 390f; private const float HeaderHeight = 56f; private const float RowHeight = 32f; private static readonly Color MainPanelColor = new Color(0.66f, 0.09f, 0.15f, 1f); private static readonly Color ResiliencyPanelColor = new Color(0.08f, 0.3f, 0.68f, 1f); private static RectTransform? activeRoot; private static readonly SkillId[] MainSkills = new SkillId[10] { SkillId.Strength, SkillId.Endurance, SkillId.WallClimbing, SkillId.RopeClimbing, SkillId.VineClimbing, SkillId.Athletics, SkillId.Agility, SkillId.Vitality, SkillId.WetGrip, SkillId.ClimbingTenacity }; private static readonly SkillId[] ResiliencySkills = new SkillId[8] { SkillId.Toxicology, SkillId.ColdTolerance, SkillId.HeatTolerance, SkillId.DrowsyTolerance, SkillId.SporeTolerance, SkillId.HungerTolerance, SkillId.CurseTolerance, SkillId.PetrificationResistance }; public static void Register() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown LocalizationService.LanguageChanged += OnLanguageChanged; object obj = <>O.<1>__BuildPauseMenu; if (obj == null) { BuilderDelegate val = BuildPauseMenu; <>O.<1>__BuildPauseMenu = val; obj = (object)val; } MenuAPI.AddToPauseMenu((BuilderDelegate)obj); } public static void Unregister() { LocalizationService.LanguageChanged -= OnLanguageChanged; activeRoot = null; } private static void BuildPauseMenu(Transform parent) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) RemoveLegacyControls(parent); Transform val = parent.Find("UI_PEAKUsageSkills_ReleasePanels"); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } RectTransform component = new GameObject("UI_PEAKUsageSkills_ReleasePanels", new Type[2] { typeof(RectTransform), typeof(SkillPanelRefresher) }).GetComponent<RectTransform>(); ((Transform)component).SetParent(parent, false); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; activeRoot = component; BuildSection(component, "Main", LocalizationService.Get("section.main"), MainSkills, MainPanelColor, new Vector2(24f, -88f), alignRight: false); BuildSection(component, "Resiliency", LocalizationService.Get("section.resiliency"), ResiliencySkills, ResiliencyPanelColor, new Vector2(24f, -88f - SectionHeight(MainSkills.Length) - 18f), alignRight: false); SkillTooltip.Create(component); Refresh(component); Plugin.ModLog.LogInfo((object)"[UsageSkills:UI] release skill panels built; values refresh once when the pause UI opens"); } private static void BuildSection(RectTransform root, string sectionName, string title, SkillId[] skills, Color panelColor, Vector2 offset, bool alignRight) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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_007f: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_012b: 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_0161: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) PeakMenuButton val = MenuAPI.CreatePauseMenuButton(title); ((Object)((Component)val).gameObject).name = "UI_PEAKUsageSkills_Section_" + sectionName; RectTransform component = ((Component)val).GetComponent<RectTransform>(); ((Transform)component).SetParent((Transform)(object)root, false); Vector2 anchorMin = (component.anchorMax = (alignRight ? new Vector2(1f, 1f) : new Vector2(0f, 1f))); component.anchorMin = anchorMin; component.pivot = (alignRight ? new Vector2(1f, 1f) : new Vector2(0f, 1f)); component.anchoredPosition = offset; component.sizeDelta = new Vector2(390f, SectionHeight(skills.Length)); LayoutElement component2 = ((Component)val).GetComponent<LayoutElement>(); if ((Object)(object)component2 != (Object)null) { component2.ignoreLayout = true; } ConfigurePanelGraphics(val, panelColor); Button[] componentsInChildren = ((Component)val).GetComponentsInChildren<Button>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((Behaviour)componentsInChildren[i]).enabled = false; } TextMeshProUGUI[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<TextMeshProUGUI>(true); foreach (TextMeshProUGUI val3 in componentsInChildren2) { if (string.Equals(((TMP_Text)val3).text, title, StringComparison.OrdinalIgnoreCase)) { ((TMP_Text)val3).rectTransform.anchorMin = new Vector2(0f, 1f); ((TMP_Text)val3).rectTransform.anchorMax = new Vector2(1f, 1f); ((TMP_Text)val3).rectTransform.pivot = new Vector2(0.5f, 1f); ((TMP_Text)val3).rectTransform.anchoredPosition = new Vector2(0f, -8f); ((TMP_Text)val3).rectTransform.sizeDelta = new Vector2(-20f, 32f); ((TMP_Text)val3).fontSize = 26f; break; } } for (int j = 0; j < skills.Length; j++) { BuildSkillRow(component, skills[j], j, alignRight); } } private static void BuildSkillRow(RectTransform panel, SkillId skillId, int index, bool openTooltipLeft) { //IL_0044: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("UI_PEAKUsageSkills_" + skillId, new Type[3] { typeof(RectTransform), typeof(Image), typeof(SkillHoverTarget) }); RectTransform component = val.GetComponent<RectTransform>(); ((Transform)component).SetParent((Transform)(object)panel, false); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(0.5f, 1f); component.anchoredPosition = new Vector2(0f, -56f - (float)index * 32f); component.sizeDelta = new Vector2(-24f, 32f); ((Graphic)val.GetComponent<Image>()).color = new Color(1f, 1f, 1f, 0.001f); PeakText obj = MenuAPI.CreateText(string.Empty, "Text"); ((Component)obj).transform.SetParent((Transform)(object)component, false); RectTransform component2 = ((Component)obj).GetComponent<RectTransform>(); obj.SetFontSize(24f); Fill(component2, Vector2.zero, Vector2.zero); ((TMP_Text)obj.TextMesh).alignment = (TextAlignmentOptions)4097; ((TMP_Text)obj.TextMesh).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)obj.TextMesh).overflowMode = (TextOverflowModes)0; ((Graphic)obj.TextMesh).raycastTarget = false; ((TMP_Text)obj.TextMesh).richText = false; SkillHoverTarget component3 = val.GetComponent<SkillHoverTarget>(); component3.Skill = skillId; component3.OpenTooltipLeft = openTooltipLeft; } internal static void Refresh(RectTransform root) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) RefreshSectionTitle(root, "Main", "section.main"); RefreshSectionTitle(root, "Resiliency", "section.resiliency"); foreach (SkillId value in Enum.GetValues(typeof(SkillId))) { Transform? obj = FindDeep((Transform)(object)root, "UI_PEAKUsageSkills_" + value); object obj2; if (obj == null) { obj2 = null; } else { Transform obj3 = obj.Find("Text"); obj2 = ((obj3 != null) ? ((Component)obj3).GetComponent<PeakText>() : null); } PeakText val = (PeakText)obj2; if ((Object)(object)val != (Object)null) { val.SetText(GetSkillText(value)); Fill(((Component)val).GetComponent<RectTransform>(), Vector2.zero, Vector2.zero); } } } private static void RefreshSectionTitle(RectTransform root, string sectionName, string localizationKey) { Transform? obj = FindDeep((Transform)(object)root, "UI_PEAKUsageSkills_Section_" + sectionName); PeakMenuButton obj2 = ((obj != null) ? ((Component)obj).GetComponent<PeakMenuButton>() : null); if (obj2 != null) { obj2.SetText(LocalizationService.Get(localizationKey)); } } private static void OnLanguageChanged() { SkillTooltip.Hide(); if ((Object)(object)activeRoot != (Object)null) { Refresh(activeRoot); } } internal static string GetTooltipText(SkillId skillId) { int level = Plugin.Progression.GetLevel(skillId); switch (skillId) { case SkillId.Strength: { float value = (1f - SkillMath.AnchoredReductionMultiplier(level, Plugin.Settings.StrengthReductionPerLevel.Value)) * 100f; return LocalizationService.Get("tooltip.strength", LocalizationService.FormatNumber(value, 1), InventorySkillService.ExtraBackpackSlots); } case SkillId.Endurance: return LocalizationService.Get("tooltip.endurance", LocalizationService.FormatNumber((float)level * 0.5f, 1), LocalizationService.FormatNumber((float)level * 0.1f, 1)); case SkillId.WallClimbing: return ClimbingTooltip(LocalizationService.Get("term.wall"), level, Plugin.Settings.WallSpeedPerLevel.Value, Plugin.Settings.WallCostReductionPerLevel.Value); case SkillId.RopeClimbing: return ClimbingTooltip(LocalizationService.Get("term.rope"), level, Plugin.Settings.RopeSpeedPerLevel.Value, Plugin.Settings.RopeCostEfficiencyPerLevel.Value); case SkillId.VineClimbing: { float value = (1f - SkillMath.AnchoredReductionMultiplier(level, Plugin.Settings.VineCostEfficiencyPerLevel.Value)) * 100f; return LocalizationService.Get("tooltip.vine", LocalizationService.FormatNumber((float)level * 0.3f, 1), LocalizationService.FormatNumber(value, 1)); } case SkillId.Athletics: { float value = (1f - SkillMath.AnchoredReductionMultiplier(level, Plugin.Settings.AthleticsSprintEfficiencyPerLevel.Value)) * 100f; return LocalizationService.Get("tooltip.athletics", LocalizationService.FormatNumber(value, 1)); } case SkillId.Agility: { float value = (1f - SkillMath.AnchoredReductionMultiplier(level, Plugin.Settings.AgilityJumpEfficiencyPerLevel.Value)) * 100f; return LocalizationService.Get("tooltip.agility", LocalizationService.FormatNumber((float)level * 0.15f, 1), LocalizationService.FormatNumber(value, 1)); } case SkillId.Vitality: return ReductionTooltip(LocalizationService.Get("term.fall_injury"), level, Plugin.Settings.VitalityFallReductionPerLevel.Value); case SkillId.WetGrip: return ReductionTooltip(LocalizationService.Get("term.wet_grip_penalty"), level, Plugin.Settings.WetGripReductionPerLevel.Value); case SkillId.ClimbingTenacity: return ReductionTooltip(LocalizationService.Get("term.tenacity_penalty"), level, Plugin.Settings.ClimbingTenacityReductionPerLevel.Value); case SkillId.Toxicology: case SkillId.ColdTolerance: case SkillId.HeatTolerance: case SkillId.DrowsyTolerance: case SkillId.SporeTolerance: return ToleranceTooltip(LocalizationService.SkillName(skillId), level, hasNaturalRecovery: true); case SkillId.HungerTolerance: case SkillId.CurseTolerance: return ToleranceTooltip(LocalizationService.SkillName(skillId), level, hasNaturalRecovery: false); case SkillId.PetrificationResistance: return ToleranceTooltip(LocalizationService.SkillName(skillId), level, hasNaturalRecovery: false); default: return LocalizationService.SkillName(skillId); } } private static string ClimbingTooltip(string kind, int level, float speedRate, float costRate) { float value = (1f - SkillMath.AnchoredReductionMultiplier(level, costRate)) * 100f; return LocalizationService.Get("tooltip.climbing", kind, LocalizationService.FormatNumber(speedRate * 100f, 1), LocalizationService.FormatNumber((float)level * speedRate * 100f, 1), LocalizationService.FormatNumber(value, 1)); } private static string ReductionTooltip(string effect, int level, float rate) { float value = (1f - SkillMath.AnchoredReductionMultiplier(level, rate)) * 100f; return LocalizationService.Get("tooltip.reduction", effect, LocalizationService.FormatNumber(rate * 100f, 2), LocalizationService.FormatNumber(value, 1)); } private static string ToleranceTooltip(string condition, int level, bool hasNaturalRecovery) { float value = (1f - SkillMath.AnchoredReductionMultiplier(level, Plugin.Settings.ConditionResistancePerLevel.Value)) * 100f; string text = (hasNaturalRecovery ? LocalizationService.Get("tooltip.natural_recovery", LocalizationService.FormatNumber((float)level * Plugin.Settings.ConditionRecoveryPerLevel.Value * 100f, 1)) : ""); return LocalizationService.Get("tooltip.tolerance", condition, LocalizationService.FormatNumber(value, 1), text); } private static string GetSkillText(SkillId skillId) { int level = Plugin.Progression.GetLevel(skillId); int num = ((level < Plugin.Progression.MaximumLevel) ? SkillMath.ExperienceProgressPercent(Plugin.Progression.GetExperience(skillId), Plugin.Progression.GetExperienceToNextLevel(skillId)) : 0); return string.Format("{0} {1} {2:00}.{3:00}", LocalizationService.SkillName(skillId), LocalizationService.Get("level.abbreviation"), level, num); } private static float SectionHeight(int rowCount) { return 56f + (float)rowCount * 32f + 16f; } private static void ConfigurePanelGraphics(PeakMenuButton panel, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) panel.SetColor(color, true); Stretch(((Graphic)panel.Panel).rectTransform); ((Graphic)panel.Panel).raycastTarget = false; ((Transform)((Graphic)panel.Panel).rectTransform).SetAsFirstSibling(); ConfigureBorder(((Graphic)panel.BorderTop).rectTransform, top: true); ConfigureBorder(((Graphic)panel.BorderBottom).rectTransform, top: false); ((Graphic)panel.BorderTop).raycastTarget = false; ((Graphic)panel.BorderBottom).raycastTarget = false; } private static void Stretch(RectTransform rect) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.pivot = new Vector2(0.5f, 0.5f); rect.anchoredPosition = Vector2.zero; rect.sizeDelta = Vector2.zero; } private static void Fill(RectTransform rect, Vector2 minimumInset, Vector2 maximumInset) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.pivot = new Vector2(0.5f, 0.5f); rect.offsetMin = minimumInset; rect.offsetMax = maximumInset; } private static void ConfigureBorder(RectTransform border, bool top) { //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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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) Rect rect = border.rect; float num = Mathf.Max(3f, ((Rect)(ref rect)).height); border.anchorMin = new Vector2(0f, top ? 1f : 0f); border.anchorMax = new Vector2(1f, top ? 1f : 0f); border.pivot = new Vector2(0.5f, top ? 1f : 0f); border.anchoredPosition = Vector2.zero; border.sizeDelta = new Vector2(0f, num); } internal static string WrapTooltipText(string message, int maximumLineLength = 58) { if (ContainsCjk(message)) { int num = Math.Max(32, maximumLineLength - 14); StringBuilder stringBuilder = new StringBuilder(message.Length + 8); int num2 = 0; foreach (char c in message) { if (c == '\n') { stringBuilder.Append(c); num2 = 0; continue; } int num3 = ((!IsCjk(c)) ? 1 : 2); if (num2 > 0 && num2 + num3 > num) { stringBuilder.Append('\n'); num2 = 0; if (char.IsWhiteSpace(c)) { continue; } } stringBuilder.Append(c); num2 += num3; } return stringBuilder.ToString(); } string[] array = message.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); StringBuilder stringBuilder2 = new StringBuilder(message.Length + 8); int num4 = 0; string[] array2 = array; foreach (string text in array2) { if (num4 > 0 && num4 + 1 + text.Length > maximumLineLength) { stringBuilder2.Append('\n'); num4 = 0; } if (num4 > 0) { stringBuilder2.Append(' '); num4++; } stringBuilder2.Append(text); num4 += text.Length; } return stringBuilder2.ToString(); } private static bool ContainsCjk(string value) { for (int i = 0; i < value.Length; i++) { if (IsCjk(value[i])) { return true; } } return false; } private static bool IsCjk(char character) { if ((character < '\u3040' || character > 'ヿ') && (character < '㐀' || character > '鿿')) { if (character >= '가') { return character <= '\ud7af'; } return false; } return true; } private static void RemoveLegacyControls(Transform parent) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown string[] array = new string[3] { "UI_MainMenuButton_PEAKUsageSkills", "UI_MainMenuButton_PEAKUsageSkillsTestLevel", "UI_MainMenuButton_PEAKUsageSkillsReset" }; foreach (string text in array) { Transform val = parent.Find(text); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } List<GameObject> list = new List<GameObject>(); foreach (Transform item in parent) { Transform val2 = item; if (((Object)val2).name.StartsWith("UI_MainMenuButton_PEAKUsageSkillsStatus_", StringComparison.Ordinal)) { list.Add(((Component)val2).gameObject); } } foreach (GameObject item2 in list) { Object.Destroy((Object)(object)item2); } } private static Transform? FindDeep(Transform root, string objectName) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown foreach (Transform item in root) { Transform val = item; if (((Object)val).name == objectName) { return val; } Transform val2 = FindDeep(val, objectName); if ((Object)(object)val2 != (Object)null) { return val2; } } return null; } } internal sealed class SkillPanelRefresher : MonoBehaviour { private void OnEnable() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown PauseMenuIntegration.Refresh((RectTransform)((Component)this).transform); } } internal sealed class SkillHoverTarget : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler { public SkillId Skill { get; set; } public bool OpenTooltipLeft { get; set; } public void OnPointerEnter(PointerEventData eventData) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown SkillTooltip.Show((RectTransform)((Component)this).transform, PauseMenuIntegration.GetTooltipText(Skill), OpenTooltipLeft); } public void OnPointerExit(PointerEventData eventData) { SkillTooltip.Hide(); } } internal sealed class SkillTooltip : MonoBehaviour { private static SkillTooltip? instance; private RectTransform rect; private PeakText text; public static void Create(RectTransform parent) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("UI_PEAKUsageSkills_Tooltip", new Type[3] { typeof(RectTransform), typeof(Image), typeof(SkillTooltip) }); RectTransform component = val.GetComponent<RectTransform>(); ((Transform)component).SetParent((Transform)(object)parent, false); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); component.anchorMax = val2; component.anchorMin = val2; component.pivot = new Vector2(0f, 0.5f); component.sizeDelta = new Vector2(500f, 124f); ((Graphic)val.GetComponent<Image>()).color = new Color(0.035f, 0.035f, 0.04f, 0.94f); PeakText val3 = MenuAPI.CreateText(string.Empty, "Text"); ((Component)val3).transform.SetParent((Transform)(object)component, false); RectTransform component2 = ((Component)val3).GetComponent<RectTransform>(); val3.SetFontSize(21f); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = new Vector2(12f, 8f); component2.offsetMax = new Vector2(-12f, -8f); ((TMP_Text)val3.TextMesh).alignment = (TextAlignmentOptions)4097; ((TMP_Text)val3.TextMesh).textWrappingMode = (TextWrappingModes)1; ((TMP_Text)val3.TextMesh).overflowMode = (TextOverflowModes)3; ((Graphic)val3.TextMesh).raycastTarget = false; SkillTooltip component3 = val.GetComponent<SkillTooltip>(); component3.rect = component; component3.text = val3; instance = component3; val.SetActive(false); } public static void Show(RectTransform target, string message, bool openLeft) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00f0: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)instance == (Object)null)) { ((Component)instance).gameObject.SetActive(true); instance.text.SetText(PauseMenuIntegration.WrapTooltipText(message)); RectTransform component = ((Component)instance.text).GetComponent<RectTransform>(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = new Vector2(12f, 8f); component.offsetMax = new Vector2(-12f, -8f); Vector3[] array = (Vector3[])(object)new Vector3[4]; target.GetWorldCorners(array); instance.rect.pivot = new Vector2(openLeft ? 1f : 0f, 0.5f); ((Transform)instance.rect).position = (openLeft ? array[1] : array[2]) + Vector3.right * (openLeft ? (-16f) : 16f); ((Transform)instance.rect).SetAsLastSibling(); } } public static void Hide() { if ((Object)(object)instance != (Object)null) { ((Component)instance).gameObject.SetActive(false); } } } } namespace PEAKUsageSkills.Tracking { internal sealed class ActivitySampler : MonoBehaviour { private const float SampleInterval = 0.2f; private Character? sampledCharacter; private Vector3 lastPosition; private float lastSampleTime; private bool hasPosition; private void Update() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_045f: Unknown result type (might be due to invalid IL or missing references) //IL_0465: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_0470: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime - lastSampleTime < 0.2f) { return; } lastSampleTime = Time.unscaledTime; Character localCharacter = Character.localCharacter; if ((Object)(object)localCharacter == (Object)null || !Plugin.RunState.IsGameplayScene) { ResetSample(localCharacter); return; } Vector3 center = localCharacter.Center; if ((Object)(object)sampledCharacter != (Object)(object)localCharacter || !hasPosition) { sampledCharacter = localCharacter; lastPosition = center; hasPosition = true; return; } Vector3 val = center - lastPosition; lastPosition = center; float magnitude = ((Vector3)(ref val)).magnitude; if (Plugin.Diagnostics != null) { Plugin.Diagnostics.Metrics.LastPhysicalMovementDistance = magnitude; } if (magnitude > 5f) { Plugin.Diagnostics?.RecordRejected(SkillId.Strength, "TeleportDelta", magnitude); Plugin.Diagnostics?.RecordRejected(SkillId.WallClimbing, "TeleportDelta", magnitude); Plugin.Diagnostics?.RecordRejected(SkillId.RopeClimbing, "TeleportDelta", magnitude); Plugin.Diagnostics?.RecordRejected(SkillId.VineClimbing, "TeleportDelta", magnitude); Plugin.Diagnostics?.RecordRejected(SkillId.Athletics, "TeleportDelta", magnitude); Plugin.Diagnostics?.RecordRejected(SkillId.WetGrip, "TeleportDelta", magnitude); Plugin.Diagnostics?.RecordRejected(SkillId.ClimbingTenacity, "TeleportDelta", magnitude); Plugin.Diagnostics?.RecordRejected(SkillId.HungerTolerance, "TeleportDelta", magnitude); return; } if (magnitude >= 0.01f) { float num = Plugin.Diagnostics?.Metrics.RawWeight ?? 0f; if (num >= 0.025f) { double work = num * magnitude; Plugin.Progression.AwardWork(SkillId.Strength, work, Plugin.Settings.StrengthXpPerWork.Value, localCharacter.data.isClimbingAnything ? "WeightedClimbing" : "WeightedMovement"); } CharacterRefs refs = localCharacter.refs; float? obj; if (refs == null) { obj = null; } else { CharacterAfflictions afflictions = refs.afflictions; obj = ((afflictions != null) ? new float?(afflictions.GetCurrentStatus((STATUSTYPE)1)) : ((float?)null)); } float? num2 = obj; double num3 = SkillMath.HungerMovementWork(num2.GetValueOrDefault(), magnitude, Plugin.Settings.HungerTrainingThreshold.Value); if (num3 > 0.0) { Plugin.Progression.AwardWork(SkillId.HungerTolerance, num3, Plugin.Settings.HungerMovementXpPerWork.Value, "HungryMovement"); } } bool num4 = localCharacter.data.isClimbing && !localCharacter.data.isRopeClimbing && !localCharacter.data.isVineClimbing; bool flag = (Object)(object)localCharacter.input != (Object)null && ((Vector2)(ref localCharacter.input.movementInput)).sqrMagnitude >= 0.01f; float num5 = ((num4 && flag) ? magnitude : 0f); float num6 = ((localCharacter.data.isRopeClimbing && flag) ? magnitude : 0f); float num7 = ((localCharacter.data.isVineClimbing && flag) ? magnitude : 0f); if (Plugin.Diagnostics != null) { Plugin.Diagnostics.Metrics.LastWallVerticalDelta = num5; Plugin.Diagnostics.Metrics.LastRopeVerticalDelta = num6; Plugin.Diagnostics.Metrics.LastVineVerticalDelta = num7; } if (num5 >= 0.005f) { if (Plugin.Diagnostics != null) { Plugin.Diagnostics.Metrics.SessionWallVerticalDistance += num5; } Plugin.Progression.AwardWork(SkillId.WallClimbing, num5, Plugin.Settings.WallClimbingXpPerMeter.Value, "IntentionalWallDistance"); float num8 = Mathf.Clamp01(localCharacter.data.slippy); if (num8 > 0.001f) { Plugin.Progression.AwardWork(SkillId.WetGrip, num5 * num8, Plugin.Settings.WetGripXpPerMeter.Value, "SlipperyWallDistance"); } if (localCharacter.GetTotalStamina() < 0.2f) { Plugin.Progression.AwardWork(SkillId.ClimbingTenacity, num5, Plugin.Settings.ClimbingTenacityXpPerMeter.Value, "LowStaminaWallDistance"); } } if (num6 >= 0.005f) { Plugin.Progression.AwardWork(SkillId.RopeClimbing, num6, Plugin.Settings.RopeClimbingXpPerMeter.Value, "IntentionalRopeDistance"); } if (num7 >= 0.005f) { Plugin.Progression.AwardWork(SkillId.VineClimbing, num7, Plugin.Settings.VineClimbingXpPerMeter.Value, "IntentionalVineDistance"); } Vector2 val2 = new Vector2(val.x, val.z); float magnitude2 = ((Vector2)(ref val2)).magnitude; if (localCharacter.data.isGrounded && !localCharacter.data.isClimbingAnything && (Object)(object)localCharacter.input != (Object)null && ((Vector2)(ref localCharacter.input.movementInput)).sqrMagnitude >= 0.01f && magnitude2 >= 0.01f) { Plugin.Progression.AwardWork(SkillId.Athletics, magnitude2, localCharacter.data.isSprinting ? Plugin.Settings.AthleticsSprintXpPerMeter.Value : Plugin.Settings.AthleticsXpPerMeter.Value, localCharacter.data.isSprinting ? "GroundSprinting" : "GroundMovement"); } } private void ResetSample(Character? character) { sampledCharacter = character; hasPosition = false; if (Plugin.Diagnostics != null) { Plugin.Diagnostics.Metrics.LastWallVerticalDelta = 0f; Plugin.Diagnostics.Metrics.LastRopeVerticalDelta = 0f; Plugin.Diagnostics.Metrics.LastVineVerticalDelta = 0f; } } } } namespace PEAKUsageSkills.Persistence { internal sealed class ProgressionSave { public int SchemaVersion { get; set; } = 1; public DateTime LastSavedUtc { get; set; } = DateTime.UtcNow; public Dictionary<string, SkillSave> Skills { get; set; } = new Dictionary<string, SkillSave>(StringComparer.Ordinal); } internal sealed class SkillSave { public int Level { get; set; } = 1; public double Experience { get; set; } public double LifetimeWork { get; set; } } internal sealed class SaveStore { private const int MaximumBackups = 5; private readonly ManualLogSource log; private readonly string saveDirectory; private readonly string savePath; private readonly string backupDirectory; public SaveStore(ManualLogSource log) { this.log = log; saveDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LandCrab", "PEAK", "PEAKUsageSkills"); savePath = Path.Combine(saveDirectory, "progression.json"); backupDirectory = Path.Combine(saveDirectory, "backups"); } public ProgressionSave Load() { Directory.CreateDirectory(saveDirectory); Directory.CreateDirectory(backupDirectory); if (TryLoad(savePath, out ProgressionSave save)) { log.LogInfo((object)("Loaded usage-skill progression from " + savePath)); return save; } foreach (string item in GetBackupsNewestFirst()) { if (TryLoad(item, out save)) { log.LogWarning((object)("Recovered usage-skill progression from backup " + item)); return save; } } log.LogInfo((object)"No valid usage-skill save found; starting new progression."); return new ProgressionSave(); } public void Save(ProgressionSave save) { Directory.CreateDirectory(saveDirectory); Directory.CreateDirectory(backupDirectory); save.LastSavedUtc = DateTime.UtcNow; string text = savePath + ".tmp"; string contents = JsonConvert.SerializeObject((object)save, (Formatting)1); File.WriteAllText(text, contents); if (File.Exists(savePath)) { string destinationBackupFileName = Path.Combine(backupDirectory, $"progression-{DateTime.UtcNow:yyyyMMdd-HHmmss-fff}.json"); File.Replace(text, savePath, destinationBackupFileName, ignoreMetadataErrors: true); } else { File.Move(text, savePath); } CleanupBackups(); } private bool TryLoad(string path, out ProgressionSave? save) { save = null; if (!File.Exists(path)) { return false; } try { save = JsonConvert.DeserializeObject<ProgressionSave>(File.ReadAllText(path)); return save != null && save.SchemaVersion == 1 && save.Skills != null; } catch (Exception ex) { log.LogWarning((object)("Could not load progression file " + path + ": " + ex.Message)); return false; } } private IEnumerable<string> GetBackupsNewestFirst() { if (!Directory.Exists(backupDirectory)) { return Array.Empty<string>(); } return Directory.GetFiles(backupDirectory, "progression-*.json").OrderByDescending(File.GetLastWriteTimeUtc); } private void CleanupBackups() { string[] array = GetBackupsNewestFirst().ToArray(); for (int i = 5; i < array.Length; i++) { try { File.Delete(array[i]); } catch (Exception ex) { log.LogWarning((object)("Could not remove old progression backup " + array[i] + ": " + ex.Message)); } } } } } namespace PEAKUsageSkills.Localization { internal static class LocalizationService { private sealed class LocaleDefinition { public string FileName { get; } public Language[] Languages { get; } public LocaleDefinition(string fileName, params Language[] languages) { FileName = fileName; Languages = languages; } } private const string TranslationIndexPrefix = "ELINS_PEAK_"; private static readonly Dictionary<string, string> English = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "section.main", "MAIN SKILLS" }, { "section.resiliency", "RESILIENCY" }, { "level.abbreviation", "Lv." }, { "skill.strength", "Strength" }, { "skill.endurance", "Endurance" }, { "skill.wall_climbing", "Wall Climbing" }, { "skill.rope_climbing", "Rope Climbing" }, { "skill.vine_climbing", "Vine Climbing" }, { "skill.athletics", "Athletics" }, { "skill.agility", "Agility" }, { "skill.vitality", "Vitality" }, { "skill.wet_grip", "Wet Grip" }, { "skill.climbing_tenacity", "Climbing Tenacity" }, { "skill.poison", "Poison" }, { "skill.cold", "Cold" }, { "skill.heat", "Heat" }, { "skill.drowsy", "Drowsy" }, { "skill.spores", "Spores" }, { "skill.hunger", "Hunger" }, { "skill.curse", "Curse" }, { "skill.petrification", "Petrification" }, { "term.wall", "wall" }, { "term.rope", "rope" }, { "term.vine", "vine" }, { "term.fall_injury", "fall Injury" }, { "term.wet_grip_penalty", "rain/slippery climbing pull and wind stamina drain" }, { "term.tenacity_penalty", "climbing control, slide, and stamina penalties below 20% stamina" }, { "tooltip.strength", "Reduces carried Weight and adds item slots to Backpacks, Fanny Packs, and Jet Packs at levels 20/40/70/120/200. Current: {0}% less Weight, +{1} slots." }, { "tooltip.endurance", "Adds 0.5% base stamina and 0.1% regeneration per level. Current: +{0} stamina, +{1}% regeneration." }, { "tooltip.vine", "Improves vine speed by 0.3% per level, reduces stamina cost, and retains light slide momentum. Current: +{0}% speed, {1}% less cost." }, { "tooltip.athletics", "Improves ground force by 0.1% and sprint force by another 0.2% per level. Current sprint cost reduction: {0}%." }, { "tooltip.agility", "Improves jump impulse and very lightly improves air control. Current: +{0}% jump impulse, {1}% less jump cost." }, { "tooltip.climbing", "Improves {0} climbing speed by {1}% per level and reduces stamina cost. Current: +{2}% speed, {3}% less cost." }, { "tooltip.reduction", "Reduces {0} using the {1}% anchored curve. Current bonus: {2}% reduction." }, { "tooltip.tolerance", "Reduces incoming {0}. Current reduction: {1}%.{2} XP comes only from receiving the affliction." }, { "tooltip.natural_recovery", " Natural recovery: +{0}%." } }; private static readonly Dictionary<SkillId, string> SkillKeys = new Dictionary<SkillId, string> { { SkillId.Strength, "skill.strength" }, { SkillId.Endurance, "skill.endurance" }, { SkillId.WallClimbing, "skill.wall_climbing" }, { SkillId.RopeClimbing, "skill.rope_climbing" }, { SkillId.VineClimbing, "skill.vine_climbing" }, { SkillId.Athletics, "skill.athletics" }, { SkillId.Agility, "skill.agility" }, { SkillId.Vitality, "skill.vitality" }, { SkillId.WetGrip, "skill.wet_grip" }, { SkillId.ClimbingTenacity, "skill.climbing_tenacity" }, { SkillId.Toxicology, "skill.poison" }, { SkillId.ColdTolerance, "skill.cold" }, { SkillId.HeatTolerance, "skill.heat" }, { SkillId.DrowsyTolerance, "skill.drowsy" }, { SkillId.SporeTolerance, "skill.spores" }, { SkillId.HungerTolerance, "skill.hunger" }, { SkillId.CurseTolerance, "skill.curse" }, { SkillId.PetrificationResistance, "skill.petrification" } }; private static readonly LocaleDefinition[] Locales = new LocaleDefinition[6] { new LocaleDefinition("fr.json", (Language)1), new LocaleDefinition("de.json", (Language)3), new LocaleDefinition("es.json", (Language)4, (Language)5), new LocaleDefinition("zh-CN.json", (Language)9), new LocaleDefinition("ja.json", (Language)11), new LocaleDefinition("ko.json", (Language)12) }; private static bool initialized; private static bool nativeRegistrationAvailable; public static string Locale => GetLocaleCode(LocalizedText.CURRENT_LANGUAGE); public static event Action? LanguageChanged; public static void Initialize(string pluginDirectory) { if (initialized) { Shutdown(); } try { RegisterEnglish(); string localizationDirectory = Path.Combine(pluginDirectory, "Localization"); LocaleDefinition[] locales = Locales; foreach (LocaleDefinition locale in locales) { LoadAndRegister(localizationDirectory, locale); } LocalizedText.OnLangugageChanged = (Action)Delegate.Combine(LocalizedText.OnLangugageChanged, new Action(OnGameLanguageChanged)); nativeRegistrationAvailable = true; initialized = true; Plugin.ModLog.LogInfo((object)$"[UsageSkills:Localization] registered={English.Count} locale={Locale} source=PEAK selector"); } catch (Exception ex) { nativeRegistrationAvailable = false; initialized = false; Plugin.ModLog.LogWarning((object)("[UsageSkills:Localization] PEAK localization registration failed; using English. " + ex.GetType().Name + ": " + ex.Message)); } } public static void Shutdown() { if (initialized) { LocalizedText.OnLangugageChanged = (Action)Delegate.Remove(LocalizedText.OnLangugageChanged, new Action(OnGameLanguageChanged)); LocalizationService.LanguageChanged = null; nativeRegistrationAvailable = false; initialized = false; } } public static string Get(string key, params object[] args) { string value = ""; if (nativeRegistrationAvailable) { try { value = LocalizedText.GetText(GetTranslationIndex(key), false); } catch (Exception ex) { Plugin.ModLog.LogWarning((object)("[UsageSkills:Localization] lookup failed key=" + key + "; using English. " + ex.GetType().Name + ": " + ex.Message)); } } if (string.IsNullOrEmpty(value) && !English.TryGetValue(key, out value)) { value = key; } if (args == null || args.Length == 0) { return value; } try { return string.Format(GetFormattingCulture(), value, args); } catch (FormatException ex2) { Plugin.ModLog.LogWarning((object)("[UsageSkills:Localization] invalid format key=" + key + " locale=" + Locale + "; using English. " + ex2.Message)); string value2; return English.TryGetValue(key, out value2) ? string.Format(GetFormattingCulture(), value2, args) : value; } } public static string FormatNumber(float value, int decimalPlaces) { return value.ToString("F" + decimalPlaces, GetFormattingCulture()); } public static string SkillName(SkillId skillId) { if (!SkillKeys.TryGetValue(skillId, out string value)) { return skillId.ToString(); } return Get(value); } private static void RegisterEnglish() { foreach (KeyValuePair<string, string> item in English) { MenuAPI.CreateLocalization(GetTranslationIndex(item.Key)).AddLocalization(item.Value, (Language)0); } } private static void LoadAndRegister(string localizationDirectory, LocaleDefinition locale) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) string path = Path.Combine(localizationDirectory, locale.FileName); try { if (!File.Exists(path)) { Plugin.ModLog.LogWarning((object)("[UsageSkills:Localization] missing=" + locale.FileName + "; affected languages use English")); return; } Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(path)); if (dictionary == null || dictionary.Count == 0) { Plugin.ModLog.LogWarning((object)("[UsageSkills:Localization] empty=" + locale.FileName + "; affected languages use English")); return; } int num = 0; int num2 = 0; foreach (string key in English.Keys) { if (!dictionary.TryGetValue(key, out var value) || string.IsNullOrWhiteSpace(value)) { num2++; continue; } Language[] languages = locale.Languages; foreach (Language val in languages) { MenuAPI.CreateLocalization(GetTranslationIndex(key)).AddLocalization(value, val); } num++; } Plugin.ModLog.LogInfo((object)$"[UsageSkills:Localization] file={locale.FileName} registered={num} missing={num2}"); } catch (Exception ex) { Plugin.ModLog.LogWarning((object)("[UsageSkills:Localization] failed=" + locale.FileName + "; affected languages use English. " + ex.GetType().Name + ": " + ex.Message)); } } private static void OnGameLanguageChanged() { Plugin.ModLog.LogInfo((object)("[UsageSkills:Localization] changed locale=" + Locale)); try { LocalizationService.LanguageChanged?.Invoke(); } catch (Exception ex) { Plugin.ModLog.LogWarning((object)("[UsageSkills:Localization] UI refresh failed after language change. " + ex.GetType().Name + ": " + ex.Message)); } } private static string GetTranslationIndex(string key) { return "ELINS_PEAK_" + key.Replace('.', '_').ToUpperInvariant(); } private static CultureInfo GetFormattingCulture() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected I4, but got Unknown Language cURRENT_LANGUAGE = LocalizedText.CURRENT_LANGUAGE; return (cURRENT_LANGUAGE - 1) switch { 0 => CultureInfo.GetCultureInfo("fr-FR"), 2 => CultureInfo.GetCultureInfo("de-DE"), 4 => CultureInfo.GetCultureInfo("es-MX"), 3 => CultureInfo.GetCultureInfo("es-ES"), 8 => CultureInfo.GetCultureInfo("zh-CN"), 10 => CultureInfo.GetCultureInfo("ja-JP"), 11 => CultureInfo.GetCultureInfo("ko-KR"), _ => CultureInfo.GetCultureInfo("en-US"), }; } private static string GetLocaleCode(Language language) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected I4, but got Unknown return (language - 1) switch { 0 => "fr", 1 => "it", 2 => "de", 3 => "es-ES", 4 => "es-419", 5 => "pt-BR", 6 => "ru", 7 => "uk", 8 => "zh-CN", 9 => "zh-TW", 10 => "ja", 11 => "ko", 12 => "pl", 13 => "tr", _ => "en", }; } } } namespace PEAKUsageSkills.GameAdapters { internal static class ConditionSkillAdapter { public static bool TryGetToleranceSkill(STATUSTYPE statusType, out SkillId skillId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected I4, but got Unknown switch (statusType - 1) { case 2: skillId = SkillId.Toxicology; return true; case 1: skillId = SkillId.ColdTolerance; return true; case 7: skillId = SkillId.HeatTolerance; return true; case 5: skillId = SkillId.DrowsyTolerance; return true; case 9: skillId = SkillId.SporeTolerance; return true; case 0: skillId = SkillId.HungerTolerance; return true; case 4: skillId = SkillId.CurseTolerance; return true; case 12: skillId = SkillId.PetrificationResistance; return true; default: skillId = SkillId.Strength; return false; } } public static bool TryGetRecoveryTolerance(STATUSTYPE statusType, out SkillId skillId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown switch (statusType - 2) { case 1: skillId = SkillId.Toxicology; return true; case 0: skillId = SkillId.ColdTolerance; return true; case 6: skillId = SkillId.HeatTolerance; return true; case 4: skillId = SkillId.DrowsyTolerance; return true; case 8: skillId = SkillId.SporeTolerance; return true; default: skillId = SkillId.Strength; return false; } } } internal static class InventorySkillService { private static Character? cachedBackpackCharacter; private static ItemInstanceData? cachedBackpackInstance; private static BackpackData? cachedBackpackData; public static int ExtraBackpackSlots => SkillMath.ExtraBackpackSlots(Plugin.Progression.GetLevel(SkillId.Strength)); public static bool IsItemStorage(BackpackType backpackType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 if ((int)backpackType != 1 && (int)backpackType != 2) { return (int)backpackType == 3; } return true; } public static int BaseItemSlots(BackpackType backpackType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected I4, but got Unknown return (backpackType - 1) switch { 0 => 4, 1 => 2, 2 => 1, _ => 0, }; } public static int DesiredItemCapacity(BackpackType backpackType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) int num = BaseItemSlots(backpackType); if (num != 0) { return num + ExtraBackpackSlots; } return 0; } public static int EnsureBackpackCapacity(BackpackData? data, BackpackType backpackType, string source) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_00a0: Expected O, but got Unknown if (data == null || !IsItemStorage(backpackType)) { return 0; } ItemSlot[] array = data.itemSlots ?? Array.Empty<ItemSlot>(); int num = Math.Max(DesiredItemCapacity(backpackType), HighestOccupiedIndex(array) + 1); if (array.Length < num) { Array.Resize(ref array, num); data.itemSlots = array; Plugin.ModLog.LogInfo((object)($"[UsageSkills:Inventory] {backpackType} item slots expanded to {num} " + $"strength={Plugin.Progression.GetLevel(SkillId.Strength)} source={source}")); } for (int i = 0; i < array.Length; i++) { ItemSlot[] array2 = array; int num2 = i; if (array2[num2] == null) { array2[num2] = new ItemSlot((byte)i); } } return num; } public static BackpackData? TryGetEquippedBackpackData(Character? character, out BackpackType backpackType) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected I4, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) backpackType = (BackpackType)(int)((character == null) ? ((BackpackType?)null) : character.player?.backpackSlot?.backpackType).GetValueOrDefault(); if (((character == null) ? null : character.player?.backpackSlot) == null || ((ItemSlot)character.player.backpackSlot).IsEmpty()) { cachedBackpackCharacter = character; cachedBackpackInstance = null; cachedBackpackData = null; return null; } if (!IsItemStorage(backpackType)) { cachedBackpackCharacter = character; cachedBackpackInstance = ((ItemSlot)character.player.backpackSlot).data; cachedBackpackData = null; return null; } ItemInstanceData data = ((ItemSlot)character.player.backpackSlot).data; if ((Object)(object)character == (Object)(object)cachedBackpackCharacter && data == cachedBackpackInstance) { return cachedBackpackData; } try { BackpackReference fromEquippedBackpack = BackpackReference.GetFromEquippedBackpack(character); cachedBackpackCharacter = character; cachedBackpackInstance = data; cachedBackpackData = (((BackpackReference)(ref fromEquippedBackpack)).exists ? ((BackpackReference)(ref fromEquippedBackpack)).GetData() : null); return cachedBackpackData; } catch (Exception ex) { Plugin.ModLog.LogWarning((object)("[UsageSkills:Inventory] backpack lookup failed: " + ex.Message)); return null; } } private static int HighestOccupiedIndex(ItemSlot[] slots) { for (int num = slots.Length - 1; num >= 0; num--) { if (slots[num] != null && !slots[num].IsEmpty()) { return num; } } return -1; } } internal sealed class InventorySkillController : MonoBehaviour { private BackpackData? lastBackpackData; private int lastDesiredCapacity; private BackpackType lastBackpackType; private void Update() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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) BackpackType backpackType; BackpackData val = InventorySkillService.TryGetEquippedBackpackData(Character.localCharacter, out backpackType); int num = InventorySkillService.DesiredItemCapacity(backpackType); if (val != null && (val != lastBackpackData || backpackType != lastBackpackType || num != lastDesiredCapacity || val.itemSlots.Length < num)) { InventorySkillService.EnsureBackpackCapacity(val, backpackType, "Controller"); lastBackpackData = val; lastBackpackType = backpackType; lastDesiredCapacity = num; } } } internal sealed class RunStateAdapter { private readonly UsageSkillsConfig config; public string ActiveSceneName { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).name ?? string.Empty; } } public bool IsAirport => ActiveSceneName.IndexOf("airport", StringComparison.OrdinalIgnoreCase) >= 0; public bool IsGameplayScene { get { if (!IsAirport) { return (Object)(object)Character.localCharacter != (Object)null; } return false; } } public bool IsExperienceEligible { get { if (!config.EnableMod.Value || !config.EnableExperience.Value || !IsGameplayScene || (Object)(object)Character.localCharacter == (Object)null) { return false; } if (!config.EnableXpInCustomRuns.Value) { return !RunSettings.IsCustomRun; } return true; } } public RunStateAdapter(UsageSkillsConfig config) { this.config = config; } } } namespace PEAKUsageSkills.GameAdapters.Patches { [HarmonyPatch(typeof(CharacterClimbing), "GetRequestedPostition")] internal static class WallClimbingSpeedPatch { private struct State { public bool Applied; public float Original; public float OriginalSlippy; } private static void Prefix(CharacterClimbing __instance, Character ___character, out State __state) { __state = new State { Applied = false, Original = __instance.climbSpeedMod, OriginalSlippy = (___character?.data.slippy ?? 0f) }; if (!((Object)(object)___character == (Object)null) && Plugin.Effects.CanApply(___character) && !___character.data.isRopeClimbing && !___character.data.isVineClimbing) { __instance.climbSpeedMod = __state.Original * Plugin.Effects.WallSpeedMultiplier; ___character.data.slippy = __state.OriginalSlippy * Plugin.Effects.WetGripPenaltyMultiplier; __state.Applied = true; } } private static void Postfix(CharacterClimbing __instance, State __state) { Restore(__instance, __state); } private static Exception? Finalizer(CharacterClimbing __instance, State __state, Exception? __exception) { Restore(__instance, __state); return __exception; } private static void Restore(CharacterClimbing instance, State state) { if (state.Applied) { instance.climbSpeedMod = state.Original; object? value = AccessTools.Field(typeof(CharacterClimbing), "character").GetValue(instance); Character val = (Character)((value is Character) ? value : null); if ((Object)(object)val != (Object)null) { val.data.slippy = state.OriginalSlippy; } } } } [HarmonyPatch(typeof(CharacterRopeHandling), "Update")] internal static class RopeClimbingSpeedPatch { private struct State { public bool Applied; public float Original; } private static void Prefix(CharacterRopeHandling __instance, Character ___character, out State __state) { __state = new State { Applied = false, Original = __instance.climbSpeedMod }; if (!((Object)(object)___character == (Object)null) && ___character.data.isRopeClimbing && Plugin.Effects.CanApply(___character)) { __instance.climbSpeedMod = __state.Original * Plugin.Effects.RopeSpeedMultiplier; __state.Applied = true; } } private static Exception? Finalizer(CharacterRopeHandling __instance, State __state, Exception? __exception) { if (__state.Applied) { __instance.climbSpeedMod = __state.Original; } return __exception; } } [HarmonyPatch(typeof(CharacterVineClimbing), "FixedUpdate")] internal static class VineClimbingSpeedPatch { private struct State { public bool Applied; public float Original; public float OriginalSlideDeceleration; } private static void Prefix(CharacterVineClimbing __instance, Character ___character, out State __state) { __state = new State { Applied = false, Original = __instance.climbSpeedMod, OriginalSlideDeceleration = __instance.slideDeceleration }; if (!((Object)(object)___character == (Object)null) && ___character.data.isVineClimbing && Plugin.Effects.CanApply(___character)) { __instance.climbSpeedMod = __state.Original * Plugin.Effects.VineSpeedMultiplier; __instance.slideDeceleration = Mathf.Lerp(__state.OriginalSlideDeceleration, 1f, Plugin.Effects.VineMomentumRetention); __state.Applied = true; } } private static Exception? Finalizer(CharacterVineClimbing __instance, State __state, Exception? __exception) { if (__state.Applied) { __instance.climbSpeedMod = __state.Original; __instance.slideDeceleration = __state.OriginalSlideDeceleration; } return __exception; } } [HarmonyPatch(typeof(WindChillZone), "ApplyStatus")] internal static class WetGripWindCostPatch { private static void Prefix(Character character, out float __state) { __state = character?.refs?.climbing?.climbingStamMinimumMultiplier ?? 1f; } private static void Postfix(Character character, float __state) { CharacterClimbing val = character?.refs?.climbing; if (!((Object)(object)val == (Object)null) && Plugin.Effects.CanApply(character)) { float climbingStamMinimumMultiplier = val.climbingStamMinimumMultiplier; if (climbingStamMinimumMultiplier > Math.Max(1f, __state)) { val.climbingStamMinimumMultiplier = 1f + (climbingStamMinimumMultiplier - 1f) * Plugin.Effects.WetGripPenaltyMultiplier; } } } } [HarmonyPatch(typeof(Character), "UpdateVariablesFixed")] internal static class LowStaminaClimbingControlPatch { private static void Postfix(Character __instance) { if (Plugin.Effects.CanApply(__instance) && __instance.data.isClimbing && !(__instance.GetTotalStamina() >= 0.2f)) { float num = Mathf.Clamp01(__instance.data.staminaMod); __instance.data.staminaMod = 1f - (1f - num) * Plugin.Effects.ClimbingTenacityPenaltyMultiplier; } } } [HarmonyPatch(typeof(CharacterClimbing), "Climbing")] internal static class LowStaminaClimbingSlidePatch { private struct State { public bool Applied; public float Original; } private static void Prefix(Character ___character, out State __state) { __state = default(State); if (!((Object)(object)___character == (Object)null) && Plugin.Effects.CanApply(___character) && !(___character.data.outOfStaminaClimbingFor <= 0f)) { __state.Original = ___character.data.outOfStaminaClimbingFor; ___character.data.outOfStaminaClimbingFor = __state.Original * Plugin.Effects.ClimbingTenacityPenaltyMultiplier; __state.Applied = true; } } private static Exception? Finalizer(Character ___character, State __state, Exception? __exception) { if (__state.Applied && (Object)(object)___character != (Object)null) { ___character.data.outOfStaminaClimbingFor = __state.Original; } return __exception; } } internal static class ExplicitItemStatusScope { [ThreadStatic] private static int depth; public static bool Active => depth > 0; public static void Enter() { depth++; } public static void Exit() { depth = Math.Max(0, depth - 1); } } internal static class EnvironmentalColdRecoveryScope { [ThreadStatic] private static int depth; public static bool Active => depth > 0; public static void Enter() { depth++; } public static void Exit() { depth = Math.Max(0, depth - 1); } } [HarmonyPatch(typeof(Action_ModifyStatus), "RunAction")] internal static class ExplicitItemStatusScopePatch { private static void Prefix() { ExplicitItemStatusScope.Enter(); } private static Exception? Finalizer(Exception? __exception) { ExplicitItemStatusScope.Exit(); return __exception; } } internal static class FallScope { [ThreadStatic] private static string? source; public static string? Source => source; public static void Enter(string value) { source = value; } public static void Exit() { source = null; } } [HarmonyPatch(typeof(CharacterMovement), "CheckFallDamage")] internal static class NormalFallScopePatch { private static void Prefix() { FallScope.Enter("CharacterMovement"); } private static Exception? Finalizer(Exception? __exception) { FallScope.Exit(); return __exception; } } [HarmonyPatch(typeof(CharacterClimbing), "CheckFallDamage")] internal static class WallFallScopePatch { private static void Prefix() { FallScope.Enter("CharacterClimbing"); } private static Exception? Finalizer(Exception? __exception) { FallScope.Exit(); return __exception; } } [HarmonyPatch(typeof(CharacterAfflictions), "AddStatus")] internal static class AddStatusPatch { private struct State { public bool Local; public float Before; public float RawRequested; public STATUSTYPE StatusType; public string Source; public bool ToleranceExposure; public SkillId ToleranceSkill; public bool EnvironmentalColdRecovery; } private unsafe static void Prefix(CharacterAfflictions __instance, STATUSTYPE statusType, ref float amount, bool fromRPC, out State __state) { //IL_004a: 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) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Invalid comparison between Unknown and I4 //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Invalid comparison between Unknown and I4 //IL_015b: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)__instance != (Object)null && (Object)(object)__instance.character != (Object)null && (Object)(object)Character.localCharacter == (Object)(object)__instance.character; __state = new State { Local = flag, Before = (flag ? __instance.GetCurrentStatus(statusType) : 0f), RawRequested = amount, StatusType = statusType, Source = (fromRPC ? "AddRPC" : "AddLocal"), ToleranceExposure = false, ToleranceSkill = SkillId.Strength, EnvironmentalColdRecovery = false }; string source = FallScope.Source; if (!flag || amount <= 0f) { return; } bool flag2 = (int)statusType == 8 && __instance.GetCurrentStatus((STATUSTYPE)2) > 0f; if (flag2 && !ExplicitItemStatusScope.Active) { EnvironmentalColdRecoveryScope.Enter(); __state.EnvironmentalColdRecovery = true; } SkillId skillId; if ((int)statusType == 0 && source != null) { float num = amount; Plugin.Progression.AwardWork(SkillId.Vitality, num, Plugin.Settings.VitalityXpPerInjury.Value, source); if (Plugin.Effects.CanApply(__instance.character)) { amount *= Plugin.Effects.VitalityFallMultiplier; } __state.Source = "Fall:" + source; Plugin.Diagnostics?.RecordFall(num, amount, source); } else if ((int)statusType == 13) { __state.Local = false; } else if (ConditionSkillAdapter.TryGetToleranceSkill(statusType, out skillId)) { __state.ToleranceExposure = true; __state.ToleranceSkill = skillId; __state.Source = "Exposure:" + ((object)(*(STATUSTYPE*)(&statusType))/*cast due to .constrained prefix*/).ToString(); if (!flag2 && Plugin.Effects.CanApply(__instance.character)) { amount *= Plugin.Effects.ConditionGainMultiplier(skillId); } } } private unsafe static void Postfix(CharacterAfflictions __instance, bool __result, State __state) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (__state.Local) { float currentStatus = __instance.GetCurrentStatus(__state.StatusType); float num = currentStatus - __state.Before; if (__state.ToleranceExposure && __state.ToleranceSkill != SkillId.HungerTolerance && __result && num > 0f) { Plugin.Progression.AwardWork(__state.ToleranceSkill, num, Plugin.Settings.ConditionXpPerStatus.Value, __state.Source); } Plugin.Diagnostics?.RecordStatusChange(((object)(*(STATUSTYPE*)(&__state.StatusType))/*cast due to .constrained prefix*/).ToString(), __state.RawRequested, num, currentStatus, __state.Source); } } private static Exception? Finalizer(Exception? __exception, State __state) { if (__state.EnvironmentalColdRecovery) { EnvironmentalColdRecoveryScope.Exit(); } return __exception; } } [HarmonyPatch(typeof(CharacterAfflictions), "AddPetrify", new Type[] { typeof(int) })] internal static class AddPetrifyPatch { private struct State { public bool Local; public int Before; public int RawRequested; } private static void Prefix(CharacterAfflictions __instance, ref int petrify, out State __state) { bool flag = (Object)(object)__instance != (Object)null && (Object)(object)__instance.character != (Object)null && (Object)(object)Character.localCharacter == (Object)(object)__instance.character; __state = new State { Local = flag, Before = (flag ? __instance.character.data.petrifyAmount : 0), RawRequested = petrify }; if (flag && petrify > 0 && Plugin.Effects.CanApply(__instance.character)) { petrify = Mathf.Max(0, Mathf.RoundToInt((float)petrify * Plugin.Effects.ConditionGainMultiplier(SkillId.PetrificationResistance))); } } private static void Postfix(CharacterAfflictions __instance, State __state) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (__state.Local && __state.RawRequested > 0) { int petrifyAmount = __instance.character.data.petrifyAmount; int num = Mathf.Max(0, petrifyAmount - __state.Before); if (num > 0) { Plugin.Progression.AwardWork(SkillId.PetrificationResistance, (double)num / 100.0, Plugin.Settings.ConditionXpPerStatus.Value, "Exposure:Petrify"); } Plugin.Diagnostics?.RecordStatusChange(((object)(STATUSTYPE)13/*cast due to .constrained prefix*/).ToString(), (float)__state.RawRequested / 100f, (float)num / 100f, (float)petrifyAmount / 100f, "Exposure:Petrify"); } } } [HarmonyPatch(typeof(CharacterAfflictions), "SubtractStatus")] internal static class SubtractStatusPatch { private struct State { public bool Local; public float Before; public float Requested; public STATUSTYPE StatusType; public string Source; public bool HasRecoveryTolerance; public SkillId RecoveryTolerance; } private unsafe static void Prefix(CharacterAfflictions __instance, STATUSTYPE statusType, ref float amount, bool fromRPC, bool decreasedNaturally, out State __state) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)__instance != (Object)null && (Object)(object)__instance.character != (Object)null && (Object)(object)Character.localCharacter == (Object)(object)__instance.character; bool flag2 = (int)statusType == 2 && EnvironmentalColdRecoveryScope.Active; bool flag3 = decreasedNaturally || flag2; __state = new State { Local = flag, Before = (flag ? __instance.GetCurrentStatus(statusType) : 0f), Requested = amount, StatusType = statusType, Source = (flag2 ? "NaturalRecovery:ColdByWarmth" : (decreasedNaturally ? "NaturalRecovery" : (fromRPC ? "SubtractRPC" : "SubtractLocal"))), HasRecoveryTolerance = ConditionSkillAdapter.TryGetRecoveryTolerance(statusType, out var skillId), RecoveryTolerance = skillId }; if (flag && flag3 && amount > 0f && __state.HasRecoveryTolerance && Plugin.Effects.CanApply(__instance.character)) { amount *= Plugin.Effects.ConditionRecoveryMultiplier(__state.RecoveryTolerance); if (!flag2) { __state.Source = "NaturalRecovery:" + ((object)(*(STATUSTYPE*)(&statusType))/*cast due to .constrained prefix*/).ToString(); } } } private unsafe static void Postfix(CharacterAfflictions __instance, State __state) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (__state.Local) { float currentStatus = __instance.GetCurrentStatus(__state.StatusType); float actual = currentStatus - __state.Before; Plugin.Diagnostics?.RecordStatusChange(((object)(*(STATUSTYPE*)(&__state.StatusType))/*cast due to .constrained prefix*/).ToString(), 0f - __state.Requested, actual, currentStatus, __state.Source); } } } [HarmonyPatch(typeof(BackpackData), "DeserializeValue")] internal static class BackpackDataDeserializePatch { private static bool Prefix(BackpackData __instance, BinaryDeserializer deserializer) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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) InventorySyncData val = default(InventorySyncData); ((InventorySyncData)(ref val)).Deserialize(deserializer); SlotData[] array = val.slots ?? Array.Empty<SlotData>(); int num = array.Length; ItemSlot[] itemSlots = __instance.itemSlots; int val2 = ((itemSlots != null) ? itemSlots.Length : 0); int num2 = Math.Max(num, val2); ItemSlot[] array2 = __instance.itemSlots ?? Array.Empty<ItemSlot>(); if (array2.Length < num2) { Array.Resize(ref array2, num2); __instance.itemSlots = array2; } Item val5 = default(Item); for (int i = 0; i < array2.Length; i++) { ItemSlot[] array3 = array2; int num3 = i; if (array3[num3] == null) { array3[num3] = new ItemSlot((byte)i); } if (i < num) { SlotData val3 = array[i]; Item val4 = (ItemDatabase.TryGetItem(val3.ItemID, ref val5) ? val5 : null); array2[i].SetItem(val4, val3.Data); } else { array2[i].SetItem((Item)null, (ItemInstanceData)null); } } Plugin.ModLog.LogInfo((object)($"[UsageSkills:Inventory] backpack deserialized serialized={num} " + $"retainedCapacity={array2.Length}")); return false; } } [HarmonyPatch(typeof(BackpackWheel), "InitWheel")] internal static class BackpackWheelCapacityPatch { private static void Prefix(BackpackWheel __instance, BackpackReference bp, ref int slotCount, BackpackType backpackType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) if (!InventorySkillService.IsItemStorage(backpackType)) { return; } BackpackData data = ((BackpackReference)(ref bp)).GetData(); slotCount = InventorySkillService.EnsureBackpackCapacity(data, backpackType, "WheelOpen"); int num = Math.Max(slotCount, data.itemSlots.Length) + 1; if (__instance.slices.Length < num) { List<BackpackWheelSlice> list = new List<BackpackWheelSlice>(__instance.slices); BackpackWheelSlice val = __instance.slices[Math.Min(1, __instance.slices.Length - 1)]; while (list.Count < num) { list.Add(Object.Instantiate<BackpackWheelSlice>(val, ((Component)val).transform.parent)); } __instance.slices = list.ToArray(); } int num2 = slotCount + 1; float num3 = 158f + (float)Math.Max(0, num2 - 5) * 20f; for (int i = 0; i < num2; i++) { float num4 = 360f / (float)num2 * (float)i + 158f; float num5 = (num4 + 112f) * (MathF.PI / 180f); Transform transform = ((Component)__instance.slices[i]).transform; transform.localRotation = Quaternion.Euler(0f, 0f, num4); transform.localPosition = new Vector3(Mathf.Cos(num5) * num3, Mathf.Sin(num5) * num3, 0f); } } } [HarmonyPatch(typeof(BackpackVisuals), "RefreshVisuals")] internal static class BackpackVisualCapacityPatch { private static void Prefix(BackpackVisuals __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_002d: Unknown result type (might be due to invalid IL or missing references) BackpackType backpackType = GetBackpackType(__instance); if (!InventorySkillService.IsItemStorage(backpackType)) { return; } BackpackData backpackData = __instance.GetBackpackData(); if (backpackData == null || __instance.backpackSlots == null || __instance.backpackSlots.Length == 0) { return; } int num = InventorySkillService.EnsureBackpackCapacity(backpackData, backpackType, "BackpackVisuals"); if (__instance.backpackSlots.Length < num) { Transform[] backpackSlots = __instance.backpackSlots; Transform[] array = (Transform[])(object)new Transform[num]; for (int i = 0; i < array.Length; i++) { array[i] = backpackSlots[i % backpackSlots.Length]; } __instance.backpackSlots = array; } } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); MethodInfo methodInfo = AccessTools.Method(typeof(BackpackVisualCapacityPatch), "GetVisualSlotCount", (Type[])null, (Type[])null); for (int i = 0; i + 1 < list.Count; i++) { if (CodeInstructionExtensions.LoadsConstant(list[i], 4L) && (list[i + 1].opcode == OpCodes.Blt || list[i + 1].opcode == OpCodes.Blt_S)) { list[i].opcode = OpCodes.Ldarg_0; list[i].operand = null; list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)methodInfo)); break; } } return list; } private static int GetVisualSlotCount(BackpackVisuals visuals) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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_0017: Unknown result type (might be due to invalid IL or missing references) BackpackType backpackType = GetBackpackType(visuals); if (!InventorySkillService.IsItemStorage(backpackType)) { return 0; } return InventorySkillService.EnsureBackpackCapacity(visuals.GetBackpackData(), backpackType, "VisualRefresh"); } private static BackpackType GetBackpackType(BackpackVisuals visuals) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) BackpackOnBackVisuals val = (BackpackOnBackVisuals)(object)((visuals is BackpackOnBackVisuals) ? visuals : null); if (val != null) { return val.backpackType; } Backpack component = ((Component)visuals).GetComponent<Backpack>(); if (!((Object)(object)component != (Object)null)) { return (BackpackType)0; } return component.backpackType; } } [HarmonyPatch(typeof(CharacterMovement), "GetMovementForce")] internal static class AthleticsMovementPatch { private static void Postfix(Character ___character, ref float __result) { if (!(__result <= 0f) && !((Object)(object)___character == (Object)null) && Plugin.Effects.CanApply(___character) && ___character.data.isGrounded && !___character.data.isClimbingAnything) { __result *= Plugin.Effects.AthleticsMovementMultiplier; if (___character.data.isSprinting) { __result *= Plugin.Effects.AthleticsSprintMovementMultiplier; } } } } [HarmonyPatch(typeof(Character), "CalculateWorldMovementDir")] internal static class AgilityAirControlPatch { private struct State { public bool Applied; public float Original; } private static void Prefix(Character __instance, out State __state) { __state = default(State); CharacterMovement val = __instance.refs?.movement; if (!((Object)(object)val == (Object)null) && !__instance.data.isGrounded && !__instance.data.isClimbingAnything && Plugin.Effects.CanApply(__instance)) { __state.Original = val.airMovementTurnSpeed; val.airMovementTurnSpeed = __state.Original * Plugin.Effects.AgilityAirControlMultiplier; __state.Applied = true; } } private static Exception? Finalizer(Character __instance, State __state, Exception? __exception) { if (__state.Applied && (Object)(object)__instance?.refs?.movement != (Object)null) { __instance.refs.movement.airMovementTurnSpeed = __state.Original; } return __exception; } } [HarmonyPatch(typeof(CharacterMovement), "JumpRpc")] internal static class AgilityJumpAwardPatch { private static void Prefix(Character ___character, bool isPalJump) { if (!((Object)(object)___character == (Object)null || (Object)(object)Character.localCharacter != (Object)(object)___character || isPalJump)) { Plugin.Progression.AwardWork(SkillId.Agility, 1.0, Plugin.Settings.AgilityXpPerJump.Value, "ExecutedJump"); } } } internal static class JumpEffectScope { [ThreadStatic] private static Character? character; [ThreadStatic] private static int depth; public static bool Matches(Character candidate) { if (depth > 0) { return (Object)(object)character == (Object)(object)candidate; } return false; } public static void Enter(Character value) { character = value; depth++; } public static void Exit() { depth = Math.Max(0, depth - 1); if (depth == 0) { character = null; } } } [HarmonyPatch] internal static class AgilityJumpRoutinePatch { private struct State { public CharacterMovement? Movement; public float OriginalJumpImpulse; public bool Applied; public bool ScopeEntered; } private static readonly FieldInfo MovementCharacterField = AccessTools.Field(typeof(CharacterMovement), "character"); private static readonly FieldInfo JumpImpulseField = AccessTools.Field(typeof(CharacterMovement), "jumpImpulse"); private static MethodBase TargetMethod() { Type type = (typeof(CharacterMovement).GetNestedType("<>c__DisplayClass69_0", BindingFlags.NonPublic) ?? throw new MissingMemberException("CharacterMovement jump display class")).GetNestedType("<<JumpRpc>g__IDoJump|0>d", BindingFlags.NonPublic) ?? throw new MissingMemberException("CharacterMovement jump iterator"); return AccessTools.Method(type, "MoveNext", (Type[])null, (Type[])null) ?? throw new MissingMethodException(type.FullName, "MoveNext"); } private static void Prefix(object __instance, out State __state) { __state = default(State); object obj = AccessTools.Field(__instance.GetType(), "<>4__this")?.GetValue(__instance); object? obj2 = ((obj == null) ? null : AccessTools.Field(obj.GetType(), "<>4__this"))?.GetValue(obj); CharacterMovement val = (CharacterMovement)((obj2 is CharacterMovement) ? obj2 : null); Character val2 = (Character)(((Object)(object)val == (Object)null) ? null : /*isinst with value type is only supported in some contexts*/); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)Character.localCharacter != (Object)(object)val2)) { __state.Movement = val; JumpEffectScope.Enter(val2); __state.ScopeEntered = true; if (Plugin.Effects.CanApply(val2)) { __state.OriginalJumpImpulse = (float)JumpImpulseField.GetValue(val); JumpImpulseField.SetValue(val, __state.OriginalJumpImpulse * Plugin.Effects.AgilityJumpMultiplier); __state.Applied = true; } } } private static Exception? Finalizer(State __state, Exception? __exception) { if (__state.Applied && (Object)(object)__state.Movement != (Object)null) { JumpImpulseField.SetValue(__state.Movement, __state.OriginalJumpImpulse); } if (__state.ScopeEntered) { JumpEffectScope.Exit(); } return __exception; } } [HarmonyPatch(typeof(Character), "GetMaxStamina")] internal static class StaminaCapacityPatch { private static void Postfix(Character __instance, ref float __result) { if (Plugin.Effects.CanApply(__instance)) { float capacityBonus = Plugin.Effects.EnduranceCapacityMultiplier - 1f; CharacterRefs refs = __instance.refs; float? obj; if (refs == null) { obj = null; } else { CharacterAfflictions afflictions = refs.afflictions; obj = ((afflictions != null) ? new float?(afflictions.statusSum) : ((float?)null)); } float? num = obj; float valueOrDefault = num.GetValueOrDefault(); __result = SkillMath.ExpandedStaminaCapacity(__result, valueOrDefault, capacityBonus); } } } internal static class EndurancePassOutThreshold { public static float GetThreshold(Character character) { if ((Object)(object)character == (Object)null || !Plugin.Effects.CanApply(character)) { return 1f; } return Math.Max(1f, Plugin.Effects.EnduranceCapacityMultiplier); } public static IEnumerable<CodeInstruction> ReplaceVanillaThreshold(IEnumerable<CodeInstruction> instructions, MethodBase originalMethod) { //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(CharacterAfflictions), "statusSum") ?? AccessTools.Method(typeof(CharacterAfflictions), "get_statusSum", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(EndurancePassOutThreshold), "GetThreshold", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Plugin.ModLog.LogError((object)("[UsageSkills:PassOut] Could not resolve statusSum getter/threshold method while patching " + originalMethod.Name + ".")); return list; } int num = 0; for (int i = 1; i < list.Count; i++) { CodeInstruction val = list[i - 1]; CodeInstruction val2 = list[i]; bool num2 = (val.opcode == OpCodes.Call || val.opcode == OpCodes.Callvirt) && object.Equals(val.operand, methodInfo); bool flag = val2.opcode == OpCodes.Ldc_R4 && val2.operand is float num3 && Math.Abs(num3 - 1f) < 0.0001f; if (num2 && flag) { val2.opcode = OpCodes.Ldarg_0; val2.operand = null; list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)methodInfo2)); num++; i++; } } if (num == 0) { Plugin.ModLog.LogError((object)("[UsageSkills:PassOut] No vanilla statusSum vs 1.0 threshold found in " + originalMethod.Name + ". Endurance pass-out scaling will not affect this method.")); } else { Plugin.ModLog.LogInfo((object)$"[UsageSkills:PassOut] Patched {originalMethod.Name} threshold(s)={num}."); } return list; } } [HarmonyPatch(typeof(Character), "HandlePassedOut")] internal static class EndurancePassOutRecoveryPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod) { return EndurancePassOutThreshold.ReplaceVanillaThreshold(instructions, __originalMethod); } } [HarmonyPatch(typeof(Character), "UseStamina")] internal static class StaminaCostPatch { private static void Prefix(Character __instance, ref float usage) { if ((Object)(object)__instance == (Object)null || (Object)(object)Character.localCharacter != (Object)(object)__instance || usage <= 0f) { return; } float num = usage; bool flag = JumpEffectScope.Matches(__instance); bool flag2 = __instance.data.isClimbing && !__instance.data.isRopeClimbing && !__instance.data.isVineClimbing; string source = (flag ? "Jumping" : (flag2 ? "WallClimbing" : (__instance.data.isRopeClimbing ? "RopeClimbing" : (__instance.data.isVineClimbing ? "VineClimbing" : (__instance.data.isSprinting ? "Sprinting" : "OtherExertion"))))); if (!__instance.infiniteStam) { Plugin.Progression.AwardWork(SkillId.Endurance, num, Plugin.Settings.EnduranceXpPerStamina.Value, source); } if (Plugin.Effects.CanApply(__instance)) { if (flag2) { usage *= Plugin.Effects.WallCostMultiplier; if (__instance.GetTotalStamina() < 0.2f) { usage *= Plugin.Effects.ClimbingTenacityPenaltyMultiplier; } } else if (__instance.data.isRopeClimbing) { usage *= Plugin.Effects.RopeCostMultiplier; } else if (__instance.data.isVineClimb