Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of balrond extra skills v0.1.0
plugins/BalrondExtraSkills.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using JetBrains.Annotations; using LitJson2; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("BalrondExtraSkills")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("BalrondExtraSkills")] [assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("f6d55ca3-92c3-4d6a-bf40-9e8f52c5a331")] [assembly: AssemblyFileVersion("0.1.4.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.4.0")] [module: UnverifiableCode] public static class BalrondHashCompat { private static readonly MethodInfo _getStableHashCodeStringBool; private static readonly MethodInfo _getStableHashCodeString; private static readonly bool _initialized; static BalrondHashCompat() { try { Type typeFromHandle = typeof(StringExtensionMethods); _getStableHashCodeStringBool = typeFromHandle.GetMethod("GetStableHashCode", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(string), typeof(bool) }, null); _getStableHashCodeString = typeFromHandle.GetMethod("GetStableHashCode", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null); _initialized = true; } catch { _initialized = false; } } public static int StableHash(string value) { if (value == null) { return 0; } try { if (_getStableHashCodeStringBool != null) { return (int)_getStableHashCodeStringBool.Invoke(null, new object[2] { value, false }); } if (_getStableHashCodeString != null) { return (int)_getStableHashCodeString.Invoke(null, new object[1] { value }); } } catch { } return FallbackStableHash(value); } private static int FallbackStableHash(string value) { int num = 5381; int num2 = num; for (int i = 0; i < value.Length; i += 2) { num = ((num << 5) + num) ^ value[i]; if (i == value.Length - 1) { break; } num2 = ((num2 << 5) + num2) ^ value[i + 1]; } return num + num2 * 1566083941; } } namespace BalrondExtraSkills { public static class BalrondDamageHub { private sealed class Box<T> { public T Value; public Box(T v) { Value = v; } } [HarmonyPatch(typeof(Character), "ApplyDamage")] private static class Character_ApplyDamage_Patch { [HarmonyPrefix] private static void Prefix(Character __instance) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { float health = ((Character)val).GetHealth(); s_preHealth.Remove(__instance); s_preHealth.Add(__instance, new Box<float>(health)); } } [HarmonyPostfix] private static void Postfix(Character __instance, HitData hit) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && s_preHealth.TryGetValue(__instance, out var value)) { float value2 = value.Value; float health = ((Character)val).GetHealth(); s_preHealth.Remove(__instance); float num = value2 - health; if (!(num <= 0f)) { FortitudeSkill.OnDamageTaken(val, hit, num); ArcanaSkill.OnDamageTaken(val, hit, num, wasBlocking: false, wasPerfectBlock: false); } } } } private static readonly ConditionalWeakTable<Character, Box<float>> s_preHealth = new ConditionalWeakTable<Character, Box<float>>(); internal static bool IsElemental(HitData hit) { if (hit == null) { return false; } return hit.m_damage.m_fire > 0f || hit.m_damage.m_frost > 0f || hit.m_damage.m_lightning > 0f; } } internal static class BalrondIconUtil { private static Sprite _placeholder; internal static Sprite Placeholder { get { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_0027: Expected O, but got Unknown //IL_003e: 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_0076: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_placeholder != (Object)null) { return _placeholder; } try { Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, new Color(0f, 0f, 0f, 0f)); val.Apply(false, true); _placeholder = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f)); ((Object)_placeholder).name = "BalrondExtraSkills_PlaceholderIcon"; } catch { _placeholder = Sprite.Create(Texture2D.blackTexture, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f)); } return _placeholder; } } } internal static class BalrondSkillLevelupMessagePatch { [HarmonyPatch(typeof(Player), "Message", new Type[] { typeof(MessageType), typeof(string), typeof(int), typeof(Sprite) })] private static class Player_Message_SkillupTokenFix { [HarmonyPrefix] private static void Prefix(ref string msg) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(msg) || !msg.StartsWith("$msg_skillup")) { return; } int num = msg.IndexOf("$skill_"); if (num < 0) { return; } int i = num + 7; int num2 = i; for (; i < msg.Length; i++) { char c = msg[i]; if (c < '0' || c > '9') { break; } } if (i == num2) { return; } string s = msg.Substring(num2, i - num2); if (!int.TryParse(s, out var result)) { return; } SkillType key = (SkillType)result; if (BalrondSkillSystem.Registered.TryGetValue(key, out var value) && value != null) { PropertyInfo propertyInfo = AccessTools.Property(value.GetType(), "NameToken"); string text; if (propertyInfo != null) { text = propertyInfo.GetValue(value, null) as string; } else { PropertyInfo propertyInfo2 = AccessTools.Property(value.GetType(), "NameTokenKey"); text = ((propertyInfo2 != null) ? (propertyInfo2.GetValue(value, null) as string) : null); } if (string.IsNullOrEmpty(text)) { text = "$tag_skill_" + (value.InternalKey ?? "skill") + "_bal"; } msg = msg.Substring(0, num) + text + msg.Substring(i); } } } } public static class BalrondSkillSystem { [HarmonyPatch(typeof(SkillsDialog), "Setup")] private static class SkillsDialog_Setup_CustomNames_Patch { [HarmonyPostfix] private static void Postfix(SkillsDialog __instance) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance == (Object)null) { return; } FieldInfo fieldInfo = AccessTools.Field(typeof(SkillsDialog), "m_elements"); if (fieldInfo == null || !(fieldInfo.GetValue(__instance) is List<GameObject> list)) { return; } foreach (GameObject item in list) { if ((Object)(object)item == (Object)null || !item.activeInHierarchy) { continue; } Transform val = Utils.FindChild(item.transform, "name", (IterativeSearchType)0); if ((Object)(object)val == (Object)null) { continue; } TMP_Text component = ((Component)val).GetComponent<TMP_Text>(); if ((Object)(object)component == (Object)null) { continue; } int num = TryParseBracketedSkillId(component.text); if (num == -1) { continue; } SkillType key = (SkillType)num; if (!Registered.TryGetValue(key, out var value) || value == null) { continue; } string text = ((Localization.instance != null) ? Localization.instance.Localize(value.NameToken) : value.NameToken); if (!string.IsNullOrEmpty(text) && text.StartsWith("[") && text.EndsWith("]")) { text = value.SkillNameEnglish; } component.text = text; Transform val2 = Utils.FindChild(item.transform, "icon", (IterativeSearchType)0); if ((Object)(object)val2 != (Object)null) { Image component2 = ((Component)val2).GetComponent<Image>(); if ((Object)(object)component2 != (Object)null) { component2.sprite = value.Icon; } } } } catch { } } private static int TryParseBracketedSkillId(string text) { if (string.IsNullOrEmpty(text)) { return -1; } int num = text.IndexOf("[skill_", StringComparison.OrdinalIgnoreCase); if (num < 0) { return -1; } int num2 = num + "[skill_".Length; int num3 = text.IndexOf(']', num2); if (num3 < 0) { return -1; } string s = text.Substring(num2, num3 - num2); int result; return int.TryParse(s, out result) ? result : (-1); } } [HarmonyPatch(typeof(Skills), "IsSkillValid")] private static class Skills_IsSkillValid_Patch { [HarmonyPrefix] private static bool Prefix(SkillType type, ref bool __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!IsCustomSkill(type)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(Skills), "GetSkillDef")] private static class Skills_GetSkillDef_Patch { [HarmonyPostfix] private static void Postfix(Skills __instance, SkillType type, ref SkillDef __result) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if (__result == null && Registered.TryGetValue(type, out var value)) { __result = new SkillDef { m_skill = type, m_icon = value.Icon, m_description = value.DescToken, m_increseStep = 1f }; EnsureDefsInSkillsList(__instance); } } } [HarmonyPatch(typeof(Skills), "Awake")] private static class Skills_Awake_Patch { [HarmonyPostfix] private static void Postfix(Skills __instance) { try { EnsureDefsInSkillsList(__instance); } catch { } } } [HarmonyPatch(typeof(Player), "Awake")] private static class Player_Awake_SeedCustomSkills_Patch { [HarmonyPostfix] private static void Postfix(Player __instance) { EnsureAllSeeded(__instance); } } [HarmonyPatch(typeof(Player), "Load")] private static class Player_Load_SeedCustomSkills_Patch { [HarmonyPostfix] private static void Postfix(Player __instance) { EnsureAllSeeded(__instance); } } [HarmonyPatch(typeof(Skills), "CheatRaiseSkill")] private static class Skills_CheatRaiseSkill_Patch { [HarmonyPrefix] private static bool Prefix(Skills __instance, string name, float value, bool showMessage) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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_0168: Expected O, but got Unknown //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) try { if (string.IsNullOrEmpty(name)) { return true; } string key = SanitizeKey(name); if (!ByAlias.TryGetValue(key, out var value2)) { return true; } if (!Registered.TryGetValue(value2, out var value3)) { return true; } object? obj = FI_Skills_m_player?.GetValue(__instance); Player val = (Player)((obj is Player) ? obj : null); if ((Object)(object)val != (Object)null) { EnsureDefsInSkillsList(__instance, forceRefresh: true); EnsureSkillSeeded(__instance, value2); } if (!(FI_Skills_m_skillData?.GetValue(__instance) is Dictionary<SkillType, Skill> dictionary)) { return false; } if (!dictionary.TryGetValue(value2, out var value4) || value4 == null) { if (MI_Skills_GetSkill != null) { MI_Skills_GetSkill.Invoke(__instance, new object[1] { value2 }); if (!(FI_Skills_m_skillData.GetValue(__instance) is Dictionary<SkillType, Skill> dictionary2) || !dictionary2.TryGetValue(value2, out value4) || value4 == null) { return false; } } else { SkillDef skillDef = __instance.GetSkillDef(value2); if (skillDef == null) { return false; } value4 = (dictionary[value2] = new Skill(skillDef)); FI_Skills_m_skillData.SetValue(__instance, dictionary); } } value4.m_level = Mathf.Clamp(value4.m_level + value, 0f, 100f); value4.m_accumulator = 0f; if (showMessage && (Object)(object)val != (Object)null) { ((Character)val).Message((MessageType)1, $"Skill increased {value3.SkillNameEnglish}: {(int)value4.m_level}", 0, value3.Icon); } Console instance = Console.instance; if (instance != null) { instance.Print($"Skill {value3.SkillNameEnglish} = {(int)value4.m_level}"); } return false; } catch { return true; } } } [HarmonyPatch(typeof(Skills), "CheatResetSkill")] private static class Skills_CheatResetSkill_Patch { [HarmonyPrefix] private static bool Prefix(Skills __instance, string name) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) try { if (string.IsNullOrEmpty(name)) { return true; } string key = SanitizeKey(name); if (!ByAlias.TryGetValue(key, out var value)) { return true; } if (!Registered.TryGetValue(value, out var value2)) { return true; } __instance.ResetSkill(value); Console instance = Console.instance; if (instance != null) { instance.Print("Skill " + value2.SkillNameEnglish + " reset"); } return false; } catch { return true; } } } public static readonly Dictionary<SkillType, BaseSkill> Registered = new Dictionary<SkillType, BaseSkill>(); private static readonly Dictionary<string, SkillType> ByAlias = new Dictionary<string, SkillType>(); private static readonly FieldInfo FI_Skills_m_skillData = AccessTools.Field(typeof(Skills), "m_skillData"); private static readonly FieldInfo FI_Skills_m_player = AccessTools.Field(typeof(Skills), "m_player"); private static readonly MethodInfo MI_Skills_GetSkill = AccessTools.Method(typeof(Skills), "GetSkill", new Type[1] { typeof(SkillType) }, (Type[])null); public static void RegisterSkill(BaseSkill skill) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 //IL_005a: 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_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_00e4: Expected I4, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected I4, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected I4, but got Unknown //IL_0146: Unknown result type (might be due to invalid IL or missing references) if (skill == null) { throw new ArgumentNullException("skill"); } string text = SanitizeKey(skill.InternalKey); if (string.IsNullOrEmpty(text)) { text = SanitizeKey(skill.SkillNameEnglish); } if ((int)skill.SkillType == 0) { skill.SkillType = GetFixedSkillType(skill); } if (Registered.TryGetValue(skill.SkillType, out var value)) { throw new InvalidOperationException($"[BalrondExtraSkills] Skill ID collision: {(int)skill.SkillType} is already used by '{value.InternalKey}', cannot register '{skill.InternalKey}'."); } Registered.Add(skill.SkillType, skill); AddAlias(text, skill.SkillType); AddAlias(skill.SkillNameEnglish, skill.SkillType); AddAlias(skill.NameToken, skill.SkillType); AddAlias("skill_" + (int)skill.SkillType, skill.SkillType); Debug.Log((object)$"[BalrondExtraSkills] Registered skill: {skill.SkillNameEnglish} (internal: {text}, type {(int)skill.SkillType})"); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { EnsureDefsInSkillsList(((Character)localPlayer).GetSkills()); EnsureSkillSeeded(((Character)localPlayer).GetSkills(), skill.SkillType); } } public static bool IsCustomSkill(SkillType type) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return Registered.ContainsKey(type); } internal static bool IsSkillEnabled(SkillType type) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (!Registered.TryGetValue(type, out var value) || value == null) { return true; } return Launch.Settings?.IsSkillEnabled(value.FixedSkillId) ?? true; } internal static float GetSkillGainMultiplier(SkillType type) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (!Registered.TryGetValue(type, out var value) || value == null) { return 1f; } ExtraSkillsConfig settings = Launch.Settings; if (settings == null) { return 1f; } return Mathf.Max(0f, settings.GetSkillGainMultiplier(value.FixedSkillId)); } internal static float GetSkillEffectMultiplier(SkillType type) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (!Registered.TryGetValue(type, out var value) || value == null) { return 1f; } ExtraSkillsConfig settings = Launch.Settings; if (settings == null) { return 1f; } return Mathf.Max(0f, settings.GetSkillEffectMultiplier(value.FixedSkillId)); } public static void RaiseSkill(Player player, SkillType type, float amount) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || amount <= 0f) { return; } if (IsCustomSkill(type)) { if (!IsSkillEnabled(type)) { return; } float skillGainMultiplier = GetSkillGainMultiplier(type); if (skillGainMultiplier <= 0f) { return; } amount *= skillGainMultiplier; if (amount <= 0f || float.IsNaN(amount) || float.IsInfinity(amount)) { return; } } Skills skills = ((Character)player).GetSkills(); if (!((Object)(object)skills == (Object)null)) { EnsureDefsInSkillsList(skills); EnsureSkillSeeded(skills, type); skills.RaiseSkill(type, amount); } } public static void NotifyAssetsReady() { try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { EnsureDefsInSkillsList(((Character)localPlayer).GetSkills(), forceRefresh: true); } Player[] array = Object.FindObjectsOfType<Player>(); foreach (Player val in array) { if (!((Object)(object)val == (Object)null)) { EnsureDefsInSkillsList(((Character)val).GetSkills(), forceRefresh: true); } } } catch { } } private static SkillType GetFixedSkillType(BaseSkill skill) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) int fixedSkillId = skill.FixedSkillId; if (fixedSkillId < 10000 || fixedSkillId > 39999) { Debug.LogWarning((object)$"[BalrondExtraSkills] Skill '{skill.InternalKey}' uses ID outside recommended range 10000-39999: {fixedSkillId}"); } return (SkillType)fixedSkillId; } private static void AddAlias(string raw, SkillType type) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) string text = SanitizeKey(raw); if (string.IsNullOrEmpty(text)) { return; } if (ByAlias.TryGetValue(text, out var value)) { if (value != type) { Debug.LogWarning((object)("[BalrondExtraSkills] Alias collision for '" + raw + "' -> '" + text + "'")); } } else { ByAlias.Add(text, type); } } private static string SanitizeKey(string s) { if (string.IsNullOrEmpty(s)) { return ""; } s = s.Trim().ToLowerInvariant(); s = s.Replace("$", ""); s = s.Replace(" ", ""); s = s.Replace("-", ""); s = s.Replace("_", ""); return s; } private static void EnsureDefsInSkillsList(Skills skills, bool forceRefresh = false) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown if ((Object)(object)skills == (Object)null) { return; } List<SkillDef> skills2 = skills.m_skills; if (skills2 == null) { return; } foreach (KeyValuePair<SkillType, BaseSkill> item in Registered) { SkillType key = item.Key; BaseSkill value = item.Value; SkillDef val = null; for (int i = 0; i < skills2.Count; i++) { SkillDef val2 = skills2[i]; if (val2 != null && val2.m_skill == key) { val = val2; break; } } if (val == null) { skills2.Add(new SkillDef { m_skill = key, m_icon = value.Icon, m_description = value.DescToken, m_increseStep = 1f }); } else if (forceRefresh) { val.m_icon = value.Icon; val.m_description = value.DescToken; } } } private static void EnsureSkillSeeded(Skills skills, SkillType type) { //IL_0045: 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_0073: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown if ((Object)(object)skills == (Object)null || FI_Skills_m_skillData == null) { return; } try { if (!(FI_Skills_m_skillData.GetValue(skills) is Dictionary<SkillType, Skill> dictionary) || dictionary.ContainsKey(type)) { return; } if (MI_Skills_GetSkill != null) { MI_Skills_GetSkill.Invoke(skills, new object[1] { type }); return; } SkillDef skillDef = skills.GetSkillDef(type); if (skillDef != null) { dictionary[type] = new Skill(skillDef); FI_Skills_m_skillData.SetValue(skills, dictionary); } } catch { } } private static void EnsureAllSeeded(Player player) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } Skills skills = ((Character)player).GetSkills(); if ((Object)(object)skills == (Object)null) { return; } EnsureDefsInSkillsList(skills); foreach (KeyValuePair<SkillType, BaseSkill> item in Registered) { EnsureSkillSeeded(skills, item.Key); } } } public class BalrondTranslator { public static Dictionary<string, Dictionary<string, string>> translations = new Dictionary<string, Dictionary<string, string>>(); public static Dictionary<string, string> getLanguage(string language) { Dictionary<string, string> result = null; try { result = translations[language]; } catch (Exception) { } return result; } } internal static class PickaxeStaminaReduction { internal static class Patches { [HarmonyPatch] private static class Attack_GetAttackStamina_Patch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Attack), "GetAttackStamina", (Type[])null, (Type[])null); } private static bool Prepare() { return TargetMethod() != null; } [HarmonyPostfix] private static void Postfix(Attack __instance, ref float __result) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Invalid comparison between Unknown and I4 if (__instance == null || __result <= 0f) { return; } object? obj = FI_AttackCharacter?.GetValue(__instance); Humanoid val = (Humanoid)((obj is Humanoid) ? obj : null); Player val2 = (Player)(object)((val is Player) ? val : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val2 != (Object)(object)Player.m_localPlayer) { return; } object? obj2 = FI_AttackWeapon?.GetValue(__instance); ItemData val3 = (ItemData)((obj2 is ItemData) ? obj2 : null); if (val3?.m_shared != null && (int)val3.m_shared.m_skillType == 12) { float pickaxeAttackCostMultiplier = GetPickaxeAttackCostMultiplier(val2); __result *= pickaxeAttackCostMultiplier; if (__result < 0f) { __result = 0f; } } } } [HarmonyPatch(typeof(Player), "UseStamina")] private static class Player_UseStamina_Fallback_Patch { private static bool _useFallback; private static bool _initialized; private static void EnsureInit() { if (!_initialized) { _initialized = true; _useFallback = AccessTools.Method(typeof(Attack), "GetAttackStamina", (Type[])null, (Type[])null) == null; } } [HarmonyPrefix] private static void Prefix(Player __instance, ref float v) { EnsureInit(); if (_useFallback && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !(v <= 0f)) { } } } private static readonly FieldInfo FI_AttackCharacter = AccessTools.Field(typeof(Attack), "m_character"); private static readonly FieldInfo FI_AttackWeapon = AccessTools.Field(typeof(Attack), "m_weapon"); } private static float GetPickaxeAttackCostMultiplier(Player player) { if ((Object)(object)player == (Object)null) { return 1f; } ExtraSkillsConfig settings = Launch.Settings; if (settings != null && !settings.PickaxeStaminaReductionEnabled.Value) { return 1f; } Skills skills = ((Character)player).GetSkills(); float num = ((skills != null) ? skills.GetSkillLevel((SkillType)12) : 0f); if (num <= 0f) { return 1f; } float num2 = Mathf.Clamp01(num / 100f); float num3 = settings?.PickaxeStaminaReductionMultiplier.Value ?? 1f; float num4 = settings?.PickaxeMaxReductionAt100.Value ?? 0.25f; float num5 = settings?.PickaxeMinCostMultiplier.Value ?? 0.2f; float num6 = num2 * Mathf.Clamp01(num4) * Mathf.Max(0f, num3); float num7 = 1f - num6; if (num7 < Mathf.Clamp01(num5)) { num7 = Mathf.Clamp01(num5); } return num7; } } public abstract class BaseSkill { public readonly int FixedSkillId; public readonly string InternalKey; public readonly string SkillNameEnglish; public readonly string DescriptionEnglish; private Sprite _icon; private readonly string _iconAssetName; public SkillType SkillType { get; internal set; } = (SkillType)0; public Sprite Icon => ((Object)(object)_icon != (Object)null) ? _icon : BalrondIconUtil.Placeholder; public string IconAssetName => _iconAssetName; public string NameToken => "$tag_skill_" + InternalKey + "_bal"; public string DescToken => "$tag_skill_" + InternalKey + "_bal_description"; protected BaseSkill(int fixedSkillId, string internalKey, string englishName, string englishDescription, string iconAssetName) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) if (fixedSkillId <= 0) { throw new ArgumentOutOfRangeException("fixedSkillId", "Skill ID must be greater than 0."); } FixedSkillId = fixedSkillId; InternalKey = internalKey ?? throw new ArgumentNullException("internalKey"); SkillNameEnglish = englishName ?? throw new ArgumentNullException("englishName"); DescriptionEnglish = englishDescription ?? ""; _iconAssetName = iconAssetName ?? ""; if (!string.IsNullOrEmpty(_iconAssetName)) { _icon = ModResourceLoader.skillIcons.Find((Sprite x) => ((Object)x).name == _iconAssetName); } try { BalrondSkillSystem.RegisterSkill(this); } catch (Exception arg) { Debug.LogWarning((object)$"[BalrondExtraSkills] Failed to auto-register skill '{englishName}': {arg}"); } } public void SetIcon(Sprite icon) { if ((Object)(object)icon != (Object)null) { _icon = icon; } } } public class DatabaseAddMethods { public void AddItems(List<GameObject> items) { foreach (GameObject item in items) { AddItem(item); } } public void AddRecipes(List<Recipe> recipes) { foreach (Recipe recipe in recipes) { AddRecipe(recipe); } } public void AddStatuseffects(List<StatusEffect> statusEffects) { foreach (StatusEffect statusEffect in statusEffects) { AddStatus(statusEffect); } } private bool IsObjectDBValid() { return (Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items.Count != 0 && ObjectDB.instance.m_recipes.Count != 0 && (Object)(object)ObjectDB.instance.GetItemPrefab("Amber") != (Object)null; } private void AddStatus(StatusEffect status) { if (!IsObjectDBValid()) { return; } if ((Object)(object)status != (Object)null) { if ((Object)(object)ObjectDB.instance.GetStatusEffect(status.m_nameHash) == (Object)null) { ObjectDB.instance.m_StatusEffects.Add(status); } else { Debug.Log((object)(Launch.projectName + ": " + ((Object)status).name + " - Status already in the game")); } } else { Debug.LogError((object)(Launch.projectName + ": " + ((Object)status).name + " - Status not found")); } } private void AddRecipe(Recipe recipe) { if (!IsObjectDBValid()) { return; } if ((Object)(object)recipe != (Object)null) { if ((Object)(object)ObjectDB.instance.m_recipes.Find((Recipe x) => ((Object)x).name == ((Object)recipe).name) == (Object)null) { if ((Object)(object)recipe.m_item != (Object)null) { ObjectDB.instance.m_recipes.Add(recipe); } } else { Debug.Log((object)(Launch.projectName + ": " + ((Object)recipe).name + " - Recipe with this name already in the Game")); } } else { Debug.LogError((object)(Launch.projectName + ": " + ((Object)recipe).name + " - Recipe not found")); } } private void AddItem(GameObject newPrefab) { if (!IsObjectDBValid()) { return; } ItemDrop component = newPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { if ((Object)(object)ObjectDB.instance.GetItemPrefab(((Object)newPrefab).name) == (Object)null) { ObjectDB.instance.m_items.Add(newPrefab); Dictionary<int, GameObject> dictionary = (Dictionary<int, GameObject>)typeof(ObjectDB).GetField("m_itemByHash", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(ObjectDB.instance); dictionary[((Object)newPrefab).name.GetHashCode()] = newPrefab; } else { Debug.Log((object)(Launch.projectName + ": " + ((Object)newPrefab).name + " - ItemDrop already exist")); } } else { Debug.LogError((object)(Launch.projectName + ": " + ((Object)newPrefab).name + " - ItemDrop not found on prefab")); } } } [Serializable] public class MappedEffectList { public List<EffectInfo> pieceEffect; public List<EffectInfo> destroyedEffects; public List<EffectInfo> hitEffects; public List<EffectInfo> switchEffect; public List<EffectInfo> blockEffect; public List<EffectInfo> equipEffect; public List<EffectInfo> hitEffect; public List<EffectInfo> hitTerrainEffect; public List<EffectInfo> holdStartEffect; public List<EffectInfo> startEffect; public List<EffectInfo> trailStartEffect; public List<EffectInfo> triggerEffect; public List<EffectInfo> unequipEffect; public EffectList createEffectListFromInfo(List<EffectInfo> list) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown EffectList val = new EffectList(); List<EffectData> list2 = new List<EffectData>(); val.m_effectPrefabs = list2.ToArray(); return val; } private EffectData createEffectData(EffectInfo info) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown EffectData val = new EffectData(); val.m_inheritParentRotation = info.inheritRotation; val.m_multiplyParentVisualScale = info.multiplyScale; val.m_childTransform = info.childName; val.m_inheritParentScale = info.inheritScale; val.m_variant = info.variant; val.m_scale = info.scale; val.m_attach = info.attach; val.m_follow = info.follow; val.m_prefab = ZNetScene.instance.m_prefabs.Find((GameObject x) => ((Object)x).name == info.name); return val; } } [Serializable] public struct EffectInfo { public string name; public bool enabled; public int variant; public bool attach; public bool follow; public bool inheritRotation; public bool inheritScale; public bool multiplyScale; public bool radnomRotation; public bool scale; public string childName; public EffectInfo(string name, bool enabled = true, int variant = -1, bool attach = false, bool follow = false, bool inheritRotation = false, bool inheritScale = false, bool multiplyScale = false, bool radnomRotation = false, bool scale = false, string childName = null) { this.name = name; this.enabled = enabled; this.variant = variant; this.attach = attach; this.follow = follow; this.inheritRotation = inheritRotation; this.inheritScale = inheritScale; this.multiplyScale = multiplyScale; this.radnomRotation = radnomRotation; this.scale = scale; this.childName = childName; } } public sealed class ModResourceLoader { public AssetBundle assetBundle; public readonly List<GameObject> itemPrefabs = new List<GameObject>(); public readonly List<GameObject> otherPrefabs = new List<GameObject>(); public static List<Sprite> skillIcons = new List<Sprite>(); public void loadAssets() { assetBundle = GetAssetBundleFromResources("balrondextraskills"); if ((Object)(object)assetBundle == (Object)null) { Debug.LogWarning((object)(Launch.projectName + " AssetBundle not found (embedded resource endswith 'BalrondExtraSkills').")); return; } string basePath = "Assets/Custom/BalrondExtraSkills/"; LoadSkillIcons(); loadItems(basePath); loadOther(basePath); } public void AddPrefabsToZnetScene(ZNetScene zNetScene) { if (!((Object)(object)zNetScene == (Object)null)) { validateAddedPrefabs(itemPrefabs, zNetScene); validateAddedPrefabs(otherPrefabs, zNetScene); zNetScene.m_prefabs.RemoveAll((GameObject x) => (Object)(object)x == (Object)null); } } public void AddItemsToObjectDB() { if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_items == null) { return; } for (int i = 0; i < itemPrefabs.Count; i++) { GameObject go = itemPrefabs[i]; if (!((Object)(object)go == (Object)null) && !((Object)(object)ObjectDB.instance.m_items.Find((GameObject x) => (Object)(object)x != (Object)null && ((Object)x).name == ((Object)go).name) != (Object)null)) { ObjectDB.instance.m_items.Add(go); } } } private void loadItems(string basePath) { string mainPath = basePath + "Items/"; string[] nameList = new string[0]; addNewPrefabToCollection(nameList, mainPath, itemPrefabs, "item", requireZNetView: false); } private void loadOther(string basePath) { string mainPath = basePath + "Other/"; string[] nameList = new string[0]; addNewPrefabToCollection(nameList, mainPath, otherPrefabs, "other", requireZNetView: false); } private void validateAddedPrefabs(List<GameObject> list, ZNetScene zNetScene) { if (list == null || (Object)(object)zNetScene == (Object)null) { return; } for (int i = 0; i < list.Count; i++) { GameObject val = list[i]; if (!((Object)(object)val == (Object)null)) { int key = BalrondHashCompat.StableHash(((Object)val).name); if (zNetScene.m_namedPrefabs.ContainsKey(key)) { Debug.LogWarning((object)("DUPLICATE: " + ((Object)val).name)); } else { zNetScene.m_prefabs.Add(val); } } } } private void addNewPrefabToCollection(string[] nameList, string mainPath, List<GameObject> prefabList, string typeName, bool requireZNetView) { if ((Object)(object)assetBundle == (Object)null || nameList == null || nameList.Length == 0) { return; } foreach (string text in nameList) { if (!string.IsNullOrEmpty(text)) { GameObject val = assetBundle.LoadAsset<GameObject>(mainPath + text + ".prefab"); if ((Object)(object)val == (Object)null) { Debug.LogWarning((object)("Could not find " + typeName + " with name: " + text + " at " + mainPath)); } else if (requireZNetView && (Object)(object)val.GetComponent<ZNetView>() == (Object)null) { Debug.LogWarning((object)("Prefab has no ZNetView (skipped): " + text)); } else { ShaderReplacment.Replace(val); prefabList.Add(val); } } } } public void LoadSkillIcons() { skillIcons.Clear(); if ((Object)(object)assetBundle == (Object)null) { return; } try { Sprite[] array = assetBundle.LoadAllAssets<Sprite>(); if (array == null || array.Length == 0) { Debug.LogWarning((object)(Launch.projectName + " No Sprite assets found in assetbundle.")); return; } foreach (Sprite val in array) { if (!((Object)(object)val == (Object)null)) { skillIcons.Add(val); } } } catch (Exception ex) { Debug.LogWarning((object)(Launch.projectName + " LoadSkillIcons failed: " + ex)); } } public static Sprite GetSkillIconByName(string iconName) { if (string.IsNullOrEmpty(iconName)) { return null; } for (int i = 0; i < skillIcons.Count; i++) { Sprite val = skillIcons[i]; if ((Object)(object)val != (Object)null && ((Object)val).name == iconName) { return val; } } return null; } private static AssetBundle GetAssetBundleFromResources(string filenameSuffix) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = null; string[] manifestResourceNames = executingAssembly.GetManifestResourceNames(); foreach (string text2 in manifestResourceNames) { if (text2.EndsWith(filenameSuffix, StringComparison.OrdinalIgnoreCase)) { text = text2; break; } } if (text == null) { return null; } using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { return null; } return AssetBundle.LoadFromStream(stream); } } public class ShaderReplacment { public static List<GameObject> prefabsToReplaceShader = new List<GameObject>(); public static List<Material> materialsInPrefabs = new List<Material>(); public string[] shaderlist = new string[49] { "Custom/AlphaParticle", "Custom/Blob", "Custom/Bonemass", "Custom/Clouds", "Custom/Creature", "Custom/Decal", "Custom/Distortion", "Custom/Flow", "Custom/FlowOpaque", "Custom/Grass", "Custom/GuiScroll", "Custom/Heightmap", "Custom/icon", "Custom/InteriorSide", "Custom/LitGui", "Custom/LitParticles", "Custom/mapshader", "Custom/ParticleDecal", "Custom/Piece", "Custom/Player", "Custom/Rug", "Custom/ShadowBlob", "Custom/SkyboxProcedural", "Custom/SkyObject", "Custom/StaticRock", "Custom/Tar", "Custom/Trilinearmap", "Custom/UI/BGBlur", "Custom/Vegetation", "Custom/Water", "Custom/WaterBottom", "Custom/WaterMask", "Custom/Yggdrasil", "Custom/Yggdrasil/root", "Hidden/BlitCopyHDRTonemap", "Hidden/Dof/DepthOfFieldHdr", "Hidden/Dof/DX11Dof", "Hidden/Internal-Loading", "Hidden/Internal-UIRDefaultWorld", "Hidden/SimpleClear", "Hidden/SunShaftsComposite", "Lux Lit Particles/ Bumped", "Lux Lit Particles/ Tess Bumped", "Particles/Standard Surface2", "Particles/Standard Unlit2", "Standard TwoSided", "ToonDeferredShading2017", "Unlit/DepthWrite", "Unlit/Lighting" }; public static List<Shader> shaders = new List<Shader>(); private static readonly HashSet<Shader> CachedShaders = new HashSet<Shader>(); public static bool debug = true; public static Shader findShader(string name) { Shader[] array = Resources.FindObjectsOfTypeAll<Shader>(); if (array.Length == 0) { Debug.LogWarning((object)"SHADER LIST IS EMPTY!"); return null; } if (debug) { } return shaders.Find((Shader x) => ((Object)x).name == name); } public static Shader GetShaderByName(string name) { return shaders.Find((Shader x) => ((Object)x).name == name.Trim()); } public static void debugShaderList(List<Shader> shadersRes) { foreach (Shader shadersRe in shadersRes) { Debug.LogWarning((object)("SHADER NAME IS: " + ((Object)shadersRe).name)); } debug = false; } public static void Replace(GameObject gameObject) { prefabsToReplaceShader.Add(gameObject); GetMaterialsInPrefab(gameObject); } public static void GetMaterialsInPrefab(GameObject gameObject) { Renderer[] componentsInChildren = gameObject.GetComponentsInChildren<Renderer>(true); Renderer[] array = componentsInChildren; foreach (Renderer val in array) { Material[] sharedMaterials = val.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { continue; } Material[] array2 = sharedMaterials; foreach (Material val2 in array2) { if ((Object)(object)val2 != (Object)null) { materialsInPrefabs.Add(val2); } } } } public static void getMeShaders() { AssetBundle[] array = Resources.FindObjectsOfTypeAll<AssetBundle>(); AssetBundle[] array2 = array; foreach (AssetBundle val in array2) { IEnumerable<Shader> enumerable3; try { IEnumerable<Shader> enumerable2; if (!val.isStreamedSceneAssetBundle || !Object.op_Implicit((Object)(object)val)) { IEnumerable<Shader> enumerable = val.LoadAllAssets<Shader>(); enumerable2 = enumerable; } else { enumerable2 = from shader in ((IEnumerable<string>)val.GetAllAssetNames()).Select((Func<string, Shader>)val.LoadAsset<Shader>) where (Object)(object)shader != (Object)null select shader; } enumerable3 = enumerable2; } catch (Exception) { continue; } if (enumerable3 == null) { continue; } foreach (Shader item in enumerable3) { CachedShaders.Add(item); } } } public static void runMaterialFix() { getMeShaders(); shaders.AddRange(CachedShaders); foreach (Material materialsInPrefab in materialsInPrefabs) { Shader shader = materialsInPrefab.shader; if (!((Object)(object)shader == (Object)null)) { string name = ((Object)shader).name; if (!(name == "Standard") && !name.Contains("ScrollingTex") && name.Contains("Balrond")) { setProperValue(materialsInPrefab, name); } } } } private static void setProperValue(Material material, string shaderName) { string name = shaderName.Replace("Balrond", "Custom"); name = checkNaming(name); Shader shaderByName = GetShaderByName(name); if (!((Object)(object)shaderByName == (Object)null)) { material.shader = shaderByName; } } private static string checkNaming(string name) { string result = name; if (name.Contains("Bumped")) { result = name.Replace("Custom", "Lux Lit Particles"); } if (name.Contains("Tess Bumped")) { result = name.Replace("Custom", "Lux Lit Particles"); } if (name.Contains("Standard Surface")) { result = name.Replace("Custom", "Particles"); result = result.Replace("Standard Surface2", "Standard Surface"); } if (name.Contains("Standard Unlit")) { result = name.Replace("Custom", "Particles"); result = result.Replace("Standard Unlit", "Standard Unlit2"); result = result.Replace("Standard Unlit22", "Standard Unlit2"); } return result; } } public class TableMapper { public static CraftingStation cauldron; public static CraftingStation workbench; public static CraftingStation heavyWorkbench; public static CraftingStation forge; public static CraftingStation ironworks; public static CraftingStation blackforge; public static CraftingStation stoneCutter; public static CraftingStation artisian; public static CraftingStation magetable; public static CraftingStation runeforge; public static CraftingStation tannery; public static CraftingStation fletcher; public static CraftingStation grill; public static CraftingStation alchemylab; public static CraftingStation shamantable; public static CraftingStation foodtable; public static List<GameObject> pieces = new List<GameObject>(); public static void setupTables(List<GameObject> list) { pieces = list; prepareTables(); } private static CraftingStation FindStation(List<GameObject> list, string name, string replacement = "piece_workbench") { GameObject val = list.Find((GameObject x) => ((Object)x).name == name); if ((Object)(object)val != (Object)null) { return val.GetComponent<CraftingStation>(); } if ((Object)(object)val == (Object)null) { val = list.Find((GameObject x) => ((Object)x).name == replacement); if ((Object)(object)val != (Object)null) { return val.GetComponent<CraftingStation>(); } } Debug.LogWarning((object)("TableMapper - Station not found: " + name)); return null; } private static void prepareTables() { cauldron = FindStation(pieces, "piece_cauldron"); workbench = FindStation(pieces, "piece_workbench"); heavyWorkbench = FindStation(pieces, "piece_heavy_workbench_bal"); forge = FindStation(pieces, "forge"); ironworks = FindStation(pieces, "piece_metalworks_bal", "forge"); blackforge = FindStation(pieces, "blackforge"); stoneCutter = FindStation(pieces, "piece_stonecutter"); artisian = FindStation(pieces, "piece_artisanstation"); runeforge = FindStation(pieces, "piece_runeforge_bal", "blackforge"); magetable = FindStation(pieces, "piece_magetable"); fletcher = FindStation(pieces, "piece_fletcher_bal"); shamantable = FindStation(pieces, "piece_shamantable_bal", "piece_magetable"); foodtable = FindStation(pieces, "piece_preptable"); alchemylab = FindStation(pieces, "piece_MeadCauldron"); } } public class JsonLoader { public string defaultPath = string.Empty; public void loadJson() { LoadTranslations(); justDefaultPath(); } public void justDefaultPath() { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "BalrondExtraSkills-translation/"); defaultPath = text; } public void createDefaultPath() { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "BalrondExtraSkills-translation/"); if (!Directory.Exists(text)) { CreateFolder(text); } else { Debug.Log((object)("BalrondExtraSkills: Folder already exists: " + text)); } defaultPath = text; } private string[] jsonFilePath(string folderName, string extension) { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "BalrondExtraSkills-translation/"); if (!Directory.Exists(text)) { CreateFolder(text); } else { Debug.Log((object)("BalrondExtraSkills: Folder already exists: " + text)); } string[] files = Directory.GetFiles(text, extension); Debug.Log((object)("BalrondExtraSkills:" + folderName + " Json Files Found: " + files.Length)); return files; } private static void CreateFolder(string path) { try { Directory.CreateDirectory(path); Debug.Log((object)"BalrondExtraSkills: Folder created successfully."); } catch (Exception ex) { Debug.Log((object)("BalrondExtraSkills: Error creating folder: " + ex.Message)); } } private void LoadTranslations() { int num = 0; string[] array = jsonFilePath("Translation", "*.json"); foreach (string text in array) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); string json = File.ReadAllText(text); JsonData jsonData = JsonMapper.ToObject(json); Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (string key in jsonData.Keys) { dictionary[key] = jsonData[key].ToString(); } if (dictionary != null) { BalrondTranslator.translations.Add(fileNameWithoutExtension, dictionary); Debug.Log((object)("BalrondExtraSkills: Json Files Language: " + fileNameWithoutExtension)); num++; } else { Debug.LogError((object)("BalrondExtraSkills: Loading FAILED file: " + text)); } } Debug.Log((object)("BalrondExtraSkills: Translation JsonFiles Loaded: " + num)); } } [BepInPlugin("balrond.astafaraios.BalrondExtraSkills", "BalrondExtraSkills", "0.1.4")] public sealed class Launch : BaseUnityPlugin { [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class ZNetScene_Awake_Path { private static bool ranFix; private static void Prefix(ZNetScene __instance) { try { if ((Object)(object)__instance == (Object)null || modResourceLoader == null) { return; } modResourceLoader.AddPrefabsToZnetScene(__instance); if (!ranFix) { ZNet val = Object.FindObjectOfType<ZNet>(); if (!((Object)(object)val != (Object)null) || !val.IsDedicated()) { ShaderReplacment.runMaterialFix(); ranFix = true; } } } catch (Exception arg) { Debug.LogError((object)string.Format("[{0}] ZNetScene_Awake_Path failed: {1}", "BalrondExtraSkills", arg)); } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] private static class ObjectDB_Awake_Path { private static void Postfix() { try { if (IsObjectDBValid() && modResourceLoader != null) { modResourceLoader.AddItemsToObjectDB(); } } catch (Exception arg) { Debug.LogError((object)string.Format("[{0}] ObjectDB_Awake_Path failed: {1}", "BalrondExtraSkills", arg)); } } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] private static class Object_CopyOtherDB_Path { private static void Postfix() { try { if (IsObjectDBValid() && modResourceLoader != null) { modResourceLoader.AddItemsToObjectDB(); } } catch (Exception arg) { Debug.LogError((object)string.Format("[{0}] Object_CopyOtherDB_Path failed: {1}", "BalrondExtraSkills", arg)); } } } private Harmony harmony; internal static Launch Instance; internal static Harmony HarmonyInstance; internal static ConfigSync ConfigSyncInstance; internal static ExtraSkillsConfig Settings; public const string PluginGUID = "balrond.astafaraios.BalrondExtraSkills"; public const string PluginName = "BalrondExtraSkills"; public const string PluginVersion = "0.1.4"; public static readonly ModResourceLoader modResourceLoader = new ModResourceLoader(); public static readonly JsonLoader jsonLoader = new JsonLoader(); public static readonly string projectName = "[BalrondExtraSkills]"; public const bool Enabled = true; public const bool DebugLog = false; private void Awake() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown Instance = this; try { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BES bootstrap 1/8] Launch.Awake entered."); bool flag = false; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BES bootstrap 2/8] Initializing Harmony."); harmony = new Harmony("balrond.astafaraios.BalrondExtraSkills"); HarmonyInstance = harmony; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BES bootstrap 3/8] Initializing ServerSync and configuration."); ConfigSyncInstance = new ConfigSync("balrond.astafaraios.BalrondExtraSkills") { DisplayName = "BalrondExtraSkills", CurrentVersion = "0.1.4", MinimumRequiredVersion = "0.1.4" }; Settings = new ExtraSkillsConfig((BaseUnityPlugin)(object)this, ConfigSyncInstance); ConfigSyncInstance.AddLockingConfigEntry<bool>(Settings.LockConfiguration); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BES bootstrap 4/8] Loading translation data."); try { if (jsonLoader != null) { jsonLoader.loadJson(); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"BalrondExtraSkills: jsonLoader is null."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("BalrondExtraSkills: Failed to load json data. " + ex)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BES bootstrap 5/8] Applying Harmony patches."); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BES bootstrap 6/8] Loading embedded assets."); try { if (modResourceLoader != null) { modResourceLoader.loadAssets(); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"BalrondExtraSkills: modResourceLoader is null."); } } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)("BalrondExtraSkills: Failed to load assets. " + ex2)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BES bootstrap 7/8] Registering custom skills."); RegisterSkillsSafe(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[BES bootstrap 8/8] Refreshing skill assets and definitions."); try { BalrondSkillSystem.NotifyAssetsReady(); } catch (Exception ex3) { ((BaseUnityPlugin)this).Logger.LogError((object)("BalrondExtraSkills: Failed during NotifyAssetsReady. " + ex3)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"BalrondExtraSkills 0.1.4 loaded with ServerSync."); } catch (Exception ex4) { ((BaseUnityPlugin)this).Logger.LogError((object)("[BES bootstrap FATAL] Startup failed after Launch was instantiated. Full exception follows:\n" + ex4)); throw; } } private void OnDestroy() { try { if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("BalrondExtraSkills: Failed during OnDestroy unpatch. " + ex)); } finally { Settings = null; ConfigSyncInstance = null; HarmonyInstance = null; harmony = null; Instance = null; } } private void RegisterSkillsSafe() { TryRegisterSkill(() => VitalitySkill.Instance, "VitalitySkill"); TryRegisterSkill(() => ArcanaSkill.Instance, "ArcanaSkill"); TryRegisterSkill(() => FortitudeSkill.Instance, "FortitudeSkill"); TryRegisterSkill(() => EnduranceSkill.Instance, "EnduranceSkill"); TryRegisterSkill(() => HaulingSkill.Instance, "HaulingSkill"); TryRegisterSkill(() => ExplorationSkill.Instance, "ExplorationSkill"); } private void TryRegisterSkill(Func<BaseSkill> getter, string skillName) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Invalid comparison between Unknown and I4 try { if (getter == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("BalrondExtraSkills: Skill getter for " + skillName + " is null.")); return; } BaseSkill baseSkill = getter(); if (baseSkill == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("BalrondExtraSkills: Skill instance for " + skillName + " is null.")); } else if ((int)baseSkill.SkillType == 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("BalrondExtraSkills: Skill " + skillName + " has SkillType.None after initialization. Check FixedSkillId / registration.")); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("BalrondExtraSkills: Failed to initialize skill " + skillName + ". " + ex)); } } public static bool IsObjectDBValid() { try { return (Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items != null && ObjectDB.instance.m_items.Count != 0 && (Object)(object)ObjectDB.instance.GetItemPrefab("Amber") != (Object)null; } catch { return false; } } } internal sealed class ExtraSkillsConfig { internal const float DefaultVitalityHealthBonusAt100 = 10f; internal const float DefaultVitalityFoodXpPerHealth = 0.01f; internal const float DefaultVitalityMaxFoodXpPerEat = 2f; internal const float DefaultVitalitySleepXpPerWake = 0.25f; internal const float DefaultVitalitySleepAwardCooldownSeconds = 2f; internal const float DefaultArcanaEitrBonusAt100 = 10f; internal const float DefaultArcanaFoodXpPerEitr = 0.02f; internal const float DefaultArcanaMaxFoodXpPerEat = 2f; internal const float DefaultArcanaXpPerElementalDamage = 0.01f; internal const float DefaultArcanaMaxXpPerHit = 1f; internal const float DefaultArcanaDotEnvironmentXpMultiplier = 0.5f; internal const float DefaultEnduranceStaminaBonusAt100 = 20f; internal const float DefaultEnduranceMinStaminaUseToTrigger = 5f; internal const float DefaultEnduranceXpFractionOfLastUse = 0.02f; internal const float DefaultEnduranceMaxXpPerExhaust = 5f; internal const float DefaultEnduranceRecoverThresholdFraction = 0.25f; internal const float DefaultEnduranceExhaustedStaminaThreshold = 1f; internal const float DefaultExplorationBaseRadius = 100f; internal const float DefaultExplorationBonusRadiusAt100 = 100f; internal const float DefaultExplorationMinRadius = 50f; internal const float DefaultExplorationMaxRadius = 300f; internal const float DefaultExplorationXpPerSuccessfulPass = 0.08f; internal const float DefaultExplorationMinTimeBetweenAwards = 1f; internal const float DefaultFortitudeArmorBonusAt100 = 10f; internal const float DefaultFortitudeXpPerHpLost = 0.016f; internal const float DefaultFortitudeMaxXpPerHit = 1f; internal const float DefaultFortitudeElementalXpMultiplier = 0.5f; internal const float DefaultHaulingCarryWeightBonusAt100 = 50f; internal const float DefaultHaulingWeightRatioThreshold = 0.9f; internal const float DefaultHaulingRunXpFraction = 0.2f; internal const float DefaultHaulingSneakXpFraction = 0.25f; internal const float DefaultHaulingJumpXpFraction = 0.35f; internal const float DefaultHaulingMaxFactorPerTrigger = 0.35f; internal const float DefaultHaulingFallbackMaxCarryWeight = 300f; internal const float DefaultMiningPickaxeBonusAt100 = 0.25f; internal const float DefaultWoodcuttingChopBonusAt100 = 0.25f; internal const float DefaultWoodcuttingMinChopToConsider = 0.001f; internal const float DefaultWoodcuttingVirtualChopMaxConvertAt100 = 0.12f; internal const bool DefaultWoodcuttingVirtualChopOnlyIfNoChopDamage = true; internal const float DefaultPickaxeMaxReductionAt100 = 0.25f; internal const float DefaultPickaxeMinCostMultiplier = 0.2f; private readonly BaseUnityPlugin _plugin; private readonly ConfigSync _configSync; internal ConfigEntry<bool> LockConfiguration { get; private set; } internal ConfigEntry<bool> VitalityEnabled { get; private set; } internal ConfigEntry<float> VitalityGainMultiplier { get; private set; } internal ConfigEntry<float> VitalityEffectMultiplier { get; private set; } internal ConfigEntry<float> VitalityHealthBonusAt100 { get; private set; } internal ConfigEntry<float> VitalityFoodXpPerHealth { get; private set; } internal ConfigEntry<float> VitalityMaxFoodXpPerEat { get; private set; } internal ConfigEntry<float> VitalitySleepXpPerWake { get; private set; } internal ConfigEntry<float> VitalitySleepAwardCooldownSeconds { get; private set; } internal ConfigEntry<bool> ArcanaEnabled { get; private set; } internal ConfigEntry<float> ArcanaGainMultiplier { get; private set; } internal ConfigEntry<float> ArcanaEffectMultiplier { get; private set; } internal ConfigEntry<float> ArcanaEitrBonusAt100 { get; private set; } internal ConfigEntry<float> ArcanaFoodXpPerEitr { get; private set; } internal ConfigEntry<float> ArcanaMaxFoodXpPerEat { get; private set; } internal ConfigEntry<float> ArcanaXpPerElementalDamage { get; private set; } internal ConfigEntry<float> ArcanaMaxXpPerHit { get; private set; } internal ConfigEntry<bool> ArcanaCountDotAndEnvironment { get; private set; } internal ConfigEntry<float> ArcanaDotEnvironmentXpMultiplier { get; private set; } internal ConfigEntry<bool> EnduranceEnabled { get; private set; } internal ConfigEntry<float> EnduranceGainMultiplier { get; private set; } internal ConfigEntry<float> EnduranceEffectMultiplier { get; private set; } internal ConfigEntry<float> EnduranceStaminaBonusAt100 { get; private set; } internal ConfigEntry<float> EnduranceMinStaminaUseToTrigger { get; private set; } internal ConfigEntry<float> EnduranceXpFractionOfLastUse { get; private set; } internal ConfigEntry<float> EnduranceMaxXpPerExhaust { get; private set; } internal ConfigEntry<float> EnduranceRecoverThresholdFraction { get; private set; } internal ConfigEntry<float> EnduranceExhaustedStaminaThreshold { get; private set; } internal ConfigEntry<bool> ExplorationEnabled { get; private set; } internal ConfigEntry<float> ExplorationGainMultiplier { get; private set; } internal ConfigEntry<float> ExplorationEffectMultiplier { get; private set; } internal ConfigEntry<float> ExplorationBaseRadius { get; private set; } internal ConfigEntry<float> ExplorationBonusRadiusAt100 { get; private set; } internal ConfigEntry<float> ExplorationMinRadius { get; private set; } internal ConfigEntry<float> ExplorationMaxRadius { get; private set; } internal ConfigEntry<float> ExplorationXpPerSuccessfulPass { get; private set; } internal ConfigEntry<float> ExplorationMinTimeBetweenAwards { get; private set; } internal ConfigEntry<bool> FortitudeEnabled { get; private set; } internal ConfigEntry<float> FortitudeGainMultiplier { get; private set; } internal ConfigEntry<float> FortitudeEffectMultiplier { get; private set; } internal ConfigEntry<float> FortitudeArmorBonusAt100 { get; private set; } internal ConfigEntry<float> FortitudeXpPerHpLost { get; private set; } internal ConfigEntry<float> FortitudeMaxXpPerHit { get; private set; } internal ConfigEntry<float> FortitudeElementalXpMultiplier { get; private set; } internal ConfigEntry<bool> HaulingEnabled { get; private set; } internal ConfigEntry<float> HaulingGainMultiplier { get; private set; } internal ConfigEntry<float> HaulingEffectMultiplier { get; private set; } internal ConfigEntry<float> HaulingCarryWeightBonusAt100 { get; private set; } internal ConfigEntry<float> HaulingWeightRatioThreshold { get; private set; } internal ConfigEntry<float> HaulingRunXpFraction { get; private set; } internal ConfigEntry<float> HaulingSneakXpFraction { get; private set; } internal ConfigEntry<float> HaulingJumpXpFraction { get; private set; } internal ConfigEntry<float> HaulingMaxFactorPerTrigger { get; private set; } internal ConfigEntry<float> HaulingFallbackMaxCarryWeight { get; private set; } internal ConfigEntry<bool> MiningBonusEnabled { get; private set; } internal ConfigEntry<float> MiningBonusMultiplier { get; private set; } internal ConfigEntry<float> MiningPickaxeBonusAt100 { get; private set; } internal ConfigEntry<bool> WoodcuttingBonusEnabled { get; private set; } internal ConfigEntry<float> WoodcuttingBonusMultiplier { get; private set; } internal ConfigEntry<float> WoodcuttingChopBonusAt100 { get; private set; } internal ConfigEntry<float> WoodcuttingMinChopToConsider { get; private set; } internal ConfigEntry<bool> WoodcuttingVirtualChopEnabled { get; private set; } internal ConfigEntry<float> WoodcuttingVirtualChopMultiplier { get; private set; } internal ConfigEntry<float> WoodcuttingVirtualChopMaxConvertAt100 { get; private set; } internal ConfigEntry<bool> WoodcuttingVirtualChopOnlyIfNoChopDamage { get; private set; } internal ConfigEntry<bool> PickaxeStaminaReductionEnabled { get; private set; } internal ConfigEntry<float> PickaxeStaminaReductionMultiplier { get; private set; } internal ConfigEntry<float> PickaxeMaxReductionAt100 { get; private set; } internal ConfigEntry<float> PickaxeMinCostMultiplier { get; private set; } internal ExtraSkillsConfig(BaseUnityPlugin plugin, ConfigSync configSync) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown _plugin = plugin; _configSync = configSync; LockConfiguration = _plugin.Config.Bind<bool>("1 - General", "Lock Configuration", true, new ConfigDescription("If enabled, synchronized settings are controlled by the server while connected.", (AcceptableValueBase)null, Array.Empty<object>())); VitalityEnabled = Synced("2 - Vitality", "Enabled", value: true, "Enable Vitality gameplay effects and XP gain. The skill remains registered so existing save data is preserved."); VitalityGainMultiplier = SyncedMultiplier("2 - Vitality", "XP Gain Multiplier", 1f, "Global multiplier applied after Vitality's event-specific XP calculation. 1 = original rate, 0 = no XP gain."); VitalityEffectMultiplier = SyncedMultiplier("2 - Vitality", "Effect Multiplier", 1f, "Multiplier applied to Vitality's configured health bonus. 1 = configured base bonus."); VitalityHealthBonusAt100 = SyncedRange("2 - Vitality", "Health Bonus At Skill 100", 10f, 0f, 10000f, "Base maximum-health bonus at Vitality 100 before Effect Multiplier. Original value: 10."); VitalityFoodXpPerHealth = SyncedRange("2 - Vitality", "Food XP Per Health", 0.01f, 0f, 100f, "Vitality XP factor per point of health on successfully eaten food, before XP Gain Multiplier. Original value: 0.01."); VitalityMaxFoodXpPerEat = SyncedRange("2 - Vitality", "Max Food XP Per Eat", 2f, 0f, 10000f, "Maximum raw Vitality XP awarded by one food consumption before XP Gain Multiplier. Original value: 2."); VitalitySleepXpPerWake = SyncedRange("2 - Vitality", "Sleep XP Per Wake", 0.25f, 0f, 10000f, "Raw Vitality XP awarded after waking, before XP Gain Multiplier. Original value: 0.25."); VitalitySleepAwardCooldownSeconds = SyncedRange("2 - Vitality", "Sleep Award Cooldown Seconds", 2f, 0f, 3600f, "Minimum time between wake XP awards. Original value: 2 seconds."); ArcanaEnabled = Synced("3 - Arcana", "Enabled", value: true, "Enable Arcana gameplay effects and XP gain. The skill remains registered so existing save data is preserved."); ArcanaGainMultiplier = SyncedMultiplier("3 - Arcana", "XP Gain Multiplier", 1f, "Global multiplier applied after Arcana's event-specific XP calculation. 1 = original rate, 0 = no XP gain."); ArcanaEffectMultiplier = SyncedMultiplier("3 - Arcana", "Effect Multiplier", 1f, "Multiplier applied to Arcana's configured Eitr bonus. 1 = configured base bonus."); ArcanaEitrBonusAt100 = SyncedRange("3 - Arcana", "Eitr Bonus At Skill 100", 10f, 0f, 10000f, "Base maximum-Eitr bonus at Arcana 100 before Effect Multiplier. Original value: 10."); ArcanaFoodXpPerEitr = SyncedRange("3 - Arcana", "Food XP Per Eitr", 0.02f, 0f, 100f, "Arcana XP factor per point of Eitr on successfully eaten food, before XP Gain Multiplier. Original value: 0.02."); ArcanaMaxFoodXpPerEat = SyncedRange("3 - Arcana", "Max Food XP Per Eat", 2f, 0f, 10000f, "Maximum raw Arcana XP from one Eitr food before XP Gain Multiplier. Original value: 2."); ArcanaXpPerElementalDamage = SyncedRange("3 - Arcana", "XP Per Elemental Damage", 0.01f, 0f, 100f, "Raw Arcana XP per point of qualifying elemental damage before DoT/environment and global XP multipliers. Original value: 0.01."); ArcanaMaxXpPerHit = SyncedRange("3 - Arcana", "Max XP Per Hit", 1f, 0f, 10000f, "Maximum raw Arcana XP from one qualifying damage event before global XP Gain Multiplier. Original value: 1."); ArcanaCountDotAndEnvironment = Synced("3 - Arcana", "Count DoT And Environment", value: true, "Allow Arcana XP from configured elemental damage-over-time and environmental hit types."); ArcanaDotEnvironmentXpMultiplier = SyncedRange("3 - Arcana", "DoT And Environment XP Multiplier", 0.5f, 0f, 1f, "Multiplier used only for Arcana DoT/environment hits. Original value: 0.5. 0 disables those awards, 1 grants full direct-hit XP."); EnduranceEnabled = Synced("4 - Endurance", "Enabled", value: true, "Enable Endurance gameplay effects and XP gain. The skill remains registered so existing save data is preserved."); EnduranceGainMultiplier = SyncedMultiplier("4 - Endurance", "XP Gain Multiplier", 1f, "Global multiplier applied after Endurance's exhaustion XP calculation. 1 = original rate, 0 = no XP gain."); EnduranceEffectMultiplier = SyncedMultiplier("4 - Endurance", "Effect Multiplier", 1f, "Multiplier applied to Endurance's configured stamina bonus. 1 = configured base bonus."); EnduranceStaminaBonusAt100 = SyncedRange("4 - Endurance", "Stamina Bonus At Skill 100", 20f, 0f, 10000f, "Base maximum-stamina bonus at Endurance 100 before Effect Multiplier. Original value: 20."); EnduranceMinStaminaUseToTrigger = SyncedRange("4 - Endurance", "Minimum Stamina Use To Trigger", 5f, 0f, 10000f, "An exhaustion-causing action must request more than this much stamina to award Endurance XP. Original value: 5."); EnduranceXpFractionOfLastUse = SyncedRange("4 - Endurance", "XP Fraction Of Last Use", 0.02f, 0f, 10f, "Fraction of the exhaustion-causing stamina cost converted to raw Endurance XP. Original value: 0.02."); EnduranceMaxXpPerExhaust = SyncedRange("4 - Endurance", "Max XP Per Exhaustion", 5f, 0f, 10000f, "Maximum raw Endurance XP per exhaustion event before XP Gain Multiplier. Original value: 5."); EnduranceRecoverThresholdFraction = SyncedRange("4 - Endurance", "Recovery Threshold Fraction", 0.25f, 0f, 1f, "Fraction of maximum stamina that must be recovered before another exhaustion award can occur. Original value: 0.25."); EnduranceExhaustedStaminaThreshold = SyncedRange("4 - Endurance", "Exhausted Stamina Threshold", 1f, 0f, 100f, "Stamina at or below this value counts as exhaustion when crossing down from a positive value. Original value: 1."); ExplorationEnabled = Synced("5 - Exploration", "Enabled", value: true, "Enable Exploration gameplay effects and XP gain. Disabled means the minimap radius is left untouched by this mod."); ExplorationGainMultiplier = SyncedMultiplier("5 - Exploration", "XP Gain Multiplier", 1f, "Global multiplier applied to Exploration XP. 1 = original rate, 0 = no XP gain."); ExplorationEffectMultiplier = SyncedMultiplier("5 - Exploration", "Effect Multiplier", 1f, "Multiplier applied to Exploration's configured bonus radius. 1 = configured base bonus."); ExplorationBaseRadius = SyncedRange("5 - Exploration", "Base Explore Radius", 100f, 0f, 10000f, "Base radius used by the Exploration calculation while the skill is enabled. Original value: 100."); ExplorationBonusRadiusAt100 = SyncedRange("5 - Exploration", "Bonus Radius At Skill 100", 100f, 0f, 10000f, "Additional discovery radius at Exploration 100 before Effect Multiplier. Original value: 100."); ExplorationMinRadius = SyncedRange("5 - Exploration", "Minimum Explore Radius", 50f, 0f, 10000f, "Lower clamp for the radius produced by this mod. Original value: 50."); ExplorationMaxRadius = SyncedRange("5 - Exploration", "Maximum Explore Radius", 300f, 0f, 10000f, "Upper clamp for the radius produced by this mod. Original value: 300."); ExplorationXpPerSuccessfulPass = SyncedRange("5 - Exploration", "XP Per Successful Explore Pass", 0.08f, 0f, 10000f, "Raw XP awarded when an exploration pass reveals at least one new map pixel, before XP Gain Multiplier. Original value: 0.08."); ExplorationMinTimeBetweenAwards = SyncedRange("5 - Exploration", "Minimum Time Between Awards", 1f, 0f, 3600f, "Anti-spam cooldown in seconds between Exploration XP awards. Original value: 1 second."); FortitudeEnabled = Synced("6 - Fortitude", "Enabled", value: true, "Enable Fortitude gameplay effects and XP gain. The skill remains registered so existing save data is preserved."); FortitudeGainMultiplier = SyncedMultiplier("6 - Fortitude", "XP Gain Multiplier", 1f, "Global multiplier applied after Fortitude's damage XP calculation. 1 = original rate, 0 = no XP gain."); FortitudeEffectMultiplier = SyncedMultiplier("6 - Fortitude", "Effect Multiplier", 1f, "Multiplier applied to Fortitude's configured armor bonus. 1 = configured base bonus."); FortitudeArmorBonusAt100 = SyncedRange("6 - Fortitude", "Armor Bonus At Skill 100", 10f, 0f, 10000f, "Base armor bonus at Fortitude 100 before Effect Multiplier. Original value: 10."); FortitudeXpPerHpLost = SyncedRange("6 - Fortitude", "XP Per HP Lost", 0.016f, 0f, 100f, "Raw Fortitude XP per point of actual post-mitigation HP lost, before other multipliers. Original value: 0.016."); FortitudeMaxXpPerHit = SyncedRange("6 - Fortitude", "Max XP Per Hit", 1f, 0f, 10000f, "Maximum raw Fortitude XP from one damage event before global XP Gain Multiplier. Original value: 1."); FortitudeElementalXpMultiplier = SyncedRange("6 - Fortitude", "Elemental Damage XP Multiplier", 0.5f, 0f, 10f, "Additional multiplier applied to Fortitude XP for elemental hits. Original value: 0.5."); HaulingEnabled = Synced("7 - Hauling", "Enabled", value: true, "Enable Hauling gameplay effects and XP gain. The skill remains registered so existing save data is preserved."); HaulingGainMultiplier = SyncedMultiplier("7 - Hauling", "XP Gain Multiplier", 1f, "Global multiplier applied after Hauling's piggy-backed XP calculation. 1 = original rate, 0 = no XP gain."); HaulingEffectMultiplier = SyncedMultiplier("7 - Hauling", "Effect Multiplier", 1f, "Multiplier applied to Hauling's configured carry-weight bonus. 1 = configured base bonus."); HaulingCarryWeightBonusAt100 = SyncedRange("7 - Hauling", "Carry Weight Bonus At Skill 100", 50f, 0f, 10000f, "Base carry-weight bonus at Hauling 100 before Effect Multiplier. Original behavior equals 50 at skill 100."); HaulingWeightRatioThreshold = SyncedRange("7 - Hauling", "Weight Ratio Threshold", 0.9f, 0f, 10f, "Current weight / max carry weight required to gain Hauling XP. Original value: 0.90."); HaulingRunXpFraction = SyncedRange("7 - Hauling", "Run XP Fraction", 0.2f, 0f, 10f, "Fraction of a qualifying Run skill accumulator gain converted into Hauling gain. Original value: 0.20."); HaulingSneakXpFraction = SyncedRange("7 - Hauling", "Sneak XP Fraction", 0.25f, 0f, 10f, "Fraction of a qualifying Sneak skill accumulator gain converted into Hauling gain. Original value: 0.25."); HaulingJumpXpFraction = SyncedRange("7 - Hauling", "Jump XP Fraction", 0.35f, 0f, 10f, "Fraction of a qualifying Jump skill accumulator gain converted into Hauling gain. Original value: 0.35."); HaulingMaxFactorPerTrigger = SyncedRange("7 - Hauling", "Max Factor Per Trigger", 0.35f, 0f, 10000f, "Safety cap on the raw factor passed to Hauling RaiseSkill for one piggy-backed trigger. Original value: 0.35."); HaulingFallbackMaxCarryWeight = SyncedRange("7 - Hauling", "Fallback Max Carry Weight", 300f, 1f, 100000f, "Fallback used only if no compatible Player GetMaxCarry* method can be resolved. Original vanilla fallback: 300."); MiningBonusEnabled = Synced("8 - Vanilla Skill Bonuses", "Mining Damage Bonus Enabled", value: true, "Enable the extra pickaxe damage scaling based on the vanilla Pickaxes skill."); MiningBonusMultiplier = SyncedMultiplier("8 - Vanilla Skill Bonuses", "Mining Damage Bonus Multiplier", 1f, "Multiplier applied to the configured Mining damage bonus. 1 = configured base bonus."); MiningPickaxeBonusAt100 = SyncedRange("8 - Vanilla Skill Bonuses", "Mining Pickaxe Bonus At Skill 100", 0.25f, 0f, 10f, "Additional pickaxe damage fraction at Pickaxes 100 before the bonus multiplier. Original value: 0.25 (+25%)."); WoodcuttingBonusEnabled = Synced("8 - Vanilla Skill Bonuses", "Woodcutting Chop Bonus Enabled", value: true, "Enable the extra chop damage scaling based on the vanilla WoodCutting skill."); WoodcuttingBonusMultiplier = SyncedMultiplier("8 - Vanilla Skill Bonuses", "Woodcutting Chop Bonus Multiplier", 1f, "Multiplier applied to the configured Woodcutting chop bonus. 1 = configured base bonus."); WoodcuttingChopBonusAt100 = SyncedRange("8 - Vanilla Skill Bonuses", "Woodcutting Chop Bonus At Skill 100", 0.25f, 0f, 10f, "Additional chop damage fraction at WoodCutting 100 before the bonus multiplier. Original value: 0.25 (+25%)."); WoodcuttingMinChopToConsider = SyncedRange("8 - Vanilla Skill Bonuses", "Woodcutting Minimum Chop To Scale", 0.001f, 0f, 1000f, "Minimum existing chop damage required before the direct Woodcutting chop bonus is applied. Original value: 0.001."); WoodcuttingVirtualChopEnabled = Synced("8 - Vanilla Skill Bonuses", "Woodcutting Virtual Chop Enabled", value: true, "Enable conversion of physical damage into tree-only chop damage based on the vanilla WoodCutting skill."); WoodcuttingVirtualChopMultiplier = SyncedMultiplier("8 - Vanilla Skill Bonuses", "Woodcutting Virtual Chop Multiplier", 1f, "Multiplier applied to the configured virtual-chop conversion. 1 = configured base conversion."); WoodcuttingVirtualChopMaxConvertAt100 = SyncedRange("8 - Vanilla Skill Bonuses", "Woodcutting Virtual Chop At Skill 100", 0.12f, 0f, 10f, "Fraction of qualifying physical damage converted to extra chop damage at WoodCutting 100 before multiplier. Original value: 0.12 (12%)."); WoodcuttingVirtualChopOnlyIfNoChopDamage = Synced("8 - Vanilla Skill Bonuses", "Woodcutting Virtual Chop Only If No Chop Damage", value: true, "If true, virtual chop is added only when the hit has no existing chop damage. Original value: true."); PickaxeStaminaReductionEnabled = Synced("8 - Vanilla Skill Bonuses", "Pickaxe Stamina Reduction Enabled", value: true, "Enable pickaxe attack stamina reduction based on the vanilla Pickaxes skill."); PickaxeStaminaReductionMultiplier = SyncedMultiplier("8 - Vanilla Skill Bonuses", "Pickaxe Stamina Reduction Multiplier", 1f, "Multiplier applied to the configured pickaxe stamina reduction. 1 = configured base reduction."); PickaxeMaxReductionAt100 = SyncedRange("8 - Vanilla Skill Bonuses", "Pickaxe Max Reduction At Skill 100", 0.25f, 0f, 1f, "Base stamina-cost reduction fraction at Pickaxes 100 before multiplier. Original value: 0.25 (25%)."); PickaxeMinCostMultiplier = SyncedRange("8 - Vanilla Skill Bonuses", "Pickaxe Minimum Cost Multiplier", 0.2f, 0f, 1f, "Lowest allowed final pickaxe stamina-cost multiplier. Original value: 0.20, meaning cost can never fall below 20% of base."); } private ConfigEntry<T> Synced<T>(string group, string name, T value, string description) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown ConfigEntry<T> val = _plugin.Config.Bind<T>(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); SyncedConfigEntry<T> syncedConfigEntry = _configSync.AddConfigEntry<T>(val); syncedConfigEntry.SynchronizedConfig = true; return val; } private ConfigEntry<float> SyncedMultiplier(string group, string name, float value, string description) { return SyncedRange(group, name, value, 0f, 10f, description); } private ConfigEntry<float> SyncedRange(string group, string name, float value, float min, float max, string description) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()); ConfigEntry<float> val2 = _plugin.Config.Bind<float>(group, name, value, val); SyncedConfigEntry<float> syncedConfigEntry = _configSync.AddConfigEntry<float>(val2); syncedConfigEntry.SynchronizedConfig = true; return val2; } internal bool IsSkillEnabled(int fixedSkillId) { return fixedSkillId switch { 15001 => VitalityEnabled.Value, 15002 => ArcanaEnabled.Value, 15003 => EnduranceEnabled.Value, 15004 => ExplorationEnabled.Value, 15005 => FortitudeEnabled.Value, 15006 => HaulingEnabled.Value, _ => true, }; } internal float GetSkillGainMultiplier(int fixedSkillId) { return fixedSkillId switch { 15001 => VitalityGainMultiplier.Value, 15002 => ArcanaGainMultiplier.Value, 15003 => EnduranceGainMultiplier.Value, 15004 => ExplorationGainMultiplier.Value, 15005 => FortitudeGainMultiplier.Value, 15006 => HaulingGainMultiplier.Value, _ => 1f, }; } internal float GetSkillEffectMultiplier(int fixedSkillId) { return fixedSkillId switch { 15001 => VitalityEffectMultiplier.Value, 15002 => ArcanaEffectMultiplier.Value, 15003 => EnduranceEffectMultiplier.Value, 15004 => ExplorationEffectMultiplier.Value, 15005 => FortitudeEffectMultiplier.Value, 15006 => HaulingEffectMultiplier.Value, _ => 1f, }; } } public sealed class ArcanaSkill : BaseSkill { internal static class ArcanaPatches { [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] public static class HealthIncreasePatches { public static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr) { int bonusEitr = GetBonusEitr(__instance); if (bonusEitr > 0) { eitr += bonusEitr; } } } [HarmonyPatch(typeof(Player), "EatFood")] [HarmonyPostfix] private static void Player_EatFood_Postfix(Player __instance, ItemData item, ref bool __result) { if (__result && !((Object)(object)__instance == (Object)null) && item != null && item.m_shared != null) { float foodEitr = item.m_shared.m_foodEitr; if (!(foodEitr <= 0f)) { RaiseFromFood(__instance, foodEitr); } } } } private static ArcanaSkill _instance; public static ArcanaSkill Instance => _instance ?? (_instance = new ArcanaSkill()); internal static bool CountDotAndEnvironment => Launch.Settings == null || Launch.Settings.ArcanaCountDotAndEnvironment.Value; internal static float DotEnvironmentXpMultiplier => (Launch.Settings != null) ? Launch.Settings.ArcanaDotEnvironmentXpMultiplier.Value : 0.5f; private ArcanaSkill() : base(15002, "arcanaskill", "Arcana", "Increases your base maximum Eitr. Improves by eating Eitr food and taking elemental damage.", "arcana") { } internal static int GetBonusEitr(Player player) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return 0; } if (!BalrondSkillSystem.IsSkillEnabled(Instance.SkillType)) { return 0; } Skills skills = ((Character)player).GetSkills(); if ((Object)(object)skills == (Object)null) { return 0; } float skillLevel = skills.GetSkillLevel(Instance.SkillType); if (skillLevel <= 0f) { return 0; } float num = Launch.Settings?.ArcanaEitrBonusAt100.Value ?? 10f; float skillEffectMultiplier = BalrondSkillSystem.GetSkillEffectMultiplier(Instance.SkillType); float num2 = Mathf.Max(0f, num) / 10f; return Mathf.FloorToInt(skillLevel / 10f * num2 * skillEffectMultiplier); } private static void RaiseFromFood(Player player, float foodEitr) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && BalrondSkillSystem.IsSkillEnabled(Instance.SkillType) && !(foodEitr <= 0f)) { ExtraSkillsConfig settings = Launch.Settings; float num = settings?.ArcanaFoodXpPerEitr.Value ?? 0.02f; float num2 = settings?.ArcanaMaxFoodXpPerEat.Value ?? 2f; float num3 = foodEitr * Mathf.Max(0f, num); num3 = Mathf.Min(num3, Mathf.Max(0f, num2)); if (!(num3 <= 0f)) { BalrondSkillSystem.RaiseSkill(player, Instance.SkillType, num3); } } } private static float GetElementalXpMultiplier(HitData hit) { //IL_0062: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_00b6: Expected I4, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Invalid comparison between Unknown and I4 //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Invalid comparison between Unknown and I4 //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Invalid comparison between Unknown and I4 if (hit == null) { return 0f; } if (!(hit.m_damage.m_fire > 0f) && !(hit.m_damage.m_frost > 0f) && !(hit.m_damage.m_lightning > 0f)) { return 0f; } HitType hitType = hit.m_hitType; HitType val = hitType; switch (val - 3) { case 0: case 1: case 5: case 7: case 8: case 9: case 10: case 11: case 12: case 14: case 15: return 0f; default: { HitType hitType2 = hit.m_hitType; HitType val2 = hitType2; if (val2 - 5 <= 2 || (int)val2 == 9 || (int)val2 == 21) { if (!CountDotAndEnvironment) { return 0f; } return Mathf.Clamp01(DotEnvironmentXpMultiplier); } if (hit.HaveAttacker() && (Object)(object)hit.GetAttacker() != (Object)null) { return 1f; } return 0f; } } } internal static void OnDamageTaken(Player player, HitData originalHit, float hpLost, bool wasBlocking, bool wasPerfectBlock) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !BalrondSkillSystem.IsSkillEnabled(Instance.SkillType)) { return; } float elementalXpMultiplier = GetElementalXpMultiplier(originalHit); if (elementalXpMultiplier <= 0f) { return; } float num = originalHit.m_damage.m_fire + originalHit.m_damage.m_frost + originalHit.m_damage.m_lightning; if (!(num <= 0f)) { ExtraSkillsConfig settings = Launch.Settings; float num2 = settings?.ArcanaXpPerElementalDamage.Value ?? 0.01f; float num3 = settings?.ArcanaMaxXpPerHit.Value ?? 1f; float num4 = num * Mathf.Max(0f, num2) * elementalXpMultiplier; num4 = Mathf.Min(num4, Mathf.Max(0f, num3)); if (!(num4 <= 0f)) { BalrondSkillSystem.RaiseSkill(player, Instance.SkillType, num4); } } } } public sealed class EnduranceSkill : BaseSkill { internal static class EndurancePatches { [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] public static class StaminaIncreasePatch { public static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr) { float bonusStamina = GetBonusStamina(__instance); if (bonusStamina > 0f) { stamina += bonusStamina; } } } [HarmonyPatch(typeof(Player), "UseStamina")] private static class Player_UseStamina_Patch { [HarmonyPrefix] private static void Prefix(Player __instance, [HarmonyArgument(0)] float v, out float __state) { __state = 0f; if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { __state = __instance.GetStamina(); } } [HarmonyPostfix] private static void Postfix(Player __instance, [HarmonyArgument(0)] float v, float __state) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { float stamina = __instance.GetStamina(); OnStaminaUsed(__instance, __state, stamina, v); } } } } private static EnduranceSkill _instance; private static bool _lockedOut; public static EnduranceSkill Instance => _instance ?? (_instance = new EnduranceSkill()); private EnduranceSkill() : base(15003, "enduranceskill", "Endurance", "Increases your base maximum stamina. Improves when you push yourself to exhaustion.", "endurance") { } internal static float GetBonusStamina(Player player) { //IL_001f: 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_00a7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return 0f; } if (!BalrondSkillSystem.IsSkillEnabled(Instance.SkillType)) { return 0f; } Skills skills = ((Character)player).GetSkills(); if ((Object)(object)skills == (Object)null) { return 0f; } float skillLevel = skills.GetSkillLevel(Instance.SkillType); if (skillLevel <= 0f) { return 0f; } float num = Launch.Settings?.EnduranceStaminaBonusAt100.Value ?? 20f; float skillEffectMultiplier = BalrondSkillSystem.GetSkillEffectMultiplier(Instance.SkillType); return skillLevel / 100f * Mathf.Max(0f, num) * skillEffectMultiplier; } internal static void OnStaminaUsed(Player player, float staminaBefore, float staminaAfter, float amountUsedRequested) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || !BalrondSkillSystem.IsSkillEnabled(Instance.SkillType)) { return; } ExtraSkillsConfig settings = Launch.Settings; float num = settings?.EnduranceRecoverThresholdFraction.Value ?? 0.25f; float num2 = settings?.EnduranceExhaustedStaminaThreshold.Value ?? 1f; float num3 = settings?.EnduranceMinStaminaUseToTrigger.Value ?? 5f; float num4 = settings?.EnduranceXpFractionOfLastUse.Value ?? 0.02f; float num5 = settings?.EnduranceMaxXpPerExhaust.Value ?? 5f; num = Mathf.Clamp01(num); num2 = Mathf.Max(0f, num2); num3 = Mathf.Max(0f, num3); num4 = Mathf.Max(0f, num4); num5 = Mathf.Max(0f, num5); if (_lockedOut) { float maxStamina = ((Character)player).GetMaxStamina(); if (!(maxStamina > 0f) || !(staminaAfter >= maxStamina * num)) { return; } _lockedOut = false; } if (!(staminaBefore > 0f) || !(staminaAfter <= num2)) { return; } float num6 = Mathf.Max(0f, amountUsedRequested); num6 = Mathf.Min(num6, staminaBefore); if (num6 <= num3) { _lockedOut = true; return; } float num7 = num6 * num4; if (num7 > num5) { num7 = num5; } if (num7 > 0f) { BalrondSkillSystem.RaiseSkill(player, Instance.SkillType, num7); } _lockedOut = true; } } public sealed class ExplorationSkill : BaseSkill { internal static class ExplorationPatches { [HarmonyPatch(typeof(Minimap), "Explore", new Type[] { typeof(Vector3), typeof(float) })] private static class Minimap_Explore_VectorRadius_Patch { [HarmonyPrefix] private static void Prefix(Minimap __instance, Vector3 p, ref float radius) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) s_passForLocalPlayer = false; s_anyNewPixelThisPass = false; Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { Vector3 val = p - ((Component)localPlayer).transform.position; if (!(((Vector3)(ref val)).sqrMagnitude > 4f) && BalrondSkillSystem.IsSkillEnabled(Instance.SkillType)) { s_passForLocalPlayer = true; radius = GetExploreRadiusFor(localPlayer); } } } [HarmonyPostfix] private static void Postfix(Minimap __instance, Vector3 p, float radius) { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) if (!s_passForLocalPlayer || !s_anyNewPixelThisPass) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).InCutscene() || ((Character)localPlayer).InIntro() || s_inAward || Time.time < s_nextAllowedAwardTime) { return; } try { s_inAward = true; ExtraSkillsConfig settings = Launch.Settings; float num = settings?.ExplorationXpPerSuccessfulPass.Value ?? 0.08f; float num2 = settings?.ExplorationMinTimeBetweenAwards.Value ?? 1f; num = Mathf.Max(0f, num); num2 = Mathf.Max(0f, num2); if (num > 0f) { BalrondSkillSystem.RaiseSkill(localPlayer, Instance.SkillType, num); } s_nextAllowedAwardTime = Time.time + num2; } finally { s_inAward = false; } } } [HarmonyPatch(typeof(Minimap), "Explore", new Type[] { typeof(int), typeof(int) })] private static class Minimap_Explore_Pixel_Patch { [HarmonyPostfix] private static void Postfix(bool __result) { if (s_passForLocalPlayer && __result) { s_anyNewPixelThisPass = true; } } } } private static ExplorationSkill _instance; private static bool s_inAward; private static bool s_passForLocalPlayer; private static bool s_anyNewPixelThisPass; private static float s_nextAllowedAwardTime; public static ExplorationSkill Instance => _instance ?? (_instance = new ExplorationSkill()); private ExplorationSkill() : base(15004, "explorationskill", "Exploration", "Increases your map discovery range. Improves when you uncover new areas.", "exploration") { } private static float GetExploreRadiusFor(Player player) { //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) ExtraSkillsConfig settings = Launch.Settings; float num = settings?.ExplorationBaseRadius.Value ?? 100f; float num2 = settings?.ExplorationBonusRadiusAt100.Value ?? 100f; float num3 = settings?.ExplorationMinRadius.Value ?? 50f; float num4 = settings?.ExplorationMaxRadius.Value ?? 300f; num = Mathf.Max(0f, num); num2 = Mathf.Max(0f, num2); num3 = Mathf.Max(0f, num3); num4 = Mathf.Max(0f, num4); if (num4 < num3) { float num5 = num4; num4 = num3; num3 = num5; } if ((Object)(object)player == (Object)null) { return Mathf.Clamp(num, num3, num4); } Skills skills = ((Character)player).GetSkills(); if ((Object)(object)skills == (Object)null) { return Mathf.Clamp(num, num3, num4); } float skillLevel = skills.GetSkillLevel(Instance.SkillType); float num6 = Mathf.Clamp01(skillLevel / 100f); float skillEffectMultiplier = BalrondSkillSystem.GetSkillEffectMultiplier(Instance.SkillType); float num7 = num + num2 * num6 * skillEffectMultiplier; return Mathf.Clamp(num7, num3, num4); } } public sealed class FortitudeSkill : BaseSkill { [HarmonyPatch(type