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 PathOfValheiman v1.3.1
plugins/PathOfValheiman/PathOfValheiman.dll
Decompiled an hour ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using PassiveTree.UI; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("PathOfValheiman")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+0971f27722431ec3d15b1abf731be08d54de986d")] [assembly: AssemblyProduct("PathOfValheiman")] [assembly: AssemblyTitle("PathOfValheiman")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PassiveTree { public static class AssetDump { [HarmonyPatch(typeof(FejdStartup), "Start")] private static class FejdStartup_Start { private static void Postfix() { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) if (done || !Plugin.DumpAssets.Value) { return; } done = true; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("== TMP fonts =="); TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>(); foreach (TMP_FontAsset val in array) { stringBuilder.AppendLine(((Object)val).name); } stringBuilder.AppendLine("== Sprites =="); foreach (Sprite item in from s in Resources.FindObjectsOfTypeAll<Sprite>() orderby ((Object)s).name select s) { object[] obj = new object[5] { ((Object)item).name, null, null, null, null }; Rect rect = item.rect; obj[1] = ((Rect)(ref rect)).width; rect = item.rect; obj[2] = ((Rect)(ref rect)).height; obj[3] = item.border; obj[4] = (Object.op_Implicit((Object)(object)item.texture) ? ((Object)item.texture).name : "-"); stringBuilder.AppendLine(string.Format("{0}\t{1}x{2}\tborder={3}\ttex={4}", obj)); } if ((Object)(object)ObjectDB.instance != (Object)null) { stringBuilder.AppendLine("== Items =="); foreach (GameObject item2 in ObjectDB.instance.m_items) { stringBuilder.AppendLine(((Object)item2).name); } stringBuilder.AppendLine("== StatusEffects =="); foreach (StatusEffect statusEffect in ObjectDB.instance.m_StatusEffects) { stringBuilder.AppendLine(((Object)statusEffect).name); } } string text = Path.Combine(Paths.BepInExRootPath, "PathOfValheiman_assets.txt"); File.WriteAllText(text, stringBuilder.ToString()); Plugin.Log.LogInfo((object)("Asset dump written: " + text)); } } private static bool done; } public static class Bombs { private class Echo : MonoBehaviour { } [HarmonyPatch(typeof(Aoe), "Setup")] private static class Aoe_Setup { private static void Postfix(Aoe __instance, Character owner, Vector3 velocity, float hitNoise, HitData hitData, ItemData item, ItemData ammo) { //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)owner == (Object)null || (Object)(object)owner != (Object)(object)Player.m_localPlayer || !IsBomb(item)) { return; } StatSet s = S; if (s == null) { return; } bool flag = (Object)(object)((Component)__instance).GetComponent<Echo>() != (Object)null; float num = Mathf.Min(StatText.CapOf(Stat.BlastRadiusPct), s.Get(Stat.BlastRadiusPct)); if (num > 0f) { __instance.m_radius *= 1f + num; SphereCollider[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<SphereCollider>(); foreach (SphereCollider obj in componentsInChildren) { obj.radius *= 1f + num; } } float num2 = s.Get(Stat.BombDamagePct); if (num2 > 0f) { ((DamageTypes)(ref __instance.m_damage)).Modify(1f + num2); } if (!flag && s.Get(Stat.BombMine) > 0f) { __instance.m_useTriggers = true; __instance.m_triggerEnterOnly = true; __instance.m_hitOwner = false; __instance.m_ttl = Mathf.Max(__instance.m_ttl, 45f); SphereCollider val = ((Component)__instance).GetComponent<SphereCollider>(); if ((Object)(object)val == (Object)null) { val = ((Component)__instance).gameObject.AddComponent<SphereCollider>(); } ((Collider)val).isTrigger = true; val.radius = Mathf.Max(val.radius, __instance.m_radius); } if (flag) { return; } float num3 = Mathf.Min(StatText.CapOf(Stat.DoubleBlastChance), s.Get(Stat.DoubleBlastChance)); if (!(num3 <= 0f) && !(Random.value >= num3)) { GameObject val2 = Object.Instantiate<GameObject>(((Component)__instance).gameObject, ((Component)__instance).transform.position, ((Component)__instance).transform.rotation); val2.AddComponent<Echo>(); Aoe component = val2.GetComponent<Aoe>(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val2); return; } component.Setup(owner, velocity, hitNoise, hitData, item, ammo); component.m_activationTimer = 0.45f; } } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] private static class InventoryGui_DoCrafting_Bombs { private static GameObject product; internal static bool MakingBomb => (Object)(object)product != (Object)null; private static void Prefix(InventoryGui __instance) { product = null; if (__instance.m_craftUpgradeItem == null && !((Object)(object)__instance.m_craftRecipe == (Object)null)) { ItemDrop item = __instance.m_craftRecipe.m_item; if ((Object)(object)item != (Object)null && IsBomb(item.m_itemData)) { product = ((Component)item).gameObject; } } } private static void Postfix(Player player) { GameObject val = product; product = null; if ((Object)(object)val == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } float num = Mathf.Min(StatText.CapOf(Stat.ExtraBombChance), Get(Stat.ExtraBombChance)); if (!(num <= 0f) && !(Random.value >= num)) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory != null) { inventory.AddItem(((Object)val).name, 1, 1, 0, 0L, "", false, true); ((Character)player).Message((MessageType)1, L.T("Лишний заряд!"), 0, (Sprite)null, false); } } } } [HarmonyPatch(typeof(Attack), "ConsumeItem")] private static class Attack_ConsumeItem { private static bool Prefix(Attack __instance) { if ((Object)(object)__instance.m_character != (Object)(object)Player.m_localPlayer) { return true; } float num = Mathf.Min(StatText.CapOf(Stat.BombSaveChance), Get(Stat.BombSaveChance)); if (num <= 0f || Random.value >= num) { return true; } ((Character)Player.m_localPlayer).Message((MessageType)1, L.T("Бомба цела!"), 0, (Sprite)null, false); return false; } } internal static bool MakingBomb => InventoryGui_DoCrafting_Bombs.MakingBomb; private static StatSet S => Progress.Current?.Stats; private static float Get(Stat s) { return S?.Get(s) ?? 0f; } private static bool IsBomb(ItemData item) { if (item != null && item.m_shared != null && item.m_shared.m_attack != null) { return item.m_shared.m_attack.m_consumeItem; } return false; } } public static class ConfigSync { [HarmonyPatch(typeof(Game), "Start")] private static class Game_Start { private static void Postfix() { Register(); requested = false; } } [HarmonyPatch(typeof(Player), "OnSpawned")] private static class Player_OnSpawned { private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { Progress progress = Progress.Peek(__instance); if (progress != null && progress.RefundedByUpdate > 0) { ((Character)__instance).Message((MessageType)2, string.Format(L.T("Дерево обновилось: {0} очков возвращено"), progress.RefundedByUpdate), 0, (Sprite)null, false); progress.RefundedByUpdate = 0; } if (progress != null && progress.TrimmedByBudget > 0) { ((Character)__instance).Message((MessageType)2, string.Format(L.T("Очков за уровень стало меньше: снято узлов {0}"), progress.TrimmedByBudget), 0, (Sprite)null, false); progress.TrimmedByBudget = 0; } } if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !requested && Plugin.ServerSync.Value) { Register(); if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { requested = true; ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PassiveTree_ConfigRequest", new object[1] { "" }); } } } } private const string RpcRequest = "PassiveTree_ConfigRequest"; private const string RpcApply = "PassiveTree_ConfigApply"; private static bool registered; private static bool requested; private static IEnumerable<KeyValuePair<string, ConfigEntryBase>> Shared() { yield return Pair("TreePower", (ConfigEntryBase)(object)Plugin.TreePower); yield return Pair("StartingGates", (ConfigEntryBase)(object)Plugin.StartingGates); yield return Pair("XpMultiplier", (ConfigEntryBase)(object)Plugin.XpMultiplier); yield return Pair("PointsPerLevel", (ConfigEntryBase)(object)Plugin.PointsPerLevel); yield return Pair("MaxLevel", (ConfigEntryBase)(object)Plugin.MaxLevel); yield return Pair("LevelBaseXp", (ConfigEntryBase)(object)Plugin.LevelBaseXp); yield return Pair("LevelExponent", (ConfigEntryBase)(object)Plugin.LevelExponent); yield return Pair("DeathXpLossPct", (ConfigEntryBase)(object)Plugin.DeathXpLossPct); yield return Pair("XpPerSkillGain", (ConfigEntryBase)(object)Plugin.XpPerSkillGain); yield return Pair("KillXpPerHealth", (ConfigEntryBase)(object)Plugin.KillXpPerHealth); yield return Pair("BossXpMultiplier", (ConfigEntryBase)(object)Plugin.BossXpMultiplier); yield return Pair("BuildXp", (ConfigEntryBase)(object)Plugin.BuildXp); yield return Pair("ExploreXpPerPixel", (ConfigEntryBase)(object)Plugin.ExploreXpPerPixel); yield return Pair("DisableVanillaSkills", (ConfigEntryBase)(object)Plugin.DisableVanillaSkills); yield return Pair("BaseSkillLevel", (ConfigEntryBase)(object)Plugin.BaseSkillLevel); yield return Pair("RespecCostPerPoint", (ConfigEntryBase)(object)Plugin.RespecCostPerPoint); yield return Pair("RespecFree", (ConfigEntryBase)(object)Plugin.RespecFree); } private static KeyValuePair<string, ConfigEntryBase> Pair(string k, ConfigEntryBase e) { return new KeyValuePair<string, ConfigEntryBase>(k, e); } private static void Register() { if (!registered && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register<string>("PassiveTree_ConfigRequest", (Action<long, string>)OnRequest); ZRoutedRpc.instance.Register<string>("PassiveTree_ConfigApply", (Action<long, string>)OnApply); registered = true; } } private static void OnRequest(long sender, string _) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !Plugin.ServerSync.Value) { return; } List<string> list = new List<string>(); foreach (KeyValuePair<string, ConfigEntryBase> item in Shared()) { list.Add(item.Key + "=" + Convert.ToString(item.Value.BoxedValue, CultureInfo.InvariantCulture)); } ZRoutedRpc.instance.InvokeRoutedRPC(sender, "PassiveTree_ConfigApply", new object[1] { string.Join(";", list.ToArray()) }); } private static void OnApply(long sender, string payload) { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || string.IsNullOrEmpty(payload)) { return; } float value = Plugin.TreePower.Value; Dictionary<string, ConfigEntryBase> dictionary = new Dictionary<string, ConfigEntryBase>(); foreach (KeyValuePair<string, ConfigEntryBase> item in Shared()) { dictionary[item.Key] = item.Value; } int num = 0; string[] array = payload.Split(new char[1] { ';' }); foreach (string text in array) { int num2 = text.IndexOf('='); if (num2 > 0 && dictionary.TryGetValue(text.Substring(0, num2), out var value2)) { string text2 = text.Substring(num2 + 1); try { value2.BoxedValue = ((value2.SettingType == typeof(bool)) ? ((object)bool.Parse(text2)) : ((value2.SettingType == typeof(int)) ? ((object)int.Parse(text2, CultureInfo.InvariantCulture)) : ((object)float.Parse(text2, CultureInfo.InvariantCulture)))); num++; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Server setting rejected: " + text + " (" + ex.Message + ")")); } } } Plugin.Log.LogInfo((object)$"Applied server settings: {num}"); if (!Mathf.Approximately(value, Plugin.TreePower.Value)) { Plugin.RebuildTree(); } } } public static class Crafting { [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] private static class InventoryGui_DoCrafting { private static void Prefix() { crafting = true; } private static void Postfix() { crafting = false; } } [HarmonyPatch(typeof(Player), "ConsumeResources")] private static class Player_ConsumeResources { private static bool Prefix(Player __instance) { if (!crafting || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } float num = Mathf.Min(StatText.CapOf(Stat.CraftRefundChance), Get(Stat.CraftRefundChance)); if (Bombs.MakingBomb) { num = Mathf.Max(num, Mathf.Min(StatText.CapOf(Stat.BombCraftRefundChance), Get(Stat.BombCraftRefundChance))); } if (num <= 0f || Random.value >= num) { return true; } ((Character)__instance).Message((MessageType)1, L.T("Материалы сохранены!"), 0, (Sprite)null, false); return false; } } [HarmonyPatch(typeof(ItemData), "GetMaxDurability", new Type[] { typeof(int) })] private static class ItemData_GetMaxDurability { private static void Postfix(ItemData __instance, ref float __result) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && __instance.m_crafterID != 0L && __instance.m_crafterID == localPlayer.GetPlayerID()) { float num = Get(Stat.CraftDurabilityPct); if (num > 0f) { __result *= 1f + num; } } } } [HarmonyPatch(typeof(Smelter), "UpdateSmelter")] private static class Smelter_UpdateSmelter { private static float saved; private static void Prefix(Smelter __instance) { saved = __instance.m_secPerProduct; float num = Mathf.Min(StatText.CapOf(Stat.SmeltSpeedPct), Get(Stat.SmeltSpeedPct)); if (num > 0f && Near((Component)(object)__instance)) { __instance.m_secPerProduct /= 1f + num; } } private static void Postfix(Smelter __instance) { __instance.m_secPerProduct = saved; } } [HarmonyPatch(typeof(Smelter), "Spawn")] private static class Smelter_Spawn { private static void Prefix(Smelter __instance, ref int stack) { float num = Mathf.Min(StatText.CapOf(Stat.ExtraBarChance), Get(Stat.ExtraBarChance)); if (num > 0f && Near((Component)(object)__instance) && Random.value < num) { stack++; } } } [HarmonyPatch(typeof(CookingStation), "UpdateCooking")] private static class CookingStation_UpdateCooking { private static bool savedOvercook; private static float[] savedTimes; private static void Prefix(CookingStation __instance) { savedOvercook = __instance.m_canOvercookItems; savedTimes = null; if (!Near((Component)(object)__instance)) { return; } if (Get(Stat.NoOvercook) > 0f) { __instance.m_canOvercookItems = false; } float num = Mathf.Min(StatText.CapOf(Stat.CookSpeedPct), Get(Stat.CookSpeedPct)); if (!(num <= 0f) && __instance.m_conversion != null) { savedTimes = new float[__instance.m_conversion.Count]; for (int i = 0; i < __instance.m_conversion.Count; i++) { savedTimes[i] = __instance.m_conversion[i].m_cookTime; ItemConversion obj = __instance.m_conversion[i]; obj.m_cookTime /= 1f + num; } } } private static void Postfix(CookingStation __instance) { __instance.m_canOvercookItems = savedOvercook; if (savedTimes != null) { for (int i = 0; i < savedTimes.Length && i < __instance.m_conversion.Count; i++) { __instance.m_conversion[i].m_cookTime = savedTimes[i]; } savedTimes = null; } } } [HarmonyPatch(typeof(Fermenter), "SlowUpdate")] private static class Fermenter_SlowUpdate { private static float saved; private static void Prefix(Fermenter __instance) { saved = __instance.m_fermentationDuration; float num = Mathf.Min(StatText.CapOf(Stat.FermentSpeedPct), Get(Stat.FermentSpeedPct)); if (num > 0f && Near((Component)(object)__instance, 60f)) { __instance.m_fermentationDuration /= 1f + num; } } private static void Postfix(Fermenter __instance) { __instance.m_fermentationDuration = saved; } } internal static bool crafting; private static StatSet S => Progress.Current?.Stats; private static float Get(Stat s) { return S?.Get(s) ?? 0f; } private static bool Near(Component c, float range = 30f) { //IL_001e: 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) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && (Object)(object)c != (Object)null) { return Vector3.Distance(((Component)localPlayer).transform.position, c.transform.position) < range; } return false; } } public static class Caps { public const float DamageTakenFloor = 0.4f; public const float MoveSpeed = 0.4f; public const float StaminaCost = -0.85f; public const float Resist = 0.75f; public const float ProcChance = 0.75f; public const float Dodge = 0.4f; public const float CritChance = 0.75f; public const float AttackSpeed = 0.45f; public const float Lifesteal = 0.1f; public const float Backstab = 1f; public const float StaggerResist = 0.85f; public const float Thorns = 0.5f; public const float ArmorPct = 0.6f; public const float RefundChance = 0.6f; public const float DamageMult = 4f; } public class SE_PassiveTree : SE_Stats { public const string SeName = "PassiveTree"; public static readonly int Hash = StringExtensionMethods.GetStableHashCode("PassiveTree"); public void Apply(StatSet s) { //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) bool recentlyCrit = Effects.RecentlyCrit; bool night = Effects.Night; float num = (Effects.Onslaught ? (0.15f + s.Get(Stat.OnslaughtPowerPct)) : 0f); base.m_healthRegenMultiplier = 1f + s.Get(Stat.HealthRegenPct) + (night ? s.Get(Stat.NightRegenPct) : 0f); base.m_staminaRegenMultiplier = 1f + s.Get(Stat.StaminaRegenPct) + (recentlyCrit ? s.Get(Stat.CritRecentStaminaRegenPct) : 0f) + (night ? s.Get(Stat.NightRegenPct) : 0f) + num; base.m_eitrRegenMultiplier = 1f + s.Get(Stat.EitrRegenPct) + (night ? s.Get(Stat.NightEitrRegenPct) : 0f); base.m_runStaminaDrainModifier = Mathf.Max(-0.85f, s.Get(Stat.RunStaminaPct)); base.m_jumpStaminaUseModifier = Mathf.Max(-0.85f, s.Get(Stat.JumpStaminaPct)); base.m_attackStaminaUseModifier = Mathf.Max(-0.85f, s.Get(Stat.AttackStaminaPct)); base.m_blockStaminaUseModifier = Mathf.Max(-0.85f, s.Get(Stat.BlockStaminaPct)); base.m_dodgeStaminaUseModifier = Mathf.Max(-0.85f, s.Get(Stat.DodgeStaminaPct)); base.m_swimStaminaUseModifier = Mathf.Max(-0.85f, s.Get(Stat.SwimStaminaPct)); base.m_sneakStaminaUseModifier = Mathf.Max(-0.85f, s.Get(Stat.SneakStaminaPct)); base.m_homeItemStaminaUseModifier = Mathf.Max(-0.85f, s.Get(Stat.ToolStaminaPct)); base.m_addArmor = s.Get(Stat.ArmorFlat) + (night ? s.Get(Stat.NightArmorFlat) : 0f); base.m_armorMultiplier = Mathf.Min(0.6f, s.Get(Stat.ArmorPct)); base.m_timedBlockBonus = s.Get(Stat.ParryBonusPct); base.m_staggerModifier = 0f - Mathf.Min(0.85f, s.Get(Stat.StaggerResistPct)); base.m_addMaxCarryWeight = s.Get(Stat.CarryWeightFlat); base.m_speedModifier = Mathf.Min(0.4f + (Effects.OnPavedGround ? Mathf.Min(0.25f, s.Get(Stat.PavedSpeedPct)) : 0f), (Effects.OnPavedGround ? Mathf.Min(0.25f, s.Get(Stat.PavedSpeedPct)) : 0f) + s.Get(Stat.MoveSpeedPct) + (recentlyCrit ? s.Get(Stat.CritRecentMoveSpeedPct) : 0f) + (night ? s.Get(Stat.NightMoveSpeedPct) : 0f) + num); base.m_swimSpeedModifier = s.Get(Stat.SwimSpeedPct); float num2 = s.Get(Stat.JumpPct); base.m_jumpModifier = new Vector3(num2 * 0.5f, num2, num2 * 0.5f); base.m_noiseModifier = s.Get(Stat.NoisePct); base.m_stealthModifier = s.Get(Stat.StealthPct) + (night ? s.Get(Stat.NightStealthPct) : 0f); base.m_fallDamageModifier = (s.Has(Stat.SafeFall) ? (-1f) : Mathf.Max(-1f, s.Get(Stat.FallDamagePct))); base.m_modifyAttackSkill = (SkillType)0; base.m_damageModifier = 1f; ((StatusEffect)this).m_tooltip = StatText.DescribeAll(s.All); } public override void ModifyAttack(SkillType skill, ref HitData hitData) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Invalid comparison between Unknown and I4 StatSet statSet = Progress.Current?.Stats; if (statSet == null || (Object)(object)((StatusEffect)this).m_character == (Object)null) { return; } float num = 1f + statSet.Get(Stat.DamagePct) + (hitData.m_ranged ? statSet.Get(Stat.RangedDamagePct) : statSet.Get(Stat.MeleeDamagePct)); Stat? stat = StatText.DamageFor(skill); if (stat.HasValue) { num += statSet.Get(stat.Value); } float num2 = statSet.Get(Stat.BerserkPct); float num3 = Mathf.Clamp01(((StatusEffect)this).m_character.GetHealth() / Mathf.Max(1f, ((StatusEffect)this).m_character.GetMaxHealth())); if (num2 > 0f) { num += num2 * Mathf.Floor((1f - num3) * 10f); } if (num3 >= 0.99f) { num += statSet.Get(Stat.FullHpDamagePct); } if (Effects.Night) { num += statSet.Get(Stat.NightDamagePct); } if (((StatusEffect)this).m_character.GetSEMan().HaveStatusEffect(SEMan.s_statusEffectRested)) { num += statSet.Get(Stat.RestedDamagePct); } if (Effects.RecentlyCrit) { num += statSet.Get(Stat.CritRecentDamagePct); } float num4 = Mathf.Min(0.75f, statSet.Get(Stat.CritChance) + ((hitData.m_damage.m_chop > 0f || hitData.m_damage.m_pickaxe > 0f) ? statSet.Get(Stat.GatherCritChance) : 0f)); if (num4 > 0f && Random.value < num4) { num *= 1.5f + statSet.Get(Stat.CritDamagePct); Effects.LastCritTime = Time.time; } ((DamageTypes)(ref hitData.m_damage)).Modify(Mathf.Clamp(num, 0f, 4f)); float num5 = 1f + statSet.Get(Stat.PhysicalDamagePct); float num6 = 1f + statSet.Get(Stat.ElementalDamagePct); hitData.m_damage.m_blunt *= num5; hitData.m_damage.m_slash *= num5; hitData.m_damage.m_pierce *= num5; hitData.m_damage.m_fire *= num6; hitData.m_damage.m_frost *= num6; hitData.m_damage.m_lightning *= num6; hitData.m_damage.m_poison *= num6; hitData.m_damage.m_spirit *= num6; if ((int)skill == 11 && statSet.Has(Stat.UnarmedGather)) { float num7 = ((DamageTypes)(ref hitData.m_damage)).GetTotalPhysicalDamage() * 0.6f; hitData.m_damage.m_chop += num7; hitData.m_damage.m_pickaxe += num7; hitData.m_toolTier = (short)Mathf.Max((int)hitData.m_toolTier, 2 + Mathf.RoundToInt(statSet.Get(Stat.UnarmedToolTier))); } float num8 = (Effects.Night ? statSet.Get(Stat.NightGatherPct) : 0f); if (hitData.m_damage.m_chop > 0f) { float num9 = ((Time.time - Effects.LastChopTime < 6f) ? statSet.Get(Stat.ChopBurnPct) : 0f); hitData.m_damage.m_chop *= 1f + statSet.Get(Stat.ChopDamagePct) + num8 + num9; Effects.LastChopTime = Time.time; } if (hitData.m_damage.m_pickaxe > 0f) { float num10 = ((Time.time - Effects.LastMineTime < 6f) ? statSet.Get(Stat.MineCrackPct) : 0f); hitData.m_damage.m_pickaxe *= 1f + statSet.Get(Stat.PickaxeDamagePct) + num8 + num10; Effects.LastMineTime = Time.time; } float totalPhysicalDamage = ((DamageTypes)(ref hitData.m_damage)).GetTotalPhysicalDamage(); bool flag = false; if (totalPhysicalDamage > 0f) { if (Random.value < Mathf.Min(0.75f, statSet.Get(Stat.IgniteChance))) { hitData.m_damage.m_fire += totalPhysicalDamage * 0.5f; float num11 = statSet.Get(Stat.EitrOnIgnite); if (num11 > 0f) { ((StatusEffect)this).m_character.AddEitr(num11); } } if (Random.value < Mathf.Min(0.75f, statSet.Get(Stat.FrostChance))) { hitData.m_damage.m_frost += totalPhysicalDamage * 0.4f; } if (Random.value < Mathf.Min(0.75f, statSet.Get(Stat.PoisonChance))) { hitData.m_damage.m_poison += totalPhysicalDamage * 0.6f; } if (Random.value < Mathf.Min(0.75f, statSet.Get(Stat.ShockChance))) { hitData.m_damage.m_lightning += totalPhysicalDamage * 0.3f; flag = true; } } if (((StatusEffect)this).m_character.GetSEMan().HaveStatusEffect(SEMan.s_statusEffectWet) && statSet.Get(Stat.WetShockPct) != 0f) { hitData.m_damage.m_lightning *= 1f + statSet.Get(Stat.WetShockPct); } float num12 = Mathf.Min(0.75f, statSet.Get(Stat.StunChance) + (flag ? statSet.Get(Stat.ShockStunChance) : 0f)); if (num12 > 0f && Random.value < num12) { hitData.m_staggerMultiplier = Mathf.Max(hitData.m_staggerMultiplier, 100f); } hitData.m_damage.m_fire *= 1f + statSet.Get(Stat.BurnDamagePct); hitData.m_damage.m_poison *= 1f + statSet.Get(Stat.PoisonDamagePct); hitData.m_damage.m_frost *= 1f + statSet.Get(Stat.FrostPotencyPct); hitData.m_damage.m_lightning *= 1f + statSet.Get(Stat.ShockDamagePct); HitData obj = hitData; obj.m_staggerMultiplier *= 1f + statSet.Get(Stat.StaggerDamagePct); HitData obj2 = hitData; obj2.m_pushForce *= 1f + statSet.Get(Stat.KnockbackPct); } public override string GetTooltipString() { return ((StatusEffect)this).m_tooltip; } } public static class Effects { [HarmonyPatch(typeof(WearNTear), "ApplyDamage")] private static class WearNTear_ApplyDamage { private static void Prefix(WearNTear __instance, ref float damage) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } float num = Mathf.Min(0.75f, Progress.Current.Stats.Get(Stat.BuildDurabilityPct)); if (!(num <= 0f)) { Piece component = ((Component)__instance).GetComponent<Piece>(); if (!((Object)(object)component == (Object)null) && component.GetCreator() == localPlayer.GetPlayerID()) { damage *= 1f - num; } } } } [HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[] { typeof(StatusEffect), typeof(bool), typeof(int), typeof(float), typeof(short) })] private static class SEMan_AddStatusEffect { private static readonly int[] Harmful = new int[12] { SEMan.s_statusEffectBurning, SEMan.s_statusEffectPoison, SEMan.s_statusEffectFrost, SEMan.s_statusEffectFreezing, SEMan.s_statusEffectCold, SEMan.s_statusEffectWet, SEMan.s_statusEffectSmoked, SEMan.s_statusEffectTared, SEMan.s_statusEffectSpirit, SEMan.s_statusEffectLightning, SEMan.s_statusEffectEncumbered, SEMan.s_statusEffectSoftDeath }; private static void Postfix(SEMan __instance, StatusEffect __result) { if ((Object)(object)__result == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)__instance.m_character != (Object)(object)Player.m_localPlayer || __result is SE_PassiveTree || __result.m_ttl <= 0f) { return; } int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)__result).name); int[] harmful = Harmful; for (int i = 0; i < harmful.Length; i++) { if (harmful[i] == stableHashCode) { return; } } StatSet stats = Progress.Current.Stats; float num = 1f + stats.Get(Stat.PotionDurationPct) + stats.Get(Stat.BuffDurationPct) + stats.Get(Stat.StatusDurationPct); if (num > 1f) { __result.m_ttl *= Mathf.Min(3f, num); } } } private struct ShipBoost { public float Sail; public float Rudder; public bool On; } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate")] private static class Ship_CustomFixedUpdate { private static void Prefix(Ship __instance, out ShipBoost __state) { __state = new ShipBoost { Sail = __instance.m_sailForceFactor, Rudder = __instance.m_rudderSpeed }; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)__instance.m_shipControlls == (Object)null || __instance.m_shipControlls.GetUser() != localPlayer.GetPlayerID()) { return; } StatSet statSet = Progress.Current?.Stats; if (statSet != null) { float num = Mathf.Min(0.4f, statSet.Get(Stat.SailSpeedPct)); float num2 = Mathf.Min(0.6f, statSet.Get(Stat.SailControlPct)); if (!(num <= 0f) || !(num2 <= 0f)) { __instance.m_sailForceFactor = __state.Sail * (1f + num); __instance.m_rudderSpeed = __state.Rudder * (1f + num2); __state.On = true; } } } private static void Postfix(Ship __instance, ShipBoost __state) { if (__state.On) { __instance.m_sailForceFactor = __state.Sail; __instance.m_rudderSpeed = __state.Rudder; } } } [HarmonyPatch(typeof(Vagon), "UpdateMass")] private static class Vagon_UpdateMass { private static void Postfix(Vagon __instance) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !__instance.IsAttached((Character)(object)localPlayer) || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsOwner()) { return; } float num = Mathf.Min(0.6f, Progress.Current?.Stats.Get(Stat.CartPullPct) ?? 0f); if (num <= 0f) { return; } Rigidbody[] bodies = __instance.m_bodies; foreach (Rigidbody val in bodies) { if ((Object)(object)val != (Object)null) { val.mass *= 1f - num; } } } } [HarmonyPatch(typeof(Skills), "GetSkillLevel")] private static class Skills_GetSkillLevel { private static bool Prefix(Skills __instance, SkillType skillType, ref float __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if ((int)skillType == 0) { __result = 0f; return false; } if (!IsLocal(__instance.m_player) || !Plugin.DisableVanillaSkills.Value) { return true; } float value = Plugin.BaseSkillLevel.Value; ((Character)__instance.m_player).GetSEMan().ModifySkillLevel(skillType, ref value); __result = Mathf.Floor(Mathf.Clamp(value, 0f, 100f)); return false; } } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] private static class Player_GetTotalFoodValue { private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr) { if (IsLocal(__instance)) { StatSet s = S; float num = ((((Character)__instance).GetSEMan() != null && ((Character)__instance).GetSEMan().HaveStatusEffect(SEMan.s_statusEffectBurning)) ? s.Get(Stat.FoodWhileBurningPct) : 0f); num += s.Get(Stat.FoodPowerPct); hp = (hp + s.Get(Stat.MaxHealthFlat)) * (1f + s.Get(Stat.MaxHealthPct) + num); stamina = (stamina + s.Get(Stat.MaxStaminaFlat)) * (1f + s.Get(Stat.MaxStaminaPct) + num); eitr = (eitr + s.Get(Stat.MaxEitrFlat)) * (1f + s.Get(Stat.MaxEitrPct) + num); } } } [HarmonyPatch(typeof(Character), "RPC_Damage")] private static class Character_RPC_Damage { private static void Prefix(Character __instance, HitData hit) { //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Invalid comparison between Unknown and I4 //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_00f2: 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) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || hit == null) { return; } StatSet s = S; Character val = (hit.HaveAttacker() ? hit.GetAttacker() : null); if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)__instance) { float num = Mathf.Min(0.4f, s.Get(Stat.DodgeChance)); if (num > 0f && Random.value < num) { hit.m_damage = default(DamageTypes); hit.m_staggerMultiplier = 0f; hit.m_pushForce = 0f; hit.m_statusEffectHash = 0; __instance.Message((MessageType)1, L.T("Уклонение!"), 0, (Sprite)null, false); return; } float num2 = Mathf.Min(0.5f, s.Get(Stat.ThornsPct)); if (num2 > 0f && !val.IsPlayer()) { HitData val2 = new HitData(); val2.m_damage.m_blunt = hit.GetTotalDamage() * num2; val2.m_point = val.GetCenterPoint(); val2.SetAttacker(__instance); val.Damage(val2); } } float num3 = 1f + s.Get(Stat.DamageTakenPct); Humanoid val3 = (Humanoid)(object)((__instance is Humanoid) ? __instance : null); ItemData val4 = ((val3 != null) ? val3.GetCurrentWeapon() : null); if (val4 == null || (int)val4.m_shared.m_skillType == 11) { num3 += s.Get(Stat.UnarmedDamageTakenPct); } num3 = Mathf.Clamp(num3, 0.4f, 5f); if (num3 != 1f) { ((DamageTypes)(ref hit.m_damage)).Modify(num3); } hit.m_damage.m_fire *= 1f - Mathf.Min(0.75f, s.Get(Stat.FireResistPct)); hit.m_damage.m_frost *= 1f - Mathf.Min(0.75f, s.Get(Stat.FrostResistPct)); hit.m_damage.m_poison *= 1f - Mathf.Min(0.75f, s.Get(Stat.PoisonResistPct)); hit.m_damage.m_lightning *= 1f - Mathf.Min(0.75f, s.Get(Stat.LightningResistPct)); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] private static class Character_RPC_Damage_Ailments { private static void Postfix(Character __instance, HitData hit) { if ((Object)(object)__instance == (Object)null || hit == null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } StatSet s = S; if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { float f = Mathf.Clamp(1f - s.Get(Stat.AilmentResistPct), 0.1f, 1f); SEMan sEMan = __instance.GetSEMan(); ScaleAilment(sEMan, SEMan.s_statusEffectBurning, f); ScaleAilment(sEMan, SEMan.s_statusEffectPoison, f); ScaleAilment(sEMan, SEMan.s_statusEffectFrost, f); } else { if ((Object)(object)hit.GetAttacker() != (Object)(object)Player.m_localPlayer) { return; } float num = s.Get(Stat.AilmentDurationPct) + s.Get(Stat.StatusDurationPct); SEMan sEMan2 = __instance.GetSEMan(); ScaleAilment(sEMan2, SEMan.s_statusEffectBurning, 1f + num + s.Get(Stat.BurnDurationPct)); ScaleAilment(sEMan2, SEMan.s_statusEffectPoison, 1f + num + s.Get(Stat.PoisonDurationPct)); ScaleAilment(sEMan2, SEMan.s_statusEffectFrost, 1f + num + s.Get(Stat.FrostDurationPct)); if (scaledTtl.Count <= 256) { return; } List<StatusEffect> list = new List<StatusEffect>(); foreach (StatusEffect key in scaledTtl.Keys) { if ((Object)(object)key == (Object)null || (Object)(object)key.m_character == (Object)null) { list.Add(key); } } foreach (StatusEffect item in list) { scaledTtl.Remove(item); } } } } [HarmonyPatch(typeof(Humanoid), "BlockAttack")] private static class Humanoid_BlockAttack { private static void Prefix(Humanoid __instance) { localBlocking = (Object)(object)__instance == (Object)(object)Player.m_localPlayer; } private static void Postfix() { localBlocking = false; } } [HarmonyPatch(typeof(ItemData), "GetBlockPower", new Type[] { typeof(int), typeof(float) })] private static class ItemData_GetBlockPower { private static void Postfix(ref float __result) { if (localBlocking) { __result *= 1f + S.Get(Stat.BlockPowerPct); } } } [HarmonyPatch(typeof(Attack), "GetAttackEitr", new Type[] { typeof(Character), typeof(ItemData) })] private static class Attack_GetAttackEitr { private static void Postfix(Character character, ref float __result) { if ((Object)(object)character == (Object)(object)Player.m_localPlayer) { __result *= Mathf.Max(0f, 1f + S.Get(Stat.EitrCostPct)); } } } [HarmonyPatch(typeof(Player), "EatFood")] private static class Player_EatFood { private static void Postfix(Player __instance, ItemData item, bool __result) { if (!__result || !IsLocal(__instance)) { return; } float num = 1f + S.Get(Stat.FoodDurationPct); if (num == 1f) { return; } foreach (Food food in __instance.m_foods) { if (food.m_item != null && food.m_item.m_shared.m_name == item.m_shared.m_name) { food.m_time = item.m_shared.m_foodBurnTime * num; } } } } [HarmonyPatch(typeof(SE_Rested), "UpdateTTL")] private static class SE_Rested_UpdateTTL { private static float lastScaled = -1f; private static void Postfix(SE_Rested __instance) { if (!((Object)(object)((StatusEffect)__instance).m_character != (Object)(object)Player.m_localPlayer)) { float num = 1f + S.Get(Stat.RestedDurationPct); if (num != 1f && ((StatusEffect)__instance).m_ttl != lastScaled) { ((StatusEffect)__instance).m_ttl = ((StatusEffect)__instance).m_ttl * num; lastScaled = ((StatusEffect)__instance).m_ttl; } } } } [HarmonyPatch(typeof(CharacterAnimEvent), "Speed")] private static class CharacterAnimEvent_Speed { private static void Postfix(CharacterAnimEvent __instance, float speedScale) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && (Object)(object)__instance.m_character == (Object)(object)localPlayer) { animBase = speedScale; } } } [HarmonyPatch(typeof(CharacterAnimEvent), "CustomFixedUpdate")] private static class CharacterAnimEvent_CustomFixedUpdate { private static void Postfix(CharacterAnimEvent __instance) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)__instance.m_character != (Object)(object)localPlayer) { return; } if (!((Character)localPlayer).InAttack()) { animBase = 1f; } else { if (__instance.m_animator.speed < 0.01f) { return; } StatSet s = S; float num = s.Get(Stat.AttackSpeedPct) + (RecentlyCrit ? s.Get(Stat.CritRecentAttackSpeedPct) : 0f) + (Onslaught ? (0.15f + s.Get(Stat.OnslaughtPowerPct)) : 0f); ItemData currentWeapon = ((Humanoid)localPlayer).GetCurrentWeapon(); if (currentWeapon != null) { Stat? stat = StatText.SpeedFor(currentWeapon.m_shared.m_skillType); if (stat.HasValue) { num += s.Get(stat.Value); } } num = Mathf.Min(0.45f, num); if (num != 0f) { __instance.m_animator.speed = Mathf.Max(0.2f, animBase * (1f + num)); } } } } [HarmonyPatch(typeof(SE_Rested), "CalculateComfortLevel", new Type[] { typeof(bool), typeof(Vector3) })] private static class SE_Rested_CalculateComfortLevel { private static void Postfix(bool inShelter, ref int __result) { if (inShelter && !((Object)(object)Player.m_localPlayer == (Object)null)) { __result += Mathf.RoundToInt(S.Get(Stat.ComfortFlat)); } } } [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static class Player_UpdatePlacement { private static void Prefix(Player __instance) { InPlacement = IsLocal(__instance); } private static void Postfix() { InPlacement = false; } } [HarmonyPatch(typeof(Player), "ConsumeResources")] private static class Player_ConsumeResources { private static bool Prefix(Player __instance) { if (!InPlacement || !IsLocal(__instance)) { return true; } float num = Mathf.Min(0.6f, S.Get(Stat.BuildRefundChance) + (Night ? S.Get(Stat.NightBuildPct) : 0f)); if (num <= 0f || Random.value >= num) { return true; } ((Character)__instance).Message((MessageType)1, L.T("Ресурсы сохранены!"), 0, (Sprite)null, false); return false; } } public static float LastCritTime = -100f; public static float OnslaughtUntil = -100f; public static float LastChopTime = -100f; public static float LastMineTime = -100f; public static bool OnPavedGround; public const float OnslaughtBase = 0.15f; public const float OnslaughtSeconds = 12f; public const float GatherWindow = 6f; public const float CritWindow = 4f; private static float basePlaceDistance = -1f; private static float baseExploreRadius = -1f; private static float basePickup = -1f; private static readonly Dictionary<StatusEffect, float> scaledTtl = new Dictionary<StatusEffect, float>(); private static bool localBlocking; private static float animBase = 1f; public static bool InPlacement; public static bool Onslaught => Time.time < OnslaughtUntil; public static bool Night { get { if ((Object)(object)EnvMan.instance != (Object)null) { return EnvMan.IsNight(); } return false; } } public static bool RecentlyCrit => Time.time - LastCritTime < 4f; private static StatSet S => Progress.Current?.Stats; public static void EnsureStatusEffect(Player p) { if (!((Object)(object)p == (Object)null) && ((Character)p).GetSEMan() != null && !((Character)p).GetSEMan().HaveStatusEffect(SE_PassiveTree.Hash)) { SE_PassiveTree sE_PassiveTree = ScriptableObject.CreateInstance<SE_PassiveTree>(); ((Object)sE_PassiveTree).name = "PassiveTree"; ((StatusEffect)sE_PassiveTree).m_name = L.T("Древо пассивок"); ((StatusEffect)sE_PassiveTree).m_ttl = 0f; ((StatusEffect)sE_PassiveTree).m_icon = ((!((Object)(object)ObjectDB.instance != (Object)null)) ? null : ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Rested"))?.m_icon); sE_PassiveTree.Apply(Progress.Get(p).Stats); ((Character)p).GetSEMan().AddStatusEffect((StatusEffect)(object)sE_PassiveTree, false, 0, 0f, (short)(-1)); Refresh(p); } } public static void Refresh(Player p) { if ((Object)(object)p == (Object)null) { return; } Progress progress = Progress.Get(p); SEMan sEMan = ((Character)p).GetSEMan(); (((sEMan != null) ? sEMan.GetStatusEffect(SE_PassiveTree.Hash) : null) as SE_PassiveTree)?.Apply(progress.Stats); if (basePlaceDistance < 0f) { basePlaceDistance = p.m_maxPlaceDistance; } p.m_maxPlaceDistance = basePlaceDistance + progress.Stats.Get(Stat.BuildRangeFlat); if (basePickup < 0f) { basePickup = p.m_autoPickupRange; } p.m_autoPickupRange = basePickup * (1f + progress.Stats.Get(Stat.PickupRangePct)); if ((Object)(object)Minimap.instance != (Object)null) { if (baseExploreRadius < 0f) { baseExploreRadius = Minimap.instance.m_exploreRadius; } Minimap.instance.m_exploreRadius = baseExploreRadius * (1f + progress.Stats.Get(Stat.ExploreRadiusPct)); } p.UpdateFood(0f, true); } public static bool IsLocal(Player p) { if ((Object)(object)p != (Object)null) { return (Object)(object)p == (Object)(object)Player.m_localPlayer; } return false; } private static void ScaleAilment(SEMan seman, int hash, float f) { if (f != 1f && seman != null) { StatusEffect statusEffect = seman.GetStatusEffect(hash); if (!((Object)(object)statusEffect == (Object)null) && !(statusEffect.m_ttl <= 0f) && (!scaledTtl.TryGetValue(statusEffect, out var value) || !Mathf.Approximately(value, statusEffect.m_ttl))) { statusEffect.m_ttl *= f; scaledTtl[statusEffect] = statusEffect.m_ttl; } } } } public static class Farming { [HarmonyPatch(typeof(Plant), "GetGrowTime")] private static class Plant_GetGrowTime { private static void Postfix(ref float __result) { StatSet s = S; if (s != null) { float num = Mathf.Min(StatText.CapOf(Stat.GrowSpeedPct), s.Get(Stat.GrowSpeedPct)); if (num > 0f) { __result /= 1f + num; } } } } [HarmonyPatch(typeof(Plant), "UpdateHealth")] private static class Plant_UpdateHealth { private static void Postfix(Plant __instance) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) StatSet s = S; if (s != null && s.Has(Stat.PlantCrowding) && (int)__instance.m_status == 2) { __instance.m_status = (Status)0; } } } [HarmonyPatch(typeof(Pickable), "Interact")] private static class Pickable_Interact_Crop { private static void Prefix(Pickable __instance, out bool __state) { __state = __instance.CanBePicked(); } private static void Postfix(Pickable __instance, Humanoid character, bool __state, bool __result) { Player localPlayer = Player.m_localPlayer; if (!__result || !__state || (Object)(object)localPlayer == (Object)null || (Object)(object)character != (Object)(object)localPlayer || __instance.CanBePicked()) { return; } StatSet s = S; if (s != null) { float num = Mathf.Min(StatText.CapOf(Stat.CropYieldChance), s.Get(Stat.CropYieldChance)); if (!(num <= 0f) && !(Random.value >= num) && IsCrop(((Component)__instance).gameObject)) { Gathering.GiveOne(localPlayer, __instance.m_itemPrefab); } } } } private static HashSet<string> crops; private static StatSet S => Progress.Current?.Stats; private static bool IsCrop(GameObject go) { if ((Object)(object)go == (Object)null) { return false; } if (crops == null) { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_prefabs == null) { return false; } HashSet<string> hashSet = new HashSet<string>(); foreach (GameObject prefab in instance.m_prefabs) { Plant val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Plant>() : null); if ((Object)(object)val == (Object)null || val.m_grownPrefabs == null) { continue; } GameObject[] grownPrefabs = val.m_grownPrefabs; foreach (GameObject val2 in grownPrefabs) { if ((Object)(object)val2 != (Object)null) { hashSet.Add(((Object)val2).name); } } } if (hashSet.Count == 0) { return false; } crops = hashSet; } string text = ((Object)go).name; int num = text.IndexOf("(Clone)"); if (num > 0) { text = text.Substring(0, num); } return crops.Contains(text); } } public static class Fishing { [HarmonyPatch(typeof(Fish), "FindFloat")] private static class Fish_FindFloat { private static float saved; private static void Prefix(Fish __instance) { saved = __instance.m_baseHookChance; float num = Mathf.Min(StatText.CapOf(Stat.FishBiteChance), Get(Stat.FishBiteChance)); if (num > 0f && HaveAngler()) { __instance.m_baseHookChance *= 1f + num; } } private static void Postfix(Fish __instance) { __instance.m_baseHookChance = saved; } } [HarmonyPatch(typeof(Fish), "GetStaminaUse")] private static class Fish_GetStaminaUse { private static void Postfix(ref float __result) { float num = Mathf.Max(StatText.CapOf(Stat.FishStaminaPct), Get(Stat.FishStaminaPct)); if (num != 0f) { __result *= Mathf.Max(0.15f, 1f + num); } } } [HarmonyPatch(typeof(Fish), "Pickup")] private static class Fish_Pickup { private static void Postfix(Fish __instance, Humanoid character, bool __result) { if (!__result || (Object)(object)character == (Object)null || (Object)(object)character != (Object)(object)Player.m_localPlayer) { return; } float num = Mathf.Min(StatText.CapOf(Stat.ExtraFishChance), Get(Stat.ExtraFishChance)); if (num <= 0f || Random.value >= num) { return; } GameObject pickupItem = __instance.m_pickupItem; if (!((Object)(object)pickupItem == (Object)null)) { Inventory inventory = character.GetInventory(); if (inventory != null) { inventory.AddItem(((Object)pickupItem.gameObject).name, 1, 1, 0, 0L, "", false, true); ((Character)character).Message((MessageType)1, L.T("Богатый улов!"), 0, (Sprite)null, false); } } } } private static StatSet S => Progress.Current?.Stats; private static float Get(Stat s) { return S?.Get(s) ?? 0f; } private static bool HaveAngler() { return (Object)(object)Player.m_localPlayer != (Object)null; } } public static class Gathering { [HarmonyPatch(typeof(Pickable), "Interact")] private static class Pickable_Interact { private static bool Prefix(Pickable __instance, out bool __state) { __state = __instance.CanBePicked(); return true; } private static void Postfix(Pickable __instance, Humanoid character, bool __state, bool __result) { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!__result || !__state || (Object)(object)localPlayer == (Object)null || (Object)(object)character != (Object)(object)localPlayer || __instance.CanBePicked()) { return; } StatSet s = S; if (s == null) { return; } float num = Mathf.Min(0.75f, s.Get(Stat.ExtraHarvestChance)); if (num > 0f && Random.value < num) { GiveOne(localPlayer, __instance.m_itemPrefab); } float num2 = Mathf.Min(8f, s.Get(Stat.MassPickupRadius)); if (num2 <= 0f || massPicking) { return; } massPicking = true; try { string text = (((Object)(object)__instance.m_itemPrefab != (Object)null) ? ((Object)__instance.m_itemPrefab).name : null); if (text == null) { return; } Pickable[] array = Object.FindObjectsOfType<Pickable>(); foreach (Pickable val in array) { if (!((Object)(object)val == (Object)(object)__instance) && val.CanBePicked() && !((Object)(object)val.m_itemPrefab == (Object)null) && !(((Object)val.m_itemPrefab).name != text) && !(Vector3.Distance(((Component)val).transform.position, ((Component)__instance).transform.position) > num2)) { val.Interact((Humanoid)(object)localPlayer, false, false); if (num > 0f && Random.value < num) { GiveOne(localPlayer, val.m_itemPrefab); } } } } finally { massPicking = false; } } } [HarmonyPatch(typeof(DropOnDestroyed), "OnDestroyed")] private static class DropOnDestroyed_OnDestroyed { private static void Prefix() { inDestroyDrop = true; } private static void Postfix() { inDestroyDrop = false; } } [HarmonyPatch(typeof(DropTable), "GetDropList", new Type[] { })] private static class DropTable_GetDropList { private static void Postfix(List<GameObject> __result) { Player localPlayer = Player.m_localPlayer; if (__result == null || __result.Count == 0 || (Object)(object)localPlayer == (Object)null) { return; } bool flag = Time.time - Effects.LastChopTime < 2f || Time.time - Effects.LastMineTime < 2f; if (!inDestroyDrop && !flag) { return; } StatSet s = S; if (s != null) { float num = Mathf.Min(0.75f, s.Get(Stat.ExtraDropChance)); if (!(num <= 0f) && !(Random.value >= num)) { __result.Add(__result[Random.Range(0, __result.Count)]); } } } } private static bool massPicking; private static bool inDestroyDrop; private static StatSet S => Progress.Current?.Stats; internal static void GiveOne(Player p, GameObject prefab) { ItemDrop val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ItemDrop>() : null); Inventory inventory = ((Humanoid)p).GetInventory(); if (!((Object)(object)val == (Object)null) && inventory != null && inventory.CanAddItem(prefab, 1)) { inventory.AddItem(val.m_itemData.m_shared.m_name, 1, 1, 0, 0L, "", false, true); } } } public static class Hugin { private const string Key = "pt_tree"; private static bool registered; private static string Body() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) string arg = ((Plugin.ToggleKey != null) ? ((object)Plugin.ToggleKey.Value/*cast due to .constrained prefix*/).ToString() : "P"); return string.Format(L.T("Ты стал сильнее, и я прилетел сказать, что с этим делать. Каждый уровень даёт очко умений — открой дерево клавишей {0}. Отмечай узлы, куда хочешь дойти, и нажимай «Применить очки»: пока не нажал, не потрачено ничего. Ромбы — мастерства, у каждого три варианта, и менять выбор можно когда угодно и бесплатно. Передумаешь совсем — очки вернутся за монеты."), arg); } private static void Register() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown if (registered) { return; } Tutorial instance = Tutorial.instance; if (!((Object)(object)instance == (Object)null) && instance.m_texts != null) { if (!instance.m_texts.Exists((TutorialText x) => x.m_name == "pt_tree")) { instance.m_texts.Add(new TutorialText { m_name = "pt_tree", m_topic = L.T("Дерево умений"), m_label = L.T("Дерево"), m_text = Body() }); } registered = true; } } public static void OnLevelUp(Player p) { if (!((Object)(object)p == (Object)null) && !((Object)(object)p != (Object)(object)Player.m_localPlayer)) { Register(); if (registered) { p.ShowTutorial("pt_tree", false); } } } } public static class IconMap { public const int TileSize = 128; public const int Columns = 16; public static readonly Dictionary<string, int> ByName = new Dictionary<string, int> { { "Артельщик", 72 }, { "Архимаг", 60 }, { "Бастион", 13 }, { "Бездонный колчан", 49 }, { "Берсерк", 36 }, { "Бесшумный выстрел", 44 }, { "Боевой маг", 54 }, { "Бой без оружия", 27 }, { "Бой без страха", 28 }, { "Болотный сборщик", 58 }, { "Бомбодел", 56 }, { "Бродяга", 64 }, { "Буря стрел", 46 }, { "Верный прицел", 99 }, { "Ветер в спину", 64 }, { "Вечная мерзлота", 57 }, { "Вожак стаи", 98 }, { "Вольный бег", 65 }, { "Вольный ветер", 65 }, { "Второе дыхание", 5 }, { "Второе сердце", 88 }, { "Вьючный мул", 72 }, { "Глаз Одина", 105 }, { "Голые руки", 29 }, { "Горн", 83 }, { "Горняк", 83 }, { "Гроза в спину", 94 }, { "Громовержец", 59 }, { "Громовой болт", 86 }, { "Громовой клинок", 86 }, { "Дальний выстрел", 99 }, { "Двойной прыжок", 69 }, { "Добивание", 37 }, { "Добивающий удар", 37 }, { "Долгая память", 88 }, { "Домашний уют", 79 }, { "Дробитель", 86 }, { "Дровосек-воин", 23 }, { "Жажда", 3 }, { "Жар питает", 7 }, { "Жатва", 73 }, { "Железная кожа", 9 }, { "Живая вода", 2 }, { "Живучий страж", 2 }, { "Заготовка леса", 82 }, { "Зажигательная стрела", 56 }, { "Заземление", 20 }, { "Закалённый в огне", 17 }, { "Закалённый жаром", 95 }, { "Запасливый хозяин", 81 }, { "Засада", 42 }, { "Зачарованные стрелы", 63 }, { "Знание земель", 38 }, { "Зов дикого", 98 }, { "Зодчий", 103 }, { "Идеальный отскок", 14 }, { "Иней", 57 }, { "Инструмент как оружие", 93 }, { "Искра молнии", 59 }, { "Искра огня", 56 }, { "Каменная кожа", 12 }, { "Каменное сердце", 12 }, { "Каменоломня", 83 }, { "Караван", 72 }, { "Кислота", 58 }, { "Киянка мастера", 86 }, { "Клинок рун", 108 }, { "Клинок ярла", 22 }, { "Ковка", 77 }, { "Колдун ночи", 90 }, { "Колчан без дна", 49 }, { "Кольчуга шипов", 16 }, { "Контратака", 14 }, { "Копейщик", 25 }, { "Кормилец", 98 }, { "Крепкая спина", 72 }, { "Крепкие кулаки", 27 }, { "Крепкие стены", 9 }, { "Крепкий дух", 1 }, { "Крепкий организм", 89 }, { "Крепкий сон", 90 }, { "Крепкое сердце", 1 }, { "Крепость", 101 }, { "Кровавый маг", 55 }, { "Кровавый удар", 3 }, { "Кровопийца", 3 }, { "Кровь Имира", 102 }, { "Кровь и эйтр", 55 }, { "Крылья Хугина", 70 }, { "Кузнечный жар", 56 }, { "Кулак Тора", 107 }, { "Кулак в броне", 27 }, { "Кулак по породе", 84 }, { "Кулачный боец", 27 }, { "Латный кулак", 28 }, { "Ледяные стрелы", 57 }, { "Лесоруб", 82 }, { "Ловчий", 99 }, { "Лёгкость заклинаний", 8 }, { "Марафонец", 65 }, { "Мастер артели", 77 }, { "Мастер оружия", 32 }, { "Мастер темпа", 30 }, { "Мастер-строитель", 76 }, { "Мастерство Натиска", 87 }, { "Мастерство брони", 9 }, { "Мастерство воды", 68 }, { "Мастерство выдержки", 12 }, { "Мастерство горна", 83 }, { "Мастерство добивания", 37 }, { "Мастерство дубины", 24 }, { "Мастерство дыхания", 4 }, { "Мастерство жизни", 0 }, { "Мастерство заклинаний", 54 }, { "Мастерство зачарования", 63 }, { "Мастерство кирки", 83 }, { "Мастерство колчана", 49 }, { "Мастерство копья", 25 }, { "Мастерство крови", 3 }, { "Мастерство кулака", 27 }, { "Мастерство лука", 44 }, { "Мастерство меча", 22 }, { "Мастерство молнии", 59 }, { "Мастерство мороза", 57 }, { "Мастерство настоя", 88 }, { "Мастерство натиска в упор", 34 }, { "Мастерство непогоды", 18 }, { "Мастерство ножа", 26 }, { "Мастерство ночи", 90 }, { "Мастерство огня", 56 }, { "Мастерство охоты", 99 }, { "Мастерство очага", 79 }, { "Мастерство парирования", 14 }, { "Мастерство пастуха", 98 }, { "Мастерство подрывника", 56 }, { "Мастерство поклажи", 72 }, { "Мастерство прыжка", 66 }, { "Мастерство рубки", 82 }, { "Мастерство рыбака", 68 }, { "Мастерство сбора", 84 }, { "Мастерство снаряда", 50 }, { "Мастерство стойкости", 89 }, { "Мастерство стола", 81 }, { "Мастерство стройки", 77 }, { "Мастерство темпа", 30 }, { "Мастерство тени", 71 }, { "Мастерство топора", 23 }, { "Мастерство точного удара", 32 }, { "Мастерство уклонения", 15 }, { "Мастерство шага", 64 }, { "Мастерство шипов", 16 }, { "Мастерство щита", 13 }, { "Мастерство эйтра", 6 }, { "Мастерство яда", 58 }, { "Материнская жила", 104 }, { "Меткий лучник", 44 }, { "Мозолистые руки", 78 }, { "Мокрая гроза", 94 }, { "Мокрая цель", 94 }, { "Молотобоец", 34 }, { "Морской ход", 64 }, { "Навес", 79 }, { "Накатом", 64 }, { "Наплечник", 9 }, { "Настой алхимика", 88 }, { "Неистовство", 100 }, { "Непогода", 94 }, { "Непоколебимый", 12 }, { "Несокрушимый", 101 }, { "Неутомимый", 31 }, { "Ночная охота", 90 }, { "Ночная смена", 90 }, { "Ночная тропа", 91 }, { "Ночной выстрел", 90 }, { "Ночной зодчий", 92 }, { "Ночной клинок", 90 }, { "Ночной охотник", 90 }, { "Ночной очаг", 90 }, { "Ночной покой", 90 }, { "Огненные болты", 56 }, { "Оперение", 99 }, { "Отравленная стрела", 58 }, { "Отражение", 16 }, { "Охотник на чудовищ", 38 }, { "Охотничья лёжка", 99 }, { "Охотничья тропа", 43 }, { "Пастух", 98 }, { "Пепел", 56 }, { "Пепел Сурта", 106 }, { "Перьевое падение", 67 }, { "Пламя Муспельхейма", 56 }, { "Пловец", 68 }, { "Плотницкая хватка", 78 }, { "Погоня", 87 }, { "Погоня за добычей", 87 }, { "Погреб", 81 }, { "Подмастерье", 78 }, { "Подрывник", 56 }, { "После удара", 33 }, { "Поток эйтра", 7 }, { "Пробой брони", 34 }, { "Пробуждение", 75 }, { "Проводник", 59 }, { "Пронзающий выстрел", 51 }, { "Прораб", 72 }, { "Пустой сосуд", 8 }, { "Пылающий клинок", 56 }, { "Раж", 87 }, { "Разведка жил", 74 }, { "Разгон", 32 }, { "Раздуть пламя", 62 }, { "Раскол", 97 }, { "Расщепление", 53 }, { "Резервуар", 6 }, { "Рикошет", 52 }, { "Ровное дыхание", 4 }, { "Родник эйтра", 6 }, { "Рой стрел", 50 }, { "Рунная заточка", 54 }, { "Рыбацкая удача", 68 }, { "Свежевание", 99 }, { "Северный доспех", 18 }, { "Северянин", 18 }, { "Секира", 23 }, { "Скалолаз", 66 }, { "Скороход", 65 }, { "Скотный двор", 98 }, { "Следопыт", 74 }, { "Смолокур", 82 }, { "Собиратель", 73 }, { "Сокрушитель", 34 }, { "Старатель", 84 }, { "Стена", 12 }, { "Стена щитов", 13 }, { "Страж ночи", 90 }, { "Стрелок", 45 }, { "Сытость", 81 }, { "Сытый", 81 }, { "Танцор", 15 }, { "Таран", 86 }, { "Твёрдая стойка", 13 }, { "Тень", 71 }, { "Тень охотника", 109 }, { "Тлеющий подсек", 96 }, { "Толстая шкура", 11 }, { "Точка слома", 37 }, { "Точка удара", 32 }, { "Тропа сборщика", 73 }, { "Трудолюбие", 78 }, { "Тугая тетива", 44 }, { "Тяжёлый доспех", 10 }, { "Тяжёлый молот", 24 }, { "Тёплая кровь", 18 }, { "Тёплый дом", 79 }, { "Убийца", 42 }, { "Удача добытчика", 75 }, { "Урожай", 81 }, { "Уют", 80 }, { "Уязвимое место", 32 }, { "Хладнокровный боец", 39 }, { "Хозяин дома", 110 }, { "Хозяйственный", 72 }, { "Хранитель очага", 79 }, { "Чистая кровь", 89 }, { "Чумной маг", 58 }, { "Шахтёр-воин", 93 }, { "Шлем", 12 }, { "Штормовой выстрел", 94 }, { "Щедрый стол", 81 }, { "Щит и меч", 85 }, { "Экономный", 77 }, { "Элементалист", 54 }, { "Эфирная тетива", 47 }, { "Ядовитый клинок", 58 }, { "Ярость", 36 }, { "Ярость раненого", 36 } }; public static readonly Dictionary<Stat, int> ByStat = new Dictionary<Stat, int> { { Stat.AccuracyPct, 99 }, { Stat.AilmentDurationPct, 60 }, { Stat.AilmentResistPct, 89 }, { Stat.ArmorFlat, 9 }, { Stat.ArmorPct, 10 }, { Stat.ArrowRangePct, 99 }, { Stat.ArrowSaveChance, 49 }, { Stat.AttackSpeedPct, 30 }, { Stat.AttackStaminaPct, 31 }, { Stat.AttrFinesse, 64 }, { Stat.AttrMight, 72 }, { Stat.AttrSpirit, 6 }, { Stat.BackstabPct, 42 }, { Stat.BerserkPct, 36 }, { Stat.BlastRadiusPct, 56 }, { Stat.BlockPowerPct, 13 }, { Stat.BlockStaminaPct, 13 }, { Stat.BombCraftRefundChance, 56 }, { Stat.BombDamagePct, 56 }, { Stat.BombMine, 56 }, { Stat.BombSaveChance, 56 }, { Stat.BreedChancePct, 98 }, { Stat.BuffDurationPct, 88 }, { Stat.BuildDurabilityPct, 77 }, { Stat.BuildRangeFlat, 76 }, { Stat.BuildRefundChance, 77 }, { Stat.BurnDamagePct, 56 }, { Stat.BurnDurationPct, 56 }, { Stat.CarryWeightFlat, 72 }, { Stat.CartPullPct, 72 }, { Stat.ChopBurnPct, 96 }, { Stat.ChopDamagePct, 82 }, { Stat.ColdResistPct, 18 }, { Stat.ComfortFlat, 79 }, { Stat.CookSpeedPct, 81 }, { Stat.CraftDurabilityPct, 9 }, { Stat.CraftRefundChance, 77 }, { Stat.CritChance, 32 }, { Stat.CritDamagePct, 32 }, { Stat.CritRecentAttackSpeedPct, 33 }, { Stat.CritRecentDamagePct, 33 }, { Stat.CritRecentMoveSpeedPct, 33 }, { Stat.CritRecentStaminaRegenPct, 33 }, { Stat.DamagePct, 21 }, { Stat.DamageTakenPct, 11 }, { Stat.DamageVsAnimalsPct, 38 }, { Stat.DamageVsBossPct, 38 }, { Stat.DamageVsBurningPct, 62 }, { Stat.DamageVsDvergrPct, 38 }, { Stat.DamageVsForestPct, 38 }, { Stat.DamageVsFrozenPct, 62 }, { Stat.DamageVsMistlandsPct, 38 }, { Stat.DamageVsMountainPct, 38 }, { Stat.DamageVsPlainsPct, 38 }, { Stat.DamageVsPoisonedPct, 62 }, { Stat.DamageVsSeaPct, 38 }, { Stat.DamageVsStaggeredPct, 34 }, { Stat.DamageVsUndeadPct, 38 }, { Stat.DamageVsWetPct, 62 }, { Stat.Dmg_Axes, 23 }, { Stat.Dmg_BloodMagic, 55 }, { Stat.Dmg_Bows, 44 }, { Stat.Dmg_Clubs, 24 }, { Stat.Dmg_Crossbows, 45 }, { Stat.Dmg_ElementalMagic, 54 }, { Stat.Dmg_Knives, 26 }, { Stat.Dmg_Pickaxes, 93 }, { Stat.Dmg_Polearms, 25 }, { Stat.Dmg_Spears, 25 }, { Stat.Dmg_Swords, 22 }, { Stat.Dmg_Unarmed, 27 }, { Stat.DodgeChance, 15 }, { Stat.DodgeStaminaPct, 15 }, { Stat.DoubleBlastChance, 56 }, { Stat.DoubleJump, 69 }, { Stat.DrawSpeedPct, 47 }, { Stat.DrawStaminaPct, 48 }, { Stat.DryingPct, 95 }, { Stat.EitrCostPct, 8 }, { Stat.EitrOnIgnite, 7 }, { Stat.EitrOnKill, 7 }, { Stat.EitrRegenPct, 7 }, { Stat.ElementalDamagePct, 63 }, { Stat.ExecutePct, 37 }, { Stat.ExploreRadiusPct, 74 }, { Stat.ExtraBarChance, 83 }, { Stat.ExtraBombChance, 56 }, { Stat.ExtraDropChance, 84 }, { Stat.ExtraFishChance, 68 }, { Stat.ExtraHarvestChance, 84 }, { Stat.ExtraProjectiles, 50 }, { Stat.FallDamagePct, 67 }, { Stat.FeedDurationPct, 98 }, { Stat.FireResistPct, 17 }, { Stat.FishBiteChance, 68 }, { Stat.FishStaminaPct, 68 }, { Stat.FoodDurationPct, 81 }, { Stat.FoodPowerPct, 81 }, { Stat.FoodWhileBurningPct, 95 }, { Stat.FrostChance, 57 }, { Stat.FrostDurationPct, 57 }, { Stat.FrostPotencyPct, 57 }, { Stat.FrostResistPct, 18 }, { Stat.FullHpDamagePct, 39 }, { Stat.GatherCritChance, 84 }, { Stat.Glide, 70 }, { Stat.HealthOnKill, 3 }, { Stat.HealthRegenPct, 2 }, { Stat.HunterDropChance, 99 }, { Stat.IgniteChance, 56 }, { Stat.JumpPct, 66 }, { Stat.JumpStaminaPct, 66 }, { Stat.KnockbackPct, 35 }, { Stat.LifestealPct, 3 }, { Stat.LightningResistPct, 20 }, { Stat.MassPickupRadius, 73 }, { Stat.MaxEitrFlat, 6 }, { Stat.MaxEitrPct, 6 }, { Stat.MaxHealthFlat, 0 }, { Stat.MaxHealthPct, 1 }, { Stat.MaxStaminaFlat, 4 }, { Stat.MaxStaminaPct, 4 }, { Stat.MeleeDamagePct, 21 }, { Stat.MineCrackPct, 97 }, { Stat.MoveSpeedPct, 64 }, { Stat.NightArmorFlat, 90 }, { Stat.NightBuildPct, 92 }, { Stat.NightDamagePct, 40 }, { Stat.NightEitrRegenPct, 90 }, { Stat.NightGatherPct, 90 }, { Stat.NightMoveSpeedPct, 91 }, { Stat.NightRegenPct, 90 }, { Stat.NightStealthPct, 90 }, { Stat.NightXpPct, 90 }, { Stat.NoOvercook, 81 }, { Stat.NoisePct, 71 }, { Stat.OnslaughtChance, 87 }, { Stat.OnslaughtPowerPct, 87 }, { Stat.ParryBonusPct, 14 }, { Stat.PavedSpeedPct, 64 }, { Stat.PhysicalDamagePct, 21 }, { Stat.PickaxeDamagePct, 83 }, { Stat.PickupRangePct, 73 }, { Stat.PoisonChance, 58 }, { Stat.PoisonDamagePct, 58 }, { Stat.PoisonDurationPct, 58 }, { Stat.PoisonResistPct, 19 }, { Stat.PotionDurationPct, 89 }, { Stat.ProjectileChain, 52 }, { Stat.ProjectilePierce, 51 }, { Stat.ProjectileSpeedPct, 46 }, { Stat.ProjectileSplit, 53 }, { Stat.RangedDamagePct, 43 }, { Stat.RestedDamagePct, 41 }, { Stat.RestedDurationPct, 80 }, { Stat.RunStaminaPct, 65 }, { Stat.SafeFall, 67 }, { Stat.SailControlPct, 64 }, { Stat.SailSpeedPct, 64 }, { Stat.ShockChance, 59 }, { Stat.ShockDamagePct, 59 }, { Stat.ShockStunChance, 86 }, { Stat.SmeltSpeedPct, 83 }, { Stat.SneakStaminaPct, 71 }, { Stat.Spd_Axes, 23 }, { Stat.Spd_Clubs, 24 }, { Stat.Spd_Knives, 26 }, { Stat.Spd_Pickaxes, 93 }, { Stat.Spd_Polearms, 25 }, { Stat.Spd_Spears, 25 }, { Stat.Spd_Swords, 22 }, { Stat.Spd_Unarmed, 27 }, { Stat.StaggerDamagePct, 34 }, { Stat.StaggerResistPct, 12 }, { Stat.StaminaOnKill, 5 }, { Stat.StaminaRegenPct, 5 }, { Stat.StatusDurationPct, 88 }, { Stat.StealthPct, 71 }, { Stat.StunChance, 86 }, { Stat.SwimSpeedPct, 68 }, { Stat.SwimStaminaPct, 68 }, { Stat.TamedDamagePct, 98 }, { Stat.TamingSpeedPct, 98 }, { Stat.ThornsPct, 16 }, { Stat.ToolStaminaPct, 78 }, { Stat.UnarmedDamageTakenPct, 28 }, { Stat.UnarmedGather, 29 }, { Stat.UnarmedToolTier, 29 }, { Stat.WetResistPct, 94 }, { Stat.WetShockPct, 94 }, { Stat.XpGainPct, 75 } }; } public static class L { public const string Folder = "Localization"; private static readonly Dictionary<string, string> map = new Dictionary<string, string>(); private static string loaded = ""; public static string Dir => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Localization"); public static string CurrentLanguage() { try { string text = ((Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : null); return string.IsNullOrEmpty(text) ? "english" : text.ToLowerInvariant(); } catch { return "english"; } } public static void EnsureLoaded() { string text = CurrentLanguage(); if (text == loaded) { return; } loaded = text; map.Clear(); if (text == "russian") { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Localization: source Russian"); } return; } string text2 = Path.Combine(Dir, text + ".txt"); if (!File.Exists(text2)) { text2 = Path.Combine(Dir, "english.txt"); } if (!File.Exists(text2)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Translation file not found: " + text2)); } return; } int num = 0; string[] array = File.ReadAllLines(text2, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text3 = array[i].TrimEnd(Array.Empty<char>()); if (text3.Length == 0 || text3[0] == '#') { continue; } int num2 = text3.IndexOf('\t'); if (num2 > 0) { string key = text3.Substring(0, num2); string text4 = text3.Substring(num2 + 1).Replace("\\n", "\n"); if (text4.Length != 0) { map[key] = text4; num++; } } } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"Localization {text}: {num} strings from {Path.GetFileName(text2)}"); } } public static string T(string source) { if (string.IsNullOrEmpty(source)) { return source; } if (!map.TryGetValue(source, out var value)) { return source; } return value; } } public static class Mechanics { [HarmonyPatch(typeof(Player), "Update")] private static class Player_Update { private static void Postfix(Player __instance) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if (++frame % 30 == 0) { Effects.EnsureStatusEffect(__instance); bool flag = false; Heightmap val = Heightmap.FindHeightmap(((Component)__instance).transform.position); if ((Object)(object)val != (Object)null) { Color paintMask = val.GetPaintMask(((Component)__instance).transform.position); flag = paintMask.b > 0.5f || paintMask.g > 0.5f; } if (flag != Effects.OnPavedGround) { Effects.OnPavedGround = flag; Effects.Refresh(__instance); } } bool recentlyCrit = Effects.RecentlyCrit; bool night = Effects.Night; bool onslaught = Effects.Onslaught; if (recentlyCrit != lastRecentCrit || night != lastNight || onslaught != lastOnslaught) { lastRecentCrit = recentlyCrit; lastNight = night; lastOnslaught = onslaught; Effects.Refresh(__instance); } StatSet stats = Progress.Current.Stats; bool flag2 = ((Character)__instance).IsOnGround(); if (flag2) { doubleJumpUsed = false; } if (!stats.Has(Stat.DoubleJump) || flag2 || doubleJumpUsed || ((Character)__instance).m_lastGroundTouch < 0.2f || ((Character)__instance).IsSwimming() || ((Character)__instance).IsDead() || ((Character)__instance).InAttack() || !((Character)__instance).TakeInput() || !ZInput.GetButtonDown("Jump")) { return; } if (!((Character)__instance).HaveStamina(((Character)__instance).m_jumpStaminaUsage)) { Hud instance = Hud.instance; if (instance != null) { instance.StaminaBarEmptyFlash(); } } else { float skillFactor = ((Character)__instance).GetSkillFactor((SkillType)100); float num = 1f + skillFactor * 0.4f; Vector3 linearVelocity = ((Character)__instance).m_body.linearVelocity; linearVelocity.y = ((Character)__instance).m_jumpForce * num * (1f + stats.Get(Stat.JumpPct)); linearVelocity += ((Character)__instance).m_moveDir * ((Character)__instance).m_jumpForceForward * num; ((Character)__instance).ForceJump(linearVelocity, true); doubleJumpUsed = true; } } } [HarmonyPatch(typeof(Player), "FixedUpdate")] private static class Player_FixedUpdate { private static void Postfix(Player __instance) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_009f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if (!Progress.Current.Stats.Has(Stat.Glide)) { Gliding = false; return; } if (((Character)__instance).IsOnGround() || ((Character)__instance).IsSwimming() || ((Character)__instance).IsDead() || ((Character)__instance).m_lastGroundTouch < 0.4f || !((Character)__instance).TakeInput() || !ZInput.GetButton("Jump")) { Gliding = false; return; } Vector3 linearVelocity = ((Character)__instance).m_body.linearVelocity; if (linearVelocity.y < -1.5f) { linearVelocity.y = -1.5f; ((Character)__instance).m_body.linearVelocity = linearVelocity; } ((Character)__instance).m_maxAirAltitude = ((Component)__instance).transform.position.y; Gliding = true; } } private static bool doubleJumpUsed; private static bool lastRecentCrit; private static bool lastNight; private static bool lastOnslaught; public static bool Gliding; private static int frame; } public class ConfigurationManagerAttributes { public int? Order; public bool? IsAdvanced; public string Category; } [BepInPlugin("treextr.pathofvalheiman", "Path of Valheiman", "1.3.1")] public class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(Menu), "IsVisible")] private static class Menu_IsVisible { private static void Postfix(ref bool __result) { if (TreeWindow.IsOpen) { __result = true; } } } [HarmonyPatch(typeof(Menu), "IsActive")] private static class Menu_IsActive { private static void Postfix(ref bool __result) { if (TreeWindow.IsOpen) { __result = true; } } } [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] private static class GameCamera_UpdateMouseCapture { private static bool Prefix() { if (!TreeWindow.IsOpen) { return true; } ZCursor.LockState = (CursorLockMode)0; ZCursor.Show(); return false; } } public const string GUID = "treextr.pathofvalheiman"; public const string NAME = "Path of Valheiman"; public const string VERSION = "1.3.1"; public static ManualLogSource Log; public static ConfigEntry<KeyboardShortcut> ToggleKey; public static ConfigEntry<float> ZoomSpeed; public static ConfigEntry<float> HudScale; public static ConfigEntry<bool> HudEnabled; public static ConfigEntry<bool> TreeSounds; public static ConfigEntry<HudCorner> HudCorner; public static ConfigEntry<Vector2> HudOffset; public static ConfigEntry<float> XpPerSkillGain; public static ConfigEntry<float> KillXpPerHealth; public static ConfigEntry<float> BossXpMultiplier; public static ConfigEntry<float> BuildXp; public static ConfigEntry<float> ExploreXpPerPixel; public static ConfigEntry<float> LevelBaseXp; public static ConfigEntry<float> LevelExponent; public static ConfigEntry<float> BaseSkillLevel; public static ConfigEntry<float> DeathXpLossPct; public static ConfigEntry<float> TreePower; public static ConfigEntry<float> XpMultiplier; public static ConfigEntry<int> MaxLevel; public static ConfigEntry<int> RespecCostPerPoint; public static ConfigEntry<int> PointsPerLevel; public static ConfigEntry<int> StartingGates; public static ConfigEntry<TreeLayout> Layout; public static ConfigEntry<bool> DebugButtons; public static ConfigEntry<bool> ShowXpMessages; public static ConfigEntry<bool> DumpAssets; public static ConfigEntry<bool> RespecFree; public static ConfigEntry<bool> DisableVanillaSkills; public static ConfigEntry<bool> ServerSync; private Harmony harmony; private static int order; private ConfigEntry<T> Cfg<T>(string section, string key, T value, string description, AcceptableValueBase range = null) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Order = order-- }; return ((BaseUnityPlugin)this).Config.Bind<T>(section, key, value, new ConfigDescription(description, range, new object[1] { configurationManagerAttributes })); } static Plugin() { order = 100; try { string configPath = Paths.ConfigPath; string text = Path.Combine(configPath, "sam.valheim.passivetree.cfg"); string text2 = Path.Combine(configPath, "treextr.pathofvalheiman.cfg"); if (File.Exists(text) && !File.Exists(text2)) { File.Move(text, text2); } } catch { } } private void Awake() { //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Unknown result type (might be due to invalid IL or missing references) //IL_05be: Unknown result type (might be due to invalid IL or missing references) //IL_05c8: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Layout = Cfg("1. Balance", "Tree layout", TreeLayout.Classic, "Which passive tree to play. Classic is the released one: a wide wheel of eight sectors. Reforged is a second, tighter design and is still being tuned: fewer but stronger nodes, travel nodes give attributes only, every cluster ends in a mastery you choose from, keystones change rules instead of stacking numbers. Points allocated in one layout are remembered separately, so you can switch back and forth without losing a build.\nКакое дерево играть. Classic — основное дерево мода, широкое колесо из восьми секторов. Reforged — второй вариант, он ещё настраивается: узлов меньше, но они весомее, транзит даёт только атрибуты, каждый кластер заканчивается мастерством на выбор, ключевые узлы меняют правила, а не складывают числа. Очки в каждом дереве запоминаются отдельно, переключаться можно без потерь."); StartingGates = Cfg("1. Balance", "Starting directions", 2, "How many roads out of the centre a character may open. With two, the early game commits you to a pair of neighbouring skills and reaching a third one means walking the long way round, the way a passive tree should.\nСколько дорог из центра может открыть персонаж. При двух начало партии — это выбор пары направлений, а третье приходится добирать длинным обходом по дереву.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8)); TreePower = Cfg("1. Balance", "Tree power", 1f, "Global multiplier for every value in the tree. 1.0 is as designed, 0.5 is half as strong, 2.0 is twice as strong. No bonus ever drops below +1% (or +1 for flat values), so nodes never become empty.\nОбщий множитель всех значений в дереве. 1.0 — как задумано, 0.5 — вдвое слабее, 2.0 — вдвое сильнее. Ни один бонус не падает ниже +1% (или +1 для плоских), поэтому пустых узлов не появится.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f)); XpMultiplier = Cfg("2. Experience and levels", "Experience multiplier", 1f, "All experience gained is multiplied by this. Lower is a slow, careful progression, higher is a fast one.\nВесь получаемый опыт умножается на это число. Ниже — медленная и осторожная прогрессия, выше — быстрая.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 20f)); PointsPerLevel = Cfg("2. Experience and levels", "Points per level", 1, "How many tree points the player gets for each new level.\nСколько очков дерева игрок получает за каждый новый уровень.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10)); MaxLevel = Cfg("2. Experience and levels", "Maximum level", 150, "The level a character stops at, and therefore the size of a finished build: at the default of 150 that is 149 points with one point per level. Lowering it later takes the excess levels back.\nНа каком уровне персонаж останавливается, то есть каким будет законченный билд: при 150 по умолчанию это 149 очков при одном очке за уровень. Если позже уменьшить, лишние уровни снимутся.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 1000)); LevelBaseXp = Cfg("2. Experience and levels", "Experience for level two", 35f, "Experience to the next level = this number x level ^ curve exponent. The default is set so that filling the tree costs about as much as levelling vanilla skills all the way.\nОпыт до следующего уровня = это число × уровень ^ показатель кривой. По умолчанию подобрано так, что полное дерево стоит примерно столько же, сколько полная прокачка ванильных навыков.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 2000f)); LevelExponent = Cfg("2. Experience and levels", "Level curve exponent", 0.9f, "How steeply levels get more expensive. Below 1.0 late levels cost less than linear growth, 1.0 is linear, 2.0 is very steep.\nНасколько круто дорожают уровни. Меньше 1.0 — поздние уровни дорожают медленнее линейного, 1.0 — линейно, 2.0 — очень круто.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f)); DeathXpLossPct = Cfg("2. Experience and levels", "Experience lost on death", 0.1f, "Share of the current level's experience burned on death. 0 loses nothing.\nДоля опыта текущего уровня, сгорающая при смерти. 0 — ничего не теряется.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f)); XpPerSkillGain = Cfg("2. Experience and levels", "Experience for actions", 3f, "Multiplier for experience from anything that would raise a vanilla skill: fighting, running, swimming, chopping, building.\nМножитель опыта за любое действие, которое качало бы ванильный навык: бой, бег, плавание, рубка, стройка.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f)); KillXpPerHealth = Cfg("2. Experience and levels", "Experience per kill", 0.4f, "Experience per kill = target's maximum health x this number x (1 + 0.25 x (target level - 1)).\nОпыт за убийство = максимум здоровья цели × это число × (1 + 0.25 × (уровень цели − 1)).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f)); BossXpMultiplier = Cfg("2. Experience and levels", "Boss experience multiplier", 8f, "How many times more experience a boss gives than a regular enemy.\nВо сколько раз босс даёт больше обычного врага.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f)); BuildXp = Cfg("2. Experience and levels", "Experience per build", 3f, "Experience for each piece placed.\nОпыт за каждую установленную деталь.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f)); ExploreXpPerPixel = Cfg("2. Experience and levels", "Experience for exploring", 0.02f, "Experience for each newly revealed patch of the map.\nОпыт за каждый новый открытый кусочек карты.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f)); DisableVanillaSkills = Cfg("3. Skills", "Disable vanilla progression", value: true, "On: Valheim's skills freeze at the level below and all of their growth turns into tree experience. Off: skills grow as in the original and the tree works on top of them.\nВключено: навыки Valheim замирают на заданном уровне, а весь их рост превращается в опыт дерева. Выключено: навыки растут как в оригинале, и дерево работает поверх них."); BaseSkillLevel = Cfg("3. Skills", "Frozen skill level", 50f, "The level every vanilla skill is held at while its progression is disabled.\nНа каком уровне держать все ванильные навыки, когда их прокачка отключена.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)); RespecCostPerPoint = Cfg("4. Refunding points", "Cost to refund a point", 10, "How many coins it costs to take one allocated point back.\nСколько монет стоит вернуть одно вложенное очко.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 500)); RespecFree = Cfg("4. Refunding points", "Free refunds", value: false, "Refund points without coins.\nВозвращать очки без монет."); ToggleKey = Cfg<KeyboardShortcut>("5. Controls", "Window key", new KeyboardShortcut((KeyCode)112, Array.Empty<KeyCode>()), "Opens and closes the passive tree.\nОткрывает и закрывает древо пассивок."); ZoomSpeed = Cfg("5. Controls", "Zoom speed", 1f, "How much one wheel notch zooms the tree. 1.0 is about a quarter step; raise it if your mouse reports tiny scroll steps, lower it if the tree jumps too far.\nНасколько один щелчок колеса меняет масштаб дерева. 1.0 — примерно четверть шага; поднимите, если мышь отдаёт очень мелкие шаги прокрутки, опустите, если дерево прыгает слишком далеко.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 4f)); TreeSounds = Cfg("5. Controls", "Tree sounds", value: true, "Play the game's own interface sounds when a node is chosen, a point is refunded and a level is gained.\nИграть звуки интерфейса при выборе узла, возврате очка и получении уровня."); HudEnabled = Cfg("5. Controls", "Level panel on the HUD", value: true, "Show a small panel with your level, the experience bar and unspent points while you play.\nПоказывать во время игры небольшую панель с уровнем, шкалой опыта и нераспределёнными очками."); HudCorner = Cfg("5. Controls", "Level panel corner", PassiveTree.UI.HudCorner.LeftOfMinimap, "Which corner of the screen the panel sits in. Top right sits under the minimap.\nВ каком углу экрана стоит панель. Правый верхний — под миникартой."); HudScale = Cfg("5. Controls", "Level panel scale", 1f, "Size of the panel.\nРазмер панели.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 2f)); HudOffset = Cfg<Vector2>("5. Controls", "Level panel offset", Vector2.zero, "Extra shift of the panel in pixels, if it collides with another mod's HUD.\nДополнительный сдвиг панели в пикселях, если она наезжает на интерфейс другого мода."); ShowXpMessages = Cfg("5. Controls", "Experience messages", value: true, "Show \"+N experience\" for kills.\nПоказывать «+N опыта» за убийства."); ServerSync = Cfg("6. Debug", "Server sync", value: true, "The server hands its balance settings to joining players so everyone plays the same tree.\nСервер раздаёт свои настройки баланса подключившимся игрокам, чтобы у всех было одинаковое дерево."); DebugButtons = Cfg("6. Debug", "Debug panel", value: false, "Off by default. Turn it on for yourself to get a panel in the tree window that grants points, experience and levels, and resets the tree for free. It is a local setting: it never reaches other players.\nПо умолчанию выключено. Включается для себя: в окне дерева появляется панель с выдачей очков, опыта и уровней и бесплатным сбросом. Настройка локальная, другим игрокам не раздаётся."); DumpAssets = Cfg("6. Debug", "Dump asset list", value: false, "Write the names of the game's sprites, fonts and items to BepInEx/PassiveTree_assets.txt on startup.\nЗаписать имена спрайтов, шрифтов и предметов игры в BepInEx/PassiveTree_assets.txt при запуске."); L.EnsureLoaded(); Tree.Build(); Tree.ApplyPower(TreePower.Value); TreePower.SettingChanged += delegate { RebuildTree(); }; HudEnabled.SettingChanged += delegate { HudWidget.Rebuild(); }; HudCorner.SettingChanged += delegate { HudWidget.Rebuild(); }; HudScale.SettingChanged += delegate { HudWidget.Rebuild(); }; HudOffset.SettingChanged += delegate { HudWidget.Rebuild(); }; Layout.SettingChanged += delegate { RebuildTree(); }; PointsPerLevel.SettingChanged += delegate { Progress.Current?.ApplyBudgetSettings(); }; MaxLevel.SettingChanged += delegate { Progress.Current?.ApplyBudgetSettings(); }; harmony = new Harmony("treextr.pathofvalheiman"); int num = 0; int num2 = 0; Type[] types = typeof(Plugin).Assembly.GetTypes(); foreach (Type type in types) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0) { try { harmony.PatchAll(type); num++; } catch (Exception ex) { num2++; Log.LogError((object)("Patch " + type.FullName