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(); foreach (TMP_FontAsset val in array) { stringBuilder.AppendLine(((Object)val).name); } stringBuilder.AppendLine("== Sprites =="); foreach (Sprite item in from s in Resources.FindObjectsOfTypeAll() 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() != (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(); 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(); if ((Object)(object)val == (Object)null) { val = ((Component)__instance).gameObject.AddComponent(); } ((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(((Component)__instance).gameObject, ((Component)__instance).transform.position, ((Component)__instance).transform.rotation); val2.AddComponent(); Aoe component = val2.GetComponent(); 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> 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 Pair(string k, ConfigEntryBase e) { return new KeyValuePair(k, e); } private static void Register() { if (!registered && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("PassiveTree_ConfigRequest", (Action)OnRequest); ZRoutedRpc.instance.Register("PassiveTree_ConfigApply", (Action)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 list = new List(); foreach (KeyValuePair 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 dictionary = new Dictionary(); foreach (KeyValuePair 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(); 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 list = new List(); 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 scaledTtl = new Dictionary(); 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(); ((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 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 hashSet = new HashSet(); foreach (GameObject prefab in instance.m_prefabs) { Plant val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : 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(); 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 __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() : 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 ByName = new Dictionary { { "Артельщик", 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 ByStat = new Dictionary { { 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 map = new Dictionary(); 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()); 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 ToggleKey; public static ConfigEntry ZoomSpeed; public static ConfigEntry HudScale; public static ConfigEntry HudEnabled; public static ConfigEntry TreeSounds; public static ConfigEntry HudCorner; public static ConfigEntry HudOffset; public static ConfigEntry XpPerSkillGain; public static ConfigEntry KillXpPerHealth; public static ConfigEntry BossXpMultiplier; public static ConfigEntry BuildXp; public static ConfigEntry ExploreXpPerPixel; public static ConfigEntry LevelBaseXp; public static ConfigEntry LevelExponent; public static ConfigEntry BaseSkillLevel; public static ConfigEntry DeathXpLossPct; public static ConfigEntry TreePower; public static ConfigEntry XpMultiplier; public static ConfigEntry MaxLevel; public static ConfigEntry RespecCostPerPoint; public static ConfigEntry PointsPerLevel; public static ConfigEntry StartingGates; public static ConfigEntry Layout; public static ConfigEntry DebugButtons; public static ConfigEntry ShowXpMessages; public static ConfigEntry DumpAssets; public static ConfigEntry RespecFree; public static ConfigEntry DisableVanillaSkills; public static ConfigEntry ServerSync; private Harmony harmony; private static int order; private ConfigEntry Cfg(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(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(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(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(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(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(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(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(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(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(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(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(1f, 50f)); BuildXp = Cfg("2. Experience and levels", "Experience per build", 3f, "Experience for each piece placed.\nОпыт за каждую установленную деталь.", (AcceptableValueBase)(object)new AcceptableValueRange(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(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(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(0, 500)); RespecFree = Cfg("4. Refunding points", "Free refunds", value: false, "Refund points without coins.\nВозвращать очки без монет."); ToggleKey = Cfg("5. Controls", "Window key", new KeyboardShortcut((KeyCode)112, Array.Empty()), "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(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(0.6f, 2f)); HudOffset = Cfg("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 + " failed: " + ex.Message)); } } } Log.LogInfo((object)string.Format("{0} {1} loaded: {2} nodes, {3} patched types, {4} failed", "Path of Valheiman", "1.3.1", Tree.Nodes.Count, num, num2)); } public static void RebuildTree() { Tree.Nodes.Clear(); L.EnsureLoaded(); Tree.Build(); Tree.ApplyPower(TreePower.Value); TreeWindow.Rebuild(); Progress current = Progress.Current; if (current != null) { current.Load(); Effects.Refresh(Player.m_localPlayer); } Log.LogInfo((object)$"Tree rebuilt: layout {Layout.Value}, power {TreePower.Value:0.##}, nodes {Tree.Nodes.Count}"); } public static float XpForLevel(int level) { return Mathf.Round(LevelBaseXp.Value * Mathf.Pow((float)Mathf.Max(1, level), LevelExponent.Value)); } private void Update() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Player.m_localPlayer == (Object)null)) { HudWidget.Ensure(); KeyboardShortcut value = ToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && !TreeWindow.TypingInSearch && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible()) { TreeWindow.Ensure(); TreeWindow.Instance.Toggle(); } } } } public class Progress { private static readonly Dictionary cache = new Dictionary(); private readonly Player player; public int Level = 1; public float Xp; public int BonusPoints; public readonly HashSet Allocated = new HashSet { "root" }; public readonly Dictionary Choices = new Dictionary(); public readonly StatSet Stats = new StatSet(); private const string KLevel = "pt.level"; private const string KXp = "pt.xp"; private const string KBonus = "pt.bonus"; private const string KNodes = "pt.nodes"; private const string KOpts = "pt.opts"; private const string KVer = "pt.ver"; public int RefundedByUpdate; public int TrimmedByBudget; public static Progress Current { get { if (!((Object)(object)Player.m_localPlayer != (Object)null)) { return null; } return Get(Player.m_localPlayer); } } public int TotalPoints => (Level - 1) * Mathf.Max(1, Plugin.PointsPerLevel.Value) + BonusPoints; public int SpentPoints => Allocated.Count - 1; public int AvailablePoints => TotalPoints - SpentPoints; public float XpToNext => Plugin.XpForLevel(Level); public int GateLimit => Mathf.Clamp(Plugin.StartingGates.Value, 1, 8); public int GatesTaken => Tree.Gates.Count((string g) => Allocated.Contains(g)); public static int PointCost { get { if (!Plugin.RespecFree.Value) { return Mathf.Max(0, Plugin.RespecCostPerPoint.Value); } return 0; } } private static string Suffix { get { if (Plugin.Layout == null || Plugin.Layout.Value != TreeLayout.Reforged) { return ""; } return ".r"; } } public event Action Changed; public static Progress Get(Player p) { if ((Object)(object)p == (Object)null) { return null; } if (!cache.TryGetValue(p, out var value)) { foreach (Player item in cache.Keys.Where((Player k) => (Object)(object)k == (Object)null).ToList()) { cache.Remove(item); } value = new Progress(p); value.Load(); cache[p] = value; } return value; } public static Progress Peek(Player p) { if (!((Object)(object)p != (Object)null) || !cache.TryGetValue(p, out var value)) { return null; } return value; } public static void Forget(Player p) { if ((Object)(object)p != (Object)null) { cache.Remove(p); } } private Progress(Player p) { player = p; } public void AddXp(float amount, string source = null, bool announce = false) { if (!(amount <= 0f) && !float.IsNaN(amount)) { amount *= Plugin.XpMultiplier.Value * (1f + Stats.Get(Stat.XpGainPct) + (Effects.Night ? Stats.Get(Stat.NightXpPct) : 0f)); Xp += amount; if (announce && Plugin.ShowXpMessages.Value && amount >= 1f) { ((Character)player).Message((MessageType)1, string.Format("+{0:0} {1}", amount, L.T("опыта")) + (string.IsNullOrEmpty(source) ? "" : (" (" + Localization.instance.Localize(source) + ")")), 0, (Sprite)null, false); } bool flag = false; while (Level < Plugin.MaxLevel.Value && Xp >= XpToNext) { Xp -= XpToNext; Level++; flag = true; } if (Level >= Plugin.MaxLevel.Value) { Xp = Mathf.Min(Xp, XpToNext); } if (flag) { Sfx.LevelUp(); Hugin.OnLevelUp(player); ((Character)player).Message((MessageType)2, string.Format(L.T("Уровень {0}! Очков доступно: {1}"), Level, AvailablePoints), 0, (Sprite)null, false); WriteTo(); } this.Changed?.Invoke(); } } public void OnDeath() { float num = Mathf.Clamp01(Plugin.DeathXpLossPct.Value); if (!(num <= 0f)) { Xp *= 1f - num; ((Character)player).Message((MessageType)1, string.Format(L.T("Потеряно {0}% опыта текущего уровня"), (num * 100f).ToString("0")), 0, (Sprite)null, false); WriteTo(); this.Changed?.Invoke(); } } public bool IsAllocated(string id) { return Allocated.Contains(id); } public bool CanAllocate(string id) { if (!Tree.Nodes.TryGetValue(id, out var value) || Allocated.Contains(id) || AvailablePoints <= 0) { return false; } if (GateBlocked(id)) { return false; } foreach (string link in value.Links) { if (Allocated.Contains(link)) { return true; } } return false; } public bool GateBlocked(string id) { if (Tree.IsGate(id) && !Allocated.Contains(id)) { return GatesTaken >= GateLimit; } return false; } public bool Allocate(string id) { if (!CanAllocate(id)) { return false; } if (Tree.Nodes[id].IsMastery && !Choices.ContainsKey(id)) { Choices[id] = 0; } Allocated.Add(id); AfterChange(); return true; } public int ChoiceOf(string id) { if (!Choices.TryGetValue(id, out var value)) { return 0; } return value; } public bool Choose(string id, int index) { if (!Tree.Nodes.TryGetValue(id, out var value) || !value.IsMastery) { return false; } index = Mathf.Clamp(index, 0, value.Options.Count - 1); if (!Allocated.Contains(id)) { if (!CanAllocate(id)) { return false; } Choices[id] = index; Allocated.Add(id); AfterChange(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Mastery {id}: taken with option {index}"); } return true; } if (ChoiceOf(id) == index) { return false; } Choices[id] = index; AfterChange(); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"Mastery {id}: switched to option {index}"); } return true; } public bool CycleChoice(string id) { if (!Tree.Nodes.TryGetValue(id, out var value) || !value.IsMastery || !Allocated.Contains(id)) { return false; } return Choose(id, (ChoiceOf(id) + 1) % value.Options.Count); } private static string CoinName() { ItemDrop val = (((Object)(object)StoreGui.instance != (Object)null) ? StoreGui.instance.m_coinPrefab : null); if ((Object)(object)val == (Object)null) { GameObject val2 = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab("Coins") : null); val = (((Object)(object)val2 != (Object)null) ? val2.GetComponent() : null); } if (!((Object)(object)val != (Object)null)) { return "$item_coins"; } return val.m_itemData.m_shared.m_name; } public int Coins() { Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null); if (val == null) { return 0; } return val.CountItems(CoinName(), -1, true); } public bool PayCoins(int amount) { if (amount <= 0) { return true; } Inventory val = (((Object)(object)player != (Object)null) ? ((Humanoid)player).GetInventory() : null); if (val == null || val.CountItems(CoinName(), -1, true) < amount) { Player obj = player; if (obj != null) { ((Character)obj).Message((MessageType)2, string.Format(L.T("Нужно {0} монет, чтобы вернуть очки"), amount), 0, (Sprite)null, false); } return false; } val.RemoveItem(CoinName(), amount, -1, true); return true; } public bool CanDeallocate(string id) { if (id == "root" || !Allocated.Contains(id)) { return false; } HashSet hashSet = new HashSet { "root" }; Queue queue = new Queue(); queue.Enqueue("root"); while (queue.Count > 0) { foreach (string link in Tree.Nodes[queue.Dequeue()].Links) { if (link != id && Allocated.Contains(link) && hashSet.Add(link)) { queue.Enqueue(link); } } } return hashSet.Count == Allocated.Count - 1; } public bool Deallocate(string id) { if (!CanDeallocate(id) || !PayCoins(PointCost)) { return false; } Allocated.Remove(id); Choices.Remove(id); AfterChange(); return true; } public bool RemainsConnected(ICollection removing) { HashSet hashSet = new HashSet { "root" }; Queue queue = new Queue(); queue.Enqueue("root"); while (queue.Count > 0) { foreach (string link in Tree.Nodes[queue.Dequeue()].Links) { if (Allocated.Contains(link) && !removing.Contains(link) && hashSet.Add(link)) { queue.Enqueue(link); } } } int num = Allocated.Count - removing.Count((string r) => Allocated.Contains(r) && r != "root"); return hashSet.Count == num; } public int TrimToBudget() { if (SpentPoints <= TotalPoints) { return 0; } Node value; List list = (from id in Allocated where id != "root" orderby (!Tree.Nodes.TryGetValue(id, out value)) ? 0f : ((Vector2)(ref value.Pos)).magnitude descending select id).ToList(); int num = 0; List list2 = new List { null }; while (SpentPoints > TotalPoints) { string text = null; for (int num2 = 0; num2 < list.Count; num2++) { if (Allocated.Contains(list[num2])) { list2[0] = list[num2]; if (RemainsConnected(list2)) { text = list[num2]; break; } } } if (text == null) { break; } Allocated.Remove(text); Choices.Remove(text); num++; } if (num > 0) { TrimmedByBudget += num; } return num; } public bool ClampToMaxLevel() { int num = Mathf.Max(1, Plugin.MaxLevel.Value); if (Level <= num) { return false; } Level = num; Xp = 0f; return true; } public void ApplyBudgetSettings() { bool flag = ClampToMaxLevel(); if (TrimToBudget() > 0) { flag = true; } if (flag) { AfterChange(); } } public int RefundCost(int count) { return PointCost * Mathf.Max(0, count); } public bool DeallocateSet(ICollection ids) { List list = ids.Where((string i) => i != "root" && Allocated.Contains(i)).ToList(); if (list.Count == 0 || !RemainsConnected(list)) { return false; } if (!PayCoins(RefundCost(list.Count))) { return false; } foreach (string item in list) { Allocated.Remove(item); Choices.Remove(item); } AfterChange(); return true; } public bool ResetTree() { if (!PayCoins(PointCost * SpentPoints)) { return false; } Allocated.Clear(); Choices.Clear(); Allocated.Add("root"); AfterChange(); return true; } public void ResetTreeFree() { Allocated.Clear(); Choices.Clear(); Allocated.Add("root"); AfterChange(); } public void ResetAll() { Level = 1; Xp = 0f; BonusPoints = 0; ResetTreeFree(); } public void AddBonusPoints(int n) { BonusPoints += n; AfterChange(); } private void AfterChange() { RecomputeStats(); WriteTo(); Effects.Refresh(player); this.Changed?.Invoke(); } public void RecomputeStats() { Stats.Clear(); foreach (string item in Allocated) { if (Tree.Nodes.TryGetValue(item, out var value)) { Stats.AddAll(value.Mods); if (value.IsMastery) { Stats.AddAll(value.Options[Mathf.Clamp(ChoiceOf(item), 0, value.Options.Count - 1)].Mods); } } } DeriveAttributes(); } private void DeriveAttributes() { float num = Stats.Get(Stat.AttrMight); float num2 = Stats.Get(Stat.AttrFinesse); float num3 = Stats.Get(Stat.AttrSpirit); if (num > 0f) { Stats.Add(Stat.MaxHealthFlat, num * 0.7f); Stats.Add(Stat.CarryWeightFlat, num * 1f); } if (num2 > 0f) { Stats.Add(Stat.StaminaRegenPct, num2 * 0.0025f); Stats.Add(Stat.MoveSpeedPct, num2 * 0.0006f); } if (num3 > 0f) { Stats.Add(Stat.MaxEitrFlat, num3 * 0.5f); Stats.Add(Stat.BuffDurationPct, num3 * 0.0025f); } } public void Load() { Dictionary customData = player.m_customData; if (customData.TryGetValue("pt.level", out var value) && int.TryParse(value, out var result)) { Level = Mathf.Max(1, result); } if (customData.TryGetValue("pt.xp", out value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { Xp = Mathf.Max(0f, result2); } if (customData.TryGetValue("pt.bonus", out value) && int.TryParse(value, out var result3)) { BonusPoints = result3; } Allocated.Clear(); Choices.Clear(); Allocated.Add("root"); if (Tree.Nodes.Count == 0) { Tree.Build(); } RefundedByUpdate = 0; string value2; int result4; bool flag = ((!customData.TryGetValue("pt.ver" + Suffix, out value2) || !int.TryParse(value2, out result4)) ? 1 : result4) != 1; if (customData.TryGetValue("pt.nodes" + Suffix, out value) && !string.IsNullOrEmpty(value) && !flag) { string[] array = value.Split(new char[1] { ',' }); foreach (string text in array) { if (!string.IsNullOrEmpty(text)) { int num = text.LastIndexOf(':'); string text2 = ((num > 0) ? text.Substring(0, num) : text); int result5; if (!Tree.Nodes.TryGetValue(text2, out var value3)) { RefundedByUpdate++; } else if (num > 0 && int.TryParse(text.Substring(num + 1), out result5) && result5 != value3.Sig) { RefundedByUpdate++; } else { Allocated.Add(text2); } } } } else if (flag && !string.IsNullOrEmpty(value)) { RefundedByUpdate = value.Split(new char[1] { ',' }).Length; } if (customData.TryGetValue("pt.opts" + Suffix, out value) && !string.IsNullOrEmpty(value)) { string[] array = value.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length == 2 && Tree.Nodes.ContainsKey(array2[0]) && int.TryParse(array2[1], out var result6)) { Choices[array2[0]] = result6; } } } int count = Allocated.Count; PruneOrphans(); RefundedByUpdate += count - Allocated.Count; if (RefundedByUpdate > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"Дерево обновилось: снято узлов {RefundedByUpdate}, очки возвращены"); } } ClampToMaxLevel(); TrimToBudget(); RecomputeStats(); } private void PruneOrphans() { if (Tree.Nodes.Count == 0) { return; } HashSet hashSet = new HashSet { "root" }; Queue queue = new Queue(); queue.Enqueue("root"); while (queue.Count > 0) { foreach (string link in Tree.Nodes[queue.Dequeue()].Links) { if (Allocated.Contains(link) && hashSet.Add(link)) { queue.Enqueue(link); } } } if (hashSet.Count == Allocated.Count) { return; } int num = Allocated.Count - hashSet.Count; Allocated.IntersectWith(hashSet); foreach (string item in Choices.Keys.Where((string k) => !Allocated.Contains(k)).ToList()) { Choices.Remove(item); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"Allocation repaired: {num} node(s) were no longer connected and their points are back"); } } public void WriteTo() { if (Tree.Nodes.Count != 0) { Dictionary customData = player.m_customData; customData["pt.level"] = Level.ToString(); customData["pt.xp"] = Xp.ToString("R", CultureInfo.InvariantCulture); customData["pt.bonus"] = BonusPoints.ToString(); customData["pt.ver" + Suffix] = 1.ToString(); customData["pt.nodes" + Suffix] = string.Join(",", from i in Allocated where i != "root" select i + ":" + Tree.Nodes[i].Sig); customData["pt.opts" + Suffix] = string.Join(",", from c in Choices where Allocated.Contains(c.Key) select c.Key + ":" + c.Value); } } } public class ProjMods : MonoBehaviour { public int Pierce; public int Chain; public int Split; public GameObject Ignore; public float DamageMult = 1f; public bool Handled; } public static class ProjectileMods { [HarmonyPatch(typeof(Attack), "Start")] private static class Attack_Start { private static void Postfix(Attack __instance, Humanoid character, ItemData weapon, bool __result) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (__result && IsLocal((Character)(object)character) && weapon != null && !((Object)(object)__instance.m_attackProjectile == (Object)null) && IsRangedSkill(weapon.m_shared.m_skillType)) { StatSet s = S; int num = Mathf.RoundToInt(s.Get(Stat.ExtraProjectiles)); if (num > 0) { __instance.m_projectiles += num; __instance.m_projectileAccuracy = Mathf.Max(__instance.m_projectileAccuracy, 5f); __instance.m_projectileAccuracyMin = Mathf.Max(__instance.m_projectileAccuracyMin, 5f); } float num2 = 1f + s.Get(Stat.ProjectileSpeedPct); __instance.m_projectileVel *= num2; __instance.m_projectileVelMin *= num2; } } } [HarmonyPatch(typeof(Humanoid), "GetAttackDrawPercentage")] private static class Humanoid_GetAttackDrawPercentage { private static void Postfix(Humanoid __instance, ref float __result) { if (!(__result <= 0f) && !(__result >= 1f) && IsLocal((Character)(object)__instance)) { __result = Mathf.Clamp01(__result * (1f + S.Get(Stat.DrawSpeedPct))); } } } [HarmonyPatch(typeof(ItemData), "GetDrawStaminaDrain")] private static class ItemData_GetDrawStaminaDrain { private static void Postfix(ref float __result) { if (__result > 0f && S != null) { __result *= Mathf.Max(0f, 1f + S.Get(Stat.DrawStaminaPct)); } } } [HarmonyPatch(typeof(Attack), "UseAmmo")] private static class Attack_UseAmmo { private static void Prefix(Attack __instance) { usingAmmo = IsLocal((Character)(object)__instance.m_character); } private static void Postfix() { usingAmmo = false; } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(ItemData), typeof(int) })] private static class Inventory_RemoveItem { private static bool Prefix(ref bool __result) { if (!usingAmmo) { return true; } float num = S.Get(Stat.ArrowSaveChance); if (num <= 0f || Random.value >= num) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(Projectile), "Setup")] private static class Projectile_Setup { private static void Postfix(Projectile __instance, Character owner, HitData hitData) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (IsLocal(owner) && hitData != null && IsRangedSkill(hitData.m_skill) && !((Object)(object)((Component)__instance).GetComponent() != (Object)null)) { StatSet s = S; int num = Mathf.RoundToInt(s.Get(Stat.ProjectilePierce)); int num2 = Mathf.RoundToInt(s.Get(Stat.ProjectileChain)); int num3 = Mathf.RoundToInt(s.Get(Stat.ProjectileSplit)); if (num > 0 || num2 > 0 || num3 > 0) { ProjMods projMods = ((Component)__instance).gameObject.AddComponent(); projMods.Pierce = num; projMods.Chain = num2; projMods.Split = num3; } } } } [HarmonyPatch(typeof(Projectile), "OnHit")] private static class Projectile_OnHit { private static bool Prefix(Projectile __instance, Collider collider) { ProjMods component = ((Component)__instance).GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component.Ignore == (Object)null || (Object)(object)collider == (Object)null) { return true; } return (Object)(object)Projectile.FindHitObject(collider) != (Object)(object)component.Ignore; } private static void Postfix(Projectile __instance, Collider collider, Vector3 hitPoint, bool water) { //IL_008d: 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_0092: 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_00b6: 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_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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) ProjMods component = ((Component)__instance).GetComponent(); if ((Object)(object)component == (Object)null || component.Handled || water || (Object)(object)collider == (Object)null) { return; } GameObject val = Projectile.FindHitObject(collider); Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)__instance.m_owner || val2.IsPlayer()) { return; } component.Handled = true; Vector3 val3 = ((((Vector3)(ref __instance.m_vel)).sqrMagnitude > 0.01f) ? ((Vector3)(ref __instance.m_vel)).normalized : ((Component)__instance).transform.forward); float num = Mathf.Max(((Vector3)(ref __instance.m_vel)).magnitude, 20f); if (component.Pierce > 0) { Spawn(__instance, component, hitPoint + val3 * 0.7f, val3 * num, ((Component)val2).gameObject, component.Pierce - 1, component.Chain, component.Split, component.DamageMult); } else if (component.Split > 0) { for (int i = 0; i < component.Split + 1; i++) { Vector3 val4 = Quaternion.AngleAxis(((float)i - (float)component.Split * 0.5f) * 24f, Vector3.up) * val3; Spawn(__instance, component, hitPoint + val4 * 0.7f, val4 * num, ((Component)val2).gameObject, 0, component.Chain, 0, component.DamageMult * 0.6f); } } else { if (component.Chain <= 0) { return; } Character val5 = null; float num2 = 196f; Vector3 val6; foreach (Character allCharacter in Character.GetAllCharacters()) { if (!((Object)(object)allCharacter == (Object)null) && !((Object)(object)allCharacter == (Object)(object)val2) && !((Object)(object)allCharacter == (Object)(object)__instance.m_owner) && !allCharacter.IsPlayer() && !allCharacter.IsDead() && !allCharacter.IsTamed()) { val6 = allCharacter.GetCenterPoint() - hitPoint; float sqrMagnitude = ((Vector3)(ref val6)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; val5 = allCharacter; } } } if ((Object)(object)val5 != (Object)null) { val6 = val5.GetCenterPoint() - hitPoint; Vector3 normalized = ((Vector3)(ref val6)).normalized; Spawn(__instance, component, hitPoint + normalized * 0.7f, normalized * num, ((Component)val2).gameObject, 0, component.Chain - 1, 0, component.DamageMult * 0.75f); } } } private static void Spawn(Projectile src, ProjMods pm, Vector3 pos, Vector3 vel, GameObject ignore, int pierce, int chain, int split, float mult) { //IL_0048: 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) //IL_0050: 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_0086: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return; } string text = ((Object)((Component)src).gameObject).name; int num = text.IndexOf("(Clone)"); if (num >= 0) { text = text.Substring(0, num); } GameObject prefab = instance.GetPrefab(text); if ((Object)(object)prefab == (Object)null) { return; } GameObject val = Object.Instantiate(prefab, pos, Quaternion.LookRotation(((Vector3)(ref vel)).normalized)); Projectile component = val.GetComponent(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val); return; } HitData originalHitData = src.m_originalHitData; HitData val2 = new HitData(); val2.m_damage = ((DamageTypes)(ref src.m_damage)).Clone(); ((DamageTypes)(ref val2.m_damage)).Modify(mult); val2.m_pushForce = src.m_attackForce; val2.m_backstabBonus = src.m_backstabBonus; val2.m_statusEffectHash = src.m_statusEffectHash; val2.m_skill = src.m_skill; val2.m_skillRaiseAmount = 0f; val2.m_dodgeable = src.m_dodgeable; val2.m_blockable = src.m_blockable; val2.m_hitType = src.m_hitType; val2.m_ranged = true; if (originalHitData != null) { val2.m_toolTier = originalHitData.m_toolTier; val2.m_itemLevel = originalHitData.m_itemLevel; val2.m_skillLevel = originalHitData.m_skillLevel; val2.m_variant = originalHitData.m_variant; } val2.SetAttacker(src.m_owner); ProjMods projMods = val.AddComponent(); projMods.Pierce = pierce; projMods.Chain = chain; projMods.Split = split; projMods.Ignore = ignore; projMods.DamageMult = mult; component.Setup(src.m_owner, vel, src.m_hitNoise, val2, src.m_weapon, src.m_ammo); } } private static bool usingAmmo; private static StatSet S => Progress.Current?.Stats; private static bool IsLocal(Character c) { if ((Object)(object)c != (Object)null) { return (Object)(object)c == (Object)(object)Player.m_localPlayer; } return false; } private static bool IsRangedSkill(SkillType t) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 if ((int)t != 8 && (int)t != 14) { return (int)t == 5; } return true; } } public static class Slide { [HarmonyPatch(typeof(Player), "FixedUpdate")] private static class Player_FixedUpdate_Slide { private static void Postfix(Player __instance) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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_00be: 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_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_0352: 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; } StatSet stats = Progress.Current.Stats; if (!stats.Has(Stat.Slide) || ((Character)__instance).IsDead() || ((Character)__instance).IsSwimming() || ((Character)__instance).IsAttached() || ((Character)__instance).InBed()) { if (Sliding) { Stop(); } return; } if (!__instance.m_crouchToggled && !((Character)__instance).IsCrouching()) { if (Sliding) { Stop(); } return; } float fixedDeltaTime = Time.fixedDeltaTime; bool flag = ((Character)__instance).IsOnGround(); Vector3 lastGroundNormal = ((Character)__instance).m_lastGroundNormal; float num = SlopeOf(lastGroundNormal); Vector3 val = Downhill(lastGroundNormal); Vector3 moveDir = ((Character)__instance).m_moveDir; moveDir.y = 0f; bool flag2 = ((Vector3)(ref moveDir)).sqrMagnitude > 0.01f && val != Vector3.zero && Vector3.Dot(((Vector3)(ref moveDir)).normalized, val) >= 0.15f; idleTime = (flag2 ? 0f : (idleTime + fixedDeltaTime)); float num2 = Mathf.Clamp(((Character)__instance).m_runSpeed * ((Character)__instance).GetRunSpeedFactor() * (1f + stats.Get(Stat.MoveSpeedPct)), 3f, 12f); float num3 = num2 * 1.25f; float num4 = Mathf.Clamp(num2 * 2f, 14f, 22f); if (!Sliding) { if (!flag || !flag2 || num < 22f) { return; } Vector3 linearVelocity = ((Character)__instance).m_body.linearVelocity; linearVelocity.y = 0f; speed = Mathf.Max(((Vector3)(ref linearVelocity)).magnitude, num3); Sliding = true; airTime = 0f; idleTime = 0f; } if (idleTime > 0.3f) { Stop(); return; } if (flag) { airTime = 0f; } else { airTime += fixedDeltaTime; } if (airTime > 0.45f) { Stop(); return; } Vector3 linearVelocity2 = ((Character)__instance).m_body.linearVelocity; Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(linearVelocity2.x, 0f, linearVelocity2.z); Vector3 val3 = ((((Vector3)(ref val2)).sqrMagnitude > 0.01f) ? ((Vector3)(ref val2)).normalized : val); if (val3 == Vector3.zero) { Stop(); return; } if (val != Vector3.zero) { val3 = Vector3.RotateTowards(val3, val, 0.9f * fixedDeltaTime, 0f); } if (flag2) { val3 = Vector3.RotateTowards(val3, ((Vector3)(ref moveDir)).normalized, 2.2f * fixedDeltaTime, 0f); } ((Vector3)(ref val3)).Normalize(); if (flag) { float num5 = ((val == Vector3.zero) ? 0f : Vector3.Dot(val3, val)); speed += 9.81f * Mathf.Sin(num * ((float)Math.PI / 180f)) * 1.6f * num5 * fixedDeltaTime; speed -= speed * speed * 0.02f * fixedDeltaTime; if (num < 16f) { speed -= 6f * fixedDeltaTime; } else if (num >= 22f) { speed = Mathf.Max(speed, num3); } speed = Mathf.Min(speed, num4); if (speed < 2.5f) { Stop(); return; } ((Character)__instance).m_maxAirAltitude = ((Component)__instance).transform.position.y; } linearVelocity2.x = val3.x * speed; linearVelocity2.z = val3.z * speed; ((Character)__instance).m_body.linearVelocity = linearVelocity2; ((Character)__instance).m_sliding = true; ((Character)__instance).m_zanim.SetFloat(ForwardSpeed, 0f); ((Character)__instance).m_zanim.SetFloat(SidewaySpeed, 0f); } } public static bool Sliding; private const float StartSlope = 22f; private const float MinSlope = 16f; private const float StopSpeed = 2.5f; private const float Drag = 0.02f; private const float Pull = 1.6f; private const float Steer = 2.2f; private const float Fall = 0.9f; private const float MaxAir = 0.45f; private const float SprintEdge = 1.25f; private const float DownhillDot = 0.15f; private const float LetGo = 0.3f; private static float speed; private static float airTime; private static float idleTime; private static readonly int ForwardSpeed = ZSyncAnimation.GetHash("forward_speed"); private static readonly int SidewaySpeed = ZSyncAnimation.GetHash("sideway_speed"); private static float SlopeOf(Vector3 n) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Mathf.Acos(Mathf.Clamp01(n.y)) * 57.29578f; } private static Vector3 Downhill(Vector3 n) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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) Vector3 val = Vector3.Cross(n, Vector3.Cross(n, Vector3.up)); val.y = 0f; if (!(((Vector3)(ref val)).sqrMagnitude > 1E-06f)) { return Vector3.zero; } return ((Vector3)(ref val)).normalized; } private static void Stop() { Sliding = false; speed = 0f; airTime = 0f; idleTime = 0f; } } public enum Stat { MaxHealthFlat, MaxHealthPct, MaxStaminaFlat, MaxStaminaPct, MaxEitrFlat, MaxEitrPct, HealthRegenPct, StaminaRegenPct, EitrRegenPct, HealthOnKill, StaminaOnKill, EitrOnKill, DamagePct, PhysicalDamagePct, ElementalDamagePct, MeleeDamagePct, RangedDamagePct, ChopDamagePct, PickaxeDamagePct, AttackSpeedPct, StunChance, OnslaughtChance, OnslaughtPowerPct, BuffDurationPct, StatusDurationPct, CritChance, CritDamagePct, GatherCritChance, CritRecentDamagePct, CritRecentAttackSpeedPct, CritRecentMoveSpeedPct, CritRecentStaminaRegenPct, DamageVsBossPct, ExecutePct, FullHpDamagePct, NightDamagePct, RestedDamagePct, BackstabPct, IgniteChance, FrostChance, PoisonChance, ShockChance, BurnDamagePct, PoisonDamagePct, FrostPotencyPct, ShockDamagePct, AilmentDurationPct, DamageVsUndeadPct, DamageVsForestPct, DamageVsAnimalsPct, DamageVsDvergrPct, DamageVsPlainsPct, DamageVsMountainPct, DamageVsSeaPct, DamageVsMistlandsPct, DamageVsBurningPct, DamageVsPoisonedPct, DamageVsFrozenPct, DamageVsStaggeredPct, DamageVsWetPct, StaggerDamagePct, KnockbackPct, LifestealPct, BerserkPct, ExtraProjectiles, ProjectilePierce, ProjectileChain, ProjectileSplit, ProjectileSpeedPct, DrawSpeedPct, DrawStaminaPct, ArrowSaveChance, Dmg_Pickaxes, Spd_Pickaxes, ChopBurnPct, MineCrackPct, BurnDurationPct, PoisonDurationPct, FrostDurationPct, ShockStunChance, AilmentResistPct, FoodWhileBurningPct, EitrOnIgnite, WetShockPct, NightMoveSpeedPct, NightRegenPct, NightStealthPct, NightGatherPct, NightBuildPct, NightArmorFlat, NightEitrRegenPct, NightXpPct, Dmg_Swords, Dmg_Knives, Dmg_Clubs, Dmg_Polearms, Dmg_Spears, Dmg_Axes, Dmg_Unarmed, Dmg_Bows, Dmg_Crossbows, Dmg_ElementalMagic, Dmg_BloodMagic, Spd_Swords, Spd_Knives, Spd_Clubs, Spd_Polearms, Spd_Spears, Spd_Axes, Spd_Unarmed, ArmorFlat, ArmorPct, DamageTakenPct, FireResistPct, FrostResistPct, PoisonResistPct, LightningResistPct, ColdResistPct, WetResistPct, DryingPct, BlockPowerPct, ParryBonusPct, StaggerResistPct, DodgeChance, ThornsPct, UnarmedDamageTakenPct, MoveSpeedPct, JumpPct, SwimSpeedPct, FallDamagePct, RunStaminaPct, JumpStaminaPct, AttackStaminaPct, BlockStaminaPct, DodgeStaminaPct, SwimStaminaPct, SneakStaminaPct, ToolStaminaPct, EitrCostPct, NoisePct, StealthPct, CarryWeightFlat, ExploreRadiusPct, PickupRangePct, TamingSpeedPct, FeedDurationPct, BreedChancePct, TamedDamagePct, HunterDropChance, ArrowRangePct, AccuracyPct, BuildRangeFlat, ComfortFlat, ExtraHarvestChance, ExtraDropChance, MassPickupRadius, BuildDurabilityPct, FoodPowerPct, PotionDurationPct, SailSpeedPct, SailControlPct, CartPullPct, PavedSpeedPct, BuildRefundChance, FoodDurationPct, RestedDurationPct, XpGainPct, GrowSpeedPct, CropYieldChance, BombDamagePct, BlastRadiusPct, BombSaveChance, DoubleBlastChance, BombMine, BombCraftRefundChance, ExtraBombChance, FishBiteChance, FishStaminaPct, ExtraFishChance, CraftRefundChance, CraftDurabilityPct, SmeltSpeedPct, CookSpeedPct, FermentSpeedPct, ExtraBarChance, NoOvercook, AttrMight, AttrFinesse, AttrSpirit, DoubleJump, Glide, SafeFall, UnarmedGather, UnarmedToolTier, Slide, PlantCrowding } public class StatSet { private readonly Dictionary v = new Dictionary(); public IEnumerable> All => v; public float Get(Stat s) { if (!v.TryGetValue(s, out var value)) { return 0f; } return value; } public bool Has(Stat s) { return Get(s) > 0f; } public void Add(Stat s, float f) { v[s] = Get(s) + f; } public void AddAll(IEnumerable> mods) { foreach (KeyValuePair mod in mods) { Add(mod.Key, mod.Value); } } public void Clear() { v.Clear(); } } public static class StatText { private class Info { public string Fmt; public bool Pct; public bool Flag; public bool Invert; public Info(string f, bool p, bool fl = false, bool inv = false) { Fmt = f; Pct = p; Flag = fl; Invert = inv; } } private static readonly Dictionary I = new Dictionary { { Stat.MaxHealthFlat, F("{0} к максимуму здоровья") }, { Stat.MaxHealthPct, P("{0} к максимуму здоровья") }, { Stat.MaxStaminaFlat, F("{0} к максимуму выносливости") }, { Stat.MaxStaminaPct, P("{0} к максимуму выносливости") }, { Stat.MaxEitrFlat, F("{0} к максимуму эйтра") }, { Stat.MaxEitrPct, P("{0} к максимуму эйтра") }, { Stat.HealthRegenPct, P("{0} к регенерации здоровья") }, { Stat.StaminaRegenPct, P("{0} к регенерации выносливости") }, { Stat.EitrRegenPct, P("{0} к регенерации эйтра") }, { Stat.HealthOnKill, F("{0} здоровья за убийство") }, { Stat.StaminaOnKill, F("{0} выносливости за убийство") }, { Stat.EitrOnKill, F("{0} эйтра за убийство") }, { Stat.DamagePct, P("{0} к урону") }, { Stat.PhysicalDamagePct, P("{0} к физическому урону") }, { Stat.ElementalDamagePct, P("{0} к стихийному урону") }, { Stat.MeleeDamagePct, P("{0} к урону в ближнем бою") }, { Stat.RangedDamagePct, P("{0} к дальнему урону") }, { Stat.ChopDamagePct, P("{0} к урону по деревьям") }, { Stat.PickaxeDamagePct, P("{0} к урону киркой") }, { Stat.StunChance, P("{0} шанс оглушить цель ударом") }, { Stat.OnslaughtChance, P("{0} шанс получить Натиск при убийстве: +15% к скорости атаки, передвижения и восстановления сил на 12 с") }, { Stat.OnslaughtPowerPct, P("{0} к силе Натиска") }, { Stat.BuffDurationPct, P("{0} к длительности собственных усилений") }, { Stat.StatusDurationPct, P("{0} к длительности всех состояний: и наложенных врагу, и своих") }, { Stat.Dmg_Pickaxes, P("{0} к урону киркой по врагам") }, { Stat.Spd_Pickaxes, P("{0} к скорости ударов киркой") }, { Stat.ChopBurnPct, P("{0} к урону по дереву, если рубишь без передышки") }, { Stat.MineCrackPct, P("{0} к урону по руде, если копаешь без передышки") }, { Stat.BurnDurationPct, P("{0} к длительности горения") }, { Stat.PoisonDurationPct, P("{0} к длительности яда") }, { Stat.FrostDurationPct, P("{0} к длительности заморозки") }, { Stat.ShockStunChance, P("{0} шанс оглушить цель, поражённую молнией") }, { Stat.AilmentResistPct, new Info("{0} к длительности горения, яда и мороза на тебе", p: true, fl: false, inv: true) }, { Stat.FoodWhileBurningPct, P("{0} к силе еды, пока ты горишь") }, { Stat.EitrOnIgnite, F("{0} эйтра за поджог врага") }, { Stat.WetShockPct, P("{0} к урону молнией, пока ты мокрый") }, { Stat.NightMoveSpeedPct, P("{0} к скорости передвижения ночью") }, { Stat.NightRegenPct, P("{0} к регенерации здоровья и выносливости ночью") }, { Stat.NightStealthPct, P("{0} к скрытности ночью") }, { Stat.NightGatherPct, P("{0} к урону по дереву и руде ночью") }, { Stat.NightBuildPct, P("{0} к шансу сберечь ресурсы при постройке ночью") }, { Stat.NightArmorFlat, F("{0} к броне ночью") }, { Stat.NightEitrRegenPct, P("{0} к регенерации эйтра ночью") }, { Stat.NightXpPct, P("{0} к получаемому опыту ночью") }, { Stat.AttackSpeedPct, P("{0} к скорости атаки") }, { Stat.CritChance, P("{0} шанс критического удара (×1.5 урона)") }, { Stat.CritDamagePct, P("{0} к урону критических ударов") }, { Stat.GatherCritChance, P("{0} шанс крита по деревьям и руде") }, { Stat.CritRecentDamagePct, P("{0} к урону в течение 4 с после крита") }, { Stat.CritRecentAttackSpeedPct, P("{0} к скорости атаки в течение 4 с после крита") }, { Stat.CritRecentMoveSpeedPct, P("{0} к скорости передвижения в течение 4 с после крита") }, { Stat.CritRecentStaminaRegenPct, P("{0} к регенерации выносливости в течение 4 с после крита") }, { Stat.DamageVsBossPct, P("{0} к урону по боссам") }, { Stat.ExecutePct, P("{0} к урону по врагам ниже 30% здоровья") }, { Stat.FullHpDamagePct, P("{0} к урону при полном здоровье") }, { Stat.NightDamagePct, P("{0} к урону ночью") }, { Stat.RestedDamagePct, P("{0} к урону с бонусом «Отдохнувший»") }, { Stat.BackstabPct, P("{0} к урону скрытных атак") }, { Stat.ExtraProjectiles, F("{0} к числу снарядов при выстреле") }, { Stat.ProjectilePierce, F("{0} к числу целей, которые пронзает снаряд") }, { Stat.ProjectileChain, F("{0} к числу отскоков снаряда к ближайшему врагу") }, { Stat.ProjectileSplit, F("{0} к числу снарядов, на которые разделяется стрела при попадании") }, { Stat.ProjectileSpeedPct, P("{0} к скорости снарядов") }, { Stat.DrawSpeedPct, P("{0} к скорости натяжения") }, { Stat.DrawStaminaPct, P("{0} к расходу выносливости при натяжении") }, { Stat.ArrowSaveChance, P("{0} шанс не потратить стрелу") }, { Stat.IgniteChance, P("{0} шанс поджечь при ударе (горение на 50% физического урона)") }, { Stat.FrostChance, P("{0} шанс заморозить при ударе (мороз на 40% физического урона)") }, { Stat.PoisonChance, P("{0} шанс отравить при ударе (яд на 60% физического урона)") }, { Stat.ShockChance, P("{0} шанс ударить молнией при ударе (30% физического урона, оглушение)") }, { Stat.BurnDamagePct, P("{0} к урону горением") }, { Stat.PoisonDamagePct, P("{0} к урону ядом") }, { Stat.FrostPotencyPct, P("{0} к силе и длительности заморозки") }, { Stat.ShockDamagePct, P("{0} к урону молнией") }, { Stat.AilmentDurationPct, P("{0} к длительности горения, яда и мороза") }, { Stat.DamageVsUndeadPct, P("{0} к урону по нежити") }, { Stat.DamageVsForestPct, P("{0} к урону по тварям Чёрного леса") }, { Stat.DamageVsAnimalsPct, P("{0} к урону по зверям") }, { Stat.DamageVsDvergrPct, P("{0} к урону по двергам") }, { Stat.DamageVsPlainsPct, P("{0} к урону по фулингам и обитателям равнин") }, { Stat.DamageVsMountainPct, P("{0} к урону по обитателям гор") }, { Stat.DamageVsSeaPct, P("{0} к урону по морским тварям") }, { Stat.DamageVsMistlandsPct, P("{0} к урону по обитателям Мгливых земель") }, { Stat.DamageVsBurningPct, P("{0} к урону по горящим целям") }, { Stat.DamageVsPoisonedPct, P("{0} к урону по отравленным целям") }, { Stat.DamageVsFrozenPct, P("{0} к урону по замороженным целям") }, { Stat.DamageVsStaggeredPct, P("{0} к урону по оглушённым целям") }, { Stat.DamageVsWetPct, P("{0} к урону по мокрым целям") }, { Stat.StaggerDamagePct, P("{0} к оглушающей силе ударов") }, { Stat.KnockbackPct, P("{0} к отбрасыванию") }, { Stat.LifestealPct, P("{0} урона возвращается здоровьем") }, { Stat.BerserkPct, P("{0} к урону за каждые 10% недостающего здоровья") }, { Stat.Dmg_Swords, P("{0} к урону мечами") }, { Stat.Dmg_Knives, P("{0} к урону ножами") }, { Stat.Dmg_Clubs, P("{0} к урону дубинами") }, { Stat.Dmg_Polearms, P("{0} к урону древковым") }, { Stat.Dmg_Spears, P("{0} к урону копьями") }, { Stat.Dmg_Axes, P("{0} к урону топорами") }, { Stat.Dmg_Unarmed, P("{0} к урону кулаками") }, { Stat.Dmg_Bows, P("{0} к урону луками") }, { Stat.Dmg_Crossbows, P("{0} к урону арбалетами") }, { Stat.Dmg_ElementalMagic, P("{0} к урону магии стихий") }, { Stat.Dmg_BloodMagic, P("{0} к урону магии крови") }, { Stat.Spd_Swords, P("{0} к скорости атаки мечами") }, { Stat.Spd_Knives, P("{0} к скорости атаки ножами") }, { Stat.Spd_Clubs, P("{0} к скорости атаки дубинами") }, { Stat.Spd_Polearms, P("{0} к скорости атаки древковым") }, { Stat.Spd_Spears, P("{0} к скорости атаки копьями") }, { Stat.Spd_Axes, P("{0} к скорости атаки топорами") }, { Stat.Spd_Unarmed, P("{0} к скорости атаки кулаками") }, { Stat.ArmorFlat, F("{0} к броне") }, { Stat.ArmorPct, P("{0} к броне") }, { Stat.DamageTakenPct, P("{0} к получаемому урону") }, { Stat.FireResistPct, P("{0} сопротивления огню") }, { Stat.FrostResistPct, P("{0} сопротивления морозу") }, { Stat.PoisonResistPct, P("{0} сопротивления яду") }, { Stat.LightningResistPct, P("{0} сопротивления молнии") }, { Stat.BlockPowerPct, P("{0} к силе блока") }, { Stat.ParryBonusPct, P("{0} к бонусу парирования") }, { Stat.StaggerResistPct, P("{0} к устойчивости к оглушению") }, { Stat.DodgeChance, P("{0} шанс уклониться от удара") }, { Stat.ThornsPct, P("{0} полученного урона возвращается атакующему") }, { Stat.UnarmedDamageTakenPct, P("{0} к получаемому урону, пока сражаешься без оружия") }, { Stat.MoveSpeedPct, P("{0} к скорости передвижения") }, { Stat.JumpPct, P("{0} к силе прыжка") }, { Stat.SwimSpeedPct, P("{0} к скорости плавания") }, { Stat.FallDamagePct, P("{0} к урону от падения") }, { Stat.RunStaminaPct, P("{0} к расходу выносливости на бег") }, { Stat.JumpStaminaPct, P("{0} к расходу выносливости на прыжок") }, { Stat.AttackStaminaPct, P("{0} к расходу выносливости на атаки") }, { Stat.BlockStaminaPct, P("{0} к расходу выносливости на блок") }, { Stat.DodgeStaminaPct, P("{0} к расходу выносливости на уворот") }, { Stat.SwimStaminaPct, P("{0} к расходу выносливости на плавание") }, { Stat.SneakStaminaPct, P("{0} к расходу выносливости в стелсе") }, { Stat.ToolStaminaPct, P("{0} к расходу выносливости молотом и инструментами") }, { Stat.EitrCostPct, P("{0} к расходу эйтра") }, { Stat.NoisePct, P("{0} к шуму") }, { Stat.StealthPct, P("{0} к скрытности") }, { Stat.CarryWeightFlat, F("{0} к грузоподъёмности") }, { Stat.ExploreRadiusPct, P("{0} к радиусу открытия карты") }, { Stat.PickupRangePct, P("{0} к радиусу подбора предметов") }, { Stat.BuildRangeFlat, F("{0} м к дальности строительства") }, { Stat.TamingSpeedPct, P("{0} к скорости приручения животных") }, { Stat.FeedDurationPct, P("{0} к тому, насколько долго животное остаётся сытым") }, { Stat.BreedChancePct, P("{0} к шансу приплода у прирученных животных") }, { Stat.TamedDamagePct, P("{0} к урону твоих прирученных животных") }, { Stat.HunterDropChance, P("{0} шанс добыть лишнюю шкуру или кусок мяса со зверя") }, { Stat.ArrowRangePct, P("{0} к дальности полёта стрел и болтов") }, { Stat.AccuracyPct, P("{0} к точности выстрела") }, { Stat.ExtraHarvestChance, P("{0} шанс сорвать лишний цветок, ягоду или гриб") }, { Stat.GrowSpeedPct, P("{0} к скорости роста посевов") }, { Stat.CropYieldChance, P("{0} шанс снять лишний урожай с грядки") }, { Stat.ExtraDropChance, P("{0} шанс выбить лишнюю древесину, камень или руду") }, { Stat.MassPickupRadius, F("Массовый сбор: одним нажатием собираешь такие же растения в радиусе {0} м") }, { Stat.BuildDurabilityPct, P("{0} к прочности построек, возведённых тобой") }, { Stat.FoodPowerPct, P("{0} к здоровью, силам и эйтру от еды") }, { Stat.PotionDurationPct, P("{0} к длительности мёдов и зелий") }, { Stat.SailSpeedPct, P("{0} к скорости корабля, пока ты за штурвалом") }, { Stat.SailControlPct, P("{0} к управляемости корабля, пока ты за штурвалом") }, { Stat.CartPullPct, P("{0} к лёгкости телеги, которую ты тянешь") }, { Stat.PavedSpeedPct, P("{0} к скорости на мощёной дороге и вспаханной земле") }, { Stat.ComfortFlat, F("{0} к уровню комфорта") }, { Stat.BuildRefundChance, P("{0} шанс не потратить ресурсы при постройке") }, { Stat.FoodDurationPct, P("{0} к длительности еды") }, { Stat.RestedDurationPct, P("{0} к длительности бонуса «Отдохнувший»") }, { Stat.XpGainPct, P("{0} к получаемому опыту") }, { Stat.DoubleJump, Flag("Двойной прыжок: нажми прыжок ещё раз в воздухе") }, { Stat.Glide, Flag("Парение: удерживай прыжок в воздухе, чтобы медленно планировать") }, { Stat.SafeFall, Flag("Урон от падения отключён") }, { Stat.Slide, Flag("Съезд: в приседе на склоне ты едешь вниз быстрее, чем бежишь") }, { Stat.PlantCrowding, Flag("Посевы не спорят за место: грядку можно сажать вплотную") }, { Stat.UnarmedGather, Flag("Кулаки рубят деревья и ломают руду") }, { Stat.UnarmedToolTier, F("{0} к уровню «инструмента» для кулаков") }, { Stat.BombDamagePct, P("{0} к урону бомб") }, { Stat.BombCraftRefundChance, P("{0} шанс не потратить материалы при изготовлении бомб") }, { Stat.ExtraBombChance, P("{0} шанс сделать на одну бомбу больше") }, { Stat.BlastRadiusPct, P("{0} к радиусу взрыва") }, { Stat.BombSaveChance, P("{0} шанс не потратить брошенную бомбу") }, { Stat.DoubleBlastChance, P("{0} шанс на второй взрыв следом за первым") }, { Stat.BombMine, Flag("Брошенная бомба ложится миной и ждёт, пока к ней не подойдёт враг") }, { Stat.FishBiteChance, P("{0} к шансу поклёвки") }, { Stat.FishStaminaPct, P("{0} к расходу выносливости на вываживание рыбы") }, { Stat.ExtraFishChance, P("{0} шанс вытащить лишнюю рыбу") }, { Stat.CraftRefundChance, P("{0} шанс не потратить ресурсы при ковке и улучшении") }, { Stat.CraftDurabilityPct, P("{0} к прочности вещей, сделанных твоими руками") }, { Stat.SmeltSpeedPct, P("{0} к скорости плавки и обжига угля") }, { Stat.CookSpeedPct, P("{0} к скорости готовки") }, { Stat.FermentSpeedPct, P("{0} к скорости брожения") }, { Stat.ExtraBarChance, P("{0} шанс получить лишний слиток из плавильни") }, { Stat.NoOvercook, Flag("Блюда на очаге больше не сгорают") }, { Stat.ColdResistPct, P("{0} к стойкости к холоду: слабее «Холод» и «Замерзание»") }, { Stat.WetResistPct, P("{0} к стойкости к сырости: слабее помеха от «Промокания»") }, { Stat.DryingPct, P("{0} к скорости высыхания") }, { Stat.AttrMight, F("{0} к Мощи — тело: за очко +0.7 здоровья и +1 к переносимому весу") }, { Stat.AttrFinesse, F("{0} к Ловкости — сноровка: за очко +0.25% к восстановлению сил и +0.06% к скорости") }, { Stat.AttrSpirit, F("{0} к Духу — воля: за очко +0.5 эйтра и +0.25% к длительности усилений") } }; private static readonly Dictionary T = new Dictionary { { Stat.MaxHealthFlat, new string[1] { "health" } }, { Stat.MaxHealthPct, new string[1] { "health" } }, { Stat.HealthRegenPct, new string[1] { "health" } }, { Stat.HealthOnKill, new string[1] { "health" } }, { Stat.MaxStaminaFlat, new string[1] { "stamina" } }, { Stat.MaxStaminaPct, new string[1] { "stamina" } }, { Stat.StaminaRegenPct, new string[1] { "stamina" } }, { Stat.StaminaOnKill, new string[1] { "stamina" } }, { Stat.MaxEitrFlat, new string[2] { "eitr", "magic" } }, { Stat.MaxEitrPct, new string[2] { "eitr", "magic" } }, { Stat.EitrRegenPct, new string[2] { "eitr", "magic" } }, { Stat.EitrOnKill, new string[2] { "eitr", "magic" } }, { Stat.EitrCostPct, new string[2] { "eitr", "magic" } }, { Stat.DamagePct, new string[1] { "damage" } }, { Stat.PhysicalDamagePct, new string[2] { "damage", "melee" } }, { Stat.ElementalDamagePct, new string[3] { "damage", "magic", "ailment" } }, { Stat.MeleeDamagePct, new string[2] { "damage", "melee" } }, { Stat.RangedDamagePct, new string[2] { "damage", "ranged" } }, { Stat.ChopDamagePct, new string[1] { "gather" } }, { Stat.PickaxeDamagePct, new string[1] { "gather" } }, { Stat.StunChance, new string[3] { "stun", "melee", "damage" } }, { Stat.ShockStunChance, new string[2] { "stun", "ailment" } }, { Stat.OnslaughtChance, new string[2] { "onslaught", "buff" } }, { Stat.OnslaughtPowerPct, new string[2] { "onslaught", "buff" } }, { Stat.BuffDurationPct, new string[1] { "buff" } }, { Stat.StatusDurationPct, new string[2] { "buff", "ailment" } }, { Stat.Dmg_Pickaxes, new string[3] { "damage", "melee", "gather" } }, { Stat.Spd_Pickaxes, new string[3] { "damage", "melee", "gather" } }, { Stat.ChopBurnPct, new string[1] { "gather" } }, { Stat.MineCrackPct, new string[1] { "gather" } }, { Stat.BurnDurationPct, new string[2] { "ailment", "magic" } }, { Stat.PoisonDurationPct, new string[2] { "ailment", "magic" } }, { Stat.FrostDurationPct, new string[2] { "ailment", "magic" } }, { Stat.AilmentResistPct, new string[2] { "defense", "ailment" } }, { Stat.FoodWhileBurningPct, new string[3] { "home", "health", "ailment" } }, { Stat.EitrOnIgnite, new string[3] { "eitr", "magic", "ailment" } }, { Stat.WetShockPct, new string[2] { "ailment", "magic" } }, { Stat.NightMoveSpeedPct, new string[2] { "night", "move" } }, { Stat.NightRegenPct, new string[3] { "night", "health", "stamina" } }, { Stat.NightStealthPct, new string[2] { "night", "stealth" } }, { Stat.NightGatherPct, new string[2] { "night", "gather" } }, { Stat.NightBuildPct, new string[2] { "night", "build" } }, { Stat.NightArmorFlat, new string[3] { "night", "armor", "defense" } }, { Stat.NightEitrRegenPct, new string[3] { "night", "eitr", "magic" } }, { Stat.NightXpPct, new string[2] { "night", "xp" } }, { Stat.AttackSpeedPct, new string[3] { "damage", "melee", "ranged" } }, { Stat.CritChance, new string[2] { "crit", "damage" } }, { Stat.CritDamagePct, new string[2] { "crit", "damage" } }, { Stat.GatherCritChance, new string[2] { "crit", "gather" } }, { Stat.CritRecentDamagePct, new string[2] { "crit", "damage" } }, { Stat.CritRecentAttackSpeedPct, new string[2] { "crit", "damage" } }, { Stat.CritRecentMoveSpeedPct, new string[2] { "crit", "move" } }, { Stat.CritRecentStaminaRegenPct, new string[2] { "crit", "stamina" } }, { Stat.DamageVsBossPct, new string[1] { "damage" } }, { Stat.ExecutePct, new string[1] { "damage" } }, { Stat.FullHpDamagePct, new string[1] { "damage" } }, { Stat.NightDamagePct, new string[2] { "damage", "stealth" } }, { Stat.RestedDamagePct, new string[2] { "damage", "home" } }, { Stat.BackstabPct, new string[2] { "damage", "stealth" } }, { Stat.IgniteChance, new string[2] { "ailment", "magic" } }, { Stat.FrostChance, new string[2] { "ailment", "magic" } }, { Stat.PoisonChance, new string[2] { "ailment", "magic" } }, { Stat.ShockChance, new string[2] { "ailment", "magic" } }, { Stat.BurnDamagePct, new string[2] { "ailment", "magic" } }, { Stat.PoisonDamagePct, new string[2] { "ailment", "magic" } }, { Stat.FrostPotencyPct, new string[2] { "ailment", "magic" } }, { Stat.ShockDamagePct, new string[2] { "ailment", "magic" } }, { Stat.AilmentDurationPct, new string[2] { "ailment", "magic" } }, { Stat.DamageVsUndeadPct, new string[1] { "damage" } }, { Stat.DamageVsForestPct, new string[1] { "damage" } }, { Stat.DamageVsAnimalsPct, new string[1] { "damage" } }, { Stat.DamageVsDvergrPct, new string[1] { "damage" } }, { Stat.DamageVsPlainsPct, new string[1] { "damage" } }, { Stat.DamageVsMountainPct, new string[1] { "damage" } }, { Stat.DamageVsSeaPct, new string[1] { "damage" } }, { Stat.DamageVsMistlandsPct, new string[1] { "damage" } }, { Stat.DamageVsBurningPct, new string[2] { "ailment", "damage" } }, { Stat.DamageVsPoisonedPct, new string[2] { "ailment", "damage" } }, { Stat.DamageVsFrozenPct, new string[2] { "ailment", "damage" } }, { Stat.DamageVsStaggeredPct, new string[2] { "damage", "melee" } }, { Stat.DamageVsWetPct, new string[2] { "ailment", "damage" } }, { Stat.StaggerDamagePct, new string[2] { "melee", "damage" } }, { Stat.KnockbackPct, new string[2] { "melee", "damage" } }, { Stat.LifestealPct, new string[2] { "health", "damage" } }, { Stat.BerserkPct, new string[2] { "damage", "health" } }, { Stat.ExtraProjectiles, new string[2] { "ranged", "proj" } }, { Stat.ProjectilePierce, new string[2] { "ranged", "proj" } }, { Stat.ProjectileChain, new string[2] { "ranged", "proj" } }, { Stat.ProjectileSplit, new string[2] { "ranged", "proj" } }, { Stat.ProjectileSpeedPct, new string[2] { "ranged", "proj" } }, { Stat.DrawSpeedPct, new string[2] { "ranged", "proj" } }, { Stat.DrawStaminaPct, new string[2] { "ranged", "stamina" } }, { Stat.ArrowSaveChance, new string[2] { "ranged", "proj" } }, { Stat.ArmorFlat, new string[2] { "armor", "defense" } }, { Stat.ArmorPct, new string[2] { "armor", "defense" } }, { Stat.DamageTakenPct, new string[1] { "defense" } }, { Stat.FireResistPct, new string[2] { "defense", "ailment" } }, { Stat.FrostResistPct, new string[2] { "defense", "ailment" } }, { Stat.PoisonResistPct, new string[2] { "defense", "ailment" } }, { Stat.LightningResistPct, new string[2] { "defense", "ailment" } }, { Stat.BlockPowerPct, new string[2] { "block", "defense" } }, { Stat.ParryBonusPct, new string[2] { "block", "defense" } }, { Stat.StaggerResistPct, new string[1] { "defense" } }, { Stat.DodgeChance, new string[2] { "dodge", "defense" } }, { Stat.ThornsPct, new string[2] { "defense", "damage" } }, { Stat.UnarmedDamageTakenPct, new string[2] { "defense", "unarmed" } }, { Stat.MoveSpeedPct, new string[1] { "move" } }, { Stat.JumpPct, new string[1] { "move" } }, { Stat.SwimSpeedPct, new string[1] { "move" } }, { Stat.FallDamagePct, new string[2] { "move", "defense" } }, { Stat.RunStaminaPct, new string[2] { "stamina", "move" } }, { Stat.JumpStaminaPct, new string[2] { "stamina", "move" } }, { Stat.AttackStaminaPct, new string[2] { "stamina", "melee" } }, { Stat.BlockStaminaPct, new string[2] { "stamina", "block" } }, { Stat.DodgeStaminaPct, new string[2] { "stamina", "dodge" } }, { Stat.SwimStaminaPct, new string[2] { "stamina", "move" } }, { Stat.SneakStaminaPct, new string[2] { "stamina", "stealth" } }, { Stat.ToolStaminaPct, new string[2] { "stamina", "build" } }, { Stat.NoisePct, new string[1] { "stealth" } }, { Stat.StealthPct, new string[1] { "stealth" } }, { Stat.CarryWeightFlat, new string[1] { "carry" } }, { Stat.ExploreRadiusPct, new string[1] { "explore" } }, { Stat.PickupRangePct, new string[2] { "carry", "explore" } }, { Stat.TamingSpeedPct, new string[1] { "taming" } }, { Stat.FeedDurationPct, new string[2] { "taming", "home" } }, { Stat.BreedChancePct, new string[1] { "taming" } }, { Stat.TamedDamagePct, new string[2] { "taming", "damage" } }, { Stat.HunterDropChance, new string[2] { "gather", "hunt" } }, { Stat.ArrowRangePct, new string[2] { "ranged", "proj" } }, { Stat.AccuracyPct, new string[2] { "ranged", "proj" } }, { Stat.BuildRangeFlat, new string[1] { "build" } }, { Stat.ExtraHarvestChance, new string[2] { "gather", "carry" } }, { Stat.GrowSpeedPct, new string[3] { "farm", "gather", "home" } }, { Stat.CropYieldChance, new string[3] { "farm", "gather", "home" } }, { Stat.PlantCrowding, new string[3] { "farm", "gather", "build" } }, { Stat.ExtraDropChance, new string[1] { "gather" } }, { Stat.MassPickupRadius, new string[2] { "gather", "carry" } }, { Stat.BuildDurabilityPct, new string[1] { "build" } }, { Stat.FoodPowerPct, new string[2] { "home", "health" } }, { Stat.PotionDurationPct, new string[2] { "buff", "health" } }, { Stat.SailSpeedPct, new string[2] { "move", "sail" } }, { Stat.SailControlPct, new string[2] { "move", "sail" } }, { Stat.CartPullPct, new string[2] { "carry", "move" } }, { Stat.PavedSpeedPct, new string[2] { "move", "build" } }, { Stat.ComfortFlat, new string[1] { "home" } }, { Stat.BuildRefundChance, new string[1] { "build" } }, { Stat.FoodDurationPct, new string[2] { "home", "health" } }, { Stat.RestedDurationPct, new string[1] { "home" } }, { Stat.XpGainPct, new string[1] { "xp" } }, { Stat.Dmg_Swords, new string[2] { "damage", "melee" } }, { Stat.Dmg_Knives, new string[3] { "damage", "melee", "stealth" } }, { Stat.Dmg_Clubs, new string[2] { "damage", "melee" } }, { Stat.Dmg_Polearms, new string[2] { "damage", "melee" } }, { Stat.Dmg_Spears, new string[2] { "damage", "melee" } }, { Stat.Dmg_Axes, new string[2] { "damage", "melee" } }, { Stat.Dmg_Unarmed, new string[3] { "damage", "melee", "unarmed" } }, { Stat.Dmg_Bows, new string[2] { "damage", "ranged" } }, { Stat.Dmg_Crossbows, new string[2] { "damage", "ranged" } }, { Stat.Dmg_ElementalMagic, new string[2] { "damage", "magic" } }, { Stat.Dmg_BloodMagic, new string[2] { "damage", "magic" } }, { Stat.Spd_Swords, new string[2] { "damage", "melee" } }, { Stat.Spd_Knives, new string[3] { "damage", "melee", "stealth" } }, { Stat.Spd_Clubs, new string[2] { "damage", "melee" } }, { Stat.Spd_Polearms, new string[2] { "damage", "melee" } }, { Stat.Spd_Spears, new string[2] { "damage", "melee" } }, { Stat.Spd_Axes, new string[2] { "damage", "melee" } }, { Stat.Spd_Unarmed, new string[3] { "damage", "melee", "unarmed" } }, { Stat.BombDamagePct, new string[2] { "bomb", "damage" } }, { Stat.BombCraftRefundChance, new string[2] { "bomb", "craft" } }, { Stat.ExtraBombChance, new string[2] { "bomb", "craft" } }, { Stat.BlastRadiusPct, new string[1] { "bomb" } }, { Stat.BombSaveChance, new string[1] { "bomb" } }, { Stat.DoubleBlastChance, new string[2] { "bomb", "damage" } }, { Stat.BombMine, new string[1] { "bomb" } }, { Stat.FishBiteChance, new string[1] { "fish" } }, { Stat.FishStaminaPct, new string[2] { "fish", "stamina" } }, { Stat.ExtraFishChance, new string[2] { "fish", "gather" } }, { Stat.CraftRefundChance, new string[2] { "craft", "build" } }, { Stat.CraftDurabilityPct, new string[1] { "craft" } }, { Stat.SmeltSpeedPct, new string[1] { "craft" } }, { Stat.CookSpeedPct, new string[2] { "craft", "home" } }, { Stat.FermentSpeedPct, new string[2] { "craft", "home" } }, { Stat.ExtraBarChance, new string[2] { "craft", "gather" } }, { Stat.NoOvercook, new string[2] { "craft", "home" } }, { Stat.ColdResistPct, new string[2] { "defense", "weather" } }, { Stat.WetResistPct, new string[2] { "defense", "weather" } }, { Stat.DryingPct, new string[2] { "weather", "home" } }, { Stat.AttrMight, new string[1] { "attr" } }, { Stat.AttrFinesse, new string[1] { "attr" } }, { Stat.AttrSpirit, new string[1] { "attr" } }, { Stat.DoubleJump, new string[1] { "move" } }, { Stat.Glide, new string[1] { "move" } }, { Stat.SafeFall, new string[2] { "move", "defense" } }, { Stat.Slide, new string[1] { "move" } }, { Stat.UnarmedGather, new string[2] { "unarmed", "gather" } }, { Stat.UnarmedToolTier, new string[2] { "unarmed", "gather" } } }; private static readonly Dictionary C = new Dictionary { { Stat.MoveSpeedPct, 0.4f }, { Stat.DamageTakenPct, -0.6f }, { Stat.ArmorPct, 0.6f }, { Stat.RunStaminaPct, -0.85f }, { Stat.ToolStaminaPct, -0.85f }, { Stat.AttackStaminaPct, -0.85f }, { Stat.BlockStaminaPct, -0.85f }, { Stat.DodgeStaminaPct, -0.85f }, { Stat.SwimStaminaPct, -0.85f }, { Stat.SneakStaminaPct, -0.85f }, { Stat.JumpStaminaPct, -0.85f }, { Stat.IgniteChance, 0.75f }, { Stat.FrostChance, 0.75f }, { Stat.PoisonChance, 0.75f }, { Stat.ShockChance, 0.75f }, { Stat.StunChance, 0.75f }, { Stat.ShockStunChance, 0.75f }, { Stat.CritChance, 0.75f }, { Stat.DodgeChance, 0.4f }, { Stat.AttackSpeedPct, 0.45f }, { Stat.LifestealPct, 0.1f }, { Stat.BackstabPct, 1f }, { Stat.StaggerResistPct, 0.85f }, { Stat.ThornsPct, 0.5f }, { Stat.BuildRefundChance, 0.6f }, { Stat.FireResistPct, 0.75f }, { Stat.FrostResistPct, 0.75f }, { Stat.ColdResistPct, 1f }, { Stat.WetResistPct, 1f }, { Stat.DryingPct, 2f }, { Stat.BombSaveChance, 0.5f }, { Stat.DoubleBlastChance, 0.5f }, { Stat.BombCraftRefundChance, 0.5f }, { Stat.ExtraBombChance, 0.5f }, { Stat.BlastRadiusPct, 1f }, { Stat.FishBiteChance, 1f }, { Stat.ExtraFishChance, 0.5f }, { Stat.FishStaminaPct, -0.85f }, { Stat.CraftRefundChance, 0.5f }, { Stat.SmeltSpeedPct, 1f }, { Stat.CookSpeedPct, 1f }, { Stat.FermentSpeedPct, 1f }, { Stat.ExtraBarChance, 0.5f }, { Stat.CraftDurabilityPct, 1f }, { Stat.PoisonResistPct, 0.75f }, { Stat.LightningResistPct, 0.75f }, { Stat.BuildDurabilityPct, 0.75f }, { Stat.CartPullPct, 0.6f }, { Stat.SailSpeedPct, 0.4f }, { Stat.SailControlPct, 0.6f }, { Stat.PavedSpeedPct, 0.25f }, { Stat.ExtraHarvestChance, 0.75f }, { Stat.GrowSpeedPct, 0.6f }, { Stat.CropYieldChance, 0.75f }, { Stat.ExtraDropChance, 0.75f }, { Stat.MassPickupRadius, 8f }, { Stat.HunterDropChance, 0.75f }, { Stat.TamingSpeedPct, 3f }, { Stat.AccuracyPct, 0.7f }, { Stat.ArrowRangePct, 1f }, { Stat.BreedChancePct, 2f }, { Stat.TamedDamagePct, 1f }, { Stat.OnslaughtChance, 1f }, { Stat.AilmentResistPct, 0.75f } }; private static readonly Dictionary A = new Dictionary { { Stat.IgniteChance, "Поджог" }, { Stat.FrostChance, "Обморожение" }, { Stat.PoisonChance, "Отравление" }, { Stat.ShockChance, "Разряд" }, { Stat.ShockStunChance, "Оглушение молнией" }, { Stat.StunChance, "Оглушение" }, { Stat.OnslaughtChance, "Натиск" }, { Stat.WetShockPct, "Проводник" }, { Stat.ExtraProjectiles, "Залп" }, { Stat.ProjectilePierce, "Пробитие" }, { Stat.ProjectileChain, "Рикошет" }, { Stat.ProjectileSplit, "Расщепление" }, { Stat.DoubleJump, "Двойной прыжок" }, { Stat.Glide, "Парение" }, { Stat.SafeFall, "Мягкое падение" }, { Stat.Slide, "Съезд" }, { Stat.PlantCrowding, "Плотная грядка" }, { Stat.UnarmedGather, "Руки-инструмент" }, { Stat.UnarmedToolTier, "Руки-инструмент" }, { Stat.MassPickupRadius, "Массовый сбор" }, { Stat.ThornsPct, "Шипы" }, { Stat.LifestealPct, "Вампиризм" }, { Stat.ExtraHarvestChance, "Удача собирателя" }, { Stat.CraftRefundChance, "Бережливая ковка" }, { Stat.ExtraBarChance, "Щедрая плавка" }, { Stat.NoOvercook, "Глаз повара" }, { Stat.BombSaveChance, "Запасливый подрывник" }, { Stat.DoubleBlastChance, "Двойной разрыв" }, { Stat.BombMine, "Мина" }, { Stat.ExtraBombChance, "Лишний заряд" }, { Stat.BombCraftRefundChance, "Экономный порох" }, { Stat.ExtraFishChance, "Богатый улов" }, { Stat.ExtraDropChance, "Удача добытчика" }, { Stat.HunterDropChance, "Свежевание" }, { Stat.ArrowSaveChance, "Бережливый колчан" }, { Stat.GatherCritChance, "Точный удар по породе" }, { Stat.BuildRefundChance, "Бережливая стройка" }, { Stat.NightBuildPct, "Ночная стройка" }, { Stat.EitrOnIgnite, "Пламя питает" }, { Stat.ChopBurnPct, "Тлеющий подсек" }, { Stat.MineCrackPct, "Трещина в породе" } }; private static readonly HashSet Lower = new HashSet { Stat.DamageTakenPct, Stat.UnarmedDamageTakenPct, Stat.NoisePct, Stat.FallDamagePct, Stat.EitrCostPct, Stat.RunStaminaPct, Stat.JumpStaminaPct, Stat.AttackStaminaPct, Stat.BlockStaminaPct, Stat.DodgeStaminaPct, Stat.SwimStaminaPct, Stat.SneakStaminaPct, Stat.ToolStaminaPct, Stat.DrawStaminaPct, Stat.FishStaminaPct }; private static readonly Dictionary TagNames = new Dictionary { { "farm", "культивация" }, { "gather", "добыча" }, { "home", "дом" }, { "craft", "ремесло" }, { "build", "стройка" }, { "melee", "ближний бой" }, { "ranged", "дальний бой" }, { "magic", "магия" }, { "damage", "урон" }, { "defense", "защита" }, { "armor", "броня" }, { "block", "блок" }, { "health", "здоровье" }, { "stamina", "выносливость" }, { "eitr", "эйтр" }, { "move", "движение" }, { "carry", "переноска" }, { "stealth", "скрытность" }, { "night", "ночь" }, { "crit", "крит" }, { "ailment", "недуги" }, { "proj", "снаряды" }, { "bomb", "бомбы" }, { "fish", "рыбалка" }, { "hunt", "охота" }, { "taming", "приручение" }, { "weather", "погода" }, { "sail", "мореходство" }, { "explore", "исследование" }, { "buff", "баффы" }, { "unarmed", "кулаки" }, { "dodge", "уклонение" }, { "stun", "оглушение" }, { "onslaught", "натиск" }, { "xp", "опыт" }, { "attr", "атрибуты" } }; private static Info P(string f) { return new Info(f, p: true); } private static Info F(string f) { return new Info(f, p: false); } private static Info Flag(string f) { return new Info(f, p: false, fl: true); } public static bool IsAbility(Stat s) { return A.ContainsKey(s); } public static string AbilityName(Stat s) { if (!A.TryGetValue(s, out var value)) { return null; } return L.T(value); } public static bool Helps(Stat s, float v) { if (!Lower.Contains(s)) { return v > 0f; } return v < 0f; } public static float CapOf(Stat s) { if (!C.TryGetValue(s, out var value)) { return 0f; } return value; } public static bool OverCap(Stat s, float v) { float num = CapOf(s); if (num != 0f) { if (!(num > 0f)) { return v < num; } return v > num; } return false; } public static string[] Tags(Stat s) { if (!T.TryGetValue(s, out var value)) { return new string[0]; } return value; } public static string TagName(string tag) { if (!TagNames.TryGetValue(tag, out var value)) { return tag; } return L.T(value); } public static List Topics(IEnumerable> mods) { List list = new List(); foreach (KeyValuePair mod in mods) { string[] array = Tags(mod.Key); for (int i = 0; i < array.Length; i++) { string item = TagName(array[i]); if (!list.Contains(item)) { list.Add(item); } } } return list; } public static (string fmt, bool pct, bool flag, bool invert) FormatInfo(Stat s) { Info info = I[s]; return (fmt: info.Fmt, pct: info.Pct, flag: info.Flag, invert: info.Invert); } public static string Describe(Stat s, float v) { Info info = I[s]; if (info.Flag) { return L.T(info.Fmt); } if (info.Invert) { v = 0f - v; } string text = ((v >= 0f) ? "+" : "−"); float num = Math.Abs(v); string text2 = (info.Pct ? $"{num * 100f:0.#}%" : $"{num:0.#}"); return string.Format(L.T(info.Fmt), text + text2); } public static string DescribeRich(Stat s, float v, string good, string bad) { Info info = I[s]; if (info.Flag) { return L.T(info.Fmt); } float num = (info.Invert ? (0f - v) : v); string text = ((num >= 0f) ? "+" : "−"); float num2 = Math.Abs(num); string text2 = (info.Pct ? $"{num2 * 100f:0.#}%" : $"{num2:0.#}"); string text3 = (Helps(s, v) ? good : bad); return string.Format(L.T(info.Fmt), "" + text + text2 + ""); } public static string DescribeAll(IEnumerable> mods) { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair mod in mods) { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(Describe(mod.Key, mod.Value)); } return stringBuilder.ToString(); } public static Stat? DamageFor(SkillType t) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected I4, but got Unknown return (t - 1) switch { 0 => Stat.Dmg_Swords, 1 => Stat.Dmg_Knives, 2 => Stat.Dmg_Clubs, 3 => Stat.Dmg_Polearms, 4 => Stat.Dmg_Spears, 6 => Stat.Dmg_Axes, 10 => Stat.Dmg_Unarmed, 11 => Stat.Dmg_Pickaxes, 7 => Stat.Dmg_Bows, 13 => Stat.Dmg_Crossbows, 8 => Stat.Dmg_ElementalMagic, 9 => Stat.Dmg_BloodMagic, _ => null, }; } public static Stat? SpeedFor(SkillType t) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected I4, but got Unknown return (t - 1) switch { 0 => Stat.Spd_Swords, 1 => Stat.Spd_Knives, 2 => Stat.Spd_Clubs, 3 => Stat.Spd_Polearms, 4 => Stat.Spd_Spears, 6 => Stat.Spd_Axes, 10 => Stat.Spd_Unarmed, 11 => Stat.Spd_Pickaxes, _ => null, }; } } public static class TamingHunting { [HarmonyPatch(typeof(Tameable), "DecreaseRemainingTime")] private static class Tameable_DecreaseRemainingTime { private static void Prefix(Tameable __instance, ref float time) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { float num = Mathf.Min(3f, S.Get(Stat.TamingSpeedPct)); if (!(num <= 0f) && !(Vector3.Distance(((Component)localPlayer).transform.position, ((Component)__instance).transform.position) > __instance.m_tamingSpeedMultiplierRange)) { time *= 1f + num; } } } } [HarmonyPatch(typeof(Tameable), "Awake")] private static class Tameable_Awake { private static void Postfix(Tameable __instance) { StatSet s = S; if (s != null) { float num = s.Get(Stat.FeedDurationPct); if (num > 0f) { __instance.m_fedDuration *= 1f + Mathf.Min(2f, num); } } } } [HarmonyPatch(typeof(Procreation), "Procreate")] private static class Procreation_Procreate { private static float saved = -1f; private static void Prefix(Procreation __instance) { StatSet s = S; if (s != null) { float num = Mathf.Min(2f, s.Get(Stat.BreedChancePct)); if (!(num <= 0f)) { saved = __instance.m_pregnancyChance; __instance.m_pregnancyChance = Mathf.Clamp01(saved * (1f + num)); } } } private static void Postfix(Procreation __instance) { if (saved >= 0f) { __instance.m_pregnancyChance = saved; saved = -1f; } } } [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] private static class CharacterDrop_GenerateDropList { private static void Postfix(CharacterDrop __instance, List> __result) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (__result == null || __result.Count == 0 || (Object)(object)localPlayer == (Object)null) { return; } Character character = __instance.m_character; if (!((Object)(object)character == (Object)null) && (int)character.m_faction == 1 && !(Vector3.Distance(((Component)localPlayer).transform.position, ((Component)character).transform.position) > 40f)) { float num = Mathf.Min(0.75f, S.Get(Stat.HunterDropChance)); if (!(num <= 0f) && !(Random.value >= num)) { KeyValuePair keyValuePair = __result[Random.Range(0, __result.Count)]; __result.Add(new KeyValuePair(keyValuePair.Key, Mathf.Max(1, keyValuePair.Value))); } } } } [HarmonyPatch(typeof(Attack), "Start")] private static class Attack_Start { private static void Postfix(Attack __instance, Humanoid character, bool __result) { if (__result && !((Object)(object)character != (Object)(object)Player.m_localPlayer) && !((Object)(object)__instance.m_attackProjectile == (Object)null)) { float num = Mathf.Min(0.7f, S.Get(Stat.AccuracyPct)); if (!(num <= 0f)) { __instance.m_projectileAccuracy *= 1f - num; __instance.m_projectileAccuracyMin *= 1f - num; } } } } [HarmonyPatch(typeof(Projectile), "Setup")] private static class Projectile_Setup { private static void Postfix(Projectile __instance, Character owner) { if (!((Object)(object)owner != (Object)(object)Player.m_localPlayer)) { float num = Mathf.Min(1f, S.Get(Stat.ArrowRangePct)); if (!(num <= 0f)) { __instance.m_ttl *= 1f + num; __instance.m_gravity *= 1f - num * 0.5f; } } } } private static StatSet S => Progress.Current?.Stats; } public static class TreeB { internal class Sm { public string Name; public (Stat, float)[] Mods; public int Count; public Sm(string n, int count, params (Stat, float)[] m) { Name = n; Count = count; Mods = m; } } internal class Cl { public string Notable; public string Extra; public string Icon; public string Mastery; public (Stat, float)[] Mods; public Sm[] Smalls; public Cl(string notable, string mastery, (Stat, float)[] mods, params Sm[] smalls) { Notable = notable; Mastery = mastery; Mods = mods; Smalls = smalls; } public Cl X(string e) { Extra = e; return this; } public Cl I(string i) { Icon = i; return this; } } internal class Key { public string Name; public string Extra; public string Icon; public (Stat, float)[] Mods; public Key(string n, string extra, params (Stat, float)[] m) { Name = n; Extra = extra; Mods = m; } public Key I(string i) { Icon = i; return this; } } internal class Mastery { public string Name; public Opt[] Options; public string Icon; public Mastery(string n, params Opt[] o) { Name = n; Options = o; } public Mastery I(string i) { Icon = i; return this; } } internal class Opt { public string Name; public string Extra; public (Stat, float)[] Mods; public Opt(string n, params (Stat, float)[] m) { Name = n; Mods = m; } public Opt X(string e) { Extra = e; return this; } } internal class Region { public Branch B; public float Span; public int Lanes; public (Stat, float)[][] Travel; public string GateName; public Cl[] Clusters; public Cl[] Rim; public Key[] Keys; } private const float R_HUB = 175f; private const float RA = 640f; private const float RB = 1180f; private const float RC = 1740f; private const float RD = 2300f; private const float RE = 2820f; private const float R_KEY = 3560f; private const float R_CAP = 4160f; private static readonly float[] Rings = new float[6] { 175f, 640f, 1180f, 1740f, 2300f, 2820f }; private static readonly Dictionary masteries = new Dictionary(); private const float NodeStep = 132f; private const float EndTangent = 1f; private const float CtrlScale = 1.3333334f; private static readonly (Stat, float)[] MIGHT = new(Stat, float)[1] { (Stat.AttrMight, 3f) }; private static readonly (Stat, float)[] FINESSE = new(Stat, float)[1] { (Stat.AttrFinesse, 3f) }; private static readonly (Stat, float)[] SPIRIT = new(Stat, float)[1] { (Stat.AttrSpirit, 3f) }; internal static (Stat, float)[] N(params (Stat, float)[] m) { return m; } private static Vector2 P(float r, float deg) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return Tree.P(r, deg); } private static Vector2 Rotate(Vector2 v, float deg) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) float num = deg * ((float)Math.PI / 180f); float num2 = Mathf.Cos(num); float num3 = Mathf.Sin(num); return new Vector2(v.x * num2 - v.y * num3, v.x * num3 + v.y * num2); } private static void Link(string a, string b) { Tree.Connect(a, b); } private static Node Add(string id, string name, NodeSize size, Branch b, Vector2 pos, params (Stat, float)[] mods) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) return Tree.AddNode(id, name, size, b, pos, mods); } private static Vector2 Bezier(Vector2 a, Vector2 c, Vector2 b, float t) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0022: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) float num = 1f - t; Vector2 val = (a + b) * 0.5f; Vector2 val2 = c - val; Vector2 val3 = (b - a) * 0.3f; Vector2 val4 = a + val3 + val2 * 1f; Vector2 val5 = b - val3 + val2 * 1f; return num * num * num * a + 3f * num * num * t * val4 + 3f * num * t * t * val5 + t * t * t * b; } private static string AttrName((Stat, float)[] mods) { if (mods.Length == 0) { return "Тропа"; } return mods[0].Item1 switch { Stat.AttrMight => "Мозоль", Stat.AttrFinesse => "Сноровка", Stat.AttrSpirit => "Наитие", _ => "Тропа", }; } private static void Travel(string id, Region r, int lane, Vector2 pos, string name = null) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) (Stat, float)[] mods = r.Travel[Mathf.Clamp(lane, 0, r.Travel.Length - 1)]; Add(id, name ?? AttrName(mods), NodeSize.Small, r.B, pos, mods); } private static float ArcLength(Vector2 a, Vector2 c, Vector2 b) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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) float num = 0f; Vector2 val = a; for (int i = 1; i <= 24; i++) { Vector2 val2 = Bezier(a, c, b, (float)i / 24f); num += Vector2.Distance(val, val2); val = val2; } return num; } private static void PlaceCluster(string id, Cl cl, string hostA, string hostB, Vector2 outward, Branch br, float maxBulge) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0087: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: 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) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) Vector2 pos = Tree.Nodes[hostA].Pos; Vector2 pos2 = Tree.Nodes[hostB].Pos; List<(string, (Stat, float)[])> list = new List<(string, (Stat, float)[])>(); Sm[] smalls = cl.Smalls; foreach (Sm sm in smalls) { for (int j = 0; j < sm.Count; j++) { list.Add((sm.Name, sm.Mods)); } } int num = list.Count + 1; int num2 = num / 2; Vector2 val = (pos + pos2) * 0.5f; Vector2 normalized = ((Vector2)(ref outward)).normalized; float[] array = new float[num]; for (int k = 0; k < num; k++) { array[k] = ((k == num2) ? 1.45f : 1f); } float[] array2 = new float[num + 1]; for (int l = 0; l <= num; l++) { float num3 = ((l == 0) ? 1f : array[l - 1]); float num4 = ((l == num) ? 1f : array[l]); array2[l] = (num3 + num4) * 0.5f; } float num5 = 0f; float[] array3 = array2; foreach (float num6 in array3) { num5 += num6; } float num7 = num5 * 132f; float num8 = Mathf.Min(maxBulge, 60f); for (float num9 = 60f; num9 <= maxBulge; num9 += 10f) { num8 = num9; if (ArcLength(pos, val + normalized * num9 * 1.3333334f, pos2) >= num7) { break; } } Vector2 c = val + normalized * num8 * 1.3333334f; float num10 = ArcLength(pos, c, pos2); List list2 = new List(); float num11 = 0f; for (int m = 0; m < num; m++) { num11 += array2[m]; list2.Add(AtLength(pos, c, pos2, num10 * num11 / num5)); } string a = hostA; for (int n = 0; n < num; n++) { string text = $"{id}_{n}"; if (n == num2) { Node node = Add(text, cl.Notable, NodeSize.Notable, br, list2[n], cl.Mods); node.Extra = cl.Extra; node.Icon = cl.Icon; string mastery = cl.Mastery; Vector2 at = list2[n]; Vector2 val2 = list2[n] - val; AddMastery(id, text, mastery, at, ((Vector2)(ref val2)).normalized, br); } else { (string, (Stat, float)[]) tuple = list[(n < num2) ? n : (n - 1)]; Add(text, tuple.Item1, NodeSize.Small, br, list2[n], tuple.Item2); } Link(a, text); a = text; } Link(a, hostB); } private static void PlaceTail(string id, Cl cl, string host, Vector2 dir, Branch br, float step = 158f) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) List<(string, (Stat, float)[])> list = new List<(string, (Stat, float)[])>(); Sm[] smalls = cl.Smalls; foreach (Sm sm in smalls) { for (int j = 0; j < sm.Count; j++) { list.Add((sm.Name, sm.Mods)); } } Vector2 val = Tree.Nodes[host].Pos; string a = host; for (int k = 0; k < list.Count; k++) { val += ((Vector2)(ref dir)).normalized * step; string text = $"{id}_{k}"; Add(text, list[k].Item1, NodeSize.Small, br, val, list[k].Item2); Link(a, text); a = text; } val += ((Vector2)(ref dir)).normalized * step; Node node = Add(id + "_n", cl.Notable, NodeSize.Notable, br, val, cl.Mods); node.Extra = cl.Extra; node.Icon = cl.Icon; Link(a, id + "_n"); AddMastery(id, id + "_n", cl.Mastery, val, dir, br); } private static Vector2 AtLength(Vector2 a, Vector2 c, Vector2 b, float target) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_004a: 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) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) float num = 0f; Vector2 val = a; for (int i = 1; i <= 64; i++) { Vector2 val2 = Bezier(a, c, b, (float)i / 64f); float num2 = Vector2.Distance(val, val2); if (num + num2 >= target) { return Vector2.Lerp(val, val2, (target - num) / Mathf.Max(0.001f, num2)); } num += num2; val = val2; } return b; } internal static bool Attach(string id, string host, string key, Vector2 at, Vector2 dir, Branch br) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) if (masteries.Count == 0) { foreach (KeyValuePair item in Masteries()) { masteries[item.Key] = item.Value; } } float[] array = new float[3] { 165f, 140f, 190f }; foreach (float num in array) { float[] array2 = new float[7] { 0f, 24f, -24f, 48f, -48f, 72f, -72f }; foreach (float deg in array2) { Vector2 val = at + Rotate(((Vector2)(ref dir)).normalized, deg) * num; if (ClearForMastery(val) && !CrossesAnyLink(at, val, host)) { Vector2 val2 = val - at; AddMastery(id, host, key, at, ((Vector2)(ref val2)).normalized, num, br); return true; } } } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Мастерству " + key + " не нашлось места у узла " + host)); } return false; } private static void MasteriesIntoHoles() { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) foreach (Node m in Tree.Nodes.Values.Where((Node n) => n.Size == NodeSize.Mastery).ToList()) { string hostId = m.Links.FirstOrDefault(); if (hostId == null || !Tree.Nodes.TryGetValue(hostId, out var value)) { continue; } string prefix = hostId + "_"; List list = Tree.Nodes.Values.Where((Node n) => n.Id != m.Id && (n.Id == hostId || n.Id.StartsWith(prefix)) && n.Size != NodeSize.Mastery).ToList(); if (list.Count >= 5) { Vector2 centre = new Vector2(list.Average((Node n) => n.Pos.x), list.Average((Node n) => n.Pos.y)); List list2 = (from n in list select Mathf.Atan2(n.Pos.y - centre.y, n.Pos.x - centre.x) * 57.29578f into x orderby x select x).ToList(); float num = 360f + list2[0] - list2[list2.Count - 1]; for (int num2 = 1; num2 < list2.Count; num2++) { num = Mathf.Max(num, list2[num2] - list2[num2 - 1]); } if (!(num > 140f) && ClearForMastery(centre, m.Id) && !CrossesAnyLink(value.Pos, centre, hostId)) { m.Pos = centre; } } } } internal static void TidyMasteries() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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) //IL_00ab: 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_00f5: 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_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) MasteriesIntoHoles(); foreach (Node item in Tree.Nodes.Values.Where((Node n) => n.Size == NodeSize.Mastery).ToList()) { string text = item.Links.FirstOrDefault(); if (text == null) { continue; } Node node = Tree.Nodes[text]; if (ClearForMastery(item.Pos, item.Id) && !CrossesAnyLink(node.Pos, item.Pos, text)) { continue; } Vector2 val = item.Pos - node.Pos; Vector2 normalized = ((Vector2)(ref val)).normalized; float[] array = new float[4] { 165f, 185f, 145f, 205f }; int num = 0; while (true) { Vector2 val2; if (num < array.Length) { float num2 = array[num]; float[] array2 = new float[12] { 0f, 30f, -30f, 60f, -60f, 90f, -90f, 120f, -120f, 150f, -150f, 180f }; int num3 = 0; while (num3 < array2.Length) { float deg = array2[num3]; val2 = node.Pos + Rotate(normalized, deg) * num2; if (!ClearForMastery(val2, item.Id) || CrossesAnyLink(node.Pos, val2, text)) { num3++; continue; } goto IL_0130; } num++; continue; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Мастерство " + item.Id + " осталось в тесноте")); } break; IL_0130: item.Pos = val2; break; } } } private static bool Clear(Vector2 pos, float radius) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) foreach (Node value in Tree.Nodes.Values) { if (Vector2.Distance(value.Pos, pos) < radius) { return false; } } return true; } private static float Radius(NodeSize s) { return s switch { NodeSize.Keystone => 38f, NodeSize.Notable => 28f, NodeSize.Small => 15f, _ => 32f, }; } private static bool ClearForMastery(Vector2 pos, string ignore = null) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) foreach (Node value in Tree.Nodes.Values) { if (!(value.Id == ignore) && Vector2.Distance(value.Pos, pos) < Radius(value.Size) + Radius(NodeSize.Mastery) + 8f) { return false; } } return true; } internal static bool CrossesAnyLink(Vector2 a, Vector2 b, string hostId) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) foreach (Node value in Tree.Nodes.Values) { foreach (string link in value.Links) { if (string.CompareOrdinal(value.Id, link) < 0 && !(value.Id == hostId) && !(link == hostId) && Intersect(a, b, value.Pos, Tree.Nodes[link].Pos)) { return true; } } } return false; } private static bool Intersect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) float num = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x); if (Mathf.Abs(num) < 1E-05f) { return false; } float num2 = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / num; float num3 = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / num; if (num2 > 0.01f && num2 < 0.99f && num3 > 0.01f) { return num3 < 0.99f; } return false; } private static void AddMastery(string id, string host, string key, Vector2 at, Vector2 dir, Branch br, float dist = 165f) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) AddMastery(id, host, key, at, dir, dist, br); } private static void AddMastery(string id, string host, string key, Vector2 at, Vector2 dir, float dist, Branch br) { //IL_002c: 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_0036: 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) if (string.IsNullOrEmpty(key) || !masteries.TryGetValue(key, out var value)) { return; } Node node = Add(id + "_m", value.Name, NodeSize.Mastery, br, at + ((Vector2)(ref dir)).normalized * dist); node.Icon = value.Icon; node.Extra = "Одно из трёх на выбор."; node.Options = value.Options.Select((Opt o) => new NodeOption { Name = o.Name, Extra = o.Extra, Mods = o.Mods.Select(((Stat, float) x) => new KeyValuePair(x.Item1, x.Item2)).ToList() }).ToList(); Link(host, id + "_m"); } private static string ArcRoad(string id, Region r, int lane, float radius, float degA, float degB, string hostA, string hostB, float spacing = 250f) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Abs(degB - degA) * ((float)Math.PI / 180f) * radius; int num2 = Mathf.Max(1, Mathf.CeilToInt(num / spacing) - 1); string text = hostA; for (int i = 1; i <= num2; i++) { float num3 = (float)i / (float)(num2 + 1); string text2 = $"{id}_{i}"; Travel(text2, r, lane, P(radius, Mathf.Lerp(degA, degB, num3))); Link(text, text2); text = text2; } Link(text, hostB); return text; } public static void Build() { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_0311: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_05b1: Unknown result type (might be due to invalid IL or missing references) //IL_0d41: Unknown result type (might be due to invalid IL or missing references) //IL_0d48: Unknown result type (might be due to invalid IL or missing references) //IL_0d4d: Unknown result type (might be due to invalid IL or missing references) //IL_0d52: Unknown result type (might be due to invalid IL or missing references) //IL_0d56: Unknown result type (might be due to invalid IL or missing references) //IL_0829: Unknown result type (might be due to invalid IL or missing references) //IL_0a7e: Unknown result type (might be due to invalid IL or missing references) //IL_0a8f: Unknown result type (might be due to invalid IL or missing references) //IL_0a94: Unknown result type (might be due to invalid IL or missing references) //IL_0a9e: Unknown result type (might be due to invalid IL or missing references) //IL_0aa3: Unknown result type (might be due to invalid IL or missing references) //IL_0ac8: Unknown result type (might be due to invalid IL or missing references) //IL_0b8d: Unknown result type (might be due to invalid IL or missing references) //IL_0b99: Unknown result type (might be due to invalid IL or missing references) //IL_0ba7: Unknown result type (might be due to invalid IL or missing references) //IL_0bfd: Unknown result type (might be due to invalid IL or missing references) masteries.Clear(); foreach (KeyValuePair item in Masteries()) { masteries[item.Key] = item.Value; } Tree.RingRadii = Rings; Tree.CaptionRadius = 4160f; Tree.AddNode("root", "Пробуждение", NodeSize.Root, Branch.Core, Vector2.zero).Extra = "Начало пути. Каждый уровень даёт одно очко."; Tree.Nodes["root"].Icon = "sprite:mapicon_start"; Region[] array = Regions(); float num = 90f + array[0].Span * 0.5f; float[] array2 = new float[array.Length]; float[] array3 = new float[array.Length]; foreach (int item2 in Enumerable.Range(0, array.Length)) { array3[item2] = num; array2[item2] = num - array[item2].Span; num = array2[item2]; } List list = new List(); for (int i = 0; i < array.Length; i++) { Region region = array[i]; Tree.Captions.Add((region.B, (array2[i] + array3[i]) * 0.5f)); Tree.Dividers.Add(array2[i]); float num2 = region.Span * 0.17f; float num3 = array2[i] + num2; float num4 = array3[i] - num2; float[] array4 = new float[region.Lanes]; for (int j = 0; j < region.Lanes; j++) { array4[j] = ((region.Lanes == 1) ? ((num3 + num4) * 0.5f) : Mathf.Lerp(num3, num4, (float)j / (float)(region.Lanes - 1))); } list.Add(array4); } for (int k = 0; k < array.Length; k++) { Region region2 = array[k]; string text = $"b{k}_gate"; Add(text, region2.GateName, NodeSize.Small, region2.B, P(175f, (array2[k] + array3[k]) * 0.5f), region2.Travel[0]); Link("root", text); for (int l = 0; l < region2.Lanes; l++) { float deg = list[k][l]; string text2 = text; for (int m = 1; m < Rings.Length; m++) { Vector2 pos = Tree.Nodes[text2].Pos; Vector2 val = P(Rings[m], deg); int num5 = Mathf.Max(1, Mathf.CeilToInt(Vector2.Distance(pos, val) / 280f) - 1); for (int n = 1; n <= num5; n++) { string text3 = $"b{k}_l{l}_r{m}_t{n}"; Travel(text3, region2, l, Vector2.Lerp(pos, val, (float)n / (float)(num5 + 1))); Link(text2, text3); text2 = text3; } string text4 = Stop(k, l, m); Travel(text4, region2, l, val); Link(text2, text4); text2 = text4; } } } Dictionary<(int, int, int), int> dictionary = new Dictionary<(int, int, int), int>(); for (int num6 = 0; num6 < array.Length; num6++) { Region region3 = array[num6]; int num7 = 0; for (int num8 = 1; num8 <= 4; num8++) { for (int num9 = 0; num9 + 1 < region3.Lanes; num9++) { if (num7 >= region3.Clusters.Length) { break; } if (num8 <= 1 && num9 != 0) { break; } dictionary[(num6, num8, num9)] = num7++; } } } for (int num10 = 0; num10 < array.Length; num10++) { Region region4 = array[num10]; for (int num11 = 1; num11 < Rings.Length; num11++) { for (int num12 = 0; num12 + 1 < region4.Lanes; num12++) { if (!dictionary.ContainsKey((num10, num11, num12))) { ArcRoad($"b{num10}_rr{num11}_{num12}", region4, (num11 % 2 == 0) ? num12 : (num12 + 1), Rings[num11], list[num10][num12], list[num10][num12 + 1], Stop(num10, num12, num11), Stop(num10, num12 + 1, num11), (num11 == Rings.Length - 1) ? 150f : 250f); } } } } for (int num13 = 0; num13 < array.Length; num13++) { Region region5 = array[num13]; int num14 = Rings.Length - 1; int[] array5 = new int[2] { -1, 1 }; foreach (int num16 in array5) { int num17 = ((num16 >= 0) ? (region5.Lanes - 1) : 0); float num18 = ((num16 < 0) ? (array2[num13] + region5.Span * 0.05f) : (array3[num13] - region5.Span * 0.05f)); string text5 = string.Format("b{0}_rim{1}", num13, (num16 < 0) ? "lo" : "hi"); Travel(text5, region5, num17, P(Rings[num14], num18)); ArcRoad(string.Format("b{0}_rimr{1}", num13, (num16 < 0) ? "lo" : "hi"), region5, num17, Rings[num14], list[num13][num17], num18, Stop(num13, num17, num14), text5, 150f); } } for (int num19 = 0; num19 < array.Length; num19++) { int num20 = (num19 + 1) % array.Length; Region region6 = array[num19]; int[] array5 = new int[2] { 2, 4 }; foreach (int num21 in array5) { string hostA = Stop(num19, 0, num21); string hostB = Stop(num20, array[num20].Lanes - 1, num21); float degA = list[num19][0]; float degB = list[num20][array[num20].Lanes - 1] - 360f * (float)((num20 == 0) ? 1 : 0); Region region7 = new Region(); region7.B = Branch.Core; region7.Travel = new(Stat, float)[1][] { Mix(region6.Travel[0], array[num20].Travel[array[num20].Travel.Length - 1]) }; Region r = region7; ArcRoad($"b{num19}x{num20}_r{num21}", r, 0, Rings[num21], degA, degB, hostA, hostB, 260f); } } for (int num22 = 0; num22 < array.Length; num22++) { Region r2 = array[num22]; int num23 = 0; for (int num24 = 1; num24 <= 4; num24++) { float num25 = Rings[num24 + 1] - Rings[num24] - 260f; for (int num26 = 0; num26 + 1 < r2.Lanes; num26++) { if (num23 >= r2.Clusters.Length) { break; } if (dictionary.ContainsKey((num22, num24, num26))) { float deg2 = (list[num22][num26] + list[num22][num26 + 1]) * 0.5f; PlaceCluster($"b{num22}_c{num23}", r2.Clusters[num23], Stop(num22, num26, num24), Stop(num22, num26 + 1, num24), P(1f, deg2), r2.B, Mathf.Min(Rings[num24] * 0.34f, num25)); num23++; } } } Cl[] array6 = r2.Clusters.Skip(num23).Concat(r2.Rim ?? new Cl[0]).ToArray(); float mid0 = (array2[num22] + array3[num22]) * 0.5f; List list2 = (from node5 in Tree.Nodes.Values where ((Vector2)(ref node5.Pos)).magnitude > Rings[5] - 30f && ((Vector2)(ref node5.Pos)).magnitude < Rings[5] + 30f && node5.Branch == r2.B orderby Mathf.DeltaAngle(mid0, Mathf.Atan2(node5.Pos.y, node5.Pos.x) * 57.29578f) select node5.Id).ToList(); Key[] keys = r2.Keys; int num27 = ((keys != null) ? keys.Length : 0); List keyAnchor = new List(); if (num27 > 0 && list2.Count > 2) { keyAnchor.Add(list2[0]); if (num27 > 1) { keyAnchor.Add(list2[list2.Count - 1]); } for (int num28 = 2; num28 < num27; num28++) { keyAnchor.Add(list2[Mathf.Clamp(list2.Count * num28 / (num27 + 1), 1, list2.Count - 2)]); } } List list3 = list2.Where((string item) => !keyAnchor.Contains(item)).ToList(); int num29 = list3.Count / 2; if (num29 < array6.Length) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"Reforged {r2.B}: мест на ободе {num29}, кластеров {array6.Length}"); } } for (int num30 = 0; num30 < array6.Length && num30 < num29; num30++) { string text6 = list3[num30 * 2]; string text7 = list3[num30 * 2 + 1]; Vector2 val2 = (Tree.Nodes[text6].Pos + Tree.Nodes[text7].Pos) * 0.5f; PlaceCluster($"b{num22}_t{num30}", array6[num30], text6, text7, ((Vector2)(ref val2)).normalized, r2.B, 430f); } for (int num31 = 0; num31 < keyAnchor.Count; num31++) { Key key = r2.Keys[num31]; Node node = Tree.Nodes[keyAnchor[num31]]; float deg3 = Mathf.Atan2(node.Pos.y, node.Pos.x) * 57.29578f; string a = node.Id; for (int num32 = 1; num32 <= 2; num32++) { string text8 = $"b{num22}_k{num31}_t{num32}"; Travel(text8, r2, 0, Vector2.Lerp(node.Pos, P(3560f, deg3), (float)num32 / 3f)); Link(a, text8); a = text8; } Node node2 = Add($"b{num22}_k{num31}", key.Name, NodeSize.Keystone, r2.B, P(3560f, deg3), key.Mods); node2.Extra = key.Extra; node2.Icon = key.Icon; Link(a, $"b{num22}_k{num31}"); } } Cl[] array7 = BorderClusters(); for (int num33 = 0; num33 < array.Length && num33 < array7.Length; num33++) { int num34 = (num33 + 1) % array.Length; string bridgeId = $"b{num33}x{num34}_r4"; List list4 = (from node5 in Tree.Nodes.Values where node5.Id.StartsWith(bridgeId + "_") orderby node5.Id select node5).ToList(); if (list4.Count >= 2) { Node node3 = list4[0]; Node node4 = list4[list4.Count - 1]; string id = $"bd{num33}"; Cl cl = array7[num33]; string id2 = node3.Id; string id3 = node4.Id; Vector2 val3 = node3.Pos + node4.Pos; PlaceCluster(id, cl, id2, id3, ((Vector2)(ref val3)).normalized, Branch.Core, Rings[4] * 0.16f); } } TidyMasteries(); Tree.Sign(); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"Reforged: узлов {Tree.Nodes.Count}"); } } private static string Stop(int region, int lane, int ring) { return $"b{region}_l{lane}_s{ring}"; } private static int NearestLane(float[] degs, float deg) { int result = 0; float num = float.MaxValue; for (int i = 0; i < degs.Length; i++) { float num2 = Mathf.Abs(Mathf.DeltaAngle(degs[i], deg)); if (num2 < num) { num = num2; result = i; } } return result; } private static (Stat, float)[] Mix((Stat, float)[] a, (Stat, float)[] b) { List<(Stat, float)> list = new List<(Stat, float)>(); (Stat, float)[] array = a; for (int i = 0; i < array.Length; i++) { (Stat, float) tuple = array[i]; list.Add((tuple.Item1, tuple.Item2 * 0.6f)); } array = b; for (int i = 0; i < array.Length; i++) { (Stat, float) tuple2 = array[i]; list.Add((tuple2.Item1, tuple2.Item2 * 0.6f)); } return list.ToArray(); } private static Sm S(string name, int count, params (Stat, float)[] mods) { return new Sm(name, count, mods); } private static Cl C(string notable, string mastery, (Stat, float)[] mods, params Sm[] smalls) { return new Cl(notable, mastery, mods, smalls); } private static Opt O(string name, params (Stat, float)[] mods) { return new Opt(name, mods); } internal static Dictionary Masteries() { Dictionary dictionary = new Dictionary(); dictionary["sword"] = new Mastery("Мастерство меча", O("Дуэлянт", (Stat.Spd_Swords, 0.1f), (Stat.ParryBonusPct, 0.25f)), O("Кровопускание", (Stat.Dmg_Swords, 0.1f), (Stat.LifestealPct, 0.015f)), O("Рунная кромка", (Stat.Dmg_Swords, 0.08f), (Stat.ElementalDamagePct, 0.1f))).I("sword"); dictionary["axe"] = new Mastery("Мастерство топора", O("Рубящий размах", (Stat.Dmg_Axes, 0.12f), (Stat.KnockbackPct, 0.2f)), O("Лесорубский хват", (Stat.Dmg_Axes, 0.08f), (Stat.ChopDamagePct, 0.25f)), O("Зазубрины", (Stat.Dmg_Axes, 0.08f), (Stat.PoisonChance, 0.1f))).I("axe"); dictionary["club"] = new Mastery("Мастерство дубины", O("Раскалывающий удар", (Stat.Dmg_Clubs, 0.12f), (Stat.StaggerDamagePct, 0.25f)), O("Оглушение", (Stat.StunChance, 0.08f), (Stat.DamageVsStaggeredPct, 0.15f)), O("Киянка", (Stat.Dmg_Clubs, 0.08f), (Stat.BuildDurabilityPct, 0.2f))).I("club"); dictionary["spear"] = new Mastery("Мастерство копья", O("Длинный выпад", (Stat.Dmg_Spears, 0.12f), (Stat.Dmg_Polearms, 0.12f)), O("Бросок", (Stat.Dmg_Spears, 0.08f), (Stat.ProjectileSpeedPct, 0.2f)), O("Упор", (Stat.Dmg_Polearms, 0.1f), (Stat.StaggerResistPct, 0.15f))).I("spear"); dictionary["knife"] = new Mastery("Мастерство ножа", O("Из-за спины", (Stat.BackstabPct, 0.25f)), O("Быстрые руки", (Stat.Spd_Knives, 0.12f), (Stat.AttackStaminaPct, -0.08f)), O("Отравленное лезвие", (Stat.Dmg_Knives, 0.08f), (Stat.PoisonChance, 0.12f))).I("knife"); dictionary["fist"] = new Mastery("Мастерство кулака", O("Костолом", (Stat.Dmg_Unarmed, 0.14f), (Stat.StunChance, 0.06f)), O("Голые руки", (Stat.UnarmedGather, 1f), (Stat.UnarmedToolTier, 1f)), O("Уклончивый бой", (Stat.Spd_Unarmed, 0.1f), (Stat.DodgeChance, 0.06f))).I("unarmed"); dictionary["crit"] = new Mastery("Мастерство точного удара", O("Открытая рана", (Stat.CritDamagePct, 0.25f)), O("Разгон", (Stat.CritRecentAttackSpeedPct, 0.15f), (Stat.CritRecentMoveSpeedPct, 0.15f)), O("Хладнокровие", (Stat.CritChance, 0.05f), (Stat.FullHpDamagePct, 0.1f))).I("crit"); dictionary["tempo"] = new Mastery("Мастерство темпа", O("Без замаха", (Stat.AttackSpeedPct, 0.06f)), O("Экономный замах", (Stat.AttackStaminaPct, -0.12f), (Stat.StaminaRegenPct, 0.1f)), O("Второй удар", (Stat.MeleeDamagePct, 0.08f), (Stat.RangedDamagePct, 0.08f))).I("atk-speed"); dictionary["stagger"] = new Mastery("Мастерство натиска в упор", O("Тяжёлая рука", (Stat.StaggerDamagePct, 0.3f)), O("Добивание оглушённых", (Stat.DamageVsStaggeredPct, 0.2f)), O("Отбросить", (Stat.KnockbackPct, 0.35f), (Stat.StunChance, 0.04f))).I("stagger-dmg"); dictionary["onslaught"] = new Mastery("Мастерство Натиска", O("Частый натиск", (Stat.OnslaughtChance, 0.12f)), O("Долгий натиск", (Stat.OnslaughtPowerPct, 0.3f)), O("Кровь в голову", (Stat.OnslaughtChance, 0.06f), (Stat.HealthOnKill, 4f))).I("onslaught"); dictionary["blood"] = new Mastery("Мастерство крови", O("Пиявка", (Stat.LifestealPct, 0.02f)), O("Жажда", (Stat.HealthOnKill, 6f), (Stat.StaminaOnKill, 6f)), O("Ярость раненого", (Stat.BerserkPct, 0.05f))).I("lifesteal"); dictionary["execute"] = new Mastery("Мастерство добивания", O("Милосердие", (Stat.ExecutePct, 0.2f)), O("Охота на крупного", (Stat.DamageVsBossPct, 0.12f)), O("Пока целы", (Stat.FullHpDamagePct, 0.15f))).I("execute"); dictionary["armor"] = new Mastery("Мастерство брони", O("Пластины", (Stat.ArmorPct, 0.12f)), O("Подбой", (Stat.ArmorFlat, 8f), (Stat.FrostResistPct, 0.12f)), O("Лёгкий доспех", (Stat.ArmorFlat, 5f), (Stat.MoveSpeedPct, 0.03f))).I("armor"); dictionary["block"] = new Mastery("Мастерство щита", O("Стена", (Stat.BlockPowerPct, 0.15f)), O("Неутомимый блок", (Stat.BlockStaminaPct, -0.2f), (Stat.StaggerResistPct, 0.1f)), O("Удар щитом", (Stat.KnockbackPct, 0.25f), (Stat.StunChance, 0.05f))).I("block"); dictionary["parry"] = new Mastery("Мастерство парирования", O("Идеальный отскок", (Stat.ParryBonusPct, 0.3f)), O("Ответный удар", (Stat.CritChance, 0.05f), (Stat.DamageVsStaggeredPct, 0.12f)), O("Контроль", (Stat.ParryBonusPct, 0.15f), (Stat.StaggerResistPct, 0.12f))).I("parry"); dictionary["dodge"] = new Mastery("Мастерство уклонения", O("Кошачья ловкость", (Stat.DodgeChance, 0.06f)), O("Лёгкий кувырок", (Stat.DodgeStaminaPct, -0.25f), (Stat.MoveSpeedPct, 0.02f)), O("Танцор", (Stat.DodgeChance, 0.03f), (Stat.CritRecentMoveSpeedPct, 0.12f))).I("dodge"); dictionary["thorns"] = new Mastery("Мастерство шипов", O("Шипы", (Stat.ThornsPct, 0.08f)), O("Чешуя", (Stat.ThornsPct, 0.04f), (Stat.ArmorFlat, 6f)), O("Жало", (Stat.ThornsPct, 0.04f), (Stat.PoisonChance, 0.1f))).I("thorns"); dictionary["resist"] = new Mastery("Мастерство стойкости", O("Закалка", (Stat.FireResistPct, 0.12f), (Stat.FrostResistPct, 0.12f)), O("Чистая кровь", (Stat.PoisonResistPct, 0.12f), (Stat.LightningResistPct, 0.12f)), O("Быстрое исцеление", (Stat.AilmentResistPct, 0.2f))).I("ailment-res"); dictionary["tough"] = new Mastery("Мастерство выдержки", O("Каменная кожа", (Stat.DamageTakenPct, -0.05f)), O("Корни", (Stat.StaggerResistPct, 0.18f)), O("Упрямство", (Stat.DamageTakenPct, -0.025f), (Stat.HealthRegenPct, 0.15f))).I("stagger-res"); dictionary["life"] = new Mastery("Мастерство жизни", O("Сердце вепря", (Stat.MaxHealthPct, 0.06f)), O("Быстрая кровь", (Stat.HealthRegenPct, 0.2f)), O("Второе дыхание", (Stat.MaxHealthFlat, 15f), (Stat.AilmentResistPct, 0.12f))).I("hp"); dictionary["stamina"] = new Mastery("Мастерство дыхания", O("Глубокий вдох", (Stat.MaxStaminaPct, 0.08f)), O("Ровное дыхание", (Stat.StaminaRegenPct, 0.18f)), O("Жилы", (Stat.MaxStaminaFlat, 12f), (Stat.RunStaminaPct, -0.12f))).I("stamina"); dictionary["food"] = new Mastery("Мастерство стола", O("Сытость", (Stat.FoodDurationPct, 0.15f)), O("Щедрая порция", (Stat.FoodPowerPct, 0.1f)), O("Повар", (Stat.FoodPowerPct, 0.05f), (Stat.ComfortFlat, 1f))).I("food"); dictionary["potion"] = new Mastery("Мастерство настоя", O("Долгий мёд", (Stat.PotionDurationPct, 0.25f)), O("Стойкий состав", (Stat.BuffDurationPct, 0.2f)), O("Противоядие", (Stat.AilmentResistPct, 0.15f), (Stat.PoisonResistPct, 0.12f))).I("buff-dur"); dictionary["eitr"] = new Mastery("Мастерство эйтра", O("Резервуар", (Stat.MaxEitrPct, 0.1f)), O("Родник", (Stat.EitrRegenPct, 0.18f)), O("Бережливость", (Stat.EitrCostPct, -0.1f))).I("eitr"); dictionary["fire"] = new Mastery("Мастерство огня", O("Искра", (Stat.IgniteChance, 0.08f)), O("Пожар", (Stat.BurnDamagePct, 0.2f), (Stat.BurnDurationPct, 0.2f)), O("Раздувать", (Stat.DamageVsBurningPct, 0.15f), (Stat.EitrOnIgnite, 2f))).I("fire"); dictionary["shock"] = new Mastery("Мастерство молнии", O("Разряд", (Stat.ShockChance, 0.08f)), O("Гром", (Stat.ShockDamagePct, 0.2f), (Stat.ShockStunChance, 0.1f)), O("Мокрая цель", (Stat.DamageVsWetPct, 0.18f), (Stat.WetShockPct, 0.2f))).I("lightning"); dictionary["frost"] = new Mastery("Мастерство мороза", O("Иней", (Stat.FrostChance, 0.08f)), O("Стужа", (Stat.FrostPotencyPct, 0.2f), (Stat.FrostDurationPct, 0.2f)), O("По льду", (Stat.DamageVsFrozenPct, 0.18f))).I("frost"); dictionary["poison"] = new Mastery("Мастерство яда", O("Споры", (Stat.PoisonChance, 0.08f)), O("Отрава", (Stat.PoisonDamagePct, 0.2f), (Stat.PoisonDurationPct, 0.2f)), O("Разъедание", (Stat.DamageVsPoisonedPct, 0.18f))).I("poison"); dictionary["enchant"] = new Mastery("Мастерство зачарования", O("Руны на клинке", (Stat.ElementalDamagePct, 0.12f), (Stat.MeleeDamagePct, 0.05f)), O("Руны на древке", (Stat.ElementalDamagePct, 0.12f), (Stat.RangedDamagePct, 0.05f)), O("Руны на коже", (Stat.ElementalDamagePct, 0.08f), (Stat.FireResistPct, 0.1f), (Stat.FrostResistPct, 0.1f))).I("magic-arrow"); dictionary["magic"] = new Mastery("Мастерство заклинаний", O("Стихийник", (Stat.Dmg_ElementalMagic, 0.14f)), O("Кровавый маг", (Stat.Dmg_BloodMagic, 0.14f)), O("Боевой маг", (Stat.Dmg_ElementalMagic, 0.07f), (Stat.EitrOnKill, 3f))).I("magic-elem"); dictionary["move"] = new Mastery("Мастерство шага", O("Вольный ветер", (Stat.MoveSpeedPct, 0.04f)), O("Торная дорога", (Stat.PavedSpeedPct, 0.1f), (Stat.MoveSpeedPct, 0.015f)), O("Второе дыхание бегуна", (Stat.RunStaminaPct, -0.15f))).I("move"); dictionary["leap"] = new Mastery("Мастерство прыжка", O("Пружина", (Stat.JumpPct, 0.14f), (Stat.JumpStaminaPct, -0.15f)), O("Разбег", (Stat.MoveSpeedPct, 0.03f), (Stat.JumpPct, 0.06f)), O("Мягкое приземление", (Stat.FallDamagePct, -0.45f))).I("jump"); dictionary["stealth"] = new Mastery("Мастерство тени", O("Тихий шаг", (Stat.StealthPct, 0.12f), (Stat.NoisePct, -0.12f)), O("Удар из тьмы", (Stat.BackstabPct, 0.2f)), O("Ночная тень", (Stat.NightStealthPct, 0.2f), (Stat.NightDamagePct, 0.08f))).I("stealth"); dictionary["bow"] = new Mastery("Мастерство лука", O("Тугая тетива", (Stat.Dmg_Bows, 0.12f)), O("Быстрый выстрел", (Stat.DrawSpeedPct, 0.15f), (Stat.DrawStaminaPct, -0.15f)), O("Верный прицел", (Stat.AccuracyPct, 0.2f), (Stat.CritChance, 0.04f))).I("bow"); dictionary["proj"] = new Mastery("Мастерство снаряда", O("Пронзающий выстрел", (Stat.ProjectilePierce, 1f)), O("Рикошет", (Stat.ProjectileChain, 1f)), O("Расщепление", (Stat.ProjectileSplit, 1f))).I("proj-extra"); dictionary["arrow"] = new Mastery("Мастерство колчана", O("Бездонный колчан", (Stat.ArrowSaveChance, 0.15f)), O("Дальний выстрел", (Stat.ArrowRangePct, 0.25f), (Stat.ProjectileSpeedPct, 0.12f)), O("Подбор стрел", (Stat.PickupRangePct, 0.25f), (Stat.ArrowSaveChance, 0.07f))).I("arrow-save"); dictionary["chop"] = new Mastery("Мастерство рубки", O("Заточка", (Stat.ChopDamagePct, 0.25f)), O("Тлеющий подсек", (Stat.ChopBurnPct, 0.2f)), O("Охапка", (Stat.ExtraDropChance, 0.1f), (Stat.CarryWeightFlat, 20f))).I("chop"); dictionary["mine"] = new Mastery("Мастерство кирки", O("Остриё", (Stat.PickaxeDamagePct, 0.25f)), O("Трещина", (Stat.MineCrackPct, 0.2f)), O("Кирка в бою", (Stat.Dmg_Pickaxes, 0.15f), (Stat.Spd_Pickaxes, 0.08f))).I("mine"); dictionary["forage"] = new Mastery("Мастерство сбора", O("Удача добытчика", (Stat.ExtraHarvestChance, 0.12f)), O("Охапка трав", (Stat.MassPickupRadius, 2.5f)), O("Цепкие руки", (Stat.PickupRangePct, 0.3f))).I("gather-crit"); dictionary["build"] = new Mastery("Мастерство стройки", O("Экономный", (Stat.BuildRefundChance, 0.12f)), O("Мастерская кладка", (Stat.BuildDurabilityPct, 0.25f)), O("Размах", (Stat.BuildRangeFlat, 3f), (Stat.ToolStaminaPct, -0.15f))).I("build-refund"); dictionary["home"] = new Mastery("Мастерство очага", O("Уют", (Stat.ComfortFlat, 2f)), O("Крепкий сон", (Stat.RestedDurationPct, 0.25f)), O("Отдохнувший боец", (Stat.RestedDamagePct, 0.1f))).I("comfort"); dictionary["carry"] = new Mastery("Мастерство поклажи", O("Крепкая спина", (Stat.CarryWeightFlat, 40f)), O("Вьючный мул", (Stat.CartPullPct, 0.25f)), O("Свободные руки", (Stat.CarryWeightFlat, 20f), (Stat.MoveSpeedPct, 0.02f))).I("carry"); dictionary["tame"] = new Mastery("Мастерство пастуха", O("Терпение", (Stat.TamingSpeedPct, 0.3f)), O("Щедрая кормушка", (Stat.FeedDurationPct, 0.35f), (Stat.BreedChancePct, 0.2f)), O("Вожак", (Stat.TamedDamagePct, 0.25f))).I("taming"); dictionary["hunt"] = new Mastery("Мастерство охоты", O("Свежевание", (Stat.HunterDropChance, 0.15f)), O("Зверобой", (Stat.DamageVsAnimalsPct, 0.15f)), O("Следопыт", (Stat.ExploreRadiusPct, 0.2f), (Stat.XpGainPct, 0.05f))).I("hunt"); dictionary["sail"] = new Mastery("Мастерство воды", O("Гребок", (Stat.SwimSpeedPct, 0.15f), (Stat.SwimStaminaPct, -0.2f)), O("Руль", (Stat.SailSpeedPct, 0.1f), (Stat.SailControlPct, 0.2f)), O("Морская охота", (Stat.DamageVsSeaPct, 0.18f))).I("swim"); dictionary["bomb"] = new Mastery("Мастерство подрывника", O("Мина", (Stat.BombMine, 1f)).X("Брошенная бомба ложится и ждёт, пока к ней не подойдёт враг."), O("Двойной разрыв", (Stat.DoubleBlastChance, 0.15f)), O("Осколки", (Stat.BombDamagePct, 0.15f), (Stat.BlastRadiusPct, 0.1f))).I("fire"); dictionary["fish"] = new Mastery("Мастерство рыбака", O("Клёв", (Stat.FishBiteChance, 0.3f)), O("Богатый улов", (Stat.ExtraFishChance, 0.12f)), O("Крепкая леска", (Stat.FishStaminaPct, -0.3f))).I("swim"); dictionary["forge"] = new Mastery("Мастерство горна", O("Бережливая ковка", (Stat.CraftRefundChance, 0.08f)), O("Плотная поковка", (Stat.CraftDurabilityPct, 0.25f)), O("Жаркий поддув", (Stat.SmeltSpeedPct, 0.3f), (Stat.ExtraBarChance, 0.05f))).I("mine"); dictionary["weather"] = new Mastery("Мастерство непогоды", O("Северная кровь", (Stat.ColdResistPct, 0.2f), (Stat.FrostResistPct, 0.1f)), O("Сухая одежда", (Stat.DryingPct, 0.5f), (Stat.WetResistPct, 0.12f)), O("Закалка непогодой", (Stat.WetResistPct, 0.25f), (Stat.HealthRegenPct, 0.1f))).I("res-frost"); dictionary["lore"] = new Mastery("Знание земель", O("Чёрный лес", (Stat.DamageVsForestPct, 0.18f), (Stat.DamageVsDvergrPct, 0.12f)), O("Горы и мгла", (Stat.DamageVsMountainPct, 0.18f), (Stat.DamageVsMistlandsPct, 0.12f)), O("Равнины и море", (Stat.DamageVsPlainsPct, 0.18f), (Stat.DamageVsSeaPct, 0.12f))).I("vs-boss"); dictionary["night"] = new Mastery("Мастерство ночи", O("Ночной клинок", (Stat.NightDamagePct, 0.12f)), O("Ночной покой", (Stat.NightRegenPct, 0.25f), (Stat.NightArmorFlat, 6f)), O("Ночная смена", (Stat.NightGatherPct, 0.2f), (Stat.NightXpPct, 0.12f))).I("night"); return dictionary; } private static Key K(string name, string extra, params (Stat, float)[] mods) { return new Key(name, extra, mods); } internal static Region[] Regions() { return new Region[6] { new Region { B = Branch.War, Span = 80f, Lanes = 4, Travel = new(Stat, float)[4][] { MIGHT, FINESSE, MIGHT, FINESSE }, GateName = "Тропа войны", Clusters = new Cl[13] { C("Клинок ярла", "sword", N((Stat.Dmg_Swords, 0.14f), (Stat.Spd_Swords, 0.06f)), S("Острота", 3, (Stat.Dmg_Swords, 0.05f)), S("Выпад", 2, (Stat.Spd_Swords, 0.03f))).I("skill:Swords"), C("Секира", "axe", N((Stat.Dmg_Axes, 0.14f), (Stat.KnockbackPct, 0.12f)), S("Лезвие", 3, (Stat.Dmg_Axes, 0.05f)), S("Взмах", 2, (Stat.Spd_Axes, 0.03f))).I("item:AxeIron"), C("Дробитель", "club", N((Stat.Dmg_Clubs, 0.14f), (Stat.StunChance, 0.05f)), S("Тяжесть", 3, (Stat.Dmg_Clubs, 0.05f)), S("Оглушающий замах", 2, (Stat.StunChance, 0.025f)), S("Короткий замах", 1, (Stat.Spd_Clubs, 0.03f))).I("item:Mace"), C("Копейщик", "spear", N((Stat.Dmg_Spears, 0.13f), (Stat.Dmg_Polearms, 0.13f)), S("Древко", 3, (Stat.Dmg_Spears, 0.05f)), S("Размах", 2, (Stat.Dmg_Polearms, 0.05f)), S("Лёгкое древко", 1, (Stat.Spd_Spears, 0.03f)), S("Перехват", 1, (Stat.Spd_Polearms, 0.03f))).I("item:SpearBronze"), C("Бой без оружия", "fist", N((Stat.Spd_Unarmed, 0.1f), (Stat.UnarmedDamageTakenPct, -0.12f)), S("Голые руки", 3, (Stat.Dmg_Unarmed, 0.06f)), S("Уклончивый бой", 2, (Stat.DodgeChance, 0.02f))).I("unarmed"), C("Мастер темпа", "tempo", N((Stat.AttackSpeedPct, 0.05f), (Stat.AttackStaminaPct, -0.08f)), S("Быстрый замах", 3, (Stat.AttackSpeedPct, 0.018f)), S("Лёгкая рука", 2, (Stat.AttackStaminaPct, -0.04f))).I("atk-speed"), C("Убийца", "knife", N((Stat.Dmg_Knives, 0.14f), (Stat.BackstabPct, 0.12f)), S("Быстрый нож", 3, (Stat.Dmg_Knives, 0.05f)), S("Тихий удар", 2, (Stat.BackstabPct, 0.05f)), S("Лёгкое лезвие", 1, (Stat.Spd_Knives, 0.03f))).I("item:KnifeChitin"), C("Мастер оружия", "crit", N((Stat.CritChance, 0.05f), (Stat.CritDamagePct, 0.12f)), S("Точный глаз", 3, (Stat.CritChance, 0.02f)), S("Жестокость", 2, (Stat.CritDamagePct, 0.06f))).I("crit"), C("Сокрушитель", "stagger", N((Stat.StaggerDamagePct, 0.2f), (Stat.DamageVsStaggeredPct, 0.12f)), S("Толчок", 3, (Stat.StaggerDamagePct, 0.07f)), S("Добивание оглушённых", 2, (Stat.DamageVsStaggeredPct, 0.05f))).I("stagger-dmg"), C("Кулачный боец", "fist", N((Stat.Dmg_Unarmed, 0.16f), (Stat.Spd_Unarmed, 0.07f)), S("Костяшки", 3, (Stat.Dmg_Unarmed, 0.06f)), S("Латный кулак", 2, (Stat.UnarmedDamageTakenPct, -0.05f))).I("skill:Unarmed"), C("Разгон", "crit", N((Stat.CritRecentDamagePct, 0.15f), (Stat.CritRecentAttackSpeedPct, 0.12f)), S("Кураж", 3, (Stat.CritRecentDamagePct, 0.06f)), S("Второе дыхание", 2, (Stat.CritRecentStaminaRegenPct, 0.08f))).I("crit"), C("Ярость", "blood", N((Stat.BerserkPct, 0.05f), (Stat.HealthRegenPct, 0.12f)), S("Горячая кровь", 3, (Stat.BerserkPct, 0.018f)), S("Терпение к боли", 2, (Stat.HealthRegenPct, 0.05f))).I("berserk"), C("Точка слома", "execute", N((Stat.ExecutePct, 0.15f), (Stat.CritChance, 0.03f)), S("Расчётливый удар", 3, (Stat.ExecutePct, 0.05f)), S("Выждать", 2, (Stat.CritChance, 0.015f))).I("execute") }, Rim = new Cl[8] { C("Пробой брони", "stagger", N((Stat.PhysicalDamagePct, 0.12f), (Stat.StaggerDamagePct, 0.15f)), S("Клин", 3, (Stat.PhysicalDamagePct, 0.04f)), S("Плечом", 2, (Stat.StaggerDamagePct, 0.06f))).I("stagger-dmg"), C("Раж", "onslaught", N((Stat.OnslaughtChance, 0.12f), (Stat.OnslaughtPowerPct, 0.2f)), S("Жажда боя", 3, (Stat.OnslaughtChance, 0.045f)), S("Азарт", 1, (Stat.OnslaughtPowerPct, 0.1f))).I("onslaught"), C("Добивающий удар", "execute", N((Stat.ExecutePct, 0.18f), (Stat.DamageVsBossPct, 0.08f)), S("Милосердие", 3, (Stat.ExecutePct, 0.07f))).I("execute"), C("Кровопийца", "blood", N((Stat.LifestealPct, 0.02f), (Stat.HealthOnKill, 6f)), S("Кровь врага", 3, (Stat.HealthOnKill, 3f)), S("Пиявка", 1, (Stat.LifestealPct, 0.008f))).I("lifesteal"), C("Инструмент как оружие", "mine", N((Stat.Dmg_Pickaxes, 0.16f), (Stat.Dmg_Axes, 0.1f)), S("Удар киркой", 3, (Stat.Dmg_Pickaxes, 0.06f))).I("mine"), C("Охотник на чудовищ", "lore", N((Stat.DamageVsBossPct, 0.12f), (Stat.DamageVsUndeadPct, 0.12f)), S("Гроза нежити", 2, (Stat.DamageVsUndeadPct, 0.06f)), S("Знание фулингов", 2, (Stat.DamageVsPlainsPct, 0.06f))).I("vs-boss"), C("Тяжёлый молот", null, N((Stat.PhysicalDamagePct, 0.1f), (Stat.AttackStaminaPct, -0.1f)), S("Крепкий хват", 3, (Stat.PhysicalDamagePct, 0.035f))).I("club"), C("Хладнокровный боец", null, N((Stat.FullHpDamagePct, 0.15f), (Stat.AilmentResistPct, 0.12f)), S("Выдержка", 3, (Stat.FullHpDamagePct, 0.05f))).I("fullhp") }, Keys = new Key[2] { K("Берсерк", "Ключевой узел: бьёшь быстрее и больнее, но защита забыта — щит не держит, броня трещит.", (Stat.AttackSpeedPct, 0.18f), (Stat.MeleeDamagePct, 0.3f), (Stat.OnslaughtChance, 0.15f), (Stat.ArmorPct, -0.4f), (Stat.BlockPowerPct, -0.6f), (Stat.DamageTakenPct, 0.2f)).I("berserk"), K("Кулак Тора", "Ключевой узел: голый кулак бьёт сильнее любого оружия, но любое оружие в руках становится обузой.", (Stat.Dmg_Unarmed, 0.6f), (Stat.Spd_Unarmed, 0.15f), (Stat.UnarmedDamageTakenPct, -0.15f), (Stat.MeleeDamagePct, -0.45f), (Stat.RangedDamagePct, -0.45f)).I("unarmed") } }, new Region { B = Branch.Steel, Span = 52f, Lanes = 3, Travel = new(Stat, float)[3][] { MIGHT, SPIRIT, MIGHT }, GateName = "Тропа стали", Clusters = new Cl[7] { C("Железная кожа", "armor", N((Stat.ArmorFlat, 12f), (Stat.ArmorPct, 0.08f)), S("Пластина", 3, (Stat.ArmorFlat, 4f)), S("Наплечник", 2, (Stat.ArmorFlat, 4f))).I("armor"), C("Бастион", "block", N((Stat.BlockPowerPct, 0.16f), (Stat.BlockStaminaPct, -0.12f)), S("Хват щита", 3, (Stat.BlockPowerPct, 0.055f)), S("Упор", 2, (Stat.BlockStaminaPct, -0.06f))).I("block"), C("Непоколебимый", "tough", N((Stat.DamageTakenPct, -0.05f), (Stat.StaggerResistPct, 0.15f)), S("Каменная кожа", 3, (Stat.DamageTakenPct, -0.018f)), S("Корни", 2, (Stat.StaggerResistPct, 0.06f))).I("stagger-res"), C("Идеальный отскок", "parry", N((Stat.ParryBonusPct, 0.25f), (Stat.CritChance, 0.03f)), S("Отскок", 3, (Stat.ParryBonusPct, 0.09f)), S("Контрудар", 2, (Stat.DamageVsStaggeredPct, 0.05f))).I("parry"), C("Танцор", "dodge", N((Stat.DodgeChance, 0.05f), (Stat.DodgeStaminaPct, -0.15f)), S("Кувырок", 3, (Stat.DodgeChance, 0.018f)), S("Скольжение", 2, (Stat.DodgeStaminaPct, -0.07f))).I("dodge"), C("Живучий страж", "tough", N((Stat.HealthRegenPct, 0.18f), (Stat.ArmorFlat, 8f)), S("Дыхание сквозь боль", 3, (Stat.HealthRegenPct, 0.06f)), S("Подкладка", 2, (Stat.ArmorFlat, 4f))).I("hp-regen"), C("Тяжёлый доспех", "armor", N((Stat.ArmorPct, 0.14f), (Stat.MoveSpeedPct, -0.03f)), S("Лишняя пластина", 3, (Stat.ArmorPct, 0.05f))).I("armor-pct") }, Rim = new Cl[8] { C("Шлем", "tough", N((Stat.StaggerResistPct, 0.18f), (Stat.DamageTakenPct, -0.03f)), S("Нащёчник", 3, (Stat.StaggerResistPct, 0.06f))).I("stagger-res"), C("Северный доспех", "weather", N((Stat.ColdResistPct, 0.16f), (Stat.ArmorFlat, 8f)), S("Подбой из меха", 3, (Stat.ColdResistPct, 0.055f))).I("res-frost"), C("Кольчуга шипов", "thorns", N((Stat.ThornsPct, 0.08f), (Stat.ArmorFlat, 8f)), S("Шипы", 3, (Stat.ThornsPct, 0.03f))).I("thorns"), C("Закалённый в огне", "resist", N((Stat.FireResistPct, 0.15f), (Stat.FrostResistPct, 0.15f)), S("Привычка к жару", 2, (Stat.FireResistPct, 0.06f)), S("Тёплая кровь", 2, (Stat.FrostResistPct, 0.06f))).I("ailment-res"), C("Чистая кровь", "resist", N((Stat.PoisonResistPct, 0.15f), (Stat.LightningResistPct, 0.15f)), S("Противоядие", 2, (Stat.PoisonResistPct, 0.06f)), S("Заземление", 2, (Stat.LightningResistPct, 0.06f))).I("ailment-res"), C("Стена", "tough", N((Stat.StaggerResistPct, 0.22f), (Stat.KnockbackPct, 0.15f)), S("Крепкая стойка", 3, (Stat.StaggerResistPct, 0.08f))).I("stagger-res"), C("Таран", "block", N((Stat.KnockbackPct, 0.25f), (Stat.StunChance, 0.05f)), S("Удар щитом", 3, (Stat.KnockbackPct, 0.09f))).I("knockback"), C("Кулак в броне", "fist", N((Stat.Dmg_Unarmed, 0.14f), (Stat.ArmorFlat, 8f)), S("Латная перчатка", 3, (Stat.Dmg_Unarmed, 0.05f))).I("unarmed") }, Keys = new Key[2] { K("Несокрушимый", "Ключевой узел: ты стена, а не таран — урона держишь вдвое меньше, но и двигаешься, и бьёшь заметно медленнее.", (Stat.DamageTakenPct, -0.22f), (Stat.ArmorPct, 0.25f), (Stat.StaggerResistPct, 0.3f), (Stat.MoveSpeedPct, -0.18f), (Stat.AttackSpeedPct, -0.2f)).I("armor-pct"), K("Отражение", "Ключевой узел: удар по тебе возвращается втрое, но собственный урон уже не тот.", (Stat.ThornsPct, 0.25f), (Stat.ArmorFlat, 20f), (Stat.DamagePct, -0.3f)).I("thorns") } }, new Region { B = Branch.Toil, Span = 57f, Lanes = 4, Travel = new(Stat, float)[4][] { FINESSE, MIGHT, FINESSE, MIGHT }, GateName = "Тропа труда", Clusters = new Cl[12] { C("Мозолистые руки", null, N((Stat.ToolStaminaPct, -0.14f), (Stat.StaminaRegenPct, 0.1f)), S("Ремесло", 3, (Stat.ToolStaminaPct, -0.05f)), S("Сноровка", 2, (Stat.StaminaRegenPct, 0.04f))).I("tool-stamina"), C("Крепкая спина", "carry", N((Stat.CarryWeightFlat, 45f), (Stat.MoveSpeedPct, 0.02f)), S("Поклажа", 3, (Stat.CarryWeightFlat, 15f)), S("Вьючный мул", 2, (Stat.CartPullPct, 0.1f))).I("carry"), C("Лесоруб", "chop", N((Stat.ChopDamagePct, 0.25f), (Stat.Dmg_Axes, 0.08f)), S("Заточка", 3, (Stat.ChopDamagePct, 0.09f)), S("Заготовка леса", 2, (Stat.ChopDamagePct, 0.07f))).I("chop"), C("Горняк", "mine", N((Stat.PickaxeDamagePct, 0.25f), (Stat.MineCrackPct, 0.12f)), S("Остриё кирки", 3, (Stat.PickaxeDamagePct, 0.09f)), S("Раскол", 2, (Stat.MineCrackPct, 0.05f))).I("mine"), C("Экономный", "build", N((Stat.BuildRefundChance, 0.14f), (Stat.ToolStaminaPct, -0.08f)), S("Расчёт", 3, (Stat.BuildRefundChance, 0.05f)), S("Артельщик", 2, (Stat.ToolStaminaPct, -0.05f))).I("build-refund"), C("Собиратель", "forage", N((Stat.ExtraHarvestChance, 0.14f), (Stat.PickupRangePct, 0.2f)), S("Собирательство", 3, (Stat.ExtraHarvestChance, 0.05f)), S("Цепкие руки", 2, (Stat.PickupRangePct, 0.08f))).I("gather-crit"), C("Мастер-строитель", "build", N((Stat.BuildDurabilityPct, 0.25f), (Stat.BuildRangeFlat, 3f)), S("Мастерская кладка", 3, (Stat.BuildDurabilityPct, 0.09f)), S("Рычаг", 2, (Stat.BuildRangeFlat, 1.2f))).I("build-refund"), C("Хранитель очага", "home", N((Stat.ComfortFlat, 2f), (Stat.RestedDurationPct, 0.2f)), S("Очаг", 2, (Stat.ComfortFlat, 1f)), S("Уютный угол", 3, (Stat.RestedDurationPct, 0.07f))).I("comfort"), C("Пастух", "tame", N((Stat.TamingSpeedPct, 0.35f), (Stat.FeedDurationPct, 0.25f)), S("Терпение", 3, (Stat.TamingSpeedPct, 0.12f)), S("Кормилец", 2, (Stat.FeedDurationPct, 0.1f))).I("taming"), C("Скотный двор", "tame", N((Stat.BreedChancePct, 0.3f), (Stat.TamedDamagePct, 0.15f)), S("Загон", 3, (Stat.BreedChancePct, 0.1f)), S("Вожак", 2, (Stat.TamedDamagePct, 0.06f))).I("taming"), C("Каменоломня", "mine", N((Stat.PickaxeDamagePct, 0.18f), (Stat.BuildRefundChance, 0.08f)), S("Отбойник", 3, (Stat.PickaxeDamagePct, 0.06f)), S("Отёска", 2, (Stat.BuildRefundChance, 0.03f))).I("mine"), C("Погреб", "food", N((Stat.FoodDurationPct, 0.16f), (Stat.CarryWeightFlat, 25f)), S("Соленья", 3, (Stat.FoodDurationPct, 0.055f)), S("Полка", 2, (Stat.CarryWeightFlat, 10f))).I("food") }, Rim = new Cl[9] { C("Смолокур", "chop", N((Stat.ChopBurnPct, 0.18f), (Stat.IgniteChance, 0.06f)), S("Смола", 3, (Stat.ChopBurnPct, 0.06f))).I("chop"), C("Щедрый стол", "food", N((Stat.FoodPowerPct, 0.1f), (Stat.ComfortFlat, 1f)), S("Повар", 3, (Stat.FoodPowerPct, 0.035f))).I("food"), C("Ночная смена", "night", N((Stat.NightGatherPct, 0.2f), (Stat.NightBuildPct, 0.15f)), S("Работа в темноте", 3, (Stat.NightGatherPct, 0.07f))).I("night"), C("Старатель", "mine", N((Stat.ExtraDropChance, 0.14f), (Stat.GatherCritChance, 0.1f)), S("Разведка жил", 3, (Stat.ExtraDropChance, 0.05f)), S("Чутьё", 2, (Stat.GatherCritChance, 0.04f))).I("gather-crack"), C("Свежевание", "hunt", N((Stat.HunterDropChance, 0.16f), (Stat.DamageVsAnimalsPct, 0.1f)), S("Охотничий нож", 3, (Stat.HunterDropChance, 0.055f))).I("hunt"), C("Навес", "weather", N((Stat.DryingPct, 0.4f), (Stat.ComfortFlat, 1f)), S("Сухая кровля", 3, (Stat.DryingPct, 0.13f))).I("comfort"), C("Горн", "forge", N((Stat.SmeltSpeedPct, 0.25f), (Stat.ExtraBarChance, 0.07f)), S("Поддув", 3, (Stat.SmeltSpeedPct, 0.08f)), S("Чистая руда", 2, (Stat.ExtraBarChance, 0.025f))).I("mine"), C("Ковка", "forge", N((Stat.CraftRefundChance, 0.07f), (Stat.CraftDurabilityPct, 0.18f)), S("Верный глаз кузнеца", 3, (Stat.CraftRefundChance, 0.022f)), S("Плотная поковка", 2, (Stat.CraftDurabilityPct, 0.06f))).I("build-refund"), C("Поварской глаз", "food", N((Stat.NoOvercook, 1f), (Stat.CookSpeedPct, 0.15f)), S("Пригляд за очагом", 3, (Stat.CookSpeedPct, 0.07f))).I("food") }, Keys = new Key[2] { K("Зодчий", "Ключевой узел: стройка почти ничего не стоит и стены держат как скала, но воин из тебя посредственный.", (Stat.BuildRefundChance, 0.4f), (Stat.BuildDurabilityPct, 0.5f), (Stat.BuildRangeFlat, 6f), (Stat.ToolStaminaPct, -0.35f), (Stat.DamagePct, -0.3f)).I("architect"), K("Караван", "Ключевой узел: унесёшь полдеревни и телега катится сама, но налегке ты уже не бегаешь.", (Stat.CarryWeightFlat, 150f), (Stat.CartPullPct, 0.5f), (Stat.ToolStaminaPct, -0.25f), (Stat.MoveSpeedPct, -0.12f), (Stat.DodgeChance, -0.1f)).I("carry") } }, new Region { B = Branch.Flesh, Span = 50f, Lanes = 3, Travel = new(Stat, float)[3][] { MIGHT, SPIRIT, MIGHT }, GateName = "Тропа плоти", Clusters = new Cl[7] { C("Крепкое сердце", "life", N((Stat.MaxHealthFlat, 25f), (Stat.MaxHealthPct, 0.05f)), S("Сердце", 3, (Stat.MaxHealthFlat, 9f)), S("Кровь", 2, (Stat.MaxHealthFlat, 9f))).I("hp"), C("Второе дыхание", "stamina", N((Stat.MaxStaminaFlat, 20f), (Stat.MaxStaminaPct, 0.06f)), S("Лёгкие", 3, (Stat.MaxStaminaFlat, 7f)), S("Дыхание", 2, (Stat.MaxStaminaPct, 0.025f))).I("stamina"), C("Живая вода", "life", N((Stat.HealthRegenPct, 0.22f), (Stat.MaxHealthFlat, 12f)), S("Ключ жизни", 3, (Stat.HealthRegenPct, 0.08f)), S("Восстановление", 2, (Stat.HealthRegenPct, 0.08f))).I("hp-regen"), C("Сытость", "food", N((Stat.FoodDurationPct, 0.18f), (Stat.FoodPowerPct, 0.07f)), S("Закром", 3, (Stat.FoodDurationPct, 0.06f)), S("Аппетит", 2, (Stat.FoodPowerPct, 0.03f))).I("food"), C("Ровное дыхание", "stamina", N((Stat.StaminaRegenPct, 0.2f), (Stat.RunStaminaPct, -0.1f)), S("Спокойный вдох", 3, (Stat.StaminaRegenPct, 0.07f)), S("Экономия силы", 2, (Stat.RunStaminaPct, -0.05f))).I("stamina"), C("Толстая шкура", "tough", N((Stat.DamageTakenPct, -0.05f), (Stat.MaxHealthFlat, 15f)), S("Дублёная кожа", 3, (Stat.DamageTakenPct, -0.018f)), S("Слой жира", 2, (Stat.MaxHealthFlat, 7f))).I("dmg-taken"), C("Второе сердце", "life", N((Stat.MaxHealthPct, 0.07f), (Stat.BuffDurationPct, 0.15f)), S("Ровный пульс", 3, (Stat.MaxHealthPct, 0.025f)), S("Долгое дыхание", 2, (Stat.BuffDurationPct, 0.06f))).I("hp-big") }, Rim = new Cl[7] { C("Тёплая кровь", "resist", N((Stat.FrostResistPct, 0.16f), (Stat.HealthRegenPct, 0.12f)), S("Жар в груди", 3, (Stat.FrostResistPct, 0.06f))).I("res-frost"), C("Северянин", "weather", N((Stat.ColdResistPct, 0.2f), (Stat.FrostResistPct, 0.08f)), S("Привычка к стуже", 3, (Stat.ColdResistPct, 0.06f)), S("Мех на плечах", 2, (Stat.FrostResistPct, 0.04f))).I("res-frost"), C("Крепкий организм", "resist", N((Stat.AilmentResistPct, 0.22f), (Stat.PoisonResistPct, 0.12f)), S("Стойкий желудок", 3, (Stat.AilmentResistPct, 0.08f))).I("ailment-res"), C("Настой алхимика", "potion", N((Stat.PotionDurationPct, 0.3f), (Stat.BuffDurationPct, 0.15f)), S("Настойка", 3, (Stat.PotionDurationPct, 0.1f))).I("buff-dur"), C("Жажда", "blood", N((Stat.HealthOnKill, 8f), (Stat.StaminaOnKill, 8f)), S("Голод", 3, (Stat.HealthOnKill, 3f))).I("lifesteal"), C("Ночной покой", "night", N((Stat.NightRegenPct, 0.3f), (Stat.NightArmorFlat, 8f)), S("Крепкий сон", 3, (Stat.NightRegenPct, 0.1f))).I("night"), C("Хозяин дома", "home", N((Stat.RestedDurationPct, 0.25f), (Stat.RestedDamagePct, 0.1f)), S("Домашний уют", 3, (Stat.RestedDurationPct, 0.08f))).I("comfort") }, Keys = new Key[2] { K("Кровь Имира", "Ключевой узел: гора мяса и крови — здоровья втрое больше, но тело тяжёлое и неповоротливое.", (Stat.MaxHealthPct, 0.45f), (Stat.HealthRegenPct, 0.35f), (Stat.StaggerResistPct, 0.25f), (Stat.MaxStaminaPct, -0.3f), (Stat.MoveSpeedPct, -0.14f)).I("giant-blood"), K("Сытый", "Ключевой узел: еда держит вдвое дольше и кормит вдвое сытнее, но вне еды тело не восстанавливается.", (Stat.FoodDurationPct, 0.6f), (Stat.FoodPowerPct, 0.3f), (Stat.ComfortFlat, 2f), (Stat.HealthRegenPct, -0.6f)).I("food") } }, new Region { B = Branch.Eitr, Span = 60f, Lanes = 4, Travel = new(Stat, float)[4][] { SPIRIT, SPIRIT, FINESSE, SPIRIT }, GateName = "Тропа эйтра", Clusters = new Cl[12] { C("Резервуар", "eitr", N((Stat.MaxEitrFlat, 20f), (Stat.MaxEitrPct, 0.08f)), S("Сосуд", 3, (Stat.MaxEitrFlat, 7f)), S("Глубина", 2, (Stat.MaxEitrPct, 0.03f))).I("eitr"), C("Родник эйтра", "eitr", N((Stat.EitrRegenPct, 0.22f), (Stat.EitrOnKill, 3f)), S("Поток", 3, (Stat.EitrRegenPct, 0.08f)), S("Медитация", 2, (Stat.EitrRegenPct, 0.08f))).I("eitr"), C("Лёгкость заклинаний", "eitr", N((Stat.EitrCostPct, -0.14f), (Stat.MaxEitrFlat, 10f)), S("Лёгкое слово", 3, (Stat.EitrCostPct, -0.05f))).I("eitr"), C("Элементалист", "magic", N((Stat.Dmg_ElementalMagic, 0.16f), (Stat.ElementalDamagePct, 0.1f)), S("Стихия", 3, (Stat.Dmg_ElementalMagic, 0.06f)), S("Зачарование", 2, (Stat.ElementalDamagePct, 0.04f))).I("magic-elem"), C("Искра огня", "fire", N((Stat.IgniteChance, 0.1f), (Stat.BurnDamagePct, 0.12f)), S("Трут", 3, (Stat.IgniteChance, 0.035f)), S("Уголь", 2, (Stat.BurnDamagePct, 0.05f))).I("fire"), C("Искра молнии", "shock", N((Stat.ShockChance, 0.1f), (Stat.ShockDamagePct, 0.12f)), S("Разряд", 3, (Stat.ShockChance, 0.035f)), S("Проводник", 2, (Stat.ShockDamagePct, 0.05f))).I("lightning"), C("Пламя Муспельхейма", "fire", N((Stat.BurnDamagePct, 0.2f), (Stat.DamageVsBurningPct, 0.15f)), S("Жар", 2, (Stat.BurnDamagePct, 0.07f)), S("Раздувать", 2, (Stat.IgniteChance, 0.03f)), S("Долгий огонь", 1, (Stat.BurnDurationPct, 0.12f))).I("fire"), C("Громовержец", "shock", N((Stat.ShockDamagePct, 0.2f), (Stat.ShockStunChance, 0.12f)), S("Раскат", 2, (Stat.ShockDamagePct, 0.07f)), S("Искра на клинке", 2, (Stat.ShockChance, 0.03f)), S("Гроза в спину", 1, (Stat.WetShockPct, 0.15f))).I("lightning"), C("Иней", "frost", N((Stat.FrostChance, 0.1f), (Stat.FrostPotencyPct, 0.12f)), S("Холодок", 3, (Stat.FrostChance, 0.035f)), S("Стужа", 2, (Stat.FrostPotencyPct, 0.05f))).I("frost"), C("Вечная мерзлота", "frost", N((Stat.FrostPotencyPct, 0.2f), (Stat.DamageVsFrozenPct, 0.15f)), S("По льду", 2, (Stat.DamageVsFrozenPct, 0.06f)), S("Долгий холод", 2, (Stat.FrostDurationPct, 0.1f)), S("Иней на кромке", 1, (Stat.FrostChance, 0.03f))).I("frost"), C("Проводник", "shock", N((Stat.ShockDamagePct, 0.18f), (Stat.LightningResistPct, 0.12f)), S("Медная нить", 3, (Stat.ShockDamagePct, 0.06f)), S("Заземление", 2, (Stat.LightningResistPct, 0.05f))).I("lightning"), C("Пепел", "fire", N((Stat.BurnDurationPct, 0.2f), (Stat.FireResistPct, 0.12f)), S("Тлеющий уголь", 3, (Stat.BurnDurationPct, 0.07f)), S("Привычка к огню", 2, (Stat.FireResistPct, 0.05f))).I("fire") }, Rim = new Cl[5] { C("Кислота", "poison", N((Stat.PoisonDurationPct, 0.2f), (Stat.DamageVsPoisonedPct, 0.12f)), S("Едкая слизь", 3, (Stat.PoisonDurationPct, 0.07f))).I("poison"), C("Чумной маг", "poison", N((Stat.PoisonChance, 0.12f), (Stat.PoisonDamagePct, 0.18f)), S("Споры", 2, (Stat.PoisonChance, 0.04f)), S("Отрава", 2, (Stat.PoisonDamagePct, 0.07f)), S("Разъедание", 1, (Stat.DamageVsPoisonedPct, 0.1f))).I("poison"), C("Кровавый маг", "magic", N((Stat.Dmg_BloodMagic, 0.18f), (Stat.LifestealPct, 0.012f)), S("Жертва", 3, (Stat.Dmg_BloodMagic, 0.06f))).I("magic-blood"), C("Бомбодел", "bomb", N((Stat.BombCraftRefundChance, 0.09f), (Stat.ExtraBombChance, 0.08f)), S("Экономный порох", 3, (Stat.BombCraftRefundChance, 0.03f)), S("Лишний заряд", 2, (Stat.ExtraBombChance, 0.03f))).I("fire"), C("Колдун ночи", "night", N((Stat.NightEitrRegenPct, 0.3f), (Stat.NightDamagePct, 0.12f)), S("Лунный свет", 3, (Stat.NightEitrRegenPct, 0.1f))).I("night") }, Keys = new Key[2] { K("Пепел Сурта", "Ключевой узел: всё, чего ты касаешься, горит — но сталь в твоих руках забыла, что такое резать.", (Stat.IgniteChance, 0.35f), (Stat.BurnDamagePct, 0.6f), (Stat.DamageVsBurningPct, 0.3f), (Stat.FireResistPct, 0.3f), (Stat.PhysicalDamagePct, -0.4f)).I("surt-ash"), K("Пустой сосуд", "Ключевой узел: эйтр течёт даром, а тело истончилось до предела.", (Stat.EitrCostPct, -0.35f), (Stat.EitrRegenPct, 0.6f), (Stat.MaxEitrPct, 0.35f), (Stat.MaxHealthPct, -0.35f)).I("eitr-cost") } }, new Region { B = Branch.Wind, Span = 63f, Lanes = 4, Travel = new(Stat, float)[4][] { FINESSE, FINESSE, SPIRIT, FINESSE }, GateName = "Тропа ветра", Clusters = new Cl[13] { C("Вольный ветер", "move", N((Stat.MoveSpeedPct, 0.04f), (Stat.RunStaminaPct, -0.1f)), S("Шаг", 3, (Stat.MoveSpeedPct, 0.012f)), S("Лёгкий ход", 2, (Stat.RunStaminaPct, -0.05f))).I("move"), C("Марафонец", "move", N((Stat.RunStaminaPct, -0.18f), (Stat.MaxStaminaFlat, 12f)), S("Лёгкие бегуна", 3, (Stat.RunStaminaPct, -0.06f)), S("Ровный бег", 2, (Stat.StaminaRegenPct, 0.05f))).I("run-stamina"), C("Меткий лучник", "bow", N((Stat.Dmg_Bows, 0.16f), (Stat.CritChance, 0.03f)), S("Тугая жила", 3, (Stat.Dmg_Bows, 0.06f)), S("Ровный выстрел", 2, (Stat.AccuracyPct, 0.06f))).I("bow"), C("Тень", "stealth", N((Stat.StealthPct, 0.18f), (Stat.NoisePct, -0.15f)), S("Тихий шаг", 3, (Stat.StealthPct, 0.06f)), S("Бесшумность", 2, (Stat.NoisePct, -0.06f))).I("stealth"), C("Тугая тетива", "bow", N((Stat.DrawSpeedPct, 0.18f), (Stat.DrawStaminaPct, -0.15f)), S("Быстрая рука", 3, (Stat.DrawSpeedPct, 0.06f)), S("Лёгкая тетива", 2, (Stat.DrawStaminaPct, -0.06f))).I("bow"), C("Скалолаз", "leap", N((Stat.JumpPct, 0.14f), (Stat.FallDamagePct, -0.3f)), S("Пружина", 3, (Stat.JumpPct, 0.05f)), S("Мягкое приземление", 2, (Stat.FallDamagePct, -0.12f))).I("jump"), C("Рой стрел", "proj", N((Stat.ExtraProjectiles, 1f), (Stat.RangedDamagePct, -0.1f)), S("Оперение", 3, (Stat.ProjectileSpeedPct, 0.07f)), S("Пучок стрел", 2, (Stat.RangedDamagePct, 0.05f))).I("proj-extra"), C("Засада", "stealth", N((Stat.BackstabPct, 0.22f), (Stat.CritDamagePct, 0.12f)), S("Удар в спину", 3, (Stat.BackstabPct, 0.08f)), S("Выстрел из тьмы", 2, (Stat.SneakStaminaPct, -0.08f))).I("backstab"), C("Стрелок", "bow", N((Stat.Dmg_Crossbows, 0.18f), (Stat.AttackSpeedPct, 0.04f)), S("Ложе арбалета", 3, (Stat.Dmg_Crossbows, 0.06f)), S("Быстрая перезарядка", 2, (Stat.AttackSpeedPct, 0.015f))).I("crossbow"), C("Подрывник", "bomb", N((Stat.BombDamagePct, 0.2f), (Stat.BlastRadiusPct, 0.12f)), S("Плотный заряд", 3, (Stat.BombDamagePct, 0.07f)), S("Бережливая рука", 2, (Stat.BombSaveChance, 0.04f))).I("fire"), C("Бездонный колчан", "arrow", N((Stat.ArrowSaveChance, 0.16f), (Stat.PickupRangePct, 0.15f)), S("Полный колчан", 3, (Stat.ArrowSaveChance, 0.055f))).I("arrow-save"), C("Ловчий", "hunt", N((Stat.AccuracyPct, 0.18f), (Stat.DamageVsAnimalsPct, 0.12f)), S("Верный глаз", 3, (Stat.AccuracyPct, 0.06f)), S("Повадки зверя", 2, (Stat.DamageVsAnimalsPct, 0.05f))).I("hunt"), C("Бесшумный выстрел", "stealth", N((Stat.Dmg_Bows, 0.12f), (Stat.NoisePct, -0.15f)), S("Войлок на тетиве", 3, (Stat.NoisePct, -0.05f)), S("Замерший вдох", 2, (Stat.Dmg_Bows, 0.045f))).I("bow") }, Rim = new Cl[7] { C("Вольный бег", "move", N((Stat.RunStaminaPct, -0.15f), (Stat.JumpStaminaPct, -0.2f), (Stat.Slide, 1f)), S("Второе дыхание бегуна", 3, (Stat.RunStaminaPct, -0.05f)), S("Твёрдая пятка", 2, (Stat.FallDamagePct, -0.06f))).X("Присядь на склоне и держи путь вниз — поедешь быстрее, чем сбежишь. Встань, чтобы затормозить.").I("run-stamina"), C("Непогода", "weather", N((Stat.WetResistPct, 0.18f), (Stat.DryingPct, 0.25f)), S("Плащ", 3, (Stat.WetResistPct, 0.06f)), S("Отряхнуться", 2, (Stat.DryingPct, 0.09f))).I("wet"), C("Дальний выстрел", "arrow", N((Stat.ArrowRangePct, 0.3f), (Stat.ProjectileSpeedPct, 0.15f)), S("Древко", 3, (Stat.ArrowRangePct, 0.1f))).I("arrow-speed"), C("Морской ход", "sail", N((Stat.SailSpeedPct, 0.12f), (Stat.SailControlPct, 0.25f)), S("Парус", 2, (Stat.SailSpeedPct, 0.045f)), S("Гребок", 2, (Stat.SwimSpeedPct, 0.07f))).I("swim"), C("Рыбацкая удача", "fish", N((Stat.ExtraFishChance, 0.12f), (Stat.FishBiteChance, 0.2f)), S("Прикормка", 3, (Stat.FishBiteChance, 0.07f)), S("Крепкая леска", 2, (Stat.FishStaminaPct, -0.07f))).I("swim"), C("Следопыт", "hunt", N((Stat.ExploreRadiusPct, 0.25f), (Stat.XpGainPct, 0.06f)), S("Любопытство", 3, (Stat.ExploreRadiusPct, 0.09f))).I("explore"), C("Ночной охотник", "night", N((Stat.NightStealthPct, 0.25f), (Stat.NightMoveSpeedPct, 0.1f)), S("Глаза ночи", 3, (Stat.NightStealthPct, 0.09f))).I("night") }, Keys = new Key[2] { K("Крылья Хугина", "Ключевой узел: держи прыжок в воздухе — и ты планируешь; падение больше не убивает, но тело стало почти невесомым.", (Stat.Glide, 1f), (Stat.SafeFall, 1f), (Stat.DoubleJump, 1f), (Stat.MoveSpeedPct, 0.1f), (Stat.ArmorPct, -0.35f), (Stat.MaxHealthPct, -0.15f)).I("glide"), K("Тень охотника", "Ключевой узел: из тени ты убиваешь с одного удара, но в открытом бою не держишь ничего.", (Stat.BackstabPct, 0.6f), (Stat.StealthPct, 0.35f), (Stat.CritDamagePct, 0.35f), (Stat.NightDamagePct, 0.2f), (Stat.MaxHealthPct, -0.3f), (Stat.ArmorPct, -0.25f)).I("hunter-shadow") } } }; } internal static Cl[] BorderClusters() { return new Cl[6] { C("Щит и меч", "block", N((Stat.BlockPowerPct, 0.12f), (Stat.MeleeDamagePct, 0.1f)), S("Строй", 2, (Stat.BlockPowerPct, 0.05f)), S("Клинок за щитом", 2, (Stat.MeleeDamagePct, 0.04f))).I("block"), C("Крепость", "build", N((Stat.BuildDurabilityPct, 0.25f), (Stat.ArmorFlat, 10f)), S("Крепкие стены", 2, (Stat.BuildDurabilityPct, 0.09f)), S("Заклёпки", 2, (Stat.ArmorFlat, 4f))).I("fortress"), C("Урожай", "food", N((Stat.FoodPowerPct, 0.1f), (Stat.ExtraHarvestChance, 0.12f)), S("Жатва", 2, (Stat.ExtraHarvestChance, 0.05f)), S("Щедрая порция", 2, (Stat.FoodPowerPct, 0.035f))).I("food"), C("Кровь и эйтр", "magic", N((Stat.Dmg_BloodMagic, 0.14f), (Stat.MaxEitrFlat, 12f)), S("Жертва крови", 2, (Stat.Dmg_BloodMagic, 0.05f)), S("Сосуд", 2, (Stat.MaxEitrFlat, 6f))).I("magic-blood"), C("Зачарованные стрелы", "enchant", N((Stat.ElementalDamagePct, 0.14f), (Stat.RangedDamagePct, 0.1f)), S("Руна на древке", 2, (Stat.ElementalDamagePct, 0.05f)), S("Зачарованный наконечник", 2, (Stat.RangedDamagePct, 0.04f))).I("magic-arrow"), C("Охотничья тропа", "hunt", N((Stat.DamageVsAnimalsPct, 0.15f), (Stat.HunterDropChance, 0.12f)), S("Следы зверя", 2, (Stat.DamageVsAnimalsPct, 0.06f)), S("Охотничья лёжка", 2, (Stat.HunterDropChance, 0.05f))).I("hunt") }; } } public enum Branch { Core, Melee, Defense, Survival, Building, Gathering, Exploration, Ranged, Magic, War, Steel, Flesh, Wind, Eitr, Toil } public enum NodeSize { Root, Small, Notable, Keystone, Mastery } public enum TreeLayout { Classic, Reforged } public class NodeOption { public string Name; public string Extra; public List> Mods = new List>(); public string Title => L.T(Name); public string Text => StatText.DescribeAll(Mods) + (string.IsNullOrEmpty(Extra) ? "" : ("\n" + L.T(Extra))); } public class Node { public string Id; public string Name; public string Extra; public string Icon; public int Sig; public NodeSize Size; public Branch Branch; public Vector2 Pos; public List> Mods = new List>(); public List Links = new List(); public List Options; public bool IsMastery { get { if (Size == NodeSize.Mastery && Options != null) { return Options.Count > 0; } return false; } } public string Title => L.T(Name); public string Description { get { if (!string.IsNullOrEmpty(Extra)) { return StatText.DescribeAll(Mods) + "\n" + L.T(Extra); } return StatText.DescribeAll(Mods); } } public List Topics { get { List> list = new List>(Mods); if (Options != null) { foreach (NodeOption option in Options) { list.AddRange(option.Mods); } } return StatText.Topics(list); } } } public static class Tree { private class Def { public string Name; public string Extra; public string Icon; public string Mast; public (Stat, float)[] Mods; public Def(string n, params (Stat, float)[] m) { Name = n; Mods = m; } public Def X(string e) { Extra = e; return this; } public Def I(string i) { Icon = i; return this; } public Def M(string key) { Mast = key; return this; } } private class Arm { public Def Small; public int Count; public int Host; public Arm(Def s, int c, int h = 0) { Small = s; Count = c; Host = h; } } private class Cluster { public Def Notable; public Arm[] Arms; public Cluster(Def n, params Arm[] a) { Notable = n; Arms = a; } } private class Sector { public Branch B; public Def Hub; public Def Road; public Def JunctionDef; public Def Keystone; public Def[] Travel; public Cluster[] C; public Cluster[] Ring; public Cluster[] Dead; public Cluster[] Shapes; } private class Boundary { public Cluster[] C; public Cluster[] Dead; public Def SpurTip; public Def SpurS1; public Def SpurS2; public bool SpurIsKeystone; } public const string RootId = "root"; public static readonly Dictionary Nodes = new Dictionary(); private const float R_HUB = 190f; private const float R1 = 560f; private const float R2 = 980f; private const float R3 = 1400f; private const float R4 = 1820f; private const float R5 = 2240f; private const float R6 = 2660f; private const float R_SPUR1 = 2760f; private const float R_SPUR2 = 2860f; private const float R_KEY = 2970f; private const float R_CAP = 3120f; private static readonly float[] ClassicRings = new float[7] { 190f, 560f, 980f, 1400f, 1820f, 2240f, 2660f }; public static float[] RingRadii = ClassicRings; public static List<(Branch B, float Angle)> Captions = new List<(Branch, float)>(); public static List Dividers = new List(); public static TreeLayout Built = TreeLayout.Classic; public static TreeLayout? Force; private static readonly float[] RingRoads = new float[6] { 560f, 980f, 1400f, 1820f, 2240f, 2660f }; private static readonly float[] SpokeRadii = new float[11] { 380f, 700f, 840f, 1120f, 1260f, 1540f, 1680f, 1960f, 2100f, 2380f, 2520f }; public static readonly Branch[] SectorOrder = new Branch[8] { Branch.Melee, Branch.Defense, Branch.Survival, Branch.Building, Branch.Gathering, Branch.Exploration, Branch.Ranged, Branch.Magic }; public static float CaptionRadius = 3120f; private static readonly int[] RingCounts = new int[6] { 10, 10, 8, 8, 8, 10 }; private const int NoRoadRing = 0; private static readonly Stat[] NotScaled = new Stat[9] { Stat.DoubleJump, Stat.Glide, Stat.SafeFall, Stat.UnarmedGather, Stat.UnarmedToolTier, Stat.ExtraProjectiles, Stat.ProjectilePierce, Stat.ProjectileChain, Stat.ProjectileSplit }; public const int ContentVersion = 1; public static IEnumerable Gates { get { if (!Nodes.TryGetValue("root", out var value)) { return new List(); } return value.Links; } } public static bool IsGate(string id) { if (Nodes.TryGetValue("root", out var value)) { return value.Links.Contains(id); } return false; } public static float SectorAngle(int k) { return 90f - 45f * (float)k; } private static float BoundaryAngle(int k) { return SectorAngle(k) - 22.5f; } public static Color BranchColor(Branch b) { //IL_0054: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) return (Color)(b switch { Branch.Melee => new Color(0.9f, 0.33f, 0.28f), Branch.Defense => new Color(0.56f, 0.66f, 0.88f), Branch.Survival => new Color(0.66f, 0.8f, 0.32f), Branch.Building => new Color(0.93f, 0.72f, 0.3f), Branch.Gathering => new Color(0.45f, 0.78f, 0.4f), Branch.Exploration => new Color(0.3f, 0.74f, 0.82f), Branch.Ranged => new Color(0.95f, 0.58f, 0.25f), Branch.Magic => new Color(0.74f, 0.52f, 0.92f), Branch.War => new Color(0.91f, 0.35f, 0.27f), Branch.Steel => new Color(0.6f, 0.7f, 0.9f), Branch.Flesh => new Color(0.86f, 0.42f, 0.52f), Branch.Wind => new Color(0.34f, 0.8f, 0.78f), Branch.Eitr => new Color(0.68f, 0.5f, 0.95f), Branch.Toil => new Color(0.9f, 0.73f, 0.34f), _ => new Color(0.96f, 0.85f, 0.6f), }); } public static string BranchTitle(Branch b) { return L.T(BranchName(b)); } public static string BranchName(Branch b) { return b switch { Branch.Melee => "Ближний бой", Branch.Defense => "Защита", Branch.Survival => "Выживание", Branch.Building => "Строительство", Branch.Gathering => "Добыча", Branch.Exploration => "Исследование", Branch.Ranged => "Дальний бой", Branch.Magic => "Магия", Branch.War => "Война", Branch.Steel => "Сталь", Branch.Flesh => "Плоть", Branch.Wind => "Ветер", Branch.Eitr => "Эйтр", Branch.Toil => "Труд", _ => "Ядро", }; } private static Def D(string n, params (Stat, float)[] m) { return new Def(n, m); } private static Arm A(Def d, int count, int host = 0) { return new Arm(d, count, host); } private static Cluster CL(Def notable, params Arm[] arms) { return new Cluster(notable, arms); } private static Def Blend(string name, Def a, Def b) { List<(Stat, float)> list = new List<(Stat, float)>(); if (a.Mods.Length != 0) { list.Add(a.Mods[0]); } if (b.Mods.Length != 0) { list.Add(b.Mods[0]); } return new Def(name, list.ToArray()); } internal static Vector2 P(float r, float deg) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) return new Vector2(r * Mathf.Cos(deg * ((float)Math.PI / 180f)), r * Mathf.Sin(deg * ((float)Math.PI / 180f))); } internal static Node AddNode(string id, string name, NodeSize size, Branch b, Vector2 pos, params (Stat, float)[] mods) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) Node node = new Node { Id = id, Name = name, Size = size, Branch = b, Pos = pos }; for (int i = 0; i < mods.Length; i++) { (Stat, float) tuple = mods[i]; node.Mods.Add(new KeyValuePair(tuple.Item1, tuple.Item2)); } Nodes[id] = node; return node; } internal static void Connect(string a, string b) { Link(a, b); } private static Node Add(string id, Def d, NodeSize size, Branch b, Vector2 pos) { //IL_003f: 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) Node node = new Node { Id = id, Name = d.Name, Extra = d.Extra, Icon = d.Icon, Size = size, Branch = b, Pos = pos }; (Stat, float)[] mods = d.Mods; for (int i = 0; i < mods.Length; i++) { (Stat, float) tuple = mods[i]; node.Mods.Add(new KeyValuePair(tuple.Item1, tuple.Item2)); } Nodes[id] = node; return node; } private static Node AddNotable(string id, Def d, NodeSize size, Branch b, Vector2 pos, Vector2 outward) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) Node result = Add(id, d, size, b, pos); if (!string.IsNullOrEmpty(d.Mast)) { TreeB.Attach(id, id, d.Mast, pos, outward, b); } return result; } private static void Link(string a, string b) { if (!Nodes[a].Links.Contains(b)) { Nodes[a].Links.Add(b); } if (!Nodes[b].Links.Contains(a)) { Nodes[b].Links.Add(a); } } private static string Hub(int k) { return $"road_h{(k % 8 + 8) % 8}"; } private static string HubBetween(int k) { return $"road_hb{(k % 8 + 8) % 8}"; } private static string Junction(int ring, int k) { return $"j{ring}_{(k % 8 + 8) % 8}"; } private static int RingCount(int ring) { return RingCounts[Mathf.Clamp(ring, 0, RingCounts.Length - 1)]; } private static string RingId(int ring, int k, int i) { return $"ring{ring}_{(k % 8 + 8) % 8}_{i}"; } private static string RingB(int ring, int k) { return RingId(ring, k, RingCount(ring) / 2); } private static void RingSlot(int ring, float deg, out int seg, out int idx) { int num = RingCount(ring); float num2 = (90f - deg) / 45f; seg = Mathf.FloorToInt(num2); idx = Mathf.RoundToInt((num2 - (float)seg) * (float)num); seg = (seg % 8 + 8) % 8; } private static string RingOrJunction(int ring, int seg, int i) { int num = RingCount(ring); if (i <= 0) { return Junction(ring, seg); } if (i >= num) { return Junction(ring, seg + 1); } return RingId(ring, seg, i); } private static string RingNear(int ring, int k, float deg) { int num = RingCount(ring); int i = Mathf.Clamp(Mathf.RoundToInt((SectorAngle(k) - deg) / 45f * (float)num), 1, num - 1); return RingId(ring, k, i); } private static float AngleOf(Vector2 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) return Mathf.Atan2(p.y, p.x) * 57.29578f; } private static string Spoke(int k, int i) { return $"sp{(k % 8 + 8) % 8}_{i}"; } private static string BSpoke(int k, int i) { return $"bsp{(k % 8 + 8) % 8}_{i}"; } private static void BuildRoads(Sector[] sectors) { //IL_0038: 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_00de: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < 8; i++) { float num = SectorAngle(i); Sector sector = sectors[i]; Add(Hub(i), sector.Hub, NodeSize.Small, sector.B, P(190f, num)); Link("root", Hub(i)); for (int j = 0; j < SpokeRadii.Length; j++) { Add(Spoke(i, j), sector.Travel[j % sector.Travel.Length], NodeSize.Small, sector.B, P(SpokeRadii[j], num)); } Sector sector2 = S(i + 1); for (int k = 3; k < SpokeRadii.Length; k++) { Add(BSpoke(i, k), Blend("Межа", sector.Road, sector2.Road), NodeSize.Small, Branch.Core, P(SpokeRadii[k], BoundaryAngle(i))); } for (int l = 0; l < RingRoads.Length; l++) { Sector sector3 = S(i + 1); Add(Junction(l, i), sector.JunctionDef, NodeSize.Small, sector.B, P(RingRoads[l], num)); int num2 = RingCount(l); for (int m = 1; m < num2; m++) { if (l != 0) { float deg = num - 45f * (float)m / (float)num2; Vector2 pos = P(RingRoads[l], deg); if (m * 2 == num2) { Add(RingId(l, i, m), Blend("Перекрёсток", sector.Road, sector3.Road), NodeSize.Small, Branch.Core, pos); } else if (m * 2 < num2) { Add(RingId(l, i, m), sector.Road, NodeSize.Small, sector.B, pos); } else { Add(RingId(l, i, m), sector3.Road, NodeSize.Small, sector3.B, pos); } } } } } for (int n = 0; n < 8; n++) { Link(Hub(n), Spoke(n, 0)); Link(Spoke(n, 0), Junction(0, n)); for (int num3 = 0; num3 < 5; num3++) { int num4 = 1 + num3 * 2; Link(Junction(num3, n), Spoke(n, num4)); Link(Spoke(n, num4), Spoke(n, num4 + 1)); Link(Spoke(n, num4 + 1), Junction(num3 + 1, n)); if (num3 != 0) { Link(RingB(num3, n), BSpoke(n, num4)); Link(BSpoke(n, num4), BSpoke(n, num4 + 1)); Link(BSpoke(n, num4 + 1), RingB(num3 + 1, n)); } } for (int num5 = 0; num5 < RingRoads.Length; num5++) { if (num5 != 0) { int num6 = RingCount(num5); Link(Junction(num5, n), RingId(num5, n, 1)); for (int num7 = 1; num7 < num6 - 1; num7++) { Link(RingId(num5, n, num7), RingId(num5, n, num7 + 1)); } Link(RingId(num5, n, num6 - 1), Junction(num5, n + 1)); } } } Sector S(int num8) { return sectors[(num8 % 8 + 8) % 8]; } } private static float ArcGeometry(float chord, float sagitta, out float radius) { radius = (chord * chord * 0.25f + sagitta * sagitta) / (2f * sagitta); float num = Mathf.Asin(Mathf.Clamp(chord / (2f * radius), -1f, 1f)); if (!(sagitta > chord * 0.5f)) { return num; } return (float)Math.PI - num; } private static float SagittaFor(float chord, int count, float spacing) { float result = 60f; float num = 0f; for (float num2 = 50f; num2 <= 460f; num2 += 5f) { float radius; float num3 = ArcGeometry(chord, num2, out radius); float num4 = 2f * radius * Mathf.Sin(num3 / (float)(count + 1)); if (num4 >= spacing) { return num2; } if (num4 > num) { num = num4; result = num2; } } return result; } private static List ArcPoints(Vector2 entry, Vector2 exit, Vector2 normal, float sagitta, int count) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) List list = new List(); float num = Vector2.Distance(entry, exit); ArcGeometry(num, sagitta, out var radius); Vector2 val = (entry + exit) * 0.5f - normal * (radius - sagitta); float num2 = Mathf.Atan2(entry.y - val.y, entry.x - val.x); float num3 = Mathf.Atan2(exit.y - val.y, exit.x - val.x); float num4 = Mathf.DeltaAngle(num2 * 57.29578f, num3 * 57.29578f) * ((float)Math.PI / 180f); if (sagitta > num * 0.5f) { num4 = ((num4 > 0f) ? (num4 - (float)Math.PI * 2f) : (num4 + (float)Math.PI * 2f)); } for (int i = 0; i < count; i++) { float num5 = ((float)i + 1f) / ((float)count + 1f); float num6 = num2 + num4 * num5; list.Add(val + new Vector2(Mathf.Cos(num6), Mathf.Sin(num6)) * radius); } return list; } private static void PlaceCluster(string id, Cluster cl, Vector2 side, string[] hosts, Branch b, float minBulge = 70f, int maxArm = 99) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_006f: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: 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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) Vector2 pos = Nodes[hosts[0]].Pos; string text = ((hosts.Length > 1) ? hosts[1] : hosts[0]); Vector2 pos2 = Nodes[text].Pos; Vector2 val = (pos + pos2) * 0.5f; Vector2 val2 = pos2 - pos; Vector2 normalized = ((Vector2)(ref val2)).normalized; Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(0f - normalized.y, normalized.x); if (Vector2.Dot(val3, side - val) < 0f) { val3 = -val3; } float num = Vector2.Distance(pos, pos2); List list = cl.Arms.ToList(); int num2 = ((list.Count > 0) ? Mathf.Min(list[0].Count, maxArm) : 2); Def d = ((list.Count > 0) ? list[0].Small : cl.Notable); List list2 = new List(); for (int i = 1; i < list.Count; i++) { for (int j = 0; j < Mathf.Min(list[i].Count, maxArm); j++) { list2.Add(list[i].Small); } } float num3 = ((list2.Count > 0) ? Mathf.Max(minBulge, SagittaFor(num, list2.Count, 74f)) : minBulge); float num4 = Mathf.Max(SagittaFor(num, num2 + 1, 84f), num3 + 96f); float num5 = num * 0.48f; if (num4 > num5) { num4 = num5; num3 = Mathf.Min(num3, num5 * 0.45f); } List list3 = ArcPoints(pos, pos2, val3, num4, num2 + 1); List list4 = new List(); for (int k = 0; k < list3.Count; k++) { bool num6 = k == num2 / 2; string text2 = (num6 ? id : $"{id}_l{k}"); if (num6) { Def notable = cl.Notable; Vector2 pos3 = list3[k]; val2 = list3[k] - Nodes[hosts[0]].Pos; AddNotable(text2, notable, NodeSize.Notable, b, pos3, ((Vector2)(ref val2)).normalized); } else { Add(text2, d, NodeSize.Small, b, list3[k]); } list4.Add(text2); } Link(hosts[0], list4[0]); for (int l = 0; l + 1 < list4.Count; l++) { Link(list4[l], list4[l + 1]); } Link(list4[list4.Count - 1], text); if (list2.Count > 0 && hosts.Length > 1) { List list5 = ArcPoints(pos, pos2, val3, num3, list2.Count); string a = hosts[0]; for (int m = 0; m < list5.Count; m++) { string text3 = $"{id}_s{m}"; Add(text3, list2[m], NodeSize.Small, b, list5[m]); Link(a, text3); a = text3; } Link(a, hosts[1]); } } private static void PlaceDeadEnd(string id, Cluster cl, string host, float deg, Branch b, int count = 3) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) Vector2 val = P(1f, deg); Vector2 pos = Nodes[host].Pos; Def d = ((cl.Arms.Length != 0) ? cl.Arms[0].Small : cl.Notable); string a = host; for (int i = 0; i < count; i++) { string text = $"{id}_d{i}"; Add(text, d, NodeSize.Small, b, pos + val * (72f * (float)(i + 1))); Link(a, text); a = text; } AddNotable(id, cl.Notable, NodeSize.Notable, b, pos + val * (72f * (float)count + 82f), val); Link(a, id); } private static void PlaceDiamond(string id, Cluster cl, string host, float deg, Branch b) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) Vector2 val = P(1f, deg); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0f - val.y, val.x); Vector2 pos = Nodes[host].Pos; Def def = ((cl.Arms.Length != 0) ? cl.Arms[0].Small : cl.Notable); Def d = ((cl.Arms.Length > 1) ? cl.Arms[1].Small : def); Add(id + "_e", def, NodeSize.Small, b, pos + val * 78f); Add(id + "_l", def, NodeSize.Small, b, pos + val * 156f + val2 * 54f); Add(id + "_r", d, NodeSize.Small, b, pos + val * 156f - val2 * 48f); AddNotable(id, cl.Notable, NodeSize.Notable, b, pos + val * 236f, val); Link(host, id + "_e"); Link(id + "_e", id + "_l"); Link(id + "_e", id + "_r"); Link(id + "_l", id); Link(id + "_r", id); } private static void PlaceSpiral(string id, Cluster cl, string host, float deg, Branch b, float curl = 95f, int count = 6) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00db: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) Vector2 pos = Nodes[host].Pos; Vector2 normalized = ((Vector2)(ref pos)).normalized; Def d = ((cl.Arms.Length != 0) ? cl.Arms[0].Small : cl.Notable); string a = host; for (int i = 0; i <= count; i++) { float num = ((float)i + 1f) / ((float)count + 1f); float deg2 = deg + curl * num * num; float r = 122f + 74f * (float)(count + 1) * num; Vector2 val = pos + normalized * (34f * num) + P(r, deg2) - P(122f, deg); string text = ((i == count) ? id : $"{id}_p{i}"); if (i == count) { Def notable = cl.Notable; Vector2 val2 = val - pos; AddNotable(text, notable, NodeSize.Notable, b, val, ((Vector2)(ref val2)).normalized); } else { Add(text, d, NodeSize.Small, b, val); } Link(a, text); a = text; } } private static void PlaceWheel(string id, Cluster cl, string host, float deg, Branch b) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) Vector2 pos = Nodes[host].Pos; float num = 92f; Vector2 val = pos + P(num + 74f, deg); Def d = ((cl.Arms.Length != 0) ? cl.Arms[0].Small : cl.Notable); if (cl.Arms.Length > 1) { _ = cl.Arms[1].Small; } string[] array = new string[6]; for (int i = 0; i < 6; i++) { float deg2 = deg + 180f + (float)i * 60f; bool flag = i == 3; array[i] = (flag ? id : $"{id}_w{i}"); if (flag) { AddNotable(array[i], cl.Notable, NodeSize.Notable, b, val + P(num, deg2), P(1f, deg2)); } else { Add(array[i], d, NodeSize.Small, b, val + P(num, deg2)); } } for (int j = 0; j < 6; j++) { Link(array[j], array[(j + 1) % 6]); } Link(host, array[0]); } private static void AddSpur(string id, string host, float deg, Def s1, Def s2, Def tip, NodeSize tipSize, Branch b1, Branch b2, Branch bt) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) Add(id + "_1", s1, NodeSize.Small, b1, P(2760f, deg)); Add(id + "_2", s2, NodeSize.Small, b2, P(2860f, deg)); Add(id, tip, tipSize, bt, P(2970f, deg)); Link(host, id + "_1"); Link(id + "_1", id + "_2"); Link(id + "_2", id); } private static void BuildSector(int k, Sector s) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_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_0068: 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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) float num = SectorAngle(k); Branch b = s.B; Vector2 val = P(1f, num); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0f - val.y, val.x); List<(Vector2, string[], float)> list = new List<(Vector2, string[], float)>(); for (int i = 0; i < 5; i++) { Vector2 val3 = P((RingRoads[i] + RingRoads[i + 1]) * 0.5f, num); list.Add((val3 - val2 * 300f, new string[2] { Junction(i, k), Junction(i + 1, k) }, 80f)); list.Add((val3 + val2 * 300f, new string[2] { Junction(i, k), Junction(i + 1, k) }, 80f)); } list.Add((P(2960f, num - 14f), new string[2] { RingNear(5, k, num - 10f), RingNear(5, k, num - 16f) }, 230f)); list.Add((P(2960f, num + 15f), new string[2] { RingNear(5, k - 1, num + 19f), RingNear(5, k - 1, num + 9f) }, 230f)); list.Add((P(375f, num) - val2 * 200f, new string[2] { Hub(k), Junction(0, k) }, 110f)); for (int j = 0; j < s.C.Length && j < list.Count; j++) { PlaceCluster($"s{k}_c{j}", s.C[j], list[j].Item1, list[j].Item2, b, list[j].Item3); } if (s.Ring != null) { (int, float, float)[] array = new(int, float, float)[1] { (3, num + 11.25f, 150f) }; for (int l = 0; l < s.Ring.Length && l < array.Length; l++) { int item = array[l].Item1; float item2 = array[l].Item2; RingSlot(item, item2, out var seg, out var idx); PlaceCluster($"s{k}_r{l}", s.Ring[l], P(RingRoads[item] + array[l].Item3, item2), new string[2] { RingOrJunction(item, seg, idx - 1), RingOrJunction(item, seg, idx + 1) }, b, 110f, 2); } } if (s.Shapes != null) { if (s.Shapes.Length != 0) { PlaceDiamond($"s{k}_sh0", s.Shapes[0], RingNear(5, k, num - 3.5f), num - 3.5f, b); } if (s.Shapes.Length > 1) { PlaceSpiral($"s{k}_sh1", s.Shapes[1], RingNear(5, k - 1, num + 4f), num + 4f, b, 40f, 4); } if (s.Shapes.Length > 2) { PlaceWheel($"s{k}_sh2", s.Shapes[2], RingNear(2, k - 1, num + 11.25f), num + 11.25f, b); } } if (s.Dead != null) { (int, float)[] array2 = new(int, float)[5] { (4, -10.5f), (4, 10.5f), (2, -13f), (3, -12f), (5, -8f) }; for (int m = 4; m < s.Dead.Length && m - 4 < array2.Length; m++) { (int, float) tuple = array2[m - 4]; float deg = num + tuple.Item2; PlaceDeadEnd($"s{k}_x{m}", s.Dead[m], RingNear(tuple.Item1, (tuple.Item2 < 0f) ? k : (k - 1), deg), deg, b); } } AddSpur($"s{k}_key", Junction(5, k), num, s.Road, s.Travel[0], s.Keystone, NodeSize.Keystone, b, b, b); } private static void BuildBoundary(int k, Boundary bd, Sector sk, Sector sk1) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) Branch b = sk.B; Branch b2 = sk1.B; float num = BoundaryAngle(k); Vector2 val = P(1f, num); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0f - val.y, val.x); List<(Vector2, string[], float)> list = new List<(Vector2, string[], float)>(); for (int i = 1; i < 4; i++) { Vector2 val3 = P((RingRoads[i] + RingRoads[i + 1]) * 0.5f, num); list.Add((val3 - val2 * 300f, new string[2] { RingB(i, k), RingB(i + 1, k) }, 80f)); list.Add((val3 + val2 * 300f, new string[2] { RingB(i, k), RingB(i + 1, k) }, 80f)); } for (int j = 0; j < bd.C.Length && j < list.Count; j++) { PlaceCluster($"b{k}_c{j}", bd.C[j], list[j].Item1, list[j].Item2, Branch.Core, list[j].Item3); } (int, float, Cluster[], int)[] array = new(int, float, Cluster[], int)[4] { (9, -90f, sk1.Dead, 2), (10, -90f, sk1.Dead, 3), (9, 90f, sk.Dead, 0), (10, 90f, sk.Dead, 1) }; for (int l = 0; l < array.Length; l++) { (int, float, Cluster[], int) tuple = array[l]; if (tuple.Item3 != null && tuple.Item4 < tuple.Item3.Length) { PlaceDeadEnd($"b{k}_d{l}", tuple.Item3[tuple.Item4], BSpoke(k, tuple.Item1), num + tuple.Item2, Branch.Core); } } if (bd.Dead != null) { float[] array2 = new float[2] { 4.5f, -4.5f }; for (int m = 0; m < bd.Dead.Length && m < array2.Length; m++) { float deg = num + array2[m]; PlaceDeadEnd($"b{k}_o{m}", bd.Dead[m], RingNear(5, k, deg), deg, Branch.Core); } } AddSpur($"b{k}_spur", RingB(5, k), num, bd.SpurS1, bd.SpurS2, bd.SpurTip, bd.SpurIsKeystone ? NodeSize.Keystone : NodeSize.Notable, b, b2, Branch.Core); } public static void ApplyPower(float factor) { if (Mathf.Approximately(factor, 1f)) { return; } foreach (Node value in Nodes.Values) { for (int i = 0; i < value.Mods.Count; i++) { KeyValuePair keyValuePair = value.Mods[i]; if (Array.IndexOf(NotScaled, keyValuePair.Key) < 0 && keyValuePair.Value != 0f) { float num = keyValuePair.Value * factor; float num2 = Mathf.Min(Mathf.Abs(keyValuePair.Value), StatText.FormatInfo(keyValuePair.Key).pct ? 0.01f : 1f); if (Mathf.Abs(num) < num2) { num = Mathf.Sign(keyValuePair.Value) * num2; } value.Mods[i] = new KeyValuePair(keyValuePair.Key, num); } } } } private static void ConnectTips() { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) List list = (from n in Nodes.Values where n.Size == NodeSize.Notable && Degree(n) == 1 orderby n.Id select n).ToList(); HashSet hashSet = new HashSet(); int num = 0; foreach (Node item in list) { if (hashSet.Contains(item.Id)) { continue; } Node node = null; float num2 = float.MaxValue; foreach (Node item2 in list) { if (item2 != item && !hashSet.Contains(item2.Id) && !item.Links.Contains(item2.Id)) { float num3 = Vector2.Distance(item.Pos, item2.Pos); if (!(num3 < 230f) && !(num3 > 560f) && !(num3 >= num2)) { node = item2; num2 = num3; } } } if (node == null) { continue; } int num4 = Mathf.Max(1, Mathf.RoundToInt(num2 / 88f) - 1); List list2 = new List(); for (int num5 = 0; num5 < num4; num5++) { list2.Add(Vector2.Lerp(item.Pos, node.Pos, ((float)num5 + 1f) / ((float)num4 + 1f))); } if (!list2.Any((Vector2 pt) => Nodes.Values.Any((Node o) => Vector2.Distance(o.Pos, pt) < 52f)) && !TreeB.CrossesAnyLink(item.Pos, node.Pos, item.Id)) { Def def = Blend("Перемычка", new Def(item.Name, item.Mods.Select((KeyValuePair m) => (Key: m.Key, m.Value * 0.3f)).ToArray()), new Def(node.Name, node.Mods.Select((KeyValuePair m) => (Key: m.Key, m.Value * 0.3f)).ToArray())); def.Name = "Перемычка"; string a = item.Id; for (int num6 = 0; num6 < list2.Count; num6++) { string text = $"bridge_{item.Id}_{num6}"; Add(text, def, NodeSize.Small, Branch.Core, list2[num6]); Link(a, text); a = text; } Link(a, node.Id); hashSet.Add(item.Id); hashSet.Add(node.Id); num++; } } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Перемычек между тупиками: {num}"); } static int Degree(Node x) { return x.Links.Count((string l) => Nodes[l].Size != NodeSize.Mastery); } } public static void Build() { if (Nodes.Count > 0) { return; } TreeLayout num = (Built = Force ?? ((Plugin.Layout != null) ? Plugin.Layout.Value : TreeLayout.Classic)); Captions.Clear(); Dividers.Clear(); if (num == TreeLayout.Reforged) { TreeB.Build(); return; } RingRadii = ClassicRings; CaptionRadius = 3120f; for (int i = 0; i < 8; i++) { Captions.Add((SectorOrder[i], SectorAngle(i))); Dividers.Add(BoundaryAngle(i)); } BuildClassic(); } private static void BuildClassic() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) Add("root", D("Пробуждение").X("Начало пути. Каждый уровень даёт одно очко.").I("sprite:mapicon_start"), NodeSize.Root, Branch.Core, Vector2.zero); Sector[] array = new Sector[8] { new Sector { B = Branch.Melee, Hub = D("Воинская выучка", (Stat.MeleeDamagePct, 0.021f)), Road = D("Хватка", (Stat.MeleeDamagePct, 0.014f)), JunctionDef = D("Развилка воинов", (Stat.MeleeDamagePct, 0.014f), (Stat.MaxHealthFlat, 2.8f)), Travel = new Def[3] { D("Крепкая рука", (Stat.MeleeDamagePct, 0.014f)), D("Боевой пыл", (Stat.MaxHealthFlat, 2.8f)), D("Закалка боем", (Stat.PhysicalDamagePct, 0.02f)) }, C = new Cluster[13] { CL(D("Клинок ярла", (Stat.Dmg_Swords, 0.12f), (Stat.Spd_Swords, 0.06f)).M("sword").I("skill:Swords"), A(D("Острота", (Stat.Dmg_Swords, 0.04f)), 3), A(D("Выпад", (Stat.Spd_Swords, 0.03f)), 3), A(D("Лёгкий замах", (Stat.AttackStaminaPct, -0.021f)), 2, 1)), CL(D("Секира", (Stat.Dmg_Axes, 0.12f), (Stat.Spd_Axes, 0.06f)).M("axe").I("item:AxeIron"), A(D("Рубака", (Stat.Dmg_Axes, 0.04f)), 3), A(D("Взмах", (Stat.Spd_Axes, 0.03f)), 3)), CL(D("Дробитель", (Stat.Dmg_Clubs, 0.096f), (Stat.Spd_Clubs, 0.05f), (Stat.StunChance, 0.035f)).M("club").I("item:MaceIron"), A(D("Палица", (Stat.Dmg_Clubs, 0.032f)), 3), A(D("Оглушающий удар", (Stat.StunChance, 0.014f)), 3)), CL(D("Копейщик", (Stat.Dmg_Spears, 0.12f), (Stat.Dmg_Polearms, 0.12f)).M("spear").I("item:AtgeirBronze"), A(D("Пика", (Stat.Dmg_Spears, 0.04f)), 3), A(D("Древко", (Stat.Dmg_Polearms, 0.04f)), 3), A(D("Длинный выпад", (Stat.Spd_Spears, 0.03f), (Stat.Spd_Polearms, 0.03f)), 2, 1)), CL(D("Кулачный боец", (Stat.Dmg_Unarmed, 0.112f), (Stat.Spd_Unarmed, 0.08f)).I("sprite:skill_dodge"), A(D("Костяшки", (Stat.Dmg_Unarmed, 0.038f)), 3), A(D("Быстрые руки", (Stat.Spd_Unarmed, 0.04f)), 3)), CL(D("Мастер темпа", (Stat.AttackSpeedPct, 0.08f)).I("item:SwordBlackmetal"), A(D("Быстрый удар", (Stat.AttackSpeedPct, 0.025f)), 3), A(D("Лёгкий замах", (Stat.AttackStaminaPct, -0.021f)), 3)), CL(D("Мастер оружия", (Stat.CritChance, 0.04f), (Stat.CritDamagePct, 0.15f), (Stat.CritRecentStaminaRegenPct, 0.06f)).M("crit").I("item:SwordSilver"), A(D("Точный удар", (Stat.CritChance, 0.015f)), 3), A(D("Кураж", (Stat.CritRecentAttackSpeedPct, 0.04f), (Stat.CritRecentMoveSpeedPct, 0.04f)), 3), A(D("Жестокость", (Stat.CritDamagePct, 0.06f)), 2, 1)), CL(D("Берсерк", (Stat.BerserkPct, 0.04f), (Stat.HealthOnKill, 4f)).M("blood").I("item:Bloodbag"), A(D("Ярость", (Stat.BerserkPct, 0.015f)), 3), A(D("Кровь врага", (Stat.HealthOnKill, 3f)), 3)), CL(D("Сокрушитель", (Stat.StaggerDamagePct, 0.2f), (Stat.KnockbackPct, 0.15f), (Stat.DamageVsStaggeredPct, 0.12f)).M("stagger").I("item:Stone"), A(D("Тяжёлый удар", (Stat.StaggerDamagePct, 0.06f)), 3), A(D("Толчок", (Stat.KnockbackPct, 0.05f)), 3)), CL(D("Раж", (Stat.OnslaughtChance, 0.15f), (Stat.OnslaughtPowerPct, 0.05f)).M("onslaught").X("Натиск ускоряет атаки, бег и восстановление сил на 12 с."), A(D("Жажда боя", (Stat.OnslaughtChance, 0.036f)), 3), A(D("Разгон", (Stat.OnslaughtPowerPct, 0.03f)), 3), A(D("Второе дыхание", (Stat.StaminaOnKill, 3f)), 2)), CL(D("Пылающий клинок", (Stat.IgniteChance, 0.05f), (Stat.BurnDamagePct, 0.06f)).M("fire").I("item:SwordFire"), A(D("Искра", (Stat.IgniteChance, 0.015f)), 3), A(D("Жар", (Stat.BurnDamagePct, 0.03f)), 3)), CL(D("Ночной клинок", (Stat.NightDamagePct, 0.084f), (Stat.NightMoveSpeedPct, 0.036f)).M("night").I("item:Thistle"), A(D("Сумрак", (Stat.NightDamagePct, 0.028f)), 3), A(D("Поступь в ночи", (Stat.NightMoveSpeedPct, 0.018f)), 3)), CL(D("После удара", (Stat.CritRecentDamagePct, 0.08f), (Stat.CritRecentAttackSpeedPct, 0.06f)).M("crit").X("Четыре секунды после крита ты быстрее и злее.").I("crit-recent"), A(D("Разгон", (Stat.CritRecentDamagePct, 0.03f)), 2), A(D("Второй замах", (Stat.CritRecentAttackSpeedPct, 0.022f)), 2)) }, Ring = new Cluster[1] { CL(D("Тяжёлый замах", (Stat.Dmg_Clubs, 0.064f), (Stat.AttackStaminaPct, -0.07f)).M("club").I("item:MaceBronze"), A(D("Палица", (Stat.Dmg_Clubs, 0.032f)), 3), A(D("Экономный замах", (Stat.AttackStaminaPct, -0.021f)), 3)) }, Dead = new Cluster[8] { CL(D("Клык", (Stat.Dmg_Swords, 0.1f)).I("skill:Swords"), A(D("Острота", (Stat.Dmg_Swords, 0.035f)), 3)), CL(D("Лезвие", (Stat.Dmg_Axes, 0.1f)).I("item:AxeBronze"), A(D("Рубака", (Stat.Dmg_Axes, 0.035f)), 3)), CL(D("Мозоли", (Stat.Dmg_Unarmed, 0.075f)).M("fist").I("item:Flint"), A(D("Костяшки", (Stat.Dmg_Unarmed, 0.026f)), 3)), CL(D("Тяжесть", (Stat.StunChance, 0.035f)).I("item:Stone"), A(D("Оглушающий удар", (Stat.StunChance, 0.014f)), 3)), CL(D("Уязвимое место", (Stat.CritDamagePct, 0.1f), (Stat.ExecutePct, 0.08f)).M("execute").I("crit"), A(D("Жестокость", (Stat.CritDamagePct, 0.035f)), 3)), CL(D("Точка удара", (Stat.CritChance, 0.022f), (Stat.StaggerDamagePct, 0.08f)).M("parry").I("crit"), A(D("Точный глаз", (Stat.CritChance, 0.008f)), 3)), CL(D("Ярость раненого", (Stat.BerserkPct, 0.05f), (Stat.HealthRegenPct, 0.05f)).M("blood").I("berserk"), A(D("Горячая кровь", (Stat.BerserkPct, 0.018f)), 3)), CL(D("Двойной удар", (Stat.AttackSpeedPct, 0.06f), (Stat.CritChance, 0.02f)).M("crit").I("item:SwordBlackmetal"), A(D("Быстрый удар", (Stat.AttackSpeedPct, 0.02f)), 3), A(D("Точный удар", (Stat.CritChance, 0.01f)), 3)) }, Shapes = new Cluster[3] { CL(D("Гроза нежити", (Stat.DamageVsUndeadPct, 0.18f)).I("item:TrophySkeleton"), A(D("Святая сталь", (Stat.DamageVsUndeadPct, 0.06f)), 1), A(D("Крепкая рука", (Stat.MeleeDamagePct, 0.021f)), 1)), CL(D("Добивающий удар", (Stat.DamageVsStaggeredPct, 0.2f), (Stat.StaggerDamagePct, 0.1f)).M("execute").I("item:MaceIron"), A(D("Добивание оглушённых", (Stat.DamageVsStaggeredPct, 0.05f)), 1)), CL(D("Вихрь клинков", (Stat.AttackSpeedPct, 0.06f), (Stat.MeleeDamagePct, 0.05f)).M("tempo").I("item:SwordBlackmetal"), A(D("Быстрый удар", (Stat.AttackSpeedPct, 0.02f)), 1), A(D("Крепкая рука", (Stat.MeleeDamagePct, 0.021f)), 1)) }, Keystone = D("Неистовство", (Stat.AttackSpeedPct, 0.22f), (Stat.MeleeDamagePct, 0.07f), (Stat.CritRecentDamagePct, 0.12f), (Stat.DamageTakenPct, 0.3f), (Stat.ArmorPct, -0.25f)).X("Ключевой узел: бьёшь быстрее и больнее, но защищаешься куда хуже.").I("item:SwordIron") }, new Sector { B = Branch.Defense, Hub = D("Крепкий щит", (Stat.ArmorFlat, 1.4f)), Road = D("Кольчуга", (Stat.ArmorFlat, 1.4f)), JunctionDef = D("Развилка стражей", (Stat.ArmorFlat, 1.4f), (Stat.MaxHealthFlat, 2.8f)), Travel = new Def[3] { D("Пластина", (Stat.ArmorFlat, 1.4f)), D("Стойкость", (Stat.MaxHealthFlat, 2.8f)), D("Выдержка", (Stat.StaggerResistPct, 0.018f)) }, C = new Cluster[13] { CL(D("Железная кожа", (Stat.ArmorFlat, 5.6f), (Stat.ArmorPct, 0.06f)).M("armor").I("item:ArmorIronChest"), A(D("Пластина", (Stat.ArmorFlat, 2.1f)), 3), A(D("Закалка", (Stat.ArmorPct, 0.025f)), 3), A(D("Живучесть", (Stat.MaxHealthFlat, 2.8f)), 2, 1)), CL(D("Бастион", (Stat.BlockPowerPct, 0.12f), (Stat.BlockStaminaPct, -0.18f)).M("block").I("skill:Blocking"), A(D("Крепкий хват", (Stat.BlockPowerPct, 0.04f)), 3), A(D("Лёгкий щит", (Stat.BlockStaminaPct, -0.06f)), 3)), CL(D("Идеальный отскок", (Stat.ParryBonusPct, 0.175f), (Stat.StunChance, 0.028f)).M("parry").I("item:ShieldBanded"), A(D("Отскок", (Stat.ParryBonusPct, 0.056f)), 3), A(D("Контрудар", (Stat.StunChance, 0.014f)), 3)), CL(D("Танцор", (Stat.DodgeChance, 0.06f), (Stat.DodgeStaminaPct, -0.15f)).M("dodge").I("sprite:skill_dodge"), A(D("Уворот", (Stat.DodgeChance, 0.02f)), 3), A(D("Кувырок", (Stat.DodgeStaminaPct, -0.06f)), 3)), CL(D("Кольчуга шипов", (Stat.ThornsPct, 0.15f), (Stat.ArmorFlat, 2.8f)).M("thorns").I("item:ArmorBronzeChest"), A(D("Шипы", (Stat.ThornsPct, 0.04f)), 3), A(D("Заклёпки", (Stat.ArmorFlat, 2.1f)), 3)), CL(D("Непоколебимый", (Stat.StaggerResistPct, 0.12f), (Stat.DamageTakenPct, -0.028f)).M("spear").I("item:ArmorIronLegs"), A(D("Упор", (Stat.StaggerResistPct, 0.03f)), 3), A(D("Стойкость", (Stat.DamageTakenPct, -0.01f)), 3)), CL(D("Закалённый в огне", (Stat.FireResistPct, 0.16f), (Stat.AilmentResistPct, 0.049f)).M("resist").X("Горение спадает с тебя быстрее.").I("item:Coal"), A(D("Огнеупорность", (Stat.FireResistPct, 0.056f)), 3), A(D("Терпение к боли", (Stat.AilmentResistPct, 0.0196f)), 3)), CL(D("Тёплая кровь", (Stat.FrostResistPct, 0.2f), (Stat.ColdResistPct, 0.12f), (Stat.DamageVsMountainPct, 0.12f)).M("weather").I("item:WolfPelt"), A(D("Мех", (Stat.FrostResistPct, 0.064f)), 3), A(D("Охота в горах", (Stat.DamageVsMountainPct, 0.04f)), 2)), CL(D("Заземление", (Stat.LightningResistPct, 0.2f)).M("spear").I("item:Bronze"), A(D("Молниеотвод", (Stat.LightningResistPct, 0.064f)), 3), A(D("Сухая одежда", (Stat.DamageTakenPct, -0.01f)), 2)), CL(D("Противоядие", (Stat.PoisonResistPct, 0.2f), (Stat.AilmentResistPct, 0.0392f)).M("potion").I("item:MeadPoisonResist"), A(D("Стойкий желудок", (Stat.PoisonResistPct, 0.064f)), 3), A(D("Чистая кровь", (Stat.AilmentResistPct, 0.0196f)), 3)), CL(D("Таран", (Stat.StunChance, 0.042f), (Stat.BlockPowerPct, 0.064f)).M("block").I("item:ShieldIronTower"), A(D("Удар щитом", (Stat.StunChance, 0.014f)), 3), A(D("Наплечник", (Stat.ArmorFlat, 2.1f)), 3)), CL(D("Страж ночи", (Stat.NightArmorFlat, 10f), (Stat.NightRegenPct, 0.1f)).M("night").I("item:HelmetIron"), A(D("Ночной дозор", (Stat.NightArmorFlat, 4f)), 3), A(D("Бдительность", (Stat.NightRegenPct, 0.04f)), 3)), CL(D("Ровное дыхание", (Stat.CritRecentStaminaRegenPct, 0.1f), (Stat.StaminaRegenPct, 0.04f)).M("tempo").X("После удачного удара силы возвращаются быстрее.").I("crit-recent"), A(D("Выдох", (Stat.CritRecentStaminaRegenPct, 0.035f)), 2), A(D("Спокойный вдох", (Stat.StaminaRegenPct, 0.015f)), 2)) }, Ring = new Cluster[1] { CL(D("Глухая оборона", (Stat.BlockPowerPct, 0.08f), (Stat.DamageTakenPct, -0.021f)).M("block").I("item:ShieldWood"), A(D("Крепкий хват", (Stat.BlockPowerPct, 0.032f)), 3), A(D("Стойкость", (Stat.DamageTakenPct, -0.01f)), 3)) }, Dead = new Cluster[9] { CL(D("Панцирь", (Stat.ArmorFlat, 4.2f)).I("item:ArmorIronChest"), A(D("Пластина", (Stat.ArmorFlat, 2.1f)), 3)), CL(D("Хват щита", (Stat.BlockPowerPct, 0.08f)).I("skill:Blocking"), A(D("Крепкий хват", (Stat.BlockPowerPct, 0.032f)), 3)), CL(D("Скольжение", (Stat.DodgeChance, 0.03f)).I("sprite:skill_dodge"), A(D("Уворот", (Stat.DodgeChance, 0.015f)), 3)), CL(D("Корни", (Stat.StaggerResistPct, 0.072f)).I("item:Stone"), A(D("Упор", (Stat.StaggerResistPct, 0.024f)), 3)), CL(D("Шлем", (Stat.StaggerResistPct, 0.08f), (Stat.DamageTakenPct, -0.015f)).M("tough").I("stagger-res"), A(D("Нащёчник", (Stat.StaggerResistPct, 0.028f)), 3)), CL(D("Наплечник", (Stat.ArmorFlat, 4.2f), (Stat.ArmorPct, 0.03f)).M("armor").I("armor"), A(D("Пластина", (Stat.ArmorFlat, 1.6f)), 3)), CL(D("Твёрдая стойка", (Stat.BlockStaminaPct, -0.08f), (Stat.ParryBonusPct, 0.06f)).M("sword").I("block"), A(D("Упор", (Stat.BlockStaminaPct, -0.03f)), 3)), CL(D("Отражение", (Stat.ThornsPct, 0.1f), (Stat.ParryBonusPct, 0.07f)).M("parry").I("item:ShieldBronzeBuckler"), A(D("Шипы", (Stat.ThornsPct, 0.03f)), 3), A(D("Отскок", (Stat.ParryBonusPct, 0.028f)), 3)), CL(D("Северянин", (Stat.ColdResistPct, 0.15f), (Stat.FrostResistPct, 0.07f)).M("weather").X("Холод и замерзание мучают заметно слабее.").I("item:WolfPelt"), A(D("Привычка к стуже", (Stat.ColdResistPct, 0.05f)), 3)) }, Shapes = new Cluster[3] { CL(D("Гроза равнин", (Stat.DamageVsPlainsPct, 0.18f)).M("lore").I("item:TrophyGoblin"), A(D("Знание фулингов", (Stat.DamageVsPlainsPct, 0.06f)), 1), A(D("Крепкий выпад", (Stat.MeleeDamagePct, 0.021f)), 1)), CL(D("Чистая кровь", (Stat.AilmentResistPct, 0.126f)).M("resist").I("item:MeadPoisonResist"), A(D("Терпение", (Stat.AilmentResistPct, 0.0294f)), 1)), CL(D("Стена", (Stat.BlockPowerPct, 0.08f), (Stat.StaggerResistPct, 0.06f)).M("tough").I("item:ShieldIronTower"), A(D("Крепкий хват", (Stat.BlockPowerPct, 0.032f)), 1), A(D("Упор", (Stat.StaggerResistPct, 0.03f)), 1)) }, Keystone = D("Несокрушимый", (Stat.DamageTakenPct, -0.126f), (Stat.ArmorPct, 0.15f), (Stat.DamagePct, -0.35f), (Stat.MoveSpeedPct, -0.1f)).X("Ключевой узел: стена, а не таран.").I("item:ArmorWolfChest") }, new Sector { B = Branch.Survival, Hub = D("Живучесть", (Stat.MaxHealthFlat, 2.8f)), Road = D("Здоровье", (Stat.MaxHealthFlat, 2.8f)), JunctionDef = D("Развилка выживших", (Stat.MaxHealthFlat, 2.8f), (Stat.MaxStaminaFlat, 4f)), Travel = new Def[3] { D("Здоровье", (Stat.MaxHealthFlat, 2.8f)), D("Восстановление", (Stat.HealthRegenPct, 0.018f)), D("Выносливость", (Stat.MaxStaminaFlat, 4f)) }, C = new Cluster[13] { CL(D("Крепкое сердце", (Stat.MaxHealthFlat, 10.5f), (Stat.MaxHealthPct, 0.05f)).M("life").I("item:MeadHealthMinor"), A(D("Здоровье", (Stat.MaxHealthFlat, 3.5f)), 3), A(D("Крепость", (Stat.MaxHealthPct, 0.02f)), 3), A(D("Восстановление", (Stat.HealthRegenPct, 0.024f)), 2, 1)), CL(D("Живая вода", (Stat.HealthRegenPct, 0.09f), (Stat.PotionDurationPct, 0.2f)).M("potion").X("Мёды и зелья держатся дольше.").I("item:MeadHealthMedium"), A(D("Регенерация", (Stat.HealthRegenPct, 0.024f)), 3), A(D("Настойка", (Stat.PotionDurationPct, 0.07f)), 3)), CL(D("Второе дыхание", (Stat.MaxStaminaFlat, 12f), (Stat.StaminaRegenPct, 0.1f)).M("stamina").I("item:MeadStaminaMinor"), A(D("Запас сил", (Stat.MaxStaminaFlat, 4f)), 3), A(D("Дыхание", (Stat.StaminaRegenPct, 0.04f)), 3)), CL(D("Сытость", (Stat.FoodDurationPct, 0.12f), (Stat.FoodPowerPct, 0.1f)).M("food").X("Повар выжимает из того же блюда больше.").I("item:CookedMeat"), A(D("Аппетит", (Stat.FoodDurationPct, 0.036f)), 3), A(D("Щедрая порция", (Stat.FoodPowerPct, 0.035f)), 3)), CL(D("Закалённый жаром", (Stat.FoodWhileBurningPct, 0.25f), (Stat.FireResistPct, 0.08f)).M("resist").X("Пока ты горишь, еда даёт больше здоровья, сил и эйтра.").I("item:Flametal"), A(D("Горячая кровь", (Stat.FoodWhileBurningPct, 0.08f)), 3), A(D("Привычка к огню", (Stat.FireResistPct, 0.04f)), 3)), CL(D("Уют", (Stat.ComfortFlat, 1f), (Stat.RestedDurationPct, 0.125f), (Stat.HealthRegenPct, 0.03f)).M("home").I("item:Tankard"), A(D("Покой", (Stat.RestedDurationPct, 0.04f)), 3), A(D("Домовитость", (Stat.HealthRegenPct, 0.018f)), 3)), CL(D("Долгая память", (Stat.StatusDurationPct, 0.12f), (Stat.OnslaughtChance, 0.06f)).X("Дольше держатся и наложенные врагу состояния, и собственные усиления."), A(D("Долгий след", (Stat.StatusDurationPct, 0.04f)), 3), A(D("Выдержка", (Stat.BuffDurationPct, 0.05f)), 3)), CL(D("Крепкий организм", (Stat.AilmentResistPct, 0.098f)).X("Горение, яд и мороз спадают с тебя быстрее.").I("item:Honey"), A(D("Терпение", (Stat.AilmentResistPct, 0.0294f)), 3), A(D("Травник", (Stat.PotionDurationPct, 0.06f)), 2)), CL(D("Кровопийца", (Stat.LifestealPct, 0.03f), (Stat.HealthOnKill, 5f)).M("blood").I("item:BloodPudding"), A(D("Голод", (Stat.LifestealPct, 0.01f)), 3), A(D("Жажда", (Stat.HealthOnKill, 3f)), 3)), CL(D("Второе сердце", (Stat.BuffDurationPct, 0.2f), (Stat.OnslaughtPowerPct, 0.04f)).M("onslaught").I("item:HardAntler"), A(D("Жажда боя", (Stat.OnslaughtChance, 0.048f)), 3), A(D("Стойкий дух", (Stat.BuffDurationPct, 0.06f)), 3)), CL(D("Ночной покой", (Stat.NightRegenPct, 0.15f), (Stat.RestedDurationPct, 0.075f)).I("sprite:mapicon_bed"), A(D("Лунный сон", (Stat.NightRegenPct, 0.05f)), 3), A(D("Тихая ночь", (Stat.RestedDurationPct, 0.03f)), 3)), CL(D("Крепкая спина", (Stat.CarryWeightFlat, 18f), (Stat.MaxStaminaPct, 0.04f)).M("stamina").I("item:BeltStrength"), A(D("Носильщик", (Stat.CarryWeightFlat, 4.8f)), 3), A(D("Плечи", (Stat.MaxStaminaFlat, 4f)), 3)), CL(D("Крепкие кулаки", (Stat.Dmg_Unarmed, 0.07f), (Stat.UnarmedDamageTakenPct, -0.04f)).I("unarmed"), A(D("Костяшки", (Stat.Dmg_Unarmed, 0.026f)), 2), A(D("Бой без оружия", (Stat.UnarmedDamageTakenPct, -0.015f)), 2)) }, Ring = new Cluster[1] { CL(D("Пастух", (Stat.TamingSpeedPct, 0.5f), (Stat.FeedDurationPct, 0.2f)).M("tame").X("Животные привыкают к тебе быстрее и дольше остаются сытыми.").I("taming:"), A(D("Спокойный нрав", (Stat.TamingSpeedPct, 0.18f)), 3), A(D("Щедрая кормушка", (Stat.FeedDurationPct, 0.08f)), 3)) }, Dead = new Cluster[9] { CL(D("Сердце", (Stat.MaxHealthFlat, 8.4f)).I("item:MeadHealthMedium"), A(D("Здоровье", (Stat.MaxHealthFlat, 2.8f)), 3)), CL(D("Ключ жизни", (Stat.HealthRegenPct, 0.06f)).I("item:Raspberry"), A(D("Регенерация", (Stat.HealthRegenPct, 0.021f)), 3)), CL(D("Кормилец", (Stat.FeedDurationPct, 0.35f)).X("Одной кормёжки хватает намного дольше.").I("taming:"), A(D("Щедрая кормушка", (Stat.FeedDurationPct, 0.1f)), 3)), CL(D("Закром", (Stat.FoodDurationPct, 0.072f)).I("item:CookedMeat"), A(D("Аппетит", (Stat.FoodDurationPct, 0.024f)), 3)), CL(D("Поварской глаз", (Stat.NoOvercook, 1f), (Stat.CookSpeedPct, 0.12f)).X("Ты снимаешь с огня вовремя: мясо больше не сгорает.").I("item:CookedMeat"), A(D("Пригляд за очагом", (Stat.CookSpeedPct, 0.06f)), 3)), CL(D("Жаркий очаг", (Stat.CookSpeedPct, 0.2f), (Stat.FoodPowerPct, 0.04f)).X("На твоём огне готовится быстрее.").I("item:Torch"), A(D("Ровный жар", (Stat.CookSpeedPct, 0.07f)), 3)), CL(D("Дыхание жизни", (Stat.StaminaRegenPct, 0.1f), (Stat.HealthRegenPct, 0.036f)).M("life").I("item:Raspberry"), A(D("Дыхание", (Stat.StaminaRegenPct, 0.035f)), 3), A(D("Регенерация", (Stat.HealthRegenPct, 0.018f)), 3)), CL(D("Огород", (Stat.GrowSpeedPct, 0.22f), (Stat.CropYieldChance, 0.1f)).X("Посевы созревают заметно быстрее, и грядка нет-нет да отдаст лишнее.").I("item:Carrot"), A(D("Всходы", (Stat.GrowSpeedPct, 0.075f)), 3)), CL(D("Плотная грядка", (Stat.PlantCrowding, 1f), (Stat.CropYieldChance, 0.08f), (Stat.FoodDurationPct, 0.05f)).X("Посевы перестают спорить за место: сажай вплотную, всё равно вызреет.").I("item:Cultivator"), A(D("Мотыга", (Stat.CropYieldChance, 0.03f)), 3)) }, Shapes = new Cluster[3] { CL(D("Охотник на зверя", (Stat.DamageVsAnimalsPct, 0.18f)).M("hunt").I("item:TrophyDeer"), A(D("Повадки зверя", (Stat.DamageVsAnimalsPct, 0.06f)), 1), A(D("Охотничий нож", (Stat.Dmg_Knives, 0.03f)), 1)), CL(D("Щедрый стол", (Stat.FoodPowerPct, 0.12f)).M("food").I("item:CookedMeat"), A(D("Щедрая порция", (Stat.FoodPowerPct, 0.035f)), 1)), CL(D("Сердце вепря", (Stat.MaxHealthPct, 0.05f), (Stat.HealthRegenPct, 0.05f)).M("life").I("item:MeadHealthMedium"), A(D("Крепость", (Stat.MaxHealthPct, 0.02f)), 1), A(D("Регенерация", (Stat.HealthRegenPct, 0.024f)), 1)) }, Keystone = D("Кровь Имира", (Stat.MaxHealthPct, 0.3f), (Stat.HealthRegenPct, 0.18f), (Stat.MaxStaminaPct, -0.35f), (Stat.MoveSpeedPct, -0.1f)).X("Ключевой узел: гора здоровья ценой выносливости и лёгкости.").I("item:HardAntler") }, new Sector { B = Branch.Building, Hub = D("Рука строителя", (Stat.ToolStaminaPct, -0.014f)), Road = D("Сноровка", (Stat.ToolStaminaPct, -0.01f)), JunctionDef = D("Развилка мастеров", (Stat.ToolStaminaPct, -0.01f), (Stat.CarryWeightFlat, 4.8f)), Travel = new Def[3] { D("Размах", (Stat.BuildRangeFlat, 0.3f)), D("Сноровка", (Stat.ToolStaminaPct, -0.01f)), D("Носильщик", (Stat.CarryWeightFlat, 3.6f)) }, C = new Cluster[13] { CL(D("Мастер-строитель", (Stat.BuildRangeFlat, 1.5f), (Stat.ToolStaminaPct, -0.035f)).M("build").I("item:Hammer"), A(D("Размах", (Stat.BuildRangeFlat, 0.4f)), 3), A(D("Плотник", (Stat.ToolStaminaPct, -0.017f)), 3)), CL(D("Экономный", (Stat.BuildRefundChance, 0.05f), (Stat.CarryWeightFlat, 6f)).M("carry").I("item:Coins"), A(D("Смекалка", (Stat.BuildRefundChance, 0.013f)), 3), A(D("Запасливость", (Stat.CarryWeightFlat, 4.8f)), 3)), CL(D("Плотницкая хватка", (Stat.ToolStaminaPct, -0.049f)).I("item:Cultivator"), A(D("Сноровка", (Stat.ToolStaminaPct, -0.017f)), 3), A(D("Ровные руки", (Stat.BuildRangeFlat, 0.3f)), 3)), CL(D("Хранитель очага", (Stat.ComfortFlat, 2f), (Stat.HealthRegenPct, 0.048f)).M("home").I("item:Torch"), A(D("Уютный угол", (Stat.RestedDurationPct, 0.03f)), 3), A(D("Тепло", (Stat.MaxHealthFlat, 2.8f)), 3)), CL(D("Киянка мастера", (Stat.Dmg_Clubs, 0.12f), (Stat.StunChance, 0.056f)).M("club").X("Строитель бьёт киянкой так, что враг теряет равновесие.").I("item:Hammer"), A(D("Крепкая рукоять", (Stat.Dmg_Clubs, 0.04f)), 3), A(D("Оглушающий замах", (Stat.StunChance, 0.017f)), 3)), CL(D("Кузнечный жар", (Stat.IgniteChance, 0.04f), (Stat.FireResistPct, 0.096f)).I("item:Coal"), A(D("Искра горна", (Stat.IgniteChance, 0.015f)), 3), A(D("Привычка к жару", (Stat.FireResistPct, 0.04f)), 3)), CL(D("Прораб", (Stat.CarryWeightFlat, 15f), (Stat.BuildRangeFlat, 0.8f)).M("chop").I("item:Hoe"), A(D("Носильщик", (Stat.CarryWeightFlat, 4.8f)), 3), A(D("Размах", (Stat.BuildRangeFlat, 0.3f)), 3)), CL(D("Крепкие стены", (Stat.BuildDurabilityPct, 0.175f), (Stat.RestedDamagePct, 0.12f)).X("Постройки, которые ты возвёл, держат удар куда лучше.").I("sprite:mapicon_house"), A(D("Крепкий сруб", (Stat.BuildDurabilityPct, 0.056f)), 3), A(D("Обшивка", (Stat.ArmorFlat, 2.1f)), 3)), CL(D("Ночной зодчий", (Stat.NightBuildPct, 0.12f), (Stat.NightRegenPct, 0.08f)).X("Ночью стройка бережёт ресурсы, а отдых восстанавливает быстрее."), A(D("Работа при луне", (Stat.NightBuildPct, 0.04f)), 3), A(D("Ночная смена", (Stat.NightRegenPct, 0.04f)), 3)), CL(D("Цепкие руки", (Stat.PickupRangePct, 0.125f)).M("forage").I("item:Wood"), A(D("Хваткость", (Stat.PickupRangePct, 0.04f)), 3), A(D("Разбор завалов", (Stat.CarryWeightFlat, 3.6f)), 2)), CL(D("Скотный двор", (Stat.BreedChancePct, 0.6f), (Stat.FeedDurationPct, 0.15f)).M("tame").X("В обжитом хозяйстве приплод случается чаще.").I("taming:"), A(D("Загон", (Stat.BreedChancePct, 0.2f)), 3), A(D("Щедрая кормушка", (Stat.FeedDurationPct, 0.06f)), 3)), CL(D("Молотобоец", (Stat.Dmg_Clubs, 0.096f), (Stat.DamageVsDvergrPct, 0.15f)).I("item:MaceBronze"), A(D("Тяжёлый молот", (Stat.StaggerDamagePct, 0.05f)), 3), A(D("Кувалда", (Stat.Dmg_Clubs, 0.032f)), 3)), CL(D("Подмастерье", (Stat.ToolStaminaPct, -0.05f), (Stat.CraftRefundChance, 0.025f)).I("tool-stamina"), A(D("Сноровка", (Stat.ToolStaminaPct, -0.018f)), 2), A(D("Экономный порез", (Stat.CraftRefundChance, 0.008f)), 2)) }, Ring = new Cluster[1] { CL(D("Точный расчёт", (Stat.BuildRefundChance, 0.03f), (Stat.BuildDurabilityPct, 0.105f)).M("build").I("item:Hoe"), A(D("Смекалка", (Stat.BuildRefundChance, 0.01f)), 3), A(D("Размах", (Stat.BuildRangeFlat, 0.3f)), 3)) }, Dead = new Cluster[9] { CL(D("Рычаг", (Stat.BuildRangeFlat, 1.2f)).I("item:Hammer"), A(D("Размах", (Stat.BuildRangeFlat, 0.35f)), 3)), CL(D("Расчёт", (Stat.BuildRefundChance, 0.03f)).I("item:Coins"), A(D("Смекалка", (Stat.BuildRefundChance, 0.01f)), 3)), CL(D("Крепкий хват", (Stat.ToolStaminaPct, -0.042f)).I("item:Cultivator"), A(D("Сноровка", (Stat.ToolStaminaPct, -0.014f)), 3)), CL(D("Очаг", (Stat.ComfortFlat, 1f)).I("item:Torch"), A(D("Уютный угол", (Stat.RestedDurationPct, 0.025f)), 3)), CL(D("Навес", (Stat.DryingPct, 0.35f), (Stat.ComfortFlat, 1f)).M("home").X("Под своей крышей одежда сохнет быстрее.").I("item:Wood"), A(D("Сухая кровля", (Stat.DryingPct, 0.12f)), 3)), CL(D("Горн", (Stat.SmeltSpeedPct, 0.2f), (Stat.ExtraBarChance, 0.06f)).M("forge").X("Плавильня и углевая яма работают быстрее, пока ты рядом.").I("item:Coal"), A(D("Поддув", (Stat.SmeltSpeedPct, 0.07f)), 3)), CL(D("Ковка", (Stat.CraftRefundChance, 0.06f), (Stat.CraftDurabilityPct, 0.15f)).X("Иногда заготовка выходит из-под молота, не съев материалов.").I("item:Hammer"), A(D("Верный глаз кузнеца", (Stat.CraftRefundChance, 0.02f)), 2), A(D("Плотная поковка", (Stat.CraftDurabilityPct, 0.05f)), 2)), CL(D("Закалка стали", (Stat.CraftDurabilityPct, 0.2f), (Stat.ToolStaminaPct, -0.05f)).X("Вещи твоей работы служат заметно дольше.").I("item:Iron"), A(D("Отпуск металла", (Stat.CraftDurabilityPct, 0.07f)), 3)), CL(D("Тяжёлый труд", (Stat.ToolStaminaPct, -0.035f), (Stat.CarryWeightFlat, 7.2f)).I("item:Cultivator"), A(D("Сноровка", (Stat.ToolStaminaPct, -0.012f)), 3), A(D("Носильщик", (Stat.CarryWeightFlat, 3.6f)), 3)) }, Shapes = new Cluster[3] { CL(D("Знаток двергов", (Stat.DamageVsDvergrPct, 0.18f)).I("item:DvergrNeedle"), A(D("Разбор механизмов", (Stat.DamageVsDvergrPct, 0.06f)), 1), A(D("Тяжёлый молот", (Stat.StaggerDamagePct, 0.03f)), 1)), CL(D("Мастерская кладка", (Stat.BuildDurabilityPct, 0.14f)).M("build").I("item:Stone"), A(D("Крепкий сруб", (Stat.BuildDurabilityPct, 0.042f)), 1)), CL(D("Бережливая артель", (Stat.BuildRefundChance, 0.05f), (Stat.CarryWeightFlat, 9f)).M("carry").I("item:Coins"), A(D("Смекалка", (Stat.BuildRefundChance, 0.0125f)), 1), A(D("Расчёт", (Stat.BuildRefundChance, 0.0125f)), 1)) }, Keystone = D("Зодчий", (Stat.BuildRefundChance, 0.15f), (Stat.BuildRangeFlat, 3f), (Stat.ToolStaminaPct, -0.175f), (Stat.MoveSpeedPct, -0.15f), (Stat.DamagePct, -0.2f)).X("Ключевой узел: стройка почти бесплатна, но воин из тебя посредственный.").I("item:BronzeNails") }, new Sector { B = Branch.Gathering, Hub = D("Крепкие плечи", (Stat.CarryWeightFlat, 3.6f)), Road = D("Крепкие плечи", (Stat.CarryWeightFlat, 3.6f)), JunctionDef = D("Развилка добытчиков", (Stat.CarryWeightFlat, 4.8f), (Stat.MaxStaminaFlat, 4f)), Travel = new Def[3] { D("Носильщик", (Stat.CarryWeightFlat, 3.6f)), D("Цепкие руки", (Stat.PickupRangePct, 0.03f)), D("Крепкие плечи", (Stat.CarryWeightFlat, 3f)) }, C = new Cluster[13] { CL(D("Лесоруб", (Stat.ChopDamagePct, 0.14f), (Stat.Dmg_Axes, 0.05f)).M("axe").I("sprite:skill_woodcutting"), A(D("Дровосек", (Stat.ChopDamagePct, 0.042f)), 3), A(D("Точило", (Stat.Dmg_Axes, 0.03f)), 3)), CL(D("Горняк", (Stat.PickaxeDamagePct, 0.14f)).M("mine").I("item:PickaxeIron"), A(D("Рудокоп", (Stat.PickaxeDamagePct, 0.042f)), 3), A(D("Каменотёс", (Stat.MineCrackPct, 0.05f)), 3)), CL(D("Старатель", (Stat.ExtraDropChance, 0.12f), (Stat.GatherCritChance, 0.06f)).X("С поваленного дерева и разбитой руды порой падает лишний кусок.").I("item:Ruby"), A(D("Щедрая жила", (Stat.ExtraDropChance, 0.04f)), 3), A(D("Удачный удар", (Stat.GatherCritChance, 0.028f)), 3)), CL(D("Дровосек-воин", (Stat.Dmg_Axes, 0.15f), (Stat.DamageVsForestPct, 0.15f)).X("Рабочий топор в бою не хуже боевого, а тварей Чёрного леса он знает лучше всего.").I("item:AxeBronze"), A(D("Рубака", (Stat.Dmg_Axes, 0.05f)), 3), A(D("Взмах", (Stat.Spd_Axes, 0.03f)), 3)), CL(D("Шахтёр-воин", (Stat.Dmg_Pickaxes, 0.18f), (Stat.Spd_Pickaxes, 0.08f)).M("mine").X("Кирка бьёт врагов не хуже, чем камень.").I("item:PickaxeBronze"), A(D("Удар киркой", (Stat.Dmg_Pickaxes, 0.06f)), 3), A(D("Быстрый замах", (Stat.Spd_Pickaxes, 0.04f)), 3)), CL(D("Тлеющий подсек", (Stat.ChopBurnPct, 0.25f), (Stat.IgniteChance, 0.025f)).X("Непрерывная рубка разогревает древесину, и она поддаётся легче.").I("item:FineWood"), A(D("Жар работы", (Stat.ChopBurnPct, 0.08f)), 3), A(D("Искра от удара", (Stat.IgniteChance, 0.013f)), 3)), CL(D("Раскол", (Stat.MineCrackPct, 0.25f)).X("Непрерывная добыча оставляет трещины, и порода крошится быстрее.").I("item:Iron"), A(D("Трещина", (Stat.MineCrackPct, 0.08f)), 3), A(D("Кайло", (Stat.PickaxeDamagePct, 0.028f)), 3)), CL(D("Вьючный мул", (Stat.CarryWeightFlat, 24f), (Stat.CartPullPct, 0.25f)).M("carry").I("item:BeltStrength"), A(D("Крепкие плечи", (Stat.CarryWeightFlat, 6f)), 3), A(D("Цепкие руки", (Stat.PickupRangePct, 0.03f)), 3)), CL(D("Неутомимый", (Stat.AttackStaminaPct, -0.105f), (Stat.MaxStaminaFlat, 10f)).M("tempo").I("item:Honey"), A(D("Экономный замах", (Stat.AttackStaminaPct, -0.028f)), 3), A(D("Запас сил", (Stat.MaxStaminaFlat, 4f)), 3)), CL(D("Удача добытчика", (Stat.ExtraHarvestChance, 0.12f), (Stat.XpGainPct, 0.06f)).M("hunt").X("Иногда с куста снимается лишний плод.").I("item:GreydwarfEye"), A(D("Щедрый куст", (Stat.ExtraHarvestChance, 0.04f)), 3), A(D("Удача", (Stat.XpGainPct, 0.03f)), 3)), CL(D("Болотный сборщик", (Stat.PoisonChance, 0.05f), (Stat.PoisonResistPct, 0.12f)).M("axe").X("Собирая ядовитые споры, привыкаешь и к яду, и к тому, как его применять.").I("item:Guck"), A(D("Споры", (Stat.PoisonChance, 0.018f)), 3), A(D("Привычка к яду", (Stat.PoisonResistPct, 0.04f)), 3)), CL(D("Ночная смена", (Stat.NightGatherPct, 0.2f), (Stat.NightXpPct, 0.1f)).M("night").I("sprite:mapicon_hammer"), A(D("Работа в темноте", (Stat.NightGatherPct, 0.06f)), 3), A(D("Лунный свет", (Stat.NightXpPct, 0.04f)), 3)), CL(D("Кулак по породе", (Stat.GatherCritChance, 0.03f), (Stat.Dmg_Unarmed, 0.05f)).I("gather-crit"), A(D("Точный скол", (Stat.GatherCritChance, 0.012f)), 2), A(D("Костяшки", (Stat.Dmg_Unarmed, 0.02f)), 2)) }, Ring = new Cluster[1] { CL(D("Жатва", (Stat.MassPickupRadius, 3f), (Stat.ExtraHarvestChance, 0.06f)).M("forage").X("Одно нажатие собирает все такие же растения вокруг.").I("item:Raspberry"), A(D("Охапка", (Stat.MassPickupRadius, 1.2f)), 3), A(D("Хваткость", (Stat.PickupRangePct, 0.025f)), 3)) }, Dead = new Cluster[9] { CL(D("Заточка", (Stat.ChopDamagePct, 0.084f)).I("sprite:skill_woodcutting"), A(D("Дровосек", (Stat.ChopDamagePct, 0.028f)), 3)), CL(D("Остриё кирки", (Stat.PickaxeDamagePct, 0.084f)).I("item:PickaxeIron"), A(D("Рудокоп", (Stat.PickaxeDamagePct, 0.028f)), 3)), CL(D("Свежевание", (Stat.HunterDropChance, 0.16f)).X("Со зверя чаще снимается лишняя шкура или кусок мяса.").I("hunt:"), A(D("Острый нож", (Stat.HunterDropChance, 0.05f)), 3)), CL(D("Чутьё", (Stat.GatherCritChance, 0.042f)).I("item:Iron"), A(D("Удачный удар", (Stat.GatherCritChance, 0.017f)), 3)), CL(D("Рыбацкая удача", (Stat.ExtraFishChance, 0.1f), (Stat.FishBiteChance, 0.15f)).M("fish").X("Клюёт чаще, и с крючка порой снимается лишняя рыба.").I("item:FishRaw"), A(D("Прикормка", (Stat.FishBiteChance, 0.05f)), 3)), CL(D("Рудоплав", (Stat.ExtraBarChance, 0.09f), (Stat.SmeltSpeedPct, 0.12f)).M("forge").X("Из той же руды порой выходит лишний слиток.").I("item:BronzeNails"), A(D("Чистая руда", (Stat.ExtraBarChance, 0.03f)), 3)), CL(D("Наковальня", (Stat.CraftDurabilityPct, 0.18f), (Stat.SmeltSpeedPct, 0.1f)).M("forge").X("Вещи твоей ковки держат удар дольше.").I("item:Iron"), A(D("Ровный молот", (Stat.CraftDurabilityPct, 0.06f)), 3)), CL(D("Клеймо мастера", (Stat.CraftRefundChance, 0.05f), (Stat.CraftDurabilityPct, 0.1f)).X("Улучшение снаряжения иногда обходится даром.").I("item:Hammer"), A(D("Точный расчёт ковки", (Stat.CraftRefundChance, 0.018f)), 3)), CL(D("Верный глаз", (Stat.ExtraDropChance, 0.08f), (Stat.ChopDamagePct, 0.056f)).M("chop").I("item:Ruby"), A(D("Щедрая жила", (Stat.ExtraDropChance, 0.03f)), 3), A(D("Дровосек", (Stat.ChopDamagePct, 0.028f)), 3)) }, Shapes = new Cluster[3] { CL(D("Гроза Чёрного леса", (Stat.DamageVsForestPct, 0.18f)).M("lore").I("item:TrophyGreydwarf"), A(D("Повадки серых", (Stat.DamageVsForestPct, 0.06f)), 1), A(D("Рубящий удар", (Stat.Dmg_Axes, 0.03f)), 1)), CL(D("Глубокая жила", (Stat.MineCrackPct, 0.2f), (Stat.PickaxeDamagePct, 0.07f)).M("mine").I("item:Iron"), A(D("Трещина", (Stat.MineCrackPct, 0.056f)), 1)), CL(D("Верная рука", (Stat.GatherCritChance, 0.06f), (Stat.ChopDamagePct, 0.07f)).M("chop").I("item:Ruby"), A(D("Удачный удар", (Stat.GatherCritChance, 0.021f)), 1), A(D("Дровосек", (Stat.ChopDamagePct, 0.042f)), 1)) }, Keystone = D("Материнская жила", (Stat.ChopDamagePct, 0.245f), (Stat.PickaxeDamagePct, 0.245f), (Stat.GatherCritChance, 0.14f), (Stat.CarryWeightFlat, 60f), (Stat.MoveSpeedPct, -0.15f), (Stat.DamagePct, -0.25f)).X("Ключевой узел: рубит и копает как машина, но в бою неуклюж.").I("item:Silver") }, new Sector { B = Branch.Exploration, Hub = D("Лёгкий шаг", (Stat.MoveSpeedPct, 0.01f)), Road = D("Проворство", (Stat.MoveSpeedPct, 0.01f)), JunctionDef = D("Развилка следопытов", (Stat.MoveSpeedPct, 0.01f), (Stat.MaxStaminaFlat, 4f)), Travel = new Def[3] { D("Проворство", (Stat.MoveSpeedPct, 0.01f)), D("Выносливость", (Stat.MaxStaminaFlat, 4f)), D("Лёгкий шаг", (Stat.RunStaminaPct, -0.015f)) }, C = new Cluster[13] { CL(D("Ветер в спину", (Stat.MoveSpeedPct, 0.012f), (Stat.PavedSpeedPct, 0.045f)).M("move").X("Мощёная дорога и вспаханное поле несут быстрее.").I("item:CapeDeerHide"), A(D("Мощёная тропа", (Stat.PavedSpeedPct, 0.0158f)), 3), A(D("Лёгкий шаг", (Stat.RunStaminaPct, -0.02f)), 3)), CL(D("Марафонец", (Stat.RunStaminaPct, -0.075f), (Stat.MaxStaminaFlat, 8f)).M("stamina").I("sprite:skill_run"), A(D("Дыхание бегуна", (Stat.RunStaminaPct, -0.025f)), 3), A(D("Запас сил", (Stat.MaxStaminaFlat, 4f)), 3)), CL(D("Скалолаз", (Stat.JumpPct, 0.15f), (Stat.JumpStaminaPct, -0.15f)).M("leap").I("sprite:skill_jump"), A(D("Прыгучесть", (Stat.JumpPct, 0.04f)), 3), A(D("Пружина", (Stat.JumpStaminaPct, -0.05f)), 3)), CL(D("Двойной прыжок", (Stat.DoubleJump, 1f)).M("leap").X("Нажми прыжок ещё раз в воздухе.").I("sprite:skill_jump"), A(D("Отталкивание", (Stat.JumpPct, 0.04f)), 3), A(D("Мягкое приземление", (Stat.FallDamagePct, -0.1f)), 3)), CL(D("Морской ход", (Stat.SailSpeedPct, 0.15f), (Stat.SailControlPct, 0.25f)).M("sail").X("Пока ты сам за штурвалом, корабль идёт быстрее и слушается руля.").I("item:Wood"), A(D("Парус", (Stat.SailSpeedPct, 0.05f)), 3), A(D("Руль", (Stat.SailControlPct, 0.08f)), 3)), CL(D("Тень", (Stat.StealthPct, 0.105f), (Stat.NoisePct, -0.105f), (Stat.SneakStaminaPct, -0.12f)).I("sprite:skill_sneak"), A(D("Тихий шаг", (Stat.NoisePct, -0.035f)), 3), A(D("Незаметность", (Stat.StealthPct, 0.035f)), 3)), CL(D("Следопыт", (Stat.ExploreRadiusPct, 0.15f), (Stat.XpGainPct, 0.08f)).M("hunt").I("sprite:mapicon_pin"), A(D("Зоркость", (Stat.ExploreRadiusPct, 0.048f)), 3), A(D("Любопытство", (Stat.XpGainPct, 0.03f)), 3)), CL(D("Ночной охотник", (Stat.NightStealthPct, 0.2f), (Stat.NightMoveSpeedPct, 0.048f)).I("item:CapeTrollHide"), A(D("Глаза ночи", (Stat.NightStealthPct, 0.06f)), 3), A(D("Поступь в ночи", (Stat.NightMoveSpeedPct, 0.018f)), 3)), CL(D("Гроза в спину", (Stat.WetShockPct, 0.2f), (Stat.DamageVsWetPct, 0.12f)).M("shock").X("Мокрая одежда проводит ток: под дождём твои молнии бьют сильнее.").I("item:Thunderstone"), A(D("Искра дождя", (Stat.ShockChance, 0.025f)), 3), A(D("Мокрый насквозь", (Stat.WetShockPct, 0.07f)), 3)), CL(D("Погоня", (Stat.OnslaughtChance, 0.15f), (Stat.MoveSpeedPct, 0.01f)).M("armor").X("Натиск после убийства.").I("sprite:skill_run"), A(D("Жажда погони", (Stat.OnslaughtChance, 0.03f)), 3), A(D("Азарт погони", (Stat.OnslaughtPowerPct, 0.03f)), 3)), CL(D("Перьевое падение", (Stat.FallDamagePct, -0.4f)).M("leap").I("item:Feathers"), A(D("Мягкое приземление", (Stat.FallDamagePct, -0.12f)), 3), A(D("Кошачья ловкость", (Stat.DodgeChance, 0.015f)), 3)), CL(D("Зов дикого", (Stat.TamingSpeedPct, 0.4f), (Stat.TamedDamagePct, 0.2f)).M("tame").X("Зверь идёт на твой зов и дерётся за тебя злее.").I("taming:"), A(D("Спокойный нрав", (Stat.TamingSpeedPct, 0.15f)), 3), A(D("Вожак", (Stat.TamedDamagePct, 0.07f)), 3)), CL(D("Мокрая цель", (Stat.DamageVsWetPct, 0.1f), (Stat.ShockChance, 0.025f)).M("shock").X("Мокрый враг хуже держит удар, особенно от молнии.").I("wet"), A(D("Дождь в лицо", (Stat.DamageVsWetPct, 0.035f)), 2), A(D("Искра дождя", (Stat.ShockChance, 0.01f)), 2)) }, Ring = new Cluster[1] { CL(D("Второе дыхание бегуна", (Stat.RunStaminaPct, -0.06f), (Stat.PavedSpeedPct, 0.036f)).M("move").I("sprite:skill_run"), A(D("Лёгкий шаг", (Stat.RunStaminaPct, -0.02f)), 3), A(D("Утоптанная тропа", (Stat.PavedSpeedPct, 0.0135f)), 3)) }, Dead = new Cluster[9] { CL(D("Пловец", (Stat.SwimSpeedPct, 0.2f), (Stat.SwimStaminaPct, -0.25f), (Stat.DamageVsSeaPct, 0.15f)).I("sprite:skill_swim"), A(D("Гребок", (Stat.SwimSpeedPct, 0.06f)), 3)), CL(D("Лёгкие бегуна", (Stat.RunStaminaPct, -0.06f)).I("sprite:skill_run"), A(D("Лёгкий шаг", (Stat.RunStaminaPct, -0.02f)), 3)), CL(D("Пружина", (Stat.JumpPct, 0.1f)).I("sprite:skill_jump"), A(D("Прыгучесть", (Stat.JumpPct, 0.035f)), 3)), CL(D("Охотничья лёжка", (Stat.HunterDropChance, 0.1f), (Stat.StealthPct, 0.05f)).I("hunt:"), A(D("Острый нож", (Stat.HunterDropChance, 0.035f)), 3)), CL(D("Непогода", (Stat.WetResistPct, 0.15f), (Stat.DryingPct, 0.25f)).M("fish").X("Дождь мешает меньше, и промокание спадает быстрее.").I("item:CapeDeerHide"), A(D("Плащ", (Stat.WetResistPct, 0.05f)), 2), A(D("Отряхнуться", (Stat.DryingPct, 0.09f)), 2)), CL(D("Терпение рыбака", (Stat.FishStaminaPct, -0.2f), (Stat.FishBiteChance, 0.1f)).M("fish").X("Вываживать рыбу заметно легче.").I("item:FishingRod"), A(D("Крепкая леска", (Stat.FishStaminaPct, -0.07f)), 3)), CL(D("Скороход", (Stat.RunStaminaPct, -0.05f), (Stat.MoveSpeedPct, 0.012f)).M("dodge").I("run-stamina"), A(D("Лёгкие бегуна", (Stat.RunStaminaPct, -0.018f)), 3)), CL(D("Скользящий шаг", (Stat.StealthPct, 0.07f), (Stat.NoisePct, -0.07f)).I("sprite:skill_sneak"), A(D("Незаметность", (Stat.StealthPct, 0.025f)), 3), A(D("Тихий шаг", (Stat.NoisePct, -0.025f)), 3)), CL(D("Накатом", (Stat.Slide, 1f)).M("sail").X("Присядь на склоне и держи путь вниз — поедешь, и быстрее, чем сбежишь на ногах. Встань, чтобы затормозить.").I("move"), A(D("Лёгкий шаг", (Stat.RunStaminaPct, -0.018f)), 3), A(D("Твёрдая пятка", (Stat.FallDamagePct, -0.06f)), 2)) }, Shapes = new Cluster[3] { CL(D("Гроза гор", (Stat.DamageVsMountainPct, 0.18f)).M("weather").I("item:TrophyWolf"), A(D("Охота в горах", (Stat.DamageVsMountainPct, 0.06f)), 1), A(D("Меткий бросок", (Stat.RangedDamagePct, 0.014f)), 1)), CL(D("Торная дорога", (Stat.PavedSpeedPct, 0.054f)).M("move").X("Мощёная дорога и вспаханная земля несут быстрее.").I("item:Stone"), A(D("Мощёная тропа", (Stat.PavedSpeedPct, 0.0158f)), 1)), CL(D("Бесшумность", (Stat.StealthPct, 0.07f), (Stat.NoisePct, -0.07f)).M("stealth").I("sprite:skill_sneak"), A(D("Незаметность", (Stat.StealthPct, 0.025f)), 1), A(D("Тихий шаг", (Stat.NoisePct, -0.025f)), 1)) }, Keystone = D("Крылья Хугина", (Stat.Glide, 1f), (Stat.SafeFall, 1f), (Stat.MaxStaminaPct, -0.25f), (Stat.ArmorPct, -0.15f)).X("Ключевой узел: планирование и никакого урона от падения, но тело лёгкое и хрупкое.").I("item:CapeFeather") }, new Sector { B = Branch.Ranged, Hub = D("Твёрдая рука", (Stat.RangedDamagePct, 0.021f)), Road = D("Меткость", (Stat.RangedDamagePct, 0.014f)), JunctionDef = D("Развилка охотников", (Stat.RangedDamagePct, 0.014f), (Stat.MaxStaminaFlat, 4f)), Travel = new Def[3] { D("Меткость", (Stat.RangedDamagePct, 0.014f)), D("Хладнокровие", (Stat.FullHpDamagePct, 0.03f)), D("Лёгкая тетива", (Stat.AttackStaminaPct, -0.017f)) }, C = new Cluster[13] { CL(D("Меткий лучник", (Stat.Dmg_Bows, 0.15f), (Stat.CritChance, 0.03f)).M("bow").I("skill:Bows"), A(D("Лучник", (Stat.Dmg_Bows, 0.05f)), 3), A(D("Точный глаз", (Stat.CritChance, 0.015f)), 3)), CL(D("Стрелок", (Stat.Dmg_Crossbows, 0.15f), (Stat.AttackSpeedPct, 0.04f)).I("skill:Crossbows"), A(D("Арбалетчик", (Stat.Dmg_Crossbows, 0.05f)), 3), A(D("Быстрая перезарядка", (Stat.AttackSpeedPct, 0.02f)), 3)), CL(D("Дальний выстрел", (Stat.ArrowRangePct, 0.3f), (Stat.ProjectileSpeedPct, 0.1f)).M("arrow").X("Стрела летит дальше и падает медленнее.").I("hunt:"), A(D("Тугая дуга", (Stat.ArrowRangePct, 0.1f)), 3), A(D("Быстрая стрела", (Stat.ProjectileSpeedPct, 0.035f)), 3)), CL(D("Тугая тетива", (Stat.DrawSpeedPct, 0.105f), (Stat.DrawStaminaPct, -0.15f)).I("item:BowFineWood"), A(D("Тетива", (Stat.DrawSpeedPct, 0.035f)), 3), A(D("Спокойное дыхание", (Stat.DrawStaminaPct, -0.06f)), 3)), CL(D("Колчан без дна", (Stat.ArrowSaveChance, 0.14f), (Stat.PickupRangePct, 0.04f)).M("forage").I("item:ArrowWood"), A(D("Бережливый стрелок", (Stat.ArrowSaveChance, 0.042f)), 3), A(D("Подбор стрел", (Stat.PickupRangePct, 0.025f)), 2)), CL(D("Рой стрел", (Stat.ExtraProjectiles, 1f), (Stat.RangedDamagePct, -0.12f)).M("proj").X("Каждый выстрел выпускает дополнительную стрелу с разбросом.").I("item:ArrowBronze"), A(D("Меткость", (Stat.RangedDamagePct, 0.025f)), 3), A(D("Твёрдая рука", (Stat.DrawSpeedPct, 0.028f)), 3)), CL(D("Пронзающий выстрел", (Stat.ProjectilePierce, 1f)).M("proj").X("Стрела проходит сквозь первую цель и летит дальше.").I("item:ArrowIron"), A(D("Острый наконечник", (Stat.ProjectileSpeedPct, 0.042f)), 3), A(D("Пробивная сила", (Stat.RangedDamagePct, 0.021f)), 3)), CL(D("Расщепление", (Stat.ProjectileSplit, 1f), (Stat.RangedDamagePct, -0.08f)).X("При попадании стрела делится надвое.").I("item:ArrowFlint"), A(D("Раскалённое древко", (Stat.RangedDamagePct, 0.025f)), 3), A(D("Расчёт", (Stat.CritDamagePct, 0.05f)), 3)), CL(D("Зажигательная стрела", (Stat.IgniteChance, 0.06f), (Stat.BurnDamagePct, 0.06f)).M("fire").I("item:ArrowFire"), A(D("Смоляной наконечник", (Stat.IgniteChance, 0.02f)), 3), A(D("Жар", (Stat.BurnDamagePct, 0.03f)), 3)), CL(D("Отравленная стрела", (Stat.PoisonChance, 0.06f), (Stat.PoisonDamagePct, 0.096f)).M("poison").I("item:ArrowPoison"), A(D("Яд на острие", (Stat.PoisonChance, 0.02f)), 3), A(D("Крепкая отрава", (Stat.PoisonDamagePct, 0.04f)), 3)), CL(D("Громовой болт", (Stat.ShockChance, 0.06f), (Stat.ShockStunChance, 0.07f)).X("Молния в болте оглушает цель.").I("item:ArrowNeedle"), A(D("Искра на болте", (Stat.ShockChance, 0.02f)), 3), A(D("Раскат", (Stat.ShockDamagePct, 0.04f)), 3)), CL(D("Ночной выстрел", (Stat.NightDamagePct, 0.084f), (Stat.DamageVsAnimalsPct, 0.15f)).M("stealth").I("item:Thistle"), A(D("Выстрел из тьмы", (Stat.NightDamagePct, 0.035f)), 3), A(D("Глаза ночи", (Stat.NightStealthPct, 0.05f)), 3)), CL(D("Оперение", (Stat.ProjectileSpeedPct, 0.1f), (Stat.AccuracyPct, 0.05f)).I("arrow-speed"), A(D("Перо", (Stat.ProjectileSpeedPct, 0.035f)), 2), A(D("Ровная рука", (Stat.AccuracyPct, 0.018f)), 2)) }, Ring = new Cluster[1] { CL(D("Ровный выстрел", (Stat.Dmg_Bows, 0.08f), (Stat.ProjectileSpeedPct, 0.07f)).M("arrow").I("skill:Bows"), A(D("Лучник", (Stat.Dmg_Bows, 0.03f)), 3), A(D("Быстрая стрела", (Stat.ProjectileSpeedPct, 0.025f)), 3)) }, Dead = new Cluster[9] { CL(D("Тугая жила", (Stat.Dmg_Bows, 0.1f)).I("skill:Bows"), A(D("Лучник", (Stat.Dmg_Bows, 0.035f)), 3)), CL(D("Ложе арбалета", (Stat.Dmg_Crossbows, 0.1f)).I("skill:Crossbows"), A(D("Арбалетчик", (Stat.Dmg_Crossbows, 0.035f)), 3)), CL(D("Оперение", (Stat.AccuracyPct, 0.2f)).X("Разброс выстрела заметно уже.").I("hunt:"), A(D("Твёрдая рука", (Stat.AccuracyPct, 0.064f)), 3)), CL(D("Полный колчан", (Stat.ArrowSaveChance, 0.07f)).I("item:ArrowWood"), A(D("Бережливый стрелок", (Stat.ArrowSaveChance, 0.025f)), 3)), CL(D("Подрывник", (Stat.BombDamagePct, 0.18f), (Stat.BlastRadiusPct, 0.1f)).X("Брошенная бомба бьёт сильнее и шире.").I("item:Ooze"), A(D("Плотный заряд", (Stat.BombDamagePct, 0.06f)), 3)), CL(D("Запал", (Stat.DoubleBlastChance, 0.1f), (Stat.BombDamagePct, 0.08f)).M("bomb").X("Иногда за первым взрывом идёт второй.").I("item:Coal"), A(D("Двойной фитиль", (Stat.DoubleBlastChance, 0.035f)), 3)), CL(D("Запасливый подрывник", (Stat.BombSaveChance, 0.1f), (Stat.BlastRadiusPct, 0.08f)).M("bomb").X("Брошенная бомба порой остаётся в сумке.").I("item:Coins"), A(D("Бережливая рука", (Stat.BombSaveChance, 0.035f)), 3)), CL(D("Минёр", (Stat.BlastRadiusPct, 0.12f), (Stat.BombDamagePct, 0.08f)).M("bomb").X("Заряд можно уложить и подождать.").I("item:Ooze"), A(D("Растяжка", (Stat.BlastRadiusPct, 0.045f)), 3)), CL(D("Скорострельность", (Stat.DrawSpeedPct, 0.07f), (Stat.AttackSpeedPct, 0.03f)).M("bow").I("item:BowFineWood"), A(D("Тетива", (Stat.DrawSpeedPct, 0.025f)), 3), A(D("Быстрая перезарядка", (Stat.AttackSpeedPct, 0.015f)), 3)) }, Shapes = new Cluster[3] { CL(D("Гроза морей", (Stat.DamageVsSeaPct, 0.18f)).M("sail").I("item:TrophySerpent"), A(D("Морская охота", (Stat.DamageVsSeaPct, 0.06f)), 1), A(D("Меткость", (Stat.RangedDamagePct, 0.014f)), 1)), CL(D("Бездонный колчан", (Stat.ArrowSaveChance, 0.14f)).M("arrow").I("item:ArrowWood"), A(D("Бережливый стрелок", (Stat.ArrowSaveChance, 0.042f)), 1)), CL(D("Верный прицел", (Stat.AccuracyPct, 0.12f), (Stat.CritChance, 0.02f)).M("bow").I("hunt:"), A(D("Твёрдая рука", (Stat.AccuracyPct, 0.04f)), 1), A(D("Ровное дыхание", (Stat.AccuracyPct, 0.032f)), 1)) }, Keystone = D("Глаз Одина", (Stat.RangedDamagePct, 0.175f), (Stat.ProjectileSplit, 1f), (Stat.CritChance, 0.05f), (Stat.MeleeDamagePct, -0.4f), (Stat.MaxHealthPct, -0.15f)).X("Ключевой узел: стрела делится при попадании, ближний бой забыт.").I("item:BowFineWood") }, new Sector { B = Branch.Magic, Hub = D("Искра эйтра", (Stat.MaxEitrFlat, 2.1f)), Road = D("Эйтр", (Stat.MaxEitrFlat, 2.1f)), JunctionDef = D("Развилка чародеев", (Stat.MaxEitrFlat, 2.8f), (Stat.EitrRegenPct, 0.021f)), Travel = new Def[3] { D("Эйтр", (Stat.MaxEitrFlat, 2.1f)), D("Сосредоточение", (Stat.EitrRegenPct, 0.021f)), D("Лёгкое заклинание", (Stat.EitrCostPct, -0.025f)) }, C = new Cluster[13] { CL(D("Резервуар", (Stat.MaxEitrFlat, 8.4f), (Stat.MaxEitrPct, 0.05f)).M("eitr").I("item:Eitr"), A(D("Эйтр", (Stat.MaxEitrFlat, 2.8f)), 3), A(D("Глубина", (Stat.MaxEitrPct, 0.02f)), 3)), CL(D("Поток эйтра", (Stat.EitrRegenPct, 0.14f), (Stat.EitrOnKill, 3f)).M("magic").I("item:StaffShield"), A(D("Сосредоточение", (Stat.EitrRegenPct, 0.042f)), 3), A(D("Медитация", (Stat.MaxEitrFlat, 2.8f)), 3)), CL(D("Лёгкость заклинаний", (Stat.EitrCostPct, -0.15f)).M("eitr").I("item:Thunderstone"), A(D("Лёгкое заклинание", (Stat.EitrCostPct, -0.05f)), 3), A(D("Экономия силы", (Stat.EitrRegenPct, 0.028f)), 3)), CL(D("Элементалист", (Stat.Dmg_ElementalMagic, 0.15f), (Stat.ElementalDamagePct, 0.08f)).M("magic").I("skill:ElementalMagic"), A(D("Стихия", (Stat.Dmg_ElementalMagic, 0.05f)), 3), A(D("Сила стихий", (Stat.ElementalDamagePct, 0.03f)), 3)), CL(D("Кровавый маг", (Stat.Dmg_BloodMagic, 0.15f), (Stat.LifestealPct, 0.02f)).M("sword").I("skill:BloodMagic"), A(D("Кровь", (Stat.Dmg_BloodMagic, 0.05f)), 3), A(D("Жертва", (Stat.MaxHealthFlat, 2.8f)), 3)), CL(D("Пламя Муспельхейма", (Stat.IgniteChance, 0.06f), (Stat.BurnDamagePct, 0.09f)).M("fire").I("item:SurtlingCore"), A(D("Искра", (Stat.IgniteChance, 0.02f)), 3), A(D("Жар", (Stat.BurnDamagePct, 0.03f)), 3), A(D("Долгий огонь", (Stat.BurnDurationPct, 0.06f)), 2, 1)), CL(D("Жар питает", (Stat.EitrOnIgnite, 4f), (Stat.IgniteChance, 0.04f)).X("Каждый поджог возвращает эйтр.").I("item:Coal"), A(D("Искра", (Stat.IgniteChance, 0.015f)), 3), A(D("Тлеющий эйтр", (Stat.EitrOnIgnite, 2f)), 2), A(D("Долгий огонь", (Stat.BurnDurationPct, 0.05f)), 2)), CL(D("Громовержец", (Stat.ShockChance, 0.06f), (Stat.ShockDamagePct, 0.12f)).M("shock").I("item:Thunderstone"), A(D("Искра молнии", (Stat.ShockChance, 0.02f)), 3), A(D("Раскат", (Stat.ShockDamagePct, 0.04f)), 3)), CL(D("Вечная мерзлота", (Stat.FrostChance, 0.06f), (Stat.FrostPotencyPct, 0.2f)).M("frost").I("item:FreezeGland"), A(D("Искра льда", (Stat.FrostChance, 0.02f)), 3), A(D("Стужа", (Stat.FrostPotencyPct, 0.07f)), 3), A(D("Долгий холод", (Stat.FrostDurationPct, 0.06f)), 2, 1)), CL(D("Чумной маг", (Stat.PoisonChance, 0.06f), (Stat.PoisonDamagePct, 0.12f), (Stat.DamageVsUndeadPct, 0.12f)).M("poison").I("item:Guck"), A(D("Отрава", (Stat.PoisonDamagePct, 0.04f)), 3), A(D("Споры", (Stat.PoisonChance, 0.02f)), 3), A(D("Долгая отрава", (Stat.PoisonDurationPct, 0.06f)), 2, 1)), CL(D("Архимаг", (Stat.AilmentDurationPct, 0.15f), (Stat.PoisonChance, 0.04f)).M("thorns").I("item:StaffFireball"), A(D("Долгая хворь", (Stat.AilmentDurationPct, 0.05f)), 3), A(D("Ядовитый шёпот", (Stat.PoisonChance, 0.015f)), 3), A(D("Стойкое проклятие", (Stat.StatusDurationPct, 0.03f)), 2)), CL(D("Колдун ночи", (Stat.NightEitrRegenPct, 0.2f), (Stat.NightDamagePct, 0.07f)).M("magic").I("moon:"), A(D("Лунный свет", (Stat.NightEitrRegenPct, 0.06f)), 3), A(D("Сумрак", (Stat.NightDamagePct, 0.028f)), 3)), CL(D("Раздуть пламя", (Stat.DamageVsBurningPct, 0.1f), (Stat.IgniteChance, 0.025f)).X("По горящему врагу твой удар тяжелее.").I("vs-ailment"), A(D("Жар", (Stat.DamageVsBurningPct, 0.035f)), 2), A(D("Трут", (Stat.IgniteChance, 0.01f)), 2)) }, Ring = new Cluster[1] { CL(D("Проводник", (Stat.ShockDamagePct, 0.08f), (Stat.ShockChance, 0.025f)).I("item:Thunderstone"), A(D("Раскат", (Stat.ShockDamagePct, 0.028f)), 3), A(D("Искра молнии", (Stat.ShockChance, 0.01f)), 3)) }, Dead = new Cluster[9] { CL(D("Сосуд", (Stat.MaxEitrFlat, 5.6f)).I("item:Eitr"), A(D("Эйтр", (Stat.MaxEitrFlat, 2.1f)), 3)), CL(D("Родник силы", (Stat.EitrRegenPct, 0.07f)).I("item:StaffShield"), A(D("Сосредоточение", (Stat.EitrRegenPct, 0.025f)), 3)), CL(D("Уголь", (Stat.BurnDamagePct, 0.06f)).I("item:Coal"), A(D("Жар", (Stat.BurnDamagePct, 0.021f)), 3)), CL(D("Иней", (Stat.FrostPotencyPct, 0.12f)).I("item:FreezeGland"), A(D("Стужа", (Stat.FrostPotencyPct, 0.04f)), 3)), CL(D("Бомбодел", (Stat.BombCraftRefundChance, 0.08f), (Stat.ExtraBombChance, 0.07f)).X("Порой заряд выходит из мастерской даром или сразу парой.").I("item:Ooze"), A(D("Экономный порох", (Stat.BombCraftRefundChance, 0.028f)), 3)), CL(D("Пороховой погреб", (Stat.ExtraBombChance, 0.1f), (Stat.BombSaveChance, 0.06f)).X("Запас бомб тает медленнее.").I("item:Coal"), A(D("Лишний заряд", (Stat.ExtraBombChance, 0.035f)), 3)), CL(D("Мокрая гроза", (Stat.DamageVsWetPct, 0.1f), (Stat.ShockDamagePct, 0.08f)).I("wet"), A(D("Проводник", (Stat.ShockDamagePct, 0.03f)), 3)), CL(D("Малый резерв", (Stat.MaxEitrFlat, 5.6f), (Stat.EitrRegenPct, 0.042f)).I("item:Eitr"), A(D("Эйтр", (Stat.MaxEitrFlat, 2.1f)), 3), A(D("Сосредоточение", (Stat.EitrRegenPct, 0.017f)), 3)), CL(D("Бродильня", (Stat.FermentSpeedPct, 0.25f), (Stat.PotionDurationPct, 0.08f)).X("Мёд поспевает раньше срока.").I("item:MeadBaseHealthMedium"), A(D("Тёплый погреб", (Stat.FermentSpeedPct, 0.09f)), 3)) }, Shapes = new Cluster[3] { CL(D("Гроза Мгливых земель", (Stat.DamageVsMistlandsPct, 0.18f)).M("lore").I("item:TrophySeeker"), A(D("Знание мглы", (Stat.DamageVsMistlandsPct, 0.06f)), 1), A(D("Стихия", (Stat.Dmg_ElementalMagic, 0.03f)), 1)), CL(D("Настой алхимика", (Stat.PotionDurationPct, 0.18f)).M("potion").I("item:MeadStaminaMinor"), A(D("Настойка", (Stat.PotionDurationPct, 0.05f)), 1)), CL(D("Родник эйтра", (Stat.EitrRegenPct, 0.07f), (Stat.MaxEitrFlat, 5.6f)).M("eitr").I("item:Eitr"), A(D("Сосредоточение", (Stat.EitrRegenPct, 0.021f)), 1), A(D("Эйтр", (Stat.MaxEitrFlat, 2.1f)), 1)) }, Keystone = D("Пепел Сурта", (Stat.ElementalDamagePct, 0.25f), (Stat.IgniteChance, 0.1f), (Stat.BurnDamagePct, 0.15f), (Stat.DamageVsBurningPct, 0.2f), (Stat.PhysicalDamagePct, -0.45f)).X("Ключевой узел: всё горит, сталь забыта.").I("item:SurtlingCore") } }; Boundary[] array2 = new Boundary[8] { new Boundary { C = new Cluster[6] { CL(D("Щит и меч", (Stat.BlockPowerPct, 0.064f), (Stat.MeleeDamagePct, 0.035f)).M("enchant").I("item:ShieldWood"), A(D("Крепкий хват", (Stat.BlockPowerPct, 0.032f)), 3), A(D("Крепкая рука", (Stat.MeleeDamagePct, 0.021f)), 3)), CL(D("Кулачный хват", (Stat.Dmg_Unarmed, 0.08f), (Stat.AttackStaminaPct, -0.03f)).M("fist").I("item:Flint"), A(D("Костяшки", (Stat.Dmg_Unarmed, 0.038f)), 3), A(D("Жилистость", (Stat.UnarmedDamageTakenPct, -0.03f)), 3)), CL(D("Бой без страха", (Stat.UnarmedDamageTakenPct, -0.1f), (Stat.Spd_Unarmed, 0.08f)).I("sprite:skill_dodge"), A(D("Закалка тела", (Stat.UnarmedDamageTakenPct, -0.03f)), 3), A(D("Быстрые руки", (Stat.Spd_Unarmed, 0.04f)), 3)), CL(D("Хладнокровный боец", (Stat.FullHpDamagePct, 0.15f), (Stat.DamageVsPlainsPct, 0.12f)).I("item:ArmorLeatherChest"), A(D("Свежесть", (Stat.FullHpDamagePct, 0.05f)), 3), A(D("Упор", (Stat.StaggerResistPct, 0.024f)), 3)), CL(D("Контратака", (Stat.ParryBonusPct, 0.126f), (Stat.StunChance, 0.035f)).M("stagger").I("item:ShieldBronzeBuckler"), A(D("Отскок", (Stat.ParryBonusPct, 0.042f)), 3), A(D("Ответный удар", (Stat.StunChance, 0.014f)), 3)), CL(D("Латный кулак", (Stat.Dmg_Unarmed, 0.09f), (Stat.ArmorFlat, 3.5f)).M("fist").I("item:ArmorBronzeChest"), A(D("Костяшки", (Stat.Dmg_Unarmed, 0.03f)), 3), A(D("Наручи", (Stat.ArmorFlat, 2.1f)), 3)) }, Dead = new Cluster[2] { CL(D("Сталь и кожа", (Stat.ArmorFlat, 3.5f)).M("thorns").I("item:ArmorLeatherChest"), A(D("Пластина", (Stat.ArmorFlat, 2.1f)), 3)), CL(D("Кулак в броне", (Stat.Dmg_Unarmed, 0.06f)).I("item:Flint"), A(D("Костяшки", (Stat.Dmg_Unarmed, 0.022f)), 3)) }, SpurTip = D("Кулак Тора", (Stat.Dmg_Unarmed, 0.3f), (Stat.Spd_Unarmed, 0.18f), (Stat.DodgeChance, 0.08f), (Stat.UnarmedDamageTakenPct, -0.15f), (Stat.UnarmedToolTier, 2f), (Stat.BlockPowerPct, -0.5f), (Stat.ArmorPct, -0.2f), (Stat.MaxHealthPct, -0.2f)).X("Ключевой узел: голые кулаки быстрее щита; с «Голыми руками» кулаки ломают любую руду и дерево, но доспех и щит почти бесполезны.").I("sprite:skill_dodge"), SpurS1 = D("Костяшки", (Stat.Dmg_Unarmed, 0.038f)), SpurS2 = D("Быстрые руки", (Stat.Spd_Unarmed, 0.04f)), SpurIsKeystone = true }, new Boundary { C = new Cluster[6] { CL(D("Крепкий дух", (Stat.MaxHealthFlat, 5.6f), (Stat.ArmorFlat, 2.1f)).I("item:ArmorLeatherChest"), A(D("Здоровье", (Stat.MaxHealthFlat, 2.8f)), 3), A(D("Пластина", (Stat.ArmorFlat, 2.1f)), 3)), CL(D("Стена щитов", (Stat.BlockPowerPct, 0.096f), (Stat.ThornsPct, 0.08f)).I("item:ShieldIronTower"), A(D("Крепкий хват", (Stat.BlockPowerPct, 0.032f)), 3), A(D("Шипы", (Stat.ThornsPct, 0.03f)), 3)), CL(D("Каменная кожа", (Stat.StaggerResistPct, 0.09f), (Stat.DamageTakenPct, -0.035f)).M("tough").I("item:Stone"), A(D("Упор", (Stat.StaggerResistPct, 0.03f)), 3), A(D("Стойкость", (Stat.DamageTakenPct, -0.01f)), 3)), CL(D("Живучий страж", (Stat.HealthRegenPct, 0.072f), (Stat.ArmorFlat, 2.8f)).I("item:ArmorBronzeChest"), A(D("Восстановление", (Stat.HealthRegenPct, 0.024f)), 3), A(D("Заклёпки", (Stat.ArmorFlat, 2.1f)), 3)), CL(D("Толстая шкура", (Stat.DamageTakenPct, -0.042f), (Stat.MaxHealthFlat, 7f)).I("item:DeerHide"), A(D("Стойкость", (Stat.DamageTakenPct, -0.014f)), 3), A(D("Здоровье", (Stat.MaxHealthFlat, 2.8f)), 3)), CL(D("Крепкий сон", (Stat.NightArmorFlat, 8f), (Stat.RestedDurationPct, 0.075f)).I("sprite:mapicon_bed"), A(D("Ночной дозор", (Stat.NightArmorFlat, 3f)), 3), A(D("Покой", (Stat.RestedDurationPct, 0.025f)), 3)) }, Dead = new Cluster[2] { CL(D("Живучесть стража", (Stat.MaxHealthFlat, 7f)).I("item:MeadHealthMinor"), A(D("Здоровье", (Stat.MaxHealthFlat, 2.8f)), 3)), CL(D("Щитоносец", (Stat.BlockPowerPct, 0.064f)).I("item:ShieldWood"), A(D("Крепкий хват", (Stat.BlockPowerPct, 0.024f)), 3)) }, SpurTip = D("Каменное сердце", (Stat.StaggerResistPct, 0.12f), (Stat.MaxHealthFlat, 10.5f)).I("item:Stone"), SpurS1 = D("Пластина", (Stat.ArmorFlat, 2.1f)), SpurS2 = D("Стойкость", (Stat.DamageTakenPct, -0.014f)) }, new Boundary { C = new Cluster[6] { CL(D("Домашний уют", (Stat.ComfortFlat, 1f), (Stat.HealthRegenPct, 0.03f)).I("sprite:mapicon_bed"), A(D("Домовитость", (Stat.HealthRegenPct, 0.018f)), 3), A(D("Покой", (Stat.RestedDurationPct, 0.03f)), 3)), CL(D("Тёплый дом", (Stat.ComfortFlat, 1f), (Stat.FoodDurationPct, 0.072f)).I("sprite:mapicon_fire"), A(D("Аппетит", (Stat.FoodDurationPct, 0.03f)), 3), A(D("Тепло", (Stat.MaxHealthFlat, 2.8f)), 3)), CL(D("Ночной очаг", (Stat.NightRegenPct, 0.12f), (Stat.NightBuildPct, 0.08f)).I("item:Torch"), A(D("Ночная смена", (Stat.NightRegenPct, 0.04f)), 3), A(D("Работа при луне", (Stat.NightBuildPct, 0.03f)), 3)), CL(D("Запасливый хозяин", (Stat.FoodDurationPct, 0.09f), (Stat.FoodPowerPct, 0.08f)).I("item:CookedMeat"), A(D("Кладовая", (Stat.FoodDurationPct, 0.03f)), 3), A(D("Носильщик", (Stat.CarryWeightFlat, 3.6f)), 3)), CL(D("Погреб", (Stat.FoodDurationPct, 0.09f), (Stat.CarryWeightFlat, 9f)).M("food").I("item:CookedMeat"), A(D("Кладовая", (Stat.FoodDurationPct, 0.03f)), 3), A(D("Носильщик", (Stat.CarryWeightFlat, 3.6f)), 3)), CL(D("Трудолюбие", (Stat.StaminaRegenPct, 0.12f), (Stat.ToolStaminaPct, -0.035f)).I("item:Cultivator"), A(D("Дыхание", (Stat.StaminaRegenPct, 0.04f)), 3), A(D("Сноровка", (Stat.ToolStaminaPct, -0.014f)), 3)) }, Dead = new Cluster[2] { CL(D("Домашний очаг", (Stat.ComfortFlat, 1f)).M("frost").I("item:Torch"), A(D("Уютный угол", (Stat.RestedDurationPct, 0.025f)), 3)), CL(D("Запасы", (Stat.FoodDurationPct, 0.06f)).I("item:CookedMeat"), A(D("Кладовая", (Stat.FoodDurationPct, 0.021f)), 3)) }, SpurTip = D("Хозяин дома", (Stat.ComfortFlat, 3f), (Stat.RestedDurationPct, 0.2f), (Stat.RestedDamagePct, 0.2f), (Stat.HealthRegenPct, 0.12f), (Stat.DamagePct, -0.2f)).X("Ключевой узел: сила в доме, за порогом слабее.").I("sprite:mapicon_bed"), SpurS1 = D("Тепло", (Stat.MaxHealthFlat, 3.5f)), SpurS2 = D("Домовитость", (Stat.HealthRegenPct, 0.024f)), SpurIsKeystone = true }, new Boundary { C = new Cluster[6] { CL(D("Хозяйственный", (Stat.CarryWeightFlat, 9f), (Stat.ToolStaminaPct, -0.021f)).I("item:Hammer"), A(D("Носильщик", (Stat.CarryWeightFlat, 3.6f)), 3), A(D("Сноровка", (Stat.ToolStaminaPct, -0.01f)), 3)), CL(D("Артельщик", (Stat.CarryWeightFlat, 12f), (Stat.BuildRefundChance, 0.03f)).I("item:Wood"), A(D("Запасливость", (Stat.CarryWeightFlat, 4.8f)), 3), A(D("Смекалка", (Stat.BuildRefundChance, 0.013f)), 3)), CL(D("Инструмент как оружие", (Stat.Dmg_Pickaxes, 0.12f), (Stat.Dmg_Axes, 0.08f)).X("Кирка и топор в бою.").I("item:PickaxeAntler"), A(D("Удар киркой", (Stat.Dmg_Pickaxes, 0.05f)), 3), A(D("Рубака", (Stat.Dmg_Axes, 0.03f)), 3)), CL(D("Мозолистые руки", (Stat.ToolStaminaPct, -0.042f), (Stat.AttackStaminaPct, -0.042f)).M("knife").I("item:LeatherScraps"), A(D("Сноровка", (Stat.ToolStaminaPct, -0.014f)), 3), A(D("Экономный замах", (Stat.AttackStaminaPct, -0.021f)), 3)), CL(D("Заготовка леса", (Stat.ChopDamagePct, 0.105f), (Stat.CarryWeightFlat, 7.2f)).I("item:Wood"), A(D("Дровосек", (Stat.ChopDamagePct, 0.035f)), 3), A(D("Носильщик", (Stat.CarryWeightFlat, 3.6f)), 3)), CL(D("Каменоломня", (Stat.PickaxeDamagePct, 0.105f), (Stat.BuildRefundChance, 0.025f)).I("item:Stone"), A(D("Рудокоп", (Stat.PickaxeDamagePct, 0.035f)), 3), A(D("Смекалка", (Stat.BuildRefundChance, 0.01f)), 3)) }, Dead = new Cluster[2] { CL(D("Ремесло", (Stat.ToolStaminaPct, -0.035f)).I("item:Hammer"), A(D("Сноровка", (Stat.ToolStaminaPct, -0.012f)), 3)), CL(D("Поклажа", (Stat.CarryWeightFlat, 10.8f)).I("item:Wood"), A(D("Носильщик", (Stat.CarryWeightFlat, 3.6f)), 3)) }, SpurTip = D("Мастер артели", (Stat.BuildRefundChance, 0.05f), (Stat.PickupRangePct, 0.1f)).I("item:Hoe"), SpurS1 = D("Сноровка", (Stat.ToolStaminaPct, -0.014f)), SpurS2 = D("Смекалка", (Stat.BuildRefundChance, 0.013f)) }, new Boundary { C = new Cluster[6] { CL(D("Бродяга", (Stat.MoveSpeedPct, 0.01f), (Stat.PickupRangePct, 0.06f)).M("dodge").I("item:CapeDeerHide"), A(D("Проворство", (Stat.MoveSpeedPct, 0.01f)), 3), A(D("Цепкие руки", (Stat.PickupRangePct, 0.03f)), 3)), CL(D("Собиратель", (Stat.PickupRangePct, 0.1f), (Stat.ExploreRadiusPct, 0.06f)).I("item:Raspberry"), A(D("Хваткость", (Stat.PickupRangePct, 0.03f)), 3), A(D("Зоркость", (Stat.ExploreRadiusPct, 0.03f)), 3)), CL(D("Ночная тропа", (Stat.NightGatherPct, 0.12f), (Stat.NightMoveSpeedPct, 0.036f)).I("item:Thistle"), A(D("Работа в темноте", (Stat.NightGatherPct, 0.04f)), 3), A(D("Поступь в ночи", (Stat.NightMoveSpeedPct, 0.018f)), 3)), CL(D("Вожак стаи", (Stat.TamedDamagePct, 0.3f), (Stat.BreedChancePct, 0.3f)).X("Твоя стая крепче и плодовитее.").I("taming:"), A(D("Вожак", (Stat.TamedDamagePct, 0.08f)), 3), A(D("Загон", (Stat.BreedChancePct, 0.1f)), 3)), CL(D("Тропа сборщика", (Stat.PickupRangePct, 0.09f), (Stat.RunStaminaPct, -0.05f)).I("item:Raspberry"), A(D("Хваткость", (Stat.PickupRangePct, 0.03f)), 3), A(D("Лёгкий шаг", (Stat.RunStaminaPct, -0.02f)), 3)), CL(D("Разведка жил", (Stat.ExploreRadiusPct, 0.09f), (Stat.GatherCritChance, 0.056f)).I("item:Ruby"), A(D("Зоркость", (Stat.ExploreRadiusPct, 0.03f)), 3), A(D("Удачный удар", (Stat.GatherCritChance, 0.021f)), 3)) }, Dead = new Cluster[2] { CL(D("Собирательство", (Stat.PickupRangePct, 0.06f)).I("item:Raspberry"), A(D("Хваткость", (Stat.PickupRangePct, 0.02f)), 3)), CL(D("Лёгкий ход", (Stat.RunStaminaPct, -0.05f)).M("stagger").I("sprite:skill_run"), A(D("Лёгкий шаг", (Stat.RunStaminaPct, -0.018f)), 3)) }, SpurTip = D("Вольный ветер", (Stat.MoveSpeedPct, 0.012f), (Stat.RunStaminaPct, -0.04f)).I("sprite:skill_run"), SpurS1 = D("Проворство", (Stat.MoveSpeedPct, 0.01f)), SpurS2 = D("Лёгкий шаг", (Stat.RunStaminaPct, -0.02f)) }, new Boundary { C = new Cluster[6] { CL(D("Охотничья тропа", (Stat.StealthPct, 0.056f), (Stat.DamageVsAnimalsPct, 0.12f)).I("skill:Bows"), A(D("Тихий шаг", (Stat.StealthPct, 0.028f)), 3), A(D("Следы зверя", (Stat.DamageVsAnimalsPct, 0.04f)), 3)), CL(D("Убийца", (Stat.Dmg_Knives, 0.15f), (Stat.BackstabPct, 0.21f)).M("knife").I("skill:Knives"), A(D("Быстрый нож", (Stat.Dmg_Knives, 0.05f), (Stat.Spd_Knives, 0.04f)), 3), A(D("Удар в спину", (Stat.BackstabPct, 0.06f)), 3)), CL(D("Ночная охота", (Stat.NightDamagePct, 0.084f), (Stat.NightStealthPct, 0.12f)).M("stealth").I("item:CapeTrollHide"), A(D("Сумрак", (Stat.NightDamagePct, 0.028f)), 3), A(D("Глаза ночи", (Stat.NightStealthPct, 0.05f)), 3)), CL(D("Охотник на чудовищ", (Stat.DamageVsBossPct, 0.2f), (Stat.ExecutePct, 0.15f)).M("execute").I("item:TrophyDeer"), A(D("Гроза чудовищ", (Stat.DamageVsBossPct, 0.06f)), 3), A(D("Добивание", (Stat.ExecutePct, 0.05f)), 3)), CL(D("Засада", (Stat.BackstabPct, 0.15f), (Stat.CritDamagePct, 0.12f)).M("knife").I("skill:Knives"), A(D("Удар в спину", (Stat.BackstabPct, 0.048f)), 3), A(D("Жестокость", (Stat.CritDamagePct, 0.05f)), 3)), CL(D("Погоня за добычей", (Stat.OnslaughtChance, 0.12f), (Stat.MoveSpeedPct, 0.01f)).M("onslaught").X("Натиск после убийства.").I("sprite:skill_run"), A(D("Жажда погони", (Stat.OnslaughtChance, 0.03f)), 3), A(D("Проворство", (Stat.MoveSpeedPct, 0.01f)), 3)) }, Dead = new Cluster[2] { CL(D("Верный глазомер", (Stat.RangedDamagePct, 0.042f)).M("enchant").I("skill:Bows"), A(D("Меткость", (Stat.RangedDamagePct, 0.014f)), 3)), CL(D("Скрытность", (Stat.StealthPct, 0.07f)).I("sprite:skill_sneak"), A(D("Тихий шаг", (Stat.StealthPct, 0.025f)), 3)) }, SpurTip = D("Тень охотника", (Stat.StealthPct, 0.175f), (Stat.BackstabPct, 0.21f), (Stat.CritDamagePct, 0.25f), (Stat.NightDamagePct, 0.105f), (Stat.MaxHealthPct, -0.25f), (Stat.ArmorPct, -0.15f)).X("Ключевой узел: бей первым, из тени и ночью, но удара в ответ не держишь.").I("item:CapeTrollHide"), SpurS1 = D("Меткость", (Stat.RangedDamagePct, 0.021f)), SpurS2 = D("Удар в спину", (Stat.BackstabPct, 0.048f)), SpurIsKeystone = true }, new Boundary { C = new Cluster[6] { CL(D("Зачарованные стрелы", (Stat.ElementalDamagePct, 0.06f), (Stat.RangedDamagePct, 0.028f)).M("enchant").I("item:ArrowFire"), A(D("Руна на древке", (Stat.ElementalDamagePct, 0.025f)), 3), A(D("Меткость", (Stat.RangedDamagePct, 0.017f)), 3)), CL(D("Ледяные стрелы", (Stat.FrostChance, 0.06f), (Stat.FrostDurationPct, 0.15f)).M("frost").I("item:ArrowFrost"), A(D("Искра льда", (Stat.FrostChance, 0.02f)), 3), A(D("Долгий холод", (Stat.FrostDurationPct, 0.05f)), 3)), CL(D("Рикошет", (Stat.ProjectileChain, 1f), (Stat.FrostChance, 0.04f)).M("proj").X("Стрела отскакивает к ближайшему врагу."), A(D("Искра льда", (Stat.FrostChance, 0.015f)), 3), A(D("По льду", (Stat.DamageVsFrozenPct, 0.03f)), 3)), CL(D("Штормовой выстрел", (Stat.ShockChance, 0.05f), (Stat.WetShockPct, 0.12f)).I("item:Thunderstone"), A(D("Искра на болте", (Stat.ShockChance, 0.018f)), 3), A(D("Мокрый насквозь", (Stat.WetShockPct, 0.05f)), 3)), CL(D("Огненные болты", (Stat.IgniteChance, 0.05f), (Stat.Dmg_Crossbows, 0.1f)).I("item:ArrowFire"), A(D("Смоляной наконечник", (Stat.IgniteChance, 0.018f)), 3), A(D("Арбалетчик", (Stat.Dmg_Crossbows, 0.04f)), 3)), CL(D("Эфирная тетива", (Stat.EitrCostPct, -0.1f), (Stat.DrawSpeedPct, 0.084f)).I("item:Eitr"), A(D("Лёгкое заклинание", (Stat.EitrCostPct, -0.04f)), 3), A(D("Тетива", (Stat.DrawSpeedPct, 0.028f)), 3)) }, Dead = new Cluster[2] { CL(D("Зачарование", (Stat.ElementalDamagePct, 0.06f)).I("item:ArrowFire"), A(D("Руна на древке", (Stat.ElementalDamagePct, 0.02f)), 3)), CL(D("Быстрый выстрел", (Stat.DrawSpeedPct, 0.07f)).I("item:BowFineWood"), A(D("Тетива", (Stat.DrawSpeedPct, 0.025f)), 3)) }, SpurTip = D("Буря стрел", (Stat.ProjectileSpeedPct, 0.14f), (Stat.ShockChance, 0.05f)).I("item:ArrowNeedle"), SpurS1 = D("Меткость", (Stat.RangedDamagePct, 0.021f)), SpurS2 = D("Раскат", (Stat.ShockDamagePct, 0.04f)) }, new Boundary { C = new Cluster[6] { CL(D("Боевой маг", (Stat.MaxEitrFlat, 4.2f), (Stat.MeleeDamagePct, 0.028f)).I("item:StaffShield"), A(D("Эйтр", (Stat.MaxEitrFlat, 2.8f)), 3), A(D("Крепкая рука", (Stat.MeleeDamagePct, 0.017f)), 3)), CL(D("Пылающий клинок", (Stat.IgniteChance, 0.06f), (Stat.BurnDamagePct, 0.072f)).I("item:SwordFire"), A(D("Искра", (Stat.IgniteChance, 0.02f)), 3), A(D("Жар", (Stat.BurnDamagePct, 0.03f)), 3)), CL(D("Громовой клинок", (Stat.ShockChance, 0.06f), (Stat.ShockStunChance, 0.056f)).I("item:Thunderstone"), A(D("Искра молнии", (Stat.ShockChance, 0.02f)), 3), A(D("Оглушающий разряд", (Stat.ShockStunChance, 0.021f)), 3)), CL(D("Ядовитый клинок", (Stat.PoisonChance, 0.06f), (Stat.DamageVsPoisonedPct, 0.1f)).M("poison").I("item:Guck"), A(D("Яд на лезвии", (Stat.PoisonChance, 0.02f)), 3), A(D("Добивание отравленных", (Stat.DamageVsPoisonedPct, 0.035f)), 3)), CL(D("Рунная заточка", (Stat.ElementalDamagePct, 0.1f), (Stat.DamageVsMistlandsPct, 0.15f)).I("item:SwordSilver"), A(D("Руна на клинке", (Stat.ElementalDamagePct, 0.035f)), 3), A(D("Острота", (Stat.Dmg_Swords, 0.04f)), 3)), CL(D("Кровавый удар", (Stat.LifestealPct, 0.02f), (Stat.Dmg_Clubs, 0.08f)).I("item:Bloodbag"), A(D("Голод", (Stat.LifestealPct, 0.01f)), 3), A(D("Палица", (Stat.Dmg_Clubs, 0.032f)), 3)) }, Dead = new Cluster[2] { CL(D("Рунная кромка", (Stat.Dmg_Swords, 0.08f)).I("item:SwordSilver"), A(D("Острота", (Stat.Dmg_Swords, 0.03f)), 3)), CL(D("Эйтр в бою", (Stat.MaxEitrFlat, 4.2f)).I("item:StaffShield"), A(D("Эйтр", (Stat.MaxEitrFlat, 2.1f)), 3)) }, SpurTip = D("Клинок рун", (Stat.FrostChance, 0.09f), (Stat.FrostPotencyPct, 0.2f), (Stat.DamageVsFrozenPct, 0.2f), (Stat.MeleeDamagePct, 0.07f), (Stat.MaxHealthPct, -0.15f)).X("Ключевой узел: сталь, окованная льдом.").I("item:SwordSilver"), SpurS1 = D("Пламя и лёд", (Stat.ElementalDamagePct, 0.03f)), SpurS2 = D("Зачарованный клинок", (Stat.Dmg_Swords, 0.04f)), SpurIsKeystone = true } }; BuildRoads(array); for (int i = 0; i < 8; i++) { BuildSector(i, array[i]); } for (int j = 0; j < 8; j++) { BuildBoundary(j, array2[j], array[j], array[(j + 1) % 8]); } ConnectTips(); TreeB.TidyMasteries(); Sign(); } internal static void Sign() { foreach (Node value in Nodes.Values) { int num = 17; num = num * 31 + StringExtensionMethods.GetStableHashCode(value.Name ?? ""); num = (int)(num * 31 + value.Size); foreach (KeyValuePair item in value.Mods.OrderBy((KeyValuePair m) => m.Key.ToString())) { num = (int)(num * 31 + item.Key); } if (value.Options != null) { foreach (NodeOption option in value.Options) { num = num * 31 + StringExtensionMethods.GetStableHashCode(option.Name ?? ""); foreach (KeyValuePair item2 in option.Mods.OrderBy((KeyValuePair m) => m.Key.ToString())) { num = (int)(num * 31 + item2.Key); } } } value.Sig = num; } } } public static class Weather { [HarmonyPatch(typeof(SE_Stats), "Setup", new Type[] { typeof(Character) })] private static class SE_Stats_Setup { private static void Postfix(SE_Stats __instance, Character character) { if ((Object)(object)character == (Object)null || (Object)(object)character != (Object)(object)Player.m_localPlayer) { return; } int num = ((StatusEffect)__instance).NameHash(); float num2; if (num == SEMan.s_statusEffectCold || num == SEMan.s_statusEffectFreezing) { num2 = Get(Stat.ColdResistPct); } else { if (num != SEMan.s_statusEffectWet) { return; } num2 = Get(Stat.WetResistPct); } num2 = Mathf.Clamp01(num2); if (!(num2 <= 0f)) { if (__instance.m_healthPerTick < 0f) { __instance.m_healthPerTick *= 1f - num2; } if (__instance.m_healthOverTime < 0f) { __instance.m_healthOverTime *= 1f - num2; } if (__instance.m_healthRegenMultiplier < 1f) { __instance.m_healthRegenMultiplier = Mathf.Lerp(__instance.m_healthRegenMultiplier, 1f, num2); } if (__instance.m_staminaRegenMultiplier < 1f) { __instance.m_staminaRegenMultiplier = Mathf.Lerp(__instance.m_staminaRegenMultiplier, 1f, num2); } if (__instance.m_eitrRegenMultiplier < 1f) { __instance.m_eitrRegenMultiplier = Mathf.Lerp(__instance.m_eitrRegenMultiplier, 1f, num2); } if (__instance.m_staminaDrainPerSec > 0f) { __instance.m_staminaDrainPerSec *= 1f - num2; } } } } [HarmonyPatch(typeof(SE_Wet), "UpdateStatusEffect")] private static class SE_Wet_Update { private static void Postfix(SE_Wet __instance, float dt) { if (!((Object)(object)((StatusEffect)__instance).m_character == (Object)null) && !((Object)(object)((StatusEffect)__instance).m_character != (Object)(object)Player.m_localPlayer)) { float num = Get(Stat.DryingPct); if (num > 0f) { ((StatusEffect)__instance).m_time = ((StatusEffect)__instance).m_time + dt * Mathf.Min(num, StatText.CapOf(Stat.DryingPct)); } } } } public static float Get(Stat s) { return Progress.Current?.Stats.Get(s) ?? 0f; } } public static class XpSources { [HarmonyPatch(typeof(Skills), "RaiseSkill")] private static class Skills_RaiseSkill { private static bool Prefix(Skills __instance, SkillType skillType, float factor) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!IsLocal(__instance.m_player)) { return true; } if ((int)skillType == 0) { return false; } Skill skill = __instance.GetSkill(skillType); if (skill == null) { return false; } float amount = skill.m_info.m_increseStep * factor * Game.m_skillGainRate * Plugin.XpPerSkillGain.Value; Progress.Current.AddXp(amount); return !Plugin.DisableVanillaSkills.Value; } } [HarmonyPatch(typeof(Skills), "OnDeath")] private static class Skills_OnDeath { private static bool Prefix(Skills __instance) { if (!IsLocal(__instance.m_player)) { return true; } Progress.Current.OnDeath(); return !Plugin.DisableVanillaSkills.Value; } } [HarmonyPatch(typeof(Character), "Damage")] private static class Character_Damage_Tamed { private static void Prefix(Character __instance, HitData hit) { if ((Object)(object)Player.m_localPlayer == (Object)null || hit == null || !hit.HaveAttacker()) { return; } Character attacker = hit.GetAttacker(); if (!((Object)(object)attacker == (Object)null) && !attacker.IsPlayer() && attacker.IsTamed()) { float num = Mathf.Min(1f, Progress.Current.Stats.Get(Stat.TamedDamagePct)); if (num > 0f) { ((DamageTypes)(ref hit.m_damage)).Modify(1f + num); } } } } [HarmonyPatch(typeof(Character), "Damage")] private static class Character_Damage { private static void Prefix(Character __instance, HitData hit) { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Expected I4, but got Unknown //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0276: 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 == (Object)null || hit == null || (Object)(object)__instance == (Object)(object)localPlayer || (Object)(object)hit.GetAttacker() != (Object)(object)localPlayer || __instance.IsPlayer()) { return; } StatSet stats = Progress.Current.Stats; float num = 1f; if (__instance.IsBoss()) { num += stats.Get(Stat.DamageVsBossPct); } if (__instance.GetHealthPercentage() < 0.3f) { num += stats.Get(Stat.ExecutePct); } SEMan sEMan = __instance.GetSEMan(); if (sEMan != null) { if (sEMan.HaveStatusEffect(SEMan.s_statusEffectBurning)) { num += stats.Get(Stat.DamageVsBurningPct); } if (sEMan.HaveStatusEffect(SEMan.s_statusEffectPoison)) { num += stats.Get(Stat.DamageVsPoisonedPct); } if (sEMan.HaveStatusEffect(SEMan.s_statusEffectFrost)) { num += stats.Get(Stat.DamageVsFrozenPct); } if (sEMan.HaveStatusEffect(SEMan.s_statusEffectWet)) { num += stats.Get(Stat.DamageVsWetPct); } } if (__instance.IsStaggering()) { num += stats.Get(Stat.DamageVsStaggeredPct); } if (num != 1f) { ((DamageTypes)(ref hit.m_damage)).Modify(num); } Faction faction = __instance.m_faction; switch (faction - 1) { case 2: num += stats.Get(Stat.DamageVsUndeadPct); break; case 1: num += stats.Get(Stat.DamageVsForestPct); break; case 0: num += stats.Get(Stat.DamageVsAnimalsPct); break; case 9: num += stats.Get(Stat.DamageVsDvergrPct); break; case 6: num += stats.Get(Stat.DamageVsPlainsPct); break; case 4: num += stats.Get(Stat.DamageVsMountainPct); break; case 5: num += stats.Get(Stat.DamageVsSeaPct); break; case 8: num += stats.Get(Stat.DamageVsMistlandsPct); break; } float num2 = Mathf.Min(1f, stats.Get(Stat.BackstabPct)); if (num2 > 0f && (Object)(object)__instance.m_baseAI != (Object)null && !__instance.m_baseAI.IsAlerted()) { hit.m_backstabBonus = Mathf.Max(hit.m_backstabBonus, 1f) * (1f + num2); } float totalDamage = hit.GetTotalDamage(); float num3 = Mathf.Min(0.1f, stats.Get(Stat.LifestealPct)); if (num3 > 0f && totalDamage > 0f) { ((Character)localPlayer).Heal(totalDamage * num3, true); } recentHits[__instance.GetZDOID()] = Time.time; if ((Object)(object)__instance.m_nview != (Object)null && !__instance.m_nview.IsOwner()) { DamageTypes val = ((DamageTypes)(ref hit.m_damage)).Clone(); ((DamageTypes)(ref val)).ApplyArmor(__instance.GetBodyArmor()); if (__instance.GetHealth() - ((DamageTypes)(ref val)).GetTotalDamage() <= 0f) { Award(__instance); } } } } [HarmonyPatch(typeof(Character), "OnDeath")] private static class Character_OnDeath { private static void Prefix(Character __instance) { //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null) && !__instance.IsPlayer() && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsOwner()) { HitData lastHit = __instance.m_lastHit; bool flag = (Object)(object)((lastHit != null) ? lastHit.GetAttacker() : null) == (Object)(object)Player.m_localPlayer; if (!flag && recentHits.TryGetValue(__instance.GetZDOID(), out var value) && Time.time - value < 10f) { flag = true; } if (flag) { Award(__instance); } } } } [HarmonyPatch(typeof(Minimap), "Explore", new Type[] { typeof(Vector3), typeof(float) })] private static class Minimap_ExploreRadius { private static void Prefix() { exploringLocally = true; } private static void Postfix() { exploringLocally = false; } } [HarmonyPatch(typeof(Minimap), "Explore", new Type[] { typeof(int), typeof(int) })] private static class Minimap_ExplorePixel { private static void Postfix(bool __result) { if (__result && exploringLocally && (Object)(object)Player.m_localPlayer != (Object)null) { Progress.Current.AddXp(Plugin.ExploreXpPerPixel.Value); } } } [HarmonyPatch(typeof(Player), "PlacePiece")] private static class Player_PlacePiece { private static void Postfix(Player __instance) { if (IsLocal(__instance)) { Progress.Current.AddXp(Plugin.BuildXp.Value); } } } [HarmonyPatch(typeof(Player), "Save")] private static class Player_Save { private static void Prefix(Player __instance) { Progress.Peek(__instance)?.WriteTo(); } } [HarmonyPatch(typeof(Player), "Load")] private static class Player_Load { private static void Postfix(Player __instance) { Progress.Forget(__instance); Progress.Get(__instance); } } private static readonly Dictionary recentHits = new Dictionary(); private static readonly HashSet awarded = new HashSet(); private static readonly Queue awardedOrder = new Queue(); private static bool exploringLocally; private static bool IsLocal(Player p) { if ((Object)(object)p != (Object)null) { return (Object)(object)p == (Object)(object)Player.m_localPlayer; } return false; } public static float KillXp(Character c) { float num = c.GetMaxHealth() * Plugin.KillXpPerHealth.Value * (1f + 0.25f * (float)(c.GetLevel() - 1)); if (c.IsBoss()) { num *= Plugin.BossXpMultiplier.Value; } return num; } private static void Award(Character c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0038: Unknown result type (might be due to invalid IL or missing references) ZDOID zDOID = c.GetZDOID(); if (awarded.Contains(zDOID)) { return; } awarded.Add(zDOID); awardedOrder.Enqueue(zDOID); while (awardedOrder.Count > 300) { awarded.Remove(awardedOrder.Dequeue()); } Progress.Current?.AddXp(KillXp(c), c.m_name, announce: true); Player localPlayer = Player.m_localPlayer; StatSet statSet = Progress.Current?.Stats; if (!((Object)(object)localPlayer == (Object)null) && statSet != null) { if (statSet.Get(Stat.HealthOnKill) > 0f) { ((Character)localPlayer).Heal(statSet.Get(Stat.HealthOnKill), true); } if (statSet.Get(Stat.StaminaOnKill) > 0f) { ((Character)localPlayer).AddStamina(statSet.Get(Stat.StaminaOnKill)); } if (statSet.Get(Stat.EitrOnKill) > 0f) { ((Character)localPlayer).AddEitr(statSet.Get(Stat.EitrOnKill)); } if (Random.value < Mathf.Min(StatText.CapOf(Stat.OnslaughtChance), statSet.Get(Stat.OnslaughtChance))) { float num = 12f * (1f + statSet.Get(Stat.BuffDurationPct) + statSet.Get(Stat.StatusDurationPct)); Effects.OnslaughtUntil = Time.time + num; Effects.Refresh(localPlayer); } } } } } namespace PassiveTree.UI { public enum HudCorner { LeftOfMinimap, TopRight, TopLeft, BottomRight, BottomLeft } public class HudWidget : MonoBehaviour { public static HudWidget Instance; private static readonly Color PanelBg = new Color(0.07f, 0.065f, 0.06f, 0.82f); private static readonly Color BarBg = new Color(0.14f, 0.13f, 0.11f, 0.95f); private static readonly Color BarFill = new Color(0.86f, 0.66f, 0.28f); private GameObject root; private RectTransform panel; private TextMeshProUGUI levelText; private TextMeshProUGUI xpText; private TextMeshProUGUI pointsText; private Image fill; private float nextRefresh; private int shownLevel = -1; private static float MinimapWidth() { //IL_003c: 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) //IL_004b: Unknown result type (might be due to invalid IL or missing references) Minimap instance = Minimap.instance; GameObject val = (((Object)(object)instance != (Object)null) ? instance.m_smallRoot : null); RectTransform val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 == (Object)null) { return 280f; } Rect rect = val2.rect; float num = ((Rect)(ref rect)).width * Mathf.Abs(((Transform)val2).lossyScale.x); if (!(num > 40f)) { return 280f; } return num; } public static void Ensure() { //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) //IL_001e: Expected O, but got Unknown if (!((Object)(object)Instance != (Object)null)) { GameObject val = new GameObject("PassiveTreeHud"); Object.DontDestroyOnLoad((Object)val); Instance = val.AddComponent(); } } public static void Rebuild() { if (!((Object)(object)Instance == (Object)null)) { if ((Object)(object)Instance.root != (Object)null) { Object.Destroy((Object)(object)Instance.root); } Instance.root = null; } } private void Build() { //IL_00bf: 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) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0380: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03c7: Unknown result type (might be due to invalid IL or missing references) //IL_03d6: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Unknown result type (might be due to invalid IL or missing references) //IL_0414: Unknown result type (might be due to invalid IL or missing references) //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_044d: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_04b0: Unknown result type (might be due to invalid IL or missing references) //IL_04da: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Unknown result type (might be due to invalid IL or missing references) //IL_04f8: Unknown result type (might be due to invalid IL or missing references) //IL_0507: Unknown result type (might be due to invalid IL or missing references) //IL_053b: Unknown result type (might be due to invalid IL or missing references) //IL_0569: Unknown result type (might be due to invalid IL or missing references) //IL_0578: Unknown result type (might be due to invalid IL or missing references) //IL_0587: Unknown result type (might be due to invalid IL or missing references) //IL_05bd: Unknown result type (might be due to invalid IL or missing references) //IL_05fb: Unknown result type (might be due to invalid IL or missing references) //IL_0610: Unknown result type (might be due to invalid IL or missing references) //IL_0625: Unknown result type (might be due to invalid IL or missing references) //IL_063a: Unknown result type (might be due to invalid IL or missing references) //IL_064e: Unknown result type (might be due to invalid IL or missing references) //IL_0681: Unknown result type (might be due to invalid IL or missing references) //IL_06aa: Unknown result type (might be due to invalid IL or missing references) //IL_06bf: Unknown result type (might be due to invalid IL or missing references) //IL_06ca: Unknown result type (might be due to invalid IL or missing references) //IL_06d4: Unknown result type (might be due to invalid IL or missing references) //IL_0703: Unknown result type (might be due to invalid IL or missing references) //IL_072d: Unknown result type (might be due to invalid IL or missing references) //IL_073c: Unknown result type (might be due to invalid IL or missing references) //IL_074b: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) UiKit.FindFonts(); Canvas obj = ((Component)this).gameObject.GetComponent() ?? ((Component)this).gameObject.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 50; if ((Object)(object)((Component)this).gameObject.GetComponent() == (Object)null) { CanvasScaler obj2 = ((Component)this).gameObject.AddComponent(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); obj2.matchWidthOrHeight = 0.5f; } root = UiKit.Go("HudRoot", ((Component)this).transform); UiKit.Stretch(root); GameObject go = UiKit.Go("Panel", root.transform); panel = UiKit.Rect(go, Vector2.zero, Vector2.zero, new Vector2(268f, 78f)); HudCorner value = Plugin.HudCorner.Value; bool flag = value == HudCorner.TopRight || value == HudCorner.TopLeft || value == HudCorner.LeftOfMinimap; bool flag2 = value == HudCorner.TopRight || value == HudCorner.BottomRight || value == HudCorner.LeftOfMinimap; RectTransform obj3 = panel; RectTransform obj4 = panel; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(flag2 ? 1f : 0f, flag ? 1f : 0f); obj4.anchorMax = val; obj3.anchorMin = val; panel.pivot = new Vector2(0.5f, 0.5f); if (value == HudCorner.LeftOfMinimap) { panel.anchoredPosition = new Vector2(0f - (MinimapWidth() + 18f + 134f), -57f) + Plugin.HudOffset.Value; } else { panel.anchoredPosition = new Vector2(flag2 ? (-152f) : 152f, flag ? (0f - ((value == HudCorner.TopRight) ? 300f : 57f)) : 57f) + Plugin.HudOffset.Value; } ((Transform)panel).localScale = Vector3.one * Mathf.Clamp(Plugin.HudScale.Value, 0.5f, 2f); Image obj5 = UiKit.Image((Transform)(object)panel, "Shadow", UiKit.SoftGlow, new Color(0f, 0f, 0f, 0.55f), (Type)0); UiKit.Stretch(((Component)obj5).gameObject, -10f, -10f, -10f, -10f); ((Graphic)obj5).raycastTarget = false; Image obj6 = UiKit.Image((Transform)(object)panel, "Plate", UiKit.White, new Color(0.055f, 0.05f, 0.045f, 0.93f), (Type)0); obj6.preserveAspect = false; ((Graphic)obj6).raycastTarget = false; UiKit.Stretch(((Component)obj6).gameObject); Edge("Top", new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, -1.5f), new Vector2(0f, 0f)); Edge("Bottom", new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0f, 0f), new Vector2(0f, 1.5f)); Edge("Left", new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(0f, 0f), new Vector2(1.5f, 0f)); Edge("Right", new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(-1.5f, 0f), new Vector2(0f, 0f)); levelText = UiKit.Text((Transform)(object)panel, "Level", "", 18f, new Color(0.86f, 0.72f, 0.45f), (TextAlignmentOptions)513, title: true); UiKit.Rect(((Component)levelText).gameObject, new Vector2(0f, 1f), new Vector2(16f, -9f), new Vector2(170f, 28f), (Vector2?)new Vector2(0f, 1f)); ((TMP_Text)levelText).characterSpacing = 3f; pointsText = UiKit.Text((Transform)(object)panel, "Points", "", 16f, new Color(0.98f, 0.84f, 0.45f), (TextAlignmentOptions)516, title: true); UiKit.Rect(((Component)pointsText).gameObject, new Vector2(1f, 1f), new Vector2(-16f, -10f), new Vector2(130f, 24f), (Vector2?)new Vector2(1f, 1f)); Image val2 = UiKit.Image((Transform)(object)panel, "Groove", UiKit.White, new Color(0.02f, 0.02f, 0.02f, 0.95f), (Type)0); val2.preserveAspect = false; ((Graphic)val2).raycastTarget = false; UiKit.Rect(((Component)val2).gameObject, new Vector2(0.5f, 1f), new Vector2(0f, -44f), new Vector2(236f, 10f)); fill = UiKit.Image(((Component)val2).transform, "Fill", UiKit.White, new Color(0.91f, 0.72f, 0.32f), (Type)0); fill.preserveAspect = false; ((Graphic)fill).raycastTarget = false; RectTransform rectTransform = ((Graphic)fill).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(0f, 1f); rectTransform.pivot = new Vector2(0f, 0.5f); rectTransform.offsetMin = new Vector2(1.5f, 1.5f); rectTransform.offsetMax = new Vector2(0f, -1.5f); Image obj7 = UiKit.Image(((Component)fill).transform, "Sheen", UiKit.White, new Color(1f, 0.93f, 0.72f, 0.35f), (Type)0); obj7.preserveAspect = false; ((Graphic)obj7).raycastTarget = false; RectTransform rectTransform2 = ((Graphic)obj7).rectTransform; rectTransform2.anchorMin = new Vector2(0f, 0.55f); rectTransform2.anchorMax = new Vector2(1f, 1f); rectTransform2.offsetMin = Vector2.zero; rectTransform2.offsetMax = Vector2.zero; xpText = UiKit.Text((Transform)(object)panel, "XpText", "", 13f, new Color(0.74f, 0.7f, 0.62f), (TextAlignmentOptions)514); UiKit.Rect(((Component)xpText).gameObject, new Vector2(0.5f, 0f), new Vector2(0f, 8f), new Vector2(244f, 18f)); void Edge(string name, Vector2 aMin, Vector2 aMax, Vector2 oMin, Vector2 oMax) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) Image obj8 = UiKit.Image((Transform)(object)panel, name, UiKit.White, new Color(0.72f, 0.58f, 0.32f, 0.85f), (Type)0); obj8.preserveAspect = false; ((Graphic)obj8).raycastTarget = false; RectTransform rectTransform3 = ((Graphic)obj8).rectTransform; rectTransform3.anchorMin = aMin; rectTransform3.anchorMax = aMax; rectTransform3.offsetMin = oMin; rectTransform3.offsetMax = oMax; } } private void Update() { if (!Plugin.HudEnabled.Value || !((Object)(object)Player.m_localPlayer != (Object)null) || !((Object)(object)Hud.instance != (Object)null) || Hud.IsUserHidden() || Minimap.IsOpen()) { if ((Object)(object)root != (Object)null) { root.SetActive(false); } return; } if ((Object)(object)root == (Object)null) { Build(); } root.SetActive(true); if (!(Time.unscaledTime < nextRefresh)) { nextRefresh = Time.unscaledTime + 0.2f; Refresh(); } } private void Refresh() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) Progress current = Progress.Current; if (current != null) { float num = ((current.XpToNext > 0f) ? Mathf.Clamp01(current.Xp / current.XpToNext) : 1f); ((Graphic)fill).rectTransform.anchorMax = new Vector2(num, 1f); ((TMP_Text)levelText).text = string.Format("{0} {1}", L.T("Уровень"), current.Level); ((TMP_Text)xpText).text = ((current.Level >= Plugin.MaxLevel.Value) ? L.T("Максимальный уровень") : $"{current.Xp:0} / {current.XpToNext:0} · {num * 100f:0}%"); int availablePoints = current.AvailablePoints; ((TMP_Text)pointsText).text = ((availablePoints > 0) ? string.Format(L.T("{0} очков"), availablePoints) : ""); if (availablePoints > 0) { float num2 = 0.75f + 0.25f * Mathf.Sin(Time.unscaledTime * 3f); ((Graphic)pointsText).color = new Color(0.98f, 0.84f, 0.45f, num2); } ((Graphic)fill).color = (Color)((availablePoints > 0) ? Color.Lerp(new Color(0.91f, 0.72f, 0.32f), new Color(1f, 0.87f, 0.55f), 0.5f + 0.5f * Mathf.Sin(Time.unscaledTime * 2.2f)) : new Color(0.75f, 0.6f, 0.28f)); shownLevel = current.Level; } } } public static class Sfx { private static bool warned; private static Vector3 Ear { get { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform : (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : null)); if ((Object)(object)val != (Object)null) { return val.position; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer != (Object)null)) { return Vector3.zero; } return ((Component)localPlayer).transform.position; } } private static InventoryGui G => InventoryGui.instance; private static bool Has(EffectList fx) { if (fx != null && fx.m_effectPrefabs != null) { return fx.m_effectPrefabs.Length != 0; } return false; } private static void Play(params EffectList[] candidates) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.TreeSounds.Value || (Object)(object)Player.m_localPlayer == (Object)null) { return; } foreach (EffectList val in candidates) { if (Has(val)) { val.Create(Ear, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); return; } } if (!warned) { warned = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Звуки дерева: игра не дала ни одного набора эффектов интерфейса, звука не будет"); } } } public static void Pick() { Play(G?.m_moveItemEffects, G?.m_setActiveGroupEffects, G?.m_openInventoryEffects); } public static void Unpick() { Play(G?.m_setActiveGroupEffects, G?.m_closeInventoryEffects, G?.m_moveItemEffects); } public static void Apply() { Play(G?.m_craftItemEffects, G?.m_moveItemEffects); } public static void LevelUp() { Play(G?.m_craftItemDoneEffects, G?.m_craftItemEffects, G?.m_openInventoryEffects); } } public class TreeWindow : MonoBehaviour { public static TreeWindow Instance; private static readonly Color BgDeep = new Color(0.035f, 0.035f, 0.045f, 0.97f); private static readonly Color PanelBg = new Color(0.07f, 0.065f, 0.06f, 0.96f); private static readonly Color GoldLine = new Color(0.78f, 0.62f, 0.32f, 0.75f); private static readonly Color GoldDim = new Color(0.78f, 0.62f, 0.32f, 0.35f); private GameObject root; private RectTransform rootRect; private RectTransform content; private RectTransform tooltip; private RectTransform viewportRect; private RectTransform masteryPanel; private RectTransform sidePanel; private RectTransform sideTab; private TextMeshProUGUI sideTabArrow; private static bool sideOpen; private Node masteryNode; private Canvas canvas; private TextMeshProUGUI levelText; private TextMeshProUGUI pointsText; private TextMeshProUGUI xpText; private TextMeshProUGUI statsText; private TextMeshProUGUI tipTitle; private TextMeshProUGUI tipKind; private TextMeshProUGUI tipBody; private TextMeshProUGUI tipFoot; private Image xpFill; private readonly Dictionary views = new Dictionary(); private readonly List links = new List(); private float zoom = 0.2f; private bool firstOpen = true; private Progress bound; private bool built; private TextMeshProUGUI searchText; private TextMeshProUGUI searchCount; private TextMeshProUGUI gateCount; private Image searchBox; private string query = ""; private bool searchFocused; private readonly HashSet hits = new HashSet(); private readonly HashSet pendingAdd = new HashSet(); private readonly HashSet refundSel = new HashSet(); private bool respec; private TextMeshProUGUI respecText; private TextMeshProUGUI confirmText; private GameObject respecPanel; private GameObject confirmBar; private Button respecBtn; private readonly List masteryQueue = new List(); private const string CGood = "#8CBF63"; private const string CBad = "#C4614F"; private const string CDim = "#8E8470"; private const string CGold = "#C9A85C"; private const string CBody = "#D8CFBB"; private float reachCache; private const float MinZoom = 0.05f; private const float MaxZoom = 2.2f; private const float ZoomStep = 1.25f; private static bool loggedScroll; public static bool IsOpen { get { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance.root != (Object)null) { return Instance.root.activeSelf; } return false; } } public static bool TypingInSearch { get { if ((Object)(object)Instance != (Object)null && IsOpen) { return Instance.searchFocused; } return false; } } private int PendingPointsLeft { get { if (bound != null) { return bound.AvailablePoints - pendingAdd.Count + refundSel.Count; } return 0; } } private int PendingChanges => pendingAdd.Count + refundSel.Count; public bool Searching => query.Length >= 2; public bool RespecMode => respec; private float Reach { get { if (reachCache <= 0f) { foreach (Node value in Tree.Nodes.Values) { reachCache = Mathf.Max(reachCache, ((Vector2)(ref value.Pos)).magnitude); } } return Mathf.Max(1f, reachCache); } } public bool IsPending(string id) { return pendingAdd.Contains(id); } private bool NeighbourTaken(Node n) { foreach (string link in n.Links) { if ((bound.IsAllocated(link) && !refundSel.Contains(link)) || pendingAdd.Contains(link)) { return true; } } return false; } private int GatesAfterPending() { int num = 0; foreach (string gate in Tree.Gates) { if ((bound.IsAllocated(gate) && !refundSel.Contains(gate)) || pendingAdd.Contains(gate)) { num++; } } return num; } public bool CanQueue(Node n) { if (bound == null || bound.IsAllocated(n.Id) || pendingAdd.Contains(n.Id)) { return false; } if (PendingPointsLeft <= 0) { return false; } if (Tree.IsGate(n.Id) && GatesAfterPending() >= bound.GateLimit) { return false; } return NeighbourTaken(n); } public void Queue(Node n) { if (pendingAdd.Remove(n.Id)) { DropOrphanPending(); Sfx.Unpick(); Refresh(); return; } if (CanQueue(n)) { pendingAdd.Add(n.Id); Sfx.Pick(); } Refresh(); } private void DropOrphanPending() { bool flag = true; while (flag) { flag = false; foreach (string item in new List(pendingAdd)) { if (!NeighbourTaken(Tree.Nodes[item])) { pendingAdd.Remove(item); flag = true; } } } } private void ApplyPending() { if (bound == null || (refundSel.Count > 0 && !bound.DeallocateSet(new List(refundSel)))) { return; } refundSel.Clear(); int num = pendingAdd.Count + 2; List list = new List(); while (pendingAdd.Count > 0 && num-- > 0) { string text = null; foreach (string item in pendingAdd) { if (bound.CanAllocate(item)) { text = item; break; } } if (text == null) { break; } bound.Allocate(text); list.Add(text); pendingAdd.Remove(text); } pendingAdd.Clear(); Sfx.Apply(); if (respec) { ToggleRespec(); } else { Refresh(); } masteryQueue.Clear(); foreach (string item2 in list) { if (Tree.Nodes.TryGetValue(item2, out var value) && value.IsMastery) { masteryQueue.Add(item2); } } NextMastery(); } public void CancelPending() { pendingAdd.Clear(); refundSel.Clear(); Refresh(); } public static void Rebuild() { if (!((Object)(object)Instance == (Object)null)) { bool isOpen = IsOpen; Instance.Close(); if ((Object)(object)Instance.root != (Object)null) { Object.Destroy((Object)(object)Instance.root); } Instance.root = null; Instance.built = false; Instance.firstOpen = true; Instance.reachCache = 0f; Instance.views.Clear(); Instance.links.Clear(); if (isOpen) { Instance.Open(); } } } public static void Ensure() { //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) //IL_001e: Expected O, but got Unknown if (!((Object)(object)Instance != (Object)null)) { GameObject val = new GameObject("PassiveTreeUI"); Object.DontDestroyOnLoad((Object)val); Instance = val.AddComponent(); } } private void Awake() { Tree.Build(); } public void Toggle() { if (IsOpen) { Close(); } else { Open(); } } public void Open() { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { L.EnsureLoaded(); if (!built) { UiKit.FindFonts(); BuildCanvas(); built = true; } Bind(Progress.Current); root.SetActive(true); if (firstOpen) { FitToScreen(); firstOpen = false; } searchFocused = false; UpdateSearchVisual(); Refresh(); } } public void Close() { CancelPending(); if ((Object)(object)root != (Object)null) { root.SetActive(false); } HideTooltip(); CloseMastery(); } private void Bind(Progress p) { if (bound != p) { if (bound != null) { bound.Changed -= Refresh; } bound = p; if (bound != null) { bound.Changed += Refresh; } } } private void BuildCanvas() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_032a: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_0405: Unknown result type (might be due to invalid IL or missing references) //IL_0414: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_0449: Unknown result type (might be due to invalid IL or missing references) //IL_0473: Unknown result type (might be due to invalid IL or missing references) //IL_0482: Unknown result type (might be due to invalid IL or missing references) //IL_0491: Unknown result type (might be due to invalid IL or missing references) //IL_04a0: Unknown result type (might be due to invalid IL or missing references) //IL_04cf: Unknown result type (might be due to invalid IL or missing references) //IL_04d4: Unknown result type (might be due to invalid IL or missing references) //IL_04f6: Unknown result type (might be due to invalid IL or missing references) //IL_0505: Unknown result type (might be due to invalid IL or missing references) //IL_0514: Unknown result type (might be due to invalid IL or missing references) //IL_0523: Unknown result type (might be due to invalid IL or missing references) //IL_0559: Unknown result type (might be due to invalid IL or missing references) //IL_058b: Unknown result type (might be due to invalid IL or missing references) //IL_05a0: Unknown result type (might be due to invalid IL or missing references) //IL_05b5: Unknown result type (might be due to invalid IL or missing references) //IL_05ca: Unknown result type (might be due to invalid IL or missing references) //IL_05de: Unknown result type (might be due to invalid IL or missing references) //IL_061e: Unknown result type (might be due to invalid IL or missing references) //IL_0686: Unknown result type (might be due to invalid IL or missing references) //IL_06a5: Unknown result type (might be due to invalid IL or missing references) //IL_06b4: Unknown result type (might be due to invalid IL or missing references) //IL_06c3: Unknown result type (might be due to invalid IL or missing references) //IL_06cf: Unknown result type (might be due to invalid IL or missing references) //IL_06de: Expected O, but got Unknown //IL_06fe: Unknown result type (might be due to invalid IL or missing references) //IL_071d: Unknown result type (might be due to invalid IL or missing references) //IL_072c: Unknown result type (might be due to invalid IL or missing references) //IL_073b: Unknown result type (might be due to invalid IL or missing references) //IL_074a: Unknown result type (might be due to invalid IL or missing references) //IL_076b: Unknown result type (might be due to invalid IL or missing references) //IL_0770: Unknown result type (might be due to invalid IL or missing references) //IL_079d: Unknown result type (might be due to invalid IL or missing references) //IL_07b3: Unknown result type (might be due to invalid IL or missing references) //IL_07c9: Unknown result type (might be due to invalid IL or missing references) //IL_07df: Unknown result type (might be due to invalid IL or missing references) //IL_07f5: Unknown result type (might be due to invalid IL or missing references) //IL_0832: Unknown result type (might be due to invalid IL or missing references) //IL_085c: Unknown result type (might be due to invalid IL or missing references) //IL_086b: Unknown result type (might be due to invalid IL or missing references) //IL_087a: Unknown result type (might be due to invalid IL or missing references) //IL_089f: Unknown result type (might be due to invalid IL or missing references) //IL_08de: Unknown result type (might be due to invalid IL or missing references) //IL_0909: Unknown result type (might be due to invalid IL or missing references) //IL_091e: Unknown result type (might be due to invalid IL or missing references) //IL_0947: Unknown result type (might be due to invalid IL or missing references) //IL_095b: Unknown result type (might be due to invalid IL or missing references) //IL_0b1f: Unknown result type (might be due to invalid IL or missing references) //IL_0b24: Unknown result type (might be due to invalid IL or missing references) //IL_0b4d: Unknown result type (might be due to invalid IL or missing references) //IL_0b5c: Unknown result type (might be due to invalid IL or missing references) //IL_0b6b: Unknown result type (might be due to invalid IL or missing references) //IL_0b7a: Unknown result type (might be due to invalid IL or missing references) //IL_0bc7: Unknown result type (might be due to invalid IL or missing references) //IL_0c1c: Unknown result type (might be due to invalid IL or missing references) //IL_0c46: Unknown result type (might be due to invalid IL or missing references) //IL_0c55: Unknown result type (might be due to invalid IL or missing references) //IL_0c64: Unknown result type (might be due to invalid IL or missing references) //IL_0c73: Unknown result type (might be due to invalid IL or missing references) //IL_0990: Unknown result type (might be due to invalid IL or missing references) //IL_09ae: Unknown result type (might be due to invalid IL or missing references) //IL_09dd: Unknown result type (might be due to invalid IL or missing references) //IL_0a07: Unknown result type (might be due to invalid IL or missing references) //IL_0a16: Unknown result type (might be due to invalid IL or missing references) //IL_0a25: Unknown result type (might be due to invalid IL or missing references) //IL_0a58: Unknown result type (might be due to invalid IL or missing references) //IL_0a64: Expected O, but got Unknown //IL_0a76: Unknown result type (might be due to invalid IL or missing references) //IL_0a82: Expected O, but got Unknown //IL_0a94: Unknown result type (might be due to invalid IL or missing references) //IL_0aa0: Expected O, but got Unknown //IL_0ab2: Unknown result type (might be due to invalid IL or missing references) //IL_0abe: Expected O, but got Unknown //IL_0ad0: Unknown result type (might be due to invalid IL or missing references) //IL_0adc: Expected O, but got Unknown //IL_0aee: Unknown result type (might be due to invalid IL or missing references) //IL_0afa: Expected O, but got Unknown //IL_0cbd: Unknown result type (might be due to invalid IL or missing references) //IL_0ccf: Unknown result type (might be due to invalid IL or missing references) //IL_0cde: Unknown result type (might be due to invalid IL or missing references) //IL_0cea: Unknown result type (might be due to invalid IL or missing references) //IL_0cf9: Expected O, but got Unknown //IL_0d22: Unknown result type (might be due to invalid IL or missing references) //IL_0d27: Unknown result type (might be due to invalid IL or missing references) //IL_0d55: Unknown result type (might be due to invalid IL or missing references) //IL_0d67: Unknown result type (might be due to invalid IL or missing references) //IL_0d76: Unknown result type (might be due to invalid IL or missing references) //IL_0da6: Unknown result type (might be due to invalid IL or missing references) //IL_0dd0: Unknown result type (might be due to invalid IL or missing references) //IL_0ddf: Unknown result type (might be due to invalid IL or missing references) //IL_0dee: Unknown result type (might be due to invalid IL or missing references) //IL_0e22: Unknown result type (might be due to invalid IL or missing references) //IL_0e31: Unknown result type (might be due to invalid IL or missing references) //IL_0e40: Unknown result type (might be due to invalid IL or missing references) //IL_0e4c: Unknown result type (might be due to invalid IL or missing references) //IL_0e5b: Expected O, but got Unknown //IL_0e7b: Unknown result type (might be due to invalid IL or missing references) //IL_0e8a: Unknown result type (might be due to invalid IL or missing references) //IL_0e99: Unknown result type (might be due to invalid IL or missing references) //IL_0ea5: Unknown result type (might be due to invalid IL or missing references) //IL_0eb4: Expected O, but got Unknown //IL_0eea: Unknown result type (might be due to invalid IL or missing references) //IL_0f31: Unknown result type (might be due to invalid IL or missing references) //IL_0f38: Unknown result type (might be due to invalid IL or missing references) //IL_0f4f: Unknown result type (might be due to invalid IL or missing references) //IL_0f69: Unknown result type (might be due to invalid IL or missing references) //IL_0f83: Unknown result type (might be due to invalid IL or missing references) //IL_0fac: Unknown result type (might be due to invalid IL or missing references) //IL_0fc1: Unknown result type (might be due to invalid IL or missing references) //IL_0fcc: Unknown result type (might be due to invalid IL or missing references) //IL_0fe0: Unknown result type (might be due to invalid IL or missing references) //IL_100b: Unknown result type (might be due to invalid IL or missing references) //IL_1015: Expected O, but got Unknown //IL_1016: Unknown result type (might be due to invalid IL or missing references) //IL_101b: Unknown result type (might be due to invalid IL or missing references) //IL_102e: Unknown result type (might be due to invalid IL or missing references) //IL_1038: Unknown result type (might be due to invalid IL or missing references) //IL_1055: Unknown result type (might be due to invalid IL or missing references) //IL_107f: Unknown result type (might be due to invalid IL or missing references) //IL_108e: Unknown result type (might be due to invalid IL or missing references) //IL_109d: Unknown result type (might be due to invalid IL or missing references) //IL_10e7: Unknown result type (might be due to invalid IL or missing references) //IL_1107: Unknown result type (might be due to invalid IL or missing references) //IL_1116: Unknown result type (might be due to invalid IL or missing references) //IL_1125: Unknown result type (might be due to invalid IL or missing references) //IL_1160: Unknown result type (might be due to invalid IL or missing references) //IL_1194: Unknown result type (might be due to invalid IL or missing references) //IL_1199: Unknown result type (might be due to invalid IL or missing references) //IL_11c7: Unknown result type (might be due to invalid IL or missing references) //IL_11d6: Unknown result type (might be due to invalid IL or missing references) //IL_11e5: Unknown result type (might be due to invalid IL or missing references) //IL_1215: Unknown result type (might be due to invalid IL or missing references) //IL_123f: Unknown result type (might be due to invalid IL or missing references) //IL_124e: Unknown result type (might be due to invalid IL or missing references) //IL_125d: Unknown result type (might be due to invalid IL or missing references) //IL_1291: Unknown result type (might be due to invalid IL or missing references) //IL_12a0: Unknown result type (might be due to invalid IL or missing references) //IL_12af: Unknown result type (might be due to invalid IL or missing references) //IL_12bb: Unknown result type (might be due to invalid IL or missing references) //IL_12ca: Expected O, but got Unknown //IL_12ea: Unknown result type (might be due to invalid IL or missing references) //IL_12f9: Unknown result type (might be due to invalid IL or missing references) //IL_1308: Unknown result type (might be due to invalid IL or missing references) //IL_1314: Unknown result type (might be due to invalid IL or missing references) //IL_1323: Expected O, but got Unknown //IL_1354: Unknown result type (might be due to invalid IL or missing references) //IL_1359: Unknown result type (might be due to invalid IL or missing references) //IL_1394: Unknown result type (might be due to invalid IL or missing references) //IL_139b: Unknown result type (might be due to invalid IL or missing references) //IL_13b2: Unknown result type (might be due to invalid IL or missing references) //IL_13cc: Unknown result type (might be due to invalid IL or missing references) //IL_13ed: Unknown result type (might be due to invalid IL or missing references) //IL_1438: Unknown result type (might be due to invalid IL or missing references) //IL_1478: Unknown result type (might be due to invalid IL or missing references) //IL_14b3: Unknown result type (might be due to invalid IL or missing references) //IL_14df: Unknown result type (might be due to invalid IL or missing references) canvas = ((Component)this).gameObject.AddComponent(); canvas.renderMode = (RenderMode)0; canvas.sortingOrder = 1000; CanvasScaler obj = ((Component)this).gameObject.AddComponent(); obj.uiScaleMode = (ScaleMode)1; obj.referenceResolution = new Vector2(1920f, 1080f); obj.matchWidthOrHeight = 0.5f; ((Component)this).gameObject.AddComponent(); root = UiKit.Go("Root", ((Component)this).transform); rootRect = UiKit.Stretch(root); Image obj2 = UiKit.Image(root.transform, "Bg", UiKit.White, BgDeep, (Type)0); UiKit.Stretch(((Component)obj2).gameObject); ((Graphic)obj2).raycastTarget = true; obj2.preserveAspect = false; GameObject val = UiKit.Go("Viewport", root.transform); viewportRect = UiKit.Stretch(val); Image obj3 = val.AddComponent(); ((Graphic)obj3).color = new Color(0f, 0f, 0f, 0f); ((Graphic)obj3).raycastTarget = true; val.AddComponent(); val.AddComponent().window = this; UiKit.Rect(((Component)UiKit.Image(val.transform, "CenterGlow", UiKit.SoftGlow, new Color(0.35f, 0.3f, 0.22f, 0.22f), (Type)0)).gameObject, new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1500f, 1500f)); GameObject go = UiKit.Go("Content", val.transform); content = UiKit.Rect(go, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero); ((Transform)content).localScale = Vector3.one * zoom; Image val2 = UiKit.Image(root.transform, "TopBar", UiKit.White, PanelBg, (Type)0); val2.preserveAspect = false; ((Graphic)val2).raycastTarget = true; RectTransform rectTransform = ((Graphic)val2).rectTransform; rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.anchoredPosition = Vector2.zero; rectTransform.sizeDelta = new Vector2(0f, 96f); UiKit.HLine(((Component)val2).transform, "TopLine", GoldLine, 0f, 1.5f, 0f, (Vector2?)new Vector2(0f, 0f)); UiKit.Ornament(((Component)val2).transform, "TopKnot", GoldLine, 9f, 560f, 30f, (Vector2?)new Vector2(0f, 0f)); TextMeshProUGUI obj4 = UiKit.Text(((Component)val2).transform, "Title", "PATH OF VALHEIMAN", 40f, UiKit.Gold, (TextAlignmentOptions)513, title: true); ((TMP_Text)obj4).characterSpacing = 4f; UiKit.Rect(((Component)obj4).gameObject, new Vector2(0f, 0.5f), new Vector2(36f, 0f), new Vector2(520f, 60f), (Vector2?)new Vector2(0f, 0.5f)); levelText = UiKit.Text(((Component)val2).transform, "Level", "", 26f, UiKit.Body, (TextAlignmentOptions)513); UiKit.Rect(((Component)levelText).gameObject, new Vector2(0f, 0.5f), new Vector2(600f, 14f), new Vector2(340f, 34f), (Vector2?)new Vector2(0f, 0.5f)); pointsText = UiKit.Text(((Component)val2).transform, "Points", "", 22f, UiKit.Gold, (TextAlignmentOptions)513); UiKit.Rect(((Component)pointsText).gameObject, new Vector2(0f, 0.5f), new Vector2(600f, -18f), new Vector2(700f, 30f), (Vector2?)new Vector2(0f, 0.5f)); Image val3 = UiKit.DarkPanel(((Component)val2).transform, "XpBg", new Color(0.12f, 0.11f, 0.1f, 1f), GoldDim, 1f); UiKit.Rect(((Component)val3).gameObject, new Vector2(1f, 0.5f), new Vector2(-96f, 0f), new Vector2(520f, 26f), (Vector2?)new Vector2(1f, 0.5f)); xpFill = UiKit.Image(((Component)val3).transform, "XpFill", UiKit.White, new Color(0.8f, 0.6f, 0.25f, 1f), (Type)0); xpFill.preserveAspect = false; RectTransform rectTransform2 = ((Graphic)xpFill).rectTransform; rectTransform2.anchorMin = new Vector2(0f, 0f); rectTransform2.anchorMax = new Vector2(0f, 1f); rectTransform2.pivot = new Vector2(0f, 0.5f); rectTransform2.offsetMin = new Vector2(3f, 3f); rectTransform2.offsetMax = new Vector2(0f, -3f); ((Component)xpFill).transform.SetAsLastSibling(); xpText = UiKit.Text(((Component)val3).transform, "XpText", "", 14f, new Color(0.98f, 0.95f, 0.88f), (TextAlignmentOptions)514); UiKit.Stretch(((Component)xpText).gameObject); ((TMP_Text)xpText).transform.SetAsLastSibling(); ((TMP_Text)xpText).outlineWidth = 0.2f; ((TMP_Text)xpText).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)200); UiKit.Button(((Component)val2).transform, "✕", new Vector2(1f, 0.5f), new Vector2(-40f, 0f), new Vector2(46f, 46f), new UnityAction(Close), 22f); UiKit.Rect(((Component)UiKit.Text(root.transform, "Hint", L.T("ЛКМ — взять узел · ПКМ — вернуть очко · колесо или +/− — масштаб · Home — вписать дерево · тянуть — прокрутка"), 14f, UiKit.Dim, (TextAlignmentOptions)513)).gameObject, new Vector2(0f, 0f), new Vector2(24f, 16f), new Vector2(900f, 22f), (Vector2?)new Vector2(0f, 0f)); Image side = UiKit.DarkPanel(root.transform, "Side", PanelBg, GoldLine); RectTransform rectTransform3 = ((Graphic)side).rectTransform; rectTransform3.anchorMin = new Vector2(1f, 0f); rectTransform3.anchorMax = new Vector2(1f, 1f); rectTransform3.pivot = new Vector2(1f, 0.5f); rectTransform3.offsetMin = new Vector2(-380f, 48f); rectTransform3.offsetMax = new Vector2(-24f, -120f); ((Graphic)side).raycastTarget = true; sidePanel = rectTransform3; TextMeshProUGUI obj5 = UiKit.Text(((Component)side).transform, "SideTitle", L.T("БОНУСЫ"), 26f, UiKit.Gold, (TextAlignmentOptions)514, title: true); ((TMP_Text)obj5).characterSpacing = 4f; UiKit.Rect(((Component)obj5).gameObject, new Vector2(0.5f, 1f), new Vector2(0f, -30f), new Vector2(300f, 36f)); UiKit.Ornament(((Component)side).transform, "SideKnot", GoldDim, -56f, 300f, 26f); statsText = UiKit.Text(((Component)side).transform, "Stats", "", 15f, UiKit.Body, (TextAlignmentOptions)257); RectTransform rectTransform4 = ((TMP_Text)statsText).rectTransform; rectTransform4.anchorMin = new Vector2(0f, 0f); rectTransform4.anchorMax = new Vector2(1f, 1f); rectTransform4.offsetMin = new Vector2(20f, (float)((Plugin.DebugButtons.Value ? 330 : 20) + 56)); rectTransform4.offsetMax = new Vector2(-20f, -68f); ((TMP_Text)statsText).overflowMode = (TextOverflowModes)1; float y; if (Plugin.DebugButtons.Value) { UiKit.HLine(((Component)side).transform, "DbgLine", GoldDim, 318f, 1f, 22f, (Vector2?)new Vector2(0f, 0f)); TextMeshProUGUI obj6 = UiKit.Text(((Component)side).transform, "DebugTitle", L.T("ОТЛАДКА"), 16f, UiKit.Danger, (TextAlignmentOptions)514, title: true); ((TMP_Text)obj6).characterSpacing = 3f; UiKit.Rect(((Component)obj6).gameObject, new Vector2(0.5f, 0f), new Vector2(0f, 296f), new Vector2(300f, 24f)); y = 258f; Btn(L.T("+1 очко"), (UnityAction)delegate { bound?.AddBonusPoints(1); }); Btn(L.T("+10 очков"), (UnityAction)delegate { bound?.AddBonusPoints(10); }); Btn(L.T("+1000 опыта"), (UnityAction)delegate { bound?.AddXp(1000f, "отладка", announce: true); }); Btn(L.T("Уровень +1"), (UnityAction)delegate { if (bound != null) { bound.AddXp(bound.XpToNext - bound.Xp + 0.01f, "отладка"); } }); Btn(L.T("Сбросить дерево"), (UnityAction)delegate { bound?.ResetTreeFree(); }); Btn(L.T("Сбросить всё"), (UnityAction)delegate { bound?.ResetAll(); }); } searchBox = UiKit.DarkPanel(root.transform, "Search", new Color(0.12f, 0.11f, 0.1f, 0.96f), GoldDim, 1.2f); UiKit.Rect(((Component)searchBox).gameObject, new Vector2(0f, 1f), new Vector2(24f, -112f), new Vector2(360f, 36f), (Vector2?)new Vector2(0f, 1f)); ((Graphic)searchBox).raycastTarget = true; ((Component)searchBox).gameObject.AddComponent().window = this; searchText = UiKit.Text(((Component)searchBox).transform, "SearchText", "", 16f, UiKit.Body, (TextAlignmentOptions)513); UiKit.Stretch(((Component)searchText).gameObject, 12f, 12f); searchCount = UiKit.Text(root.transform, "SearchCount", "", 13f, UiKit.Dim, (TextAlignmentOptions)513); UiKit.Rect(((Component)searchCount).gameObject, new Vector2(0f, 1f), new Vector2(26f, -150f), new Vector2(360f, 20f), (Vector2?)new Vector2(0f, 1f)); float num = (Plugin.DebugButtons.Value ? 330f : 20f); respecBtn = UiKit.Button(((Component)side).transform, L.T("Сбросить очки"), new Vector2(0.5f, 0f), new Vector2(0f, num + 8f), new Vector2(300f, 38f), new UnityAction(ToggleRespec), 16f); Image val4 = UiKit.DarkPanel(((Component)side).transform, "RespecPanel", new Color(0.1f, 0.09f, 0.08f, 0.99f), GoldDim, 1.2f); respecPanel = ((Component)val4).gameObject; UiKit.Rect(respecPanel, new Vector2(0.5f, 0f), new Vector2(0f, num + 54f), new Vector2(336f, 110f)); respecText = UiKit.Text(respecPanel.transform, "RespecText", "", 14f, UiKit.Body, (TextAlignmentOptions)258); UiKit.Rect(((Component)respecText).gameObject, new Vector2(0.5f, 1f), new Vector2(0f, -4f), new Vector2(320f, 46f)); UiKit.Button(respecPanel.transform, L.T("Применить"), new Vector2(0.5f, 0f), new Vector2(-80f, 6f), new Vector2(150f, 34f), new UnityAction(ApplyPending), 15f); UiKit.Button(respecPanel.transform, L.T("Отмена"), new Vector2(0.5f, 0f), new Vector2(80f, 6f), new Vector2(150f, 34f), new UnityAction(CancelRespec), 15f); respecPanel.SetActive(false); Image val5 = UiKit.Image(root.transform, "SideTab", UiKit.White, new Color(0.09f, 0.085f, 0.075f, 0.97f), (Type)0); val5.preserveAspect = false; ((Graphic)val5).raycastTarget = true; sideTab = ((Graphic)val5).rectTransform; RectTransform obj7 = sideTab; RectTransform obj8 = sideTab; Vector2 val6 = default(Vector2); ((Vector2)(ref val6))..ctor(1f, 0.5f); obj8.anchorMax = val6; obj7.anchorMin = val6; sideTab.pivot = new Vector2(1f, 0.5f); sideTab.sizeDelta = new Vector2(30f, 150f); Image obj9 = UiKit.Image((Transform)(object)sideTab, "TabEdge", UiKit.White, GoldLine, (Type)0); obj9.preserveAspect = false; ((Graphic)obj9).raycastTarget = false; RectTransform rectTransform5 = ((Graphic)obj9).rectTransform; rectTransform5.anchorMin = new Vector2(0f, 0f); rectTransform5.anchorMax = new Vector2(0f, 1f); rectTransform5.offsetMin = Vector2.zero; rectTransform5.offsetMax = new Vector2(1.5f, 0f); Button obj10 = ((Component)val5).gameObject.AddComponent