Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Reforged Classes v0.1.0
plugins/ValheimReforged.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.0.0.0")] [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 ValheimReforged { public enum Eff { None, MaxHealth, MaxStamina, MaxEitr, MoveSpeed, Damage, AbilityPower, HealthRegen, StaminaRegen, EitrRegen, LifeSteal, Cooldown, Crit, Resist, MeleeDamage, RangedDamage, MagicDamage, Execute, Opening, KillHeal, StaminaCost, SwordDamage, KnifeDamage, AxeDamage, ClubDamage, BowDamage, CrossbowDamage, ElementalDamage, BloodDamage, Experience, ArmorBonus, MiningDamage, ChoppingDamage, HarvestYield, FoodDuration, FishingSkill, ElementalResist, AmmoSave, FreeAmmo } public class Ability { public string Id; public string Glyph; public RpgClass Class; public int Col; public int Row; public string Parent; public int MaxRank = 5; public Eff Effect; public float PerRank; public int Cost = 1; public float Power = 1f; public float Cooldown; public Func<Player, int, bool> Cast; public Func<int, string> Preview; public string Name => L.T("ability." + Id + ".name"); public string Desc { get { if (!Active) { return L.F("ui.per_rank", Stats.Describe(Effect, PerRank)); } return L.T("ability." + Id + ".desc"); } } public string Info { get { string key = "ability." + Id + ".info"; if (!L.Has(key)) { return null; } return L.T(key); } } public bool Active => Cast != null; } public class Buff { public string Id; public string Name; public float Expires; public float Duration; public Dictionary<Eff, float> Mods = new Dictionary<Eff, float>(); } public static class Buffs { public static readonly List<Buff> Active = new List<Buff>(); public static void Add(string id, string name, float duration, params KeyValuePair<Eff, float>[] mods) { Active.RemoveAll((Buff b) => b.Id == id); Buff buff = new Buff { Id = id, Name = name, Expires = Time.time + duration, Duration = duration }; for (int num = 0; num < mods.Length; num++) { KeyValuePair<Eff, float> keyValuePair = mods[num]; buff.Mods[keyValuePair.Key] = keyValuePair.Value; } Active.Add(buff); } public static KeyValuePair<Eff, float> M(Eff e, float v) { return new KeyValuePair<Eff, float>(e, v); } public static float Get(Eff e) { float num = 0f; for (int num2 = Active.Count - 1; num2 >= 0; num2--) { float value; if (Active[num2].Expires <= Time.time) { Active.RemoveAt(num2); } else if (Active[num2].Mods.TryGetValue(e, out value)) { num += value; } } return num; } public static void Clear() { Active.Clear(); } } public static class Stats { private static readonly Dictionary<Eff, float> Caps = new Dictionary<Eff, float> { { Eff.MoveSpeed, 0.6f }, { Eff.Crit, 0.75f }, { Eff.LifeSteal, 0.3f }, { Eff.Cooldown, 0.5f }, { Eff.StaminaCost, 0.6f }, { Eff.Resist, 0.8f }, { Eff.ElementalResist, 0.75f }, { Eff.AmmoSave, 0.75f } }; public static float Get(Eff e) { RpgData data = Plugin.Data; if (data == null || data.Class == RpgClass.None) { return 0f; } float num = Buffs.Get(e) + Talents.Bonus(e); foreach (Ability item in Abilities.For(data.Class)) { if (item.Effect == e) { num += item.PerRank * (float)data.Rank(item.Id); } } if (!Caps.TryGetValue(e, out var value)) { return num; } return Mathf.Min(num, value); } public static float GetPassive(Eff e) { return GetTree(e) + Talents.Bonus(e); } public static float GetTree(Eff e) { RpgData data = Plugin.Data; if (data == null || data.Class == RpgClass.None) { return 0f; } float num = 0f; foreach (Ability item in Abilities.For(data.Class)) { if (item.Effect == e) { num += item.PerRank * (float)data.Rank(item.Id); } } return num; } public static string Describe(Eff e, float amount) { string text; switch (e) { case Eff.None: return ""; case Eff.MaxHealth: case Eff.MaxStamina: case Eff.MaxEitr: case Eff.FishingSkill: text = amount.ToString("0.#"); break; case Eff.HealthRegen: case Eff.StaminaRegen: case Eff.EitrRegen: text = amount.ToString("0.0"); break; default: text = (amount * 100f).ToString("0.#"); break; } return L.F("eff." + e, text); } } public static class Dash { private static Vector3 dir; private static float speed; private static float left; public static void Start(Player p, Vector3 direction, float distance, float duration) { //IL_000e: 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) direction.y = 0f; dir = ((Vector3)(ref direction)).normalized; speed = distance / duration; left = duration; } public static void Step(Player p) { //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_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_0046: 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_009f: 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_0078: 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) if (left <= 0f) { return; } float fixedDeltaTime = Time.fixedDeltaTime; left -= fixedDeltaTime; float num = speed * fixedDeltaTime; RaycastHit val = default(RaycastHit); if (Physics.Raycast(((Component)p).transform.position + Vector3.up * 0.9f, dir, ref val, num + 0.5f)) { left = 0f; return; } Rigidbody component = ((Component)p).GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.position += dir * num; return; } Transform transform = ((Component)p).transform; transform.position += dir * num; } } public static class Abilities { public static readonly List<Ability> All; private static readonly Dictionary<RpgClass, List<Ability>> byClass; private static readonly Dictionary<string, float> ready; private static readonly Dictionary<string, float> total; private static float CurrentPower; public static List<Ability> For(RpgClass c) { if (!byClass.TryGetValue(c, out var value)) { return new List<Ability>(); } return value; } public static Ability Find(string id) { return All.FirstOrDefault((Ability a) => a.Id == id); } public static float CooldownLeft(string id) { if (!ready.TryGetValue(id, out var value)) { return 0f; } return Mathf.Max(0f, value - Time.time); } public static float CooldownTotal(string id) { if (!total.TryGetValue(id, out var value)) { return 1f; } return value; } public static void Cast(Player p, RpgData d, int slot) { string text = d.Slots[slot]; Ability ability = ((text == null) ? null : Find(text)); if (ability == null || !ability.Active) { return; } int num = d.Rank(ability.Id); if (num <= 0) { return; } if (CooldownLeft(ability.Id) > 0f) { ((Character)p).Message((MessageType)1, L.F("msg.cooldown", ability.Name), 0, (Sprite)null, false); return; } CurrentPower = ability.Power; if (ability.Cast(p, num)) { float num2 = ability.Cooldown * (1f - Mathf.Min(0.5f, Stats.Get(Eff.Cooldown))); ready[ability.Id] = Time.time + num2; total[ability.Id] = num2; } } private static float FrenzyTime(int rank) { return 6f + (float)rank; } private static float FrenzyDamage(int rank) { return (0.2f + 0.06f * (float)rank) * CurrentPower; } private static float Scale(int rank, float basis, float perRank) { return (basis + perRank * (float)rank) * (1f + 0.02f * (float)((Plugin.Data == null) ? 1 : Plugin.Data.Level)) * (1f + Stats.Get(Eff.AbilityPower)) * Plugin.AbilityDamage.Value * CurrentPower; } private static void Burst(Player p, Vector3 center, float radius, DamageTypes dmg, float push) { //IL_0033: 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_0043: 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) Character[] array = Character.GetAllCharacters().ToArray(); foreach (Character val in array) { if (!((Object)(object)val == (Object)(object)p) && !val.IsDead() && BaseAI.IsEnemy((Character)(object)p, val) && !(Vector3.Distance(((Component)val).transform.position, center) > radius)) { Strike(p, val, center, dmg, push); } } } private static void Strike(Player p, Character c, Vector3 from, DamageTypes dmg, float push) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_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_000e: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_0031: 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_0039: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown HitData val = new HitData { m_damage = dmg, m_attacker = ((Character)p).GetZDOID(), m_point = c.GetCenterPoint() }; Vector3 val2 = ((Component)c).transform.position - from; val.m_dir = ((Vector3)(ref val2)).normalized; val.m_pushForce = push; HitData val3 = val; c.Damage(val3); } private static Vector3 AimPoint(Player p, float max) { //IL_0021: 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_003f: 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_0050: 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) Transform val = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform : ((Component)p).transform); RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val.position, val.forward, ref val2, max)) { return ((RaycastHit)(ref val2)).point; } return val.position + val.forward * max; } private static ItemData FindArrows(Player p, int count) { foreach (ItemData allItem in ((Humanoid)p).GetInventory().GetAllItems()) { if (allItem.m_shared.m_ammoType == "$ammo_arrows" && allItem.m_stack >= count) { return allItem; } } return null; } private static bool Eitr(Player p, float cost) { if (!((Character)p).HaveEitr(cost)) { ((Character)p).Message((MessageType)2, L.T("msg.no_eitr"), 0, (Sprite)null, false); return false; } ((Character)p).UseEitr(cost); return true; } private static float TakeArrows(Player p, int cost) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) ItemData val = FindArrows(p, cost); if (val == null) { ((Character)p).Message((MessageType)2, L.F("msg.need_arrows", cost), 0, (Sprite)null, false); return -1f; } float pierce = val.GetDamage().m_pierce; ((Humanoid)p).GetInventory().RemoveItem(val, cost); return pierce; } private static Ability P(RpgClass cls, string id, int col, int row, string parent, Eff e, float per, string glyph) { Ability obj = new Ability { Id = id, Class = cls, Col = col, Row = row, Parent = parent, Effect = e, PerRank = per, Glyph = glyph }; Add(obj); return obj; } private static Ability A(RpgClass cls, string id, int col, int row, string parent, float cd, string glyph, Func<Player, int, bool> cast) { Ability ability = new Ability { Id = id, Class = cls, Col = col, Row = row, Parent = parent, Cooldown = cd, Glyph = glyph, Cast = cast }; if (row == 4) { ability.MaxRank = 3; } Add(ability); return ability; } private static void Tip(string id, Func<int, string> preview) { Find(id).Preview = delegate(int r) { float currentPower = CurrentPower; CurrentPower = Find(id).Power; try { return preview(r); } finally { CurrentPower = currentPower; } }; } private static void Add(Ability a) { All.Add(a); if (!byClass.TryGetValue(a.Class, out var value)) { value = (byClass[a.Class] = new List<Ability>()); } value.Add(a); } static Abilities() { All = new List<Ability>(); byClass = new Dictionary<RpgClass, List<Ability>>(); ready = new Dictionary<string, float>(); total = new Dictionary<string, float>(); CurrentPower = 1f; RpgClass cls = RpgClass.Archer; RpgClass cls2 = RpgClass.Mage; RpgClass cls3 = RpgClass.Rogue; P(RpgClass.Warrior, "w_str1", 0, 1, null, Eff.Damage, 0.02f, "sword"); A(RpgClass.Warrior, "w_shock", 0, 2, "w_str1", 18f, "w_shock", delegate(Player p, int r) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0033: 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) Vfx.Cast("w_shock", ((Component)p).transform.position, ((Component)p).transform.position); Burst(p, ((Component)p).transform.position, 6f, new DamageTypes { m_blunt = Scale(r, 25f, 15f) }, 40f); return true; }); P(RpgClass.Warrior, "w_crit", 0, 3, "w_shock", Eff.Crit, 0.02f, "crit"); P(RpgClass.Warrior, "w_rage", 0, 4, "w_crit", Eff.MeleeDamage, 0.06f, "sword"); P(RpgClass.Warrior, "w_iron", 1, 1, null, Eff.MaxHealth, 10f, "heart"); A(RpgClass.Warrior, "w_wind", 1, 2, "w_iron", 45f, "w_wind", delegate(Player p, int r) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vfx.Cast("w_wind", ((Component)p).transform.position, ((Component)p).transform.position); ((Character)p).Heal(((Character)p).GetMaxHealth() * (0.15f + 0.05f * (float)r), true); ((Character)p).AddStamina(((Character)p).GetMaxStamina()); return true; }); P(RpgClass.Warrior, "w_regen", 1, 3, "w_wind", Eff.HealthRegen, 0.25f, "w_wind"); P(RpgClass.Warrior, "w_wall", 1, 4, "w_regen", Eff.Resist, 0.05f, "w_iron"); P(RpgClass.Warrior, "w_stam", 2, 1, null, Eff.MaxStamina, 6f, "a_endur"); P(RpgClass.Warrior, "w_cry", 2, 2, "w_stam", Eff.StaminaCost, 0.03f, "r_reflex"); P(RpgClass.Warrior, "w_drain", 2, 3, "w_cry", Eff.LifeSteal, 0.015f, "m_well"); P(RpgClass.Warrior, "w_quake", 2, 4, "w_drain", Eff.Execute, 0.15f, "w_shock"); P(cls, "a_sharp", 0, 1, null, Eff.Damage, 0.02f, "sword"); A(cls, "a_rain", 0, 2, "a_sharp", 22f, "a_rain", delegate(Player p, int r) { //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) //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) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) float num = TakeArrows(p, 5); if (num < 0f) { return false; } Vector3 val = AimPoint(p, 40f); Vfx.Cast("a_rain", ((Component)p).transform.position, val); Burst(p, val, 7f, new DamageTypes { m_pierce = Scale(r, 30f, 15f) + num * 2f }, 10f); return true; }); P(cls, "a_crit", 0, 3, "a_rain", Eff.Crit, 0.02f, "crit"); P(cls, "a_volley", 0, 4, "a_crit", Eff.RangedDamage, 0.06f, "a_rain"); P(cls, "a_endur", 1, 1, null, Eff.MaxStamina, 4f, "a_endur"); A(cls, "a_leap", 1, 2, "a_endur", 25f, "a_leap", delegate(Player p, int r) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vfx.Cast("a_leap", ((Component)p).transform.position, ((Component)p).transform.position); float v = FrenzyDamage(r); Buffs.Add("a_leap", L.T("ability.a_leap.name"), FrenzyTime(r), Buffs.M(Eff.BowDamage, v), Buffs.M(Eff.CrossbowDamage, v), Buffs.M(Eff.FreeAmmo, 1f)); return true; }); P(cls, "a_swift", 1, 3, "a_leap", Eff.MoveSpeed, 0.02f, "r_reflex"); P(cls, "a_focus", 1, 4, "a_swift", Eff.Opening, 0.15f, "crit"); P(cls, "a_vigor", 2, 1, null, Eff.MaxHealth, 6f, "heart"); P(cls, "a_herbs", 2, 2, "a_vigor", Eff.KillHeal, 0.01f, "w_wind"); P(cls, "a_second", 2, 3, "a_herbs", Eff.StaminaRegen, 0.4f, "a_endur"); P(cls, "a_frost", 2, 4, "a_second", Eff.Execute, 0.15f, "snow"); P(cls2, "m_power", 0, 1, null, Eff.AbilityPower, 0.03f, "m_blast"); A(cls2, "m_blast", 0, 2, "m_power", 14f, "m_blast", delegate(Player p, int r) { //IL_0015: 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_002b: 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_003a: 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) if (!Eitr(p, 25f)) { return false; } Vector3 val = AimPoint(p, 35f); Vfx.Cast("m_blast", ((Component)p).transform.position, val); Burst(p, val, 5f, new DamageTypes { m_lightning = Scale(r, 35f, 20f) }, 20f); return true; }); P(cls2, "m_crit", 0, 3, "m_blast", Eff.Crit, 0.02f, "crit"); P(cls2, "m_storm", 0, 4, "m_crit", Eff.MagicDamage, 0.06f, "m_blast"); P(cls2, "m_arcana", 1, 1, null, Eff.MaxEitr, 8f, "m_arcana"); A(cls2, "m_well", 1, 2, "m_arcana", 40f, "m_well", delegate(Player p, int r) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vfx.Cast("m_well", ((Component)p).transform.position, ((Component)p).transform.position); ((Character)p).AddEitr(((Character)p).GetMaxEitr() * (0.3f + 0.1f * (float)r)); return true; }); P(cls2, "m_flow", 1, 3, "m_well", Eff.EitrRegen, 0.4f, "m_well"); P(cls2, "m_shield", 1, 4, "m_flow", Eff.Resist, 0.05f, "w_iron"); P(cls2, "m_vit", 2, 1, null, Eff.MaxHealth, 5f, "heart"); P(cls2, "m_frost", 2, 2, "m_vit", Eff.KillHeal, 0.01f, "snow"); P(cls2, "m_cool", 2, 3, "m_frost", Eff.Cooldown, 0.03f, "clock"); P(cls2, "m_meteor", 2, 4, "m_cool", Eff.AbilityPower, 0.08f, "fire"); P(cls3, "r_blade", 0, 1, null, Eff.Damage, 0.02f, "sword"); A(cls3, "r_step", 0, 2, "r_blade", 20f, "r_reflex", delegate(Player p, int r) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vfx.Cast("r_step", ((Component)p).transform.position, ((Component)p).transform.position); Buffs.Add("r_step", "Disparada", 5f + (float)r, Buffs.M(Eff.MoveSpeed, 0.2f + 0.05f * (float)r)); return true; }); P(cls3, "r_crit", 0, 3, "r_step", Eff.Crit, 0.03f, "crit"); P(cls3, "r_execute", 0, 4, "r_crit", Eff.Execute, 0.15f, "crit"); P(cls3, "r_reflex", 1, 1, null, Eff.MoveSpeed, 0.02f, "r_reflex"); P(cls3, "r_smoke", 1, 2, "r_reflex", Eff.Resist, 0.03f, "r_venom"); P(cls3, "r_agile", 1, 3, "r_smoke", Eff.StaminaRegen, 0.4f, "a_endur"); P(cls3, "r_shadow", 1, 4, "r_agile", Eff.Opening, 0.15f, "r_step"); P(cls3, "r_vit", 2, 1, null, Eff.MaxHealth, 4f, "heart"); A(cls3, "r_venom", 2, 2, "r_vit", 20f, "r_venom", delegate(Player p, int r) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0033: 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) Vfx.Cast("r_venom", ((Component)p).transform.position, ((Component)p).transform.position); Burst(p, ((Component)p).transform.position, 5f, new DamageTypes { m_poison = Scale(r, 60f, 30f), m_pierce = Scale(r, 15f, 8f) }, 0f); return true; }); P(cls3, "r_leech", 2, 3, "r_venom", Eff.LifeSteal, 0.01f, "m_well"); P(cls3, "r_plague", 2, 4, "r_leech", Eff.AbilityPower, 0.08f, "r_venom"); Tip("w_shock", (int r) => L.F("ability.w_shock.preview", Scale(r, 25f, 15f).ToString("0"))); Tip("w_wind", (int r) => L.F("ability.w_wind.preview", ((0.15f + 0.05f * (float)r) * 100f).ToString("0"))); Tip("a_rain", (int r) => L.F("ability.a_rain.preview", Scale(r, 30f, 15f).ToString("0"))); Tip("a_leap", (int r) => L.F("ability.a_leap.preview", FrenzyTime(r).ToString("0"), Mathf.RoundToInt(FrenzyDamage(r) * 100f))); Tip("m_blast", (int r) => L.F("ability.m_blast.preview", Scale(r, 35f, 20f).ToString("0"))); Tip("m_well", (int r) => L.F("ability.m_well.preview", ((0.3f + 0.1f * (float)r) * 100f).ToString("0"))); Tip("r_step", (int r) => L.F("ability.r_step.preview", ((0.2f + 0.05f * (float)r) * 100f).ToString("0"), 5 + r)); Tip("r_venom", (int r) => L.F("ability.r_venom.preview", Scale(r, 60f, 30f).ToString("0"), Scale(r, 15f, 8f).ToString("0"))); foreach (Ability item in All) { if (!item.Active && item.Row == 4) { item.MaxRank = 3; } } } } public static class Floaters { private class F { public Vector3 Pos; public string Text; public Color Color; public int Size; public float T0; public float Dur; } public static readonly Color CritColor = new Color(1f, 0.82f, 0.25f); public static readonly Color ExecuteColor = new Color(1f, 0.4f, 0.35f); public static readonly Color OpeningColor = new Color(1f, 0.65f, 0.3f); public static readonly Color XpColor = new Color(0.55f, 0.75f, 1f); private static readonly List<F> list = new List<F>(); private static readonly Dictionary<string, float> lastShown = new Dictionary<string, float>(); private static GUIStyle style; public static void Add(Vector3 pos, string text, Color color, int size = 18, float dur = 1.2f, string throttleKey = null, float throttle = 0f) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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) if (throttleKey != null) { if (lastShown.TryGetValue(throttleKey, out var value) && Time.time - value < throttle) { return; } lastShown[throttleKey] = Time.time; if (lastShown.Count > 200) { lastShown.Clear(); } } list.Add(new F { Pos = pos, Text = text, Color = color, Size = size, T0 = Time.time, Dur = dur }); if (list.Count > 40) { list.RemoveAt(0); } } public static void Draw(float s) { //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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0092: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0168: 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_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) if (list.Count == 0) { return; } Camera main = Camera.main; if ((Object)(object)main == (Object)null) { return; } if (style == null) { style = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontStyle = (FontStyle)1 }; } Rect val2 = default(Rect); for (int num = list.Count - 1; num >= 0; num--) { F f = list[num]; float num2 = (Time.time - f.T0) / f.Dur; if (num2 >= 1f) { list.RemoveAt(num); } else { Vector3 val = main.WorldToScreenPoint(f.Pos + Vector3.up * (num2 * 1.4f)); if (!(val.z <= 0f)) { float num3 = val.x / s; float num4 = ((float)Screen.height - val.y) / s; float num5 = 1f + 0.35f * Mathf.Clamp01(1f - num2 * 7f); style.fontSize = Mathf.RoundToInt((float)f.Size * num5); float num6 = 1f - num2 * num2; ((Rect)(ref val2))..ctor(num3 - 150f, num4 - 20f, 300f, 40f); style.normal.textColor = new Color(0f, 0f, 0f, 0.85f * num6); GUI.Label(new Rect(((Rect)(ref val2)).x + 1.5f, ((Rect)(ref val2)).y + 1.5f, ((Rect)(ref val2)).width, ((Rect)(ref val2)).height), f.Text, style); style.normal.textColor = new Color(f.Color.r, f.Color.g, f.Color.b, num6); GUI.Label(val2, f.Text, style); } } } } } public static class Icons { private class Canvas { private readonly Color[] px = (Color[])(object)new Color[4096]; private void Plot(int x, int y, Color c, float a) { //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_003b: 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_005e: 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_006d: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00b1: 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_00ca: Unknown result type (might be due to invalid IL or missing references) if (x >= 0 && y >= 0 && x < 64 && y < 64) { a *= c.a; if (!(a <= 0f)) { int num = y * 64 + x; Color val = px[num]; float num2 = a + val.a * (1f - a); float num3 = 1f / num2; px[num] = new Color((c.r * a + val.r * val.a * (1f - a)) * num3, (c.g * a + val.g * val.a * (1f - a)) * num3, (c.b * a + val.b * val.a * (1f - a)) * num3, num2); } } } public void Disc(float cx, float cy, float r, Color c) { //IL_001d: 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_0037: Unknown result type (might be due to invalid IL or missing references) for (int i = (int)(cy - r - 1f); (float)i <= cy + r + 1f; i++) { for (int j = (int)(cx - r - 1f); (float)j <= cx + r + 1f; j++) { Plot(j, i, c, Mathf.Clamp01(r - Vector2.Distance(new Vector2((float)j + 0.5f, (float)i + 0.5f), new Vector2(cx, cy)) + 0.5f)); } } } public void Ring(float cx, float cy, float r, float th, Color c) { //IL_0024: 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_0039: Unknown result type (might be due to invalid IL or missing references) for (int i = (int)(cy - r - th); (float)i <= cy + r + th; i++) { for (int j = (int)(cx - r - th); (float)j <= cx + r + th; j++) { float num = Vector2.Distance(new Vector2((float)j + 0.5f, (float)i + 0.5f), new Vector2(cx, cy)); Plot(j, i, c, Mathf.Clamp01(th * 0.5f - Mathf.Abs(num - r) + 0.5f)); } } } public void Line(float x0, float y0, float x1, float y1, float th, Color c) { //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_0012: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(x0, y0); Vector2 val2 = new Vector2(x1, y1) - val; float sqrMagnitude = ((Vector2)(ref val2)).sqrMagnitude; Vector2 val3 = default(Vector2); for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { ((Vector2)(ref val3))..ctor((float)j + 0.5f, (float)i + 0.5f); float num = ((sqrMagnitude > 0f) ? Mathf.Clamp01(Vector2.Dot(val3 - val, val2) / sqrMagnitude) : 0f); float num2 = Vector2.Distance(val3, val + val2 * num); float num3 = Mathf.Clamp01(th * 0.5f - num2 + 0.5f); if (num3 > 0f) { Plot(j, i, c, num3); } } } } public void Tri(float x0, float y0, float x1, float y1, float x2, float y2, Color c) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(x0, y0); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(x1, y1); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(x2, y2); for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { Vector2 p = new Vector2((float)j + 0.5f, (float)i + 0.5f); float num = Sign(p, val, val2); float num2 = Sign(p, val2, val3); float num3 = Sign(p, val3, val); bool flag = num < 0f || num2 < 0f || num3 < 0f; bool flag2 = num > 0f || num2 > 0f || num3 > 0f; if (!(flag && flag2)) { Plot(j, i, c, 1f); } } } } private static float Sign(Vector2 p, Vector2 a, Vector2 b) { //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) return (p.x - b.x) * (a.y - b.y) - (a.x - b.x) * (p.y - b.y); } public Texture2D ToTexture() { //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_0012: 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; val.SetPixels(px); val.Apply(); return val; } } private const int N = 64; private static readonly Dictionary<string, Texture2D> cache = new Dictionary<string, Texture2D>(); public static Color Accent(RpgClass c) { //IL_0029: 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_0053: 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_007d: Unknown result type (might be due to invalid IL or missing references) return (Color)(c switch { RpgClass.Warrior => new Color(0.88f, 0.34f, 0.28f), RpgClass.Archer => new Color(0.4f, 0.76f, 0.38f), RpgClass.Mage => new Color(0.38f, 0.6f, 1f), RpgClass.Rogue => new Color(0.72f, 0.45f, 0.92f), _ => new Color(0.8f, 0.7f, 0.4f), }); } public static Texture2D ForAbility(Ability a) { return Get(a.Glyph, a.Class); } public static Texture2D ForClass(RpgClass c) { return (Texture2D)(c switch { RpgClass.Warrior => Get("w_iron", c), RpgClass.Archer => Get("a_rain", c), RpgClass.Mage => Get("m_arcana", c), _ => Get("r_step", c), }); } private static Texture2D Get(string id, RpgClass cls) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return Build(id, Accent(cls)); } public static Texture2D ForGlyph(string glyph, Color accent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Build(glyph, accent); } private static Texture2D Build(string id, Color accent) { //IL_0006: 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_004d: 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_006a: 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_0091: 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_009c: Unknown result type (might be due to invalid IL or missing references) string key = id + "#" + ColorUtility.ToHtmlStringRGB(accent); if (cache.TryGetValue(key, out var value) && (Object)(object)value != (Object)null) { return value; } Canvas canvas = new Canvas(); canvas.Disc(32f, 32f, 31f, new Color(accent.r * 0.28f, accent.g * 0.28f, accent.b * 0.28f, 1f)); canvas.Ring(32f, 32f, 29.5f, 3f, accent); Glyph(canvas, id, Color.Lerp(accent, Color.white, 0.75f)); value = canvas.ToTexture(); cache[key] = value; return value; } private static void Glyph(Canvas cv, string id, Color L) { //IL_082a: Unknown result type (might be due to invalid IL or missing references) //IL_084f: Unknown result type (might be due to invalid IL or missing references) //IL_09c0: Unknown result type (might be due to invalid IL or missing references) //IL_09e0: Unknown result type (might be due to invalid IL or missing references) //IL_0a00: Unknown result type (might be due to invalid IL or missing references) //IL_0442: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_0487: Unknown result type (might be due to invalid IL or missing references) //IL_04a7: Unknown result type (might be due to invalid IL or missing references) //IL_04c7: Unknown result type (might be due to invalid IL or missing references) //IL_04e7: Unknown result type (might be due to invalid IL or missing references) //IL_05eb: Unknown result type (might be due to invalid IL or missing references) //IL_060b: Unknown result type (might be due to invalid IL or missing references) //IL_062b: Unknown result type (might be due to invalid IL or missing references) //IL_064b: Unknown result type (might be due to invalid IL or missing references) //IL_0661: Unknown result type (might be due to invalid IL or missing references) //IL_0b20: Unknown result type (might be due to invalid IL or missing references) //IL_0b36: Unknown result type (might be due to invalid IL or missing references) //IL_0b56: Unknown result type (might be due to invalid IL or missing references) //IL_0b76: Unknown result type (might be due to invalid IL or missing references) //IL_0b96: Unknown result type (might be due to invalid IL or missing references) //IL_0bb6: Unknown result type (might be due to invalid IL or missing references) //IL_0a8d: Unknown result type (might be due to invalid IL or missing references) //IL_0ab2: Unknown result type (might be due to invalid IL or missing references) //IL_0866: Unknown result type (might be due to invalid IL or missing references) //IL_088b: Unknown result type (might be due to invalid IL or missing references) //IL_08ab: Unknown result type (might be due to invalid IL or missing references) //IL_0928: Unknown result type (might be due to invalid IL or missing references) //IL_094d: 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_07e9: Unknown result type (might be due to invalid IL or missing references) //IL_0809: Unknown result type (might be due to invalid IL or missing references) //IL_08d1: Unknown result type (might be due to invalid IL or missing references) //IL_08f1: Unknown result type (might be due to invalid IL or missing references) //IL_0911: Unknown result type (might be due to invalid IL or missing references) //IL_0ac9: Unknown result type (might be due to invalid IL or missing references) //IL_0adf: Unknown result type (might be due to invalid IL or missing references) //IL_0b04: Unknown result type (might be due to invalid IL or missing references) //IL_0678: Unknown result type (might be due to invalid IL or missing references) //IL_067e: Unknown result type (might be due to invalid IL or missing references) //IL_0698: Unknown result type (might be due to invalid IL or missing references) //IL_069e: Unknown result type (might be due to invalid IL or missing references) //IL_06b8: 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_06e5: Unknown result type (might be due to invalid IL or missing references) //IL_06fb: Unknown result type (might be due to invalid IL or missing references) //IL_0711: Unknown result type (might be due to invalid IL or missing references) //IL_050d: Unknown result type (might be due to invalid IL or missing references) //IL_052d: Unknown result type (might be due to invalid IL or missing references) //IL_0bd7: Unknown result type (might be due to invalid IL or missing references) //IL_0bf7: Unknown result type (might be due to invalid IL or missing references) //IL_0c17: Unknown result type (might be due to invalid IL or missing references) //IL_0732: Unknown result type (might be due to invalid IL or missing references) //IL_0752: Unknown result type (might be due to invalid IL or missing references) //IL_0772: Unknown result type (might be due to invalid IL or missing references) //IL_0792: Unknown result type (might be due to invalid IL or missing references) //IL_07a8: Unknown result type (might be due to invalid IL or missing references) //IL_096e: Unknown result type (might be due to invalid IL or missing references) //IL_098e: Unknown result type (might be due to invalid IL or missing references) //IL_09a4: Unknown result type (might be due to invalid IL or missing references) //IL_05a5: 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_0324: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_054e: Unknown result type (might be due to invalid IL or missing references) //IL_056e: Unknown result type (might be due to invalid IL or missing references) //IL_058e: Unknown result type (might be due to invalid IL or missing references) //IL_0a6c: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) if (id == null) { return; } switch (id.Length) { case 7: switch (id[0]) { case 'w': if (id == "w_shock") { cv.Ring(32f, 32f, 13f, 3.5f, L); cv.Ring(32f, 32f, 21f, 2.5f, L); cv.Disc(32f, 32f, 5f, L); } break; case 'a': if (id == "a_endur") { cv.Tri(32f, 48f, 17f, 20f, 47f, 20f, L); cv.Line(20f, 15f, 44f, 15f, 4f, L); } break; case 'm': if (id == "m_blast") { cv.Line(38f, 52f, 26f, 33f, 5f, L); cv.Line(26f, 33f, 38f, 31f, 5f, L); cv.Line(38f, 31f, 26f, 12f, 5f, L); } break; case 'r': if (id == "r_venom") { cv.Disc(24f, 30f, 10f, L); cv.Disc(39f, 30f, 11f, L); cv.Disc(31f, 40f, 10f, L); cv.Disc(31f, 23f, 8f, L); } break; } break; case 6: switch (id[4]) { case 'n': if (id == "w_wind") { cv.Line(32f, 17f, 32f, 47f, 7f, L); cv.Line(17f, 32f, 47f, 32f, 7f, L); } break; case 'o': if (id == "w_iron") { cv.Tri(19f, 44f, 45f, 44f, 32f, 14f, L); cv.Line(19f, 46f, 45f, 46f, 9f, L); } break; case 'i': if (id == "a_rain") { float[] array = new float[3] { -12f, 0f, 12f }; foreach (float num4 in array) { cv.Line(32f + num4 + 6f, 50f, 32f + num4 - 6f, 22f, 3f, L); cv.Tri(32f + num4 - 11f, 27f, 32f + num4 - 1f, 27f, 32f + num4 - 6f, 15f, L); } } break; case 'a': if (id == "a_leap") { cv.Line(12f, 32f, 48f, 32f, 5f, L); cv.Tri(54f, 32f, 42f, 22f, 42f, 42f, L); cv.Line(12f, 32f, 6f, 24f, 4f, L); cv.Line(12f, 32f, 6f, 40f, 4f, L); cv.Line(20f, 32f, 14f, 24f, 4f, L); cv.Line(20f, 32f, 14f, 40f, 4f, L); } break; case 'l': if (id == "m_well") { cv.Disc(32f, 25f, 11f, L); cv.Tri(21.5f, 29f, 42.5f, 29f, 32f, 51f, L); } break; case 'e': if (id == "r_step") { cv.Disc(19f, 32f, 7f, Fade(L, 0.35f)); cv.Disc(32f, 32f, 7.5f, Fade(L, 0.65f)); cv.Disc(45f, 32f, 8f, L); } break; } break; case 8: switch (id[0]) { case 'm': if (id == "m_arcana") { cv.Line(32f, 11f, 32f, 53f, 3f, L); cv.Line(11f, 32f, 53f, 32f, 3f, L); cv.Line(17f, 17f, 47f, 47f, 3f, L); cv.Line(17f, 47f, 47f, 17f, 3f, L); cv.Disc(32f, 32f, 8f, L); } break; case 'r': if (id == "r_reflex") { cv.Line(14f, 41f, 50f, 41f, 4f, L); cv.Line(9f, 32f, 43f, 32f, 4f, L); cv.Line(16f, 23f, 49f, 23f, 4f, L); } break; } break; case 4: switch (id[3]) { case 'r': if (id == "star") { cv.Line(32f, 10f, 32f, 54f, 4f, L); cv.Line(10f, 32f, 54f, 32f, 4f, L); cv.Line(17f, 17f, 47f, 47f, 3f, L); cv.Line(47f, 17f, 17f, 47f, 3f, L); cv.Disc(32f, 32f, 6f, L); } break; case 'k': if (id == "pick") { cv.Line(32f, 10f, 32f, 54f, 5f, L); cv.Line(12f, 42f, 32f, 50f, 5f, L); cv.Line(52f, 42f, 32f, 50f, 5f, L); } break; case 'f': if (id == "leaf") { cv.Disc(32f, 30f, 14f, L); cv.Tri(32f, 52f, 18f, 32f, 46f, 32f, L); cv.Line(32f, 52f, 32f, 60f, 3f, L); } break; case 'l': if (id == "bowl") { cv.Tri(12f, 38f, 52f, 38f, 32f, 16f, L); cv.Line(26f, 46f, 26f, 56f, 3f, L); cv.Line(38f, 46f, 38f, 56f, 3f, L); } break; case 'h': if (id == "fish") { cv.Disc(28f, 32f, 13f, L); cv.Tri(38f, 32f, 54f, 44f, 54f, 20f, L); } break; case 'w': if (id == "snow") { float[] array = new float[3] { 0f, 60f, 120f }; for (int i = 0; i < array.Length; i++) { float num = array[i] * ((float)Math.PI / 180f); float num2 = Mathf.Cos(num) * 19f; float num3 = Mathf.Sin(num) * 19f; cv.Line(32f - num2, 32f - num3, 32f + num2, 32f + num3, 3.5f, L); } } break; case 'e': if (id == "fire") { cv.Disc(32f, 26f, 11f, L); cv.Tri(22f, 31f, 42f, 31f, 32f, 54f, L); } break; case 't': if (id == "crit") { cv.Ring(32f, 32f, 13f, 3f, L); cv.Disc(32f, 32f, 3.5f, L); cv.Line(32f, 9f, 32f, 21f, 3f, L); cv.Line(32f, 43f, 32f, 55f, 3f, L); cv.Line(9f, 32f, 21f, 32f, 3f, L); cv.Line(43f, 32f, 55f, 32f, 3f, L); } break; } break; case 5: switch (id[0]) { case 's': if (id == "sword") { cv.Line(17f, 17f, 46f, 46f, 5f, L); cv.Line(17f, 36f, 36f, 17f, 5f, L); cv.Disc(46f, 46f, 3f, L); } break; case 'c': if (id == "clock") { cv.Ring(32f, 32f, 17f, 3.5f, L); cv.Line(32f, 32f, 32f, 44f, 3f, L); cv.Line(32f, 32f, 41f, 32f, 3f, L); } break; case 'h': if (id == "heart") { cv.Disc(24f, 37f, 9f, L); cv.Disc(40f, 37f, 9f, L); cv.Tri(15.5f, 33f, 48.5f, 33f, 32f, 12f, L); } break; } break; case 3: if (id == "axe") { cv.Line(22f, 12f, 40f, 52f, 5f, L); cv.Tri(38f, 54f, 56f, 42f, 36f, 30f, L); } break; } } private static Color Fade(Color c, float a) { //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_000c: 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) return new Color(c.r, c.g, c.b, a); } } public static class L { private const string Fallback = "English"; private static Dictionary<string, string> english; private static Dictionary<string, string> current; private static string lang = ""; private static float nextCheck; private static string PluginDir => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations"); private static string UserDir => Path.Combine(Paths.ConfigPath, Path.Combine("ValheimReforged", "Translations")); private static string GameLanguage() { try { return (Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : "English"; } catch { return "English"; } } private static void Ensure() { if (english == null) { english = LoadEmbeddedEnglish(); Overlay(english, Path.Combine(PluginDir, "English.yml")); Overlay(english, Path.Combine(UserDir, "English.yml")); current = english; try { Directory.CreateDirectory(UserDir); } catch { } } if (Time.realtimeSinceStartup < nextCheck) { return; } nextCheck = Time.realtimeSinceStartup + 1f; string text = GameLanguage(); if (text == lang) { return; } lang = text; if (text == "English") { current = english; return; } Dictionary<string, string> dictionary = new Dictionary<string, string>(); bool flag = Overlay(dictionary, Path.Combine(PluginDir, text + ".yml")); flag |= Overlay(dictionary, Path.Combine(UserDir, text + ".yml")); if (!flag) { flag = TryRelated(dictionary, text); } current = dictionary; if (flag) { Plugin.Log.LogInfo((object)("Translation loaded: " + text + " (" + dictionary.Count + " entries)")); } else { Plugin.Log.LogInfo((object)("No translation for '" + text + "'; using English. Copy English.yml to " + Path.Combine(UserDir, text + ".yml") + " and translate the values to add it.")); } } private static bool TryRelated(Dictionary<string, string> into, string g) { int num = g.IndexOf('_'); string text = ((num > 0) ? g.Substring(0, num) : g); string[] array = new string[2] { PluginDir, UserDir }; foreach (string path in array) { try { if (!Directory.Exists(path)) { continue; } string[] files = Directory.GetFiles(path, text + "*.yml"); foreach (string path2 in files) { if (Overlay(into, path2)) { Plugin.Log.LogInfo((object)("Using related translation " + Path.GetFileName(path2) + " for " + g)); return true; } } } catch { } } return false; } private static Dictionary<string, string> LoadEmbeddedEnglish() { Dictionary<string, string> dictionary = new Dictionary<string, string>(); try { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("English.yml"); if (stream != null) { using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); Parse(streamReader.ReadToEnd(), dictionary); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read embedded English.yml: " + ex.Message)); } return dictionary; } private static bool Overlay(Dictionary<string, string> into, string path) { try { if (!File.Exists(path)) { return false; } Parse(File.ReadAllText(path, Encoding.UTF8), into); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read " + path + ": " + ex.Message)); return false; } } private static void Parse(string text, Dictionary<string, string> into) { string[] array = text.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(new char[1] { '\r' }).Trim(); if (text2.Length != 0 && text2[0] != '#') { int num = text2.IndexOf(':'); if (num > 0) { string key = text2.Substring(0, num).Trim(); string v = text2.Substring(num + 1).Trim(); into[key] = Unquote(v); } } } } private static string Unquote(string v) { if (v.Length >= 2 && v[0] == '"' && v[v.Length - 1] == '"') { StringBuilder stringBuilder = new StringBuilder(); for (int i = 1; i < v.Length - 1; i++) { char c = v[i]; if (c == '\\' && i + 1 < v.Length - 1) { char c2 = v[++i]; stringBuilder.Append(c2 switch { 't' => '\t', 'n' => '\n', _ => c2, }); } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } if (v.Length >= 2 && v[0] == '\'' && v[v.Length - 1] == '\'') { return v.Substring(1, v.Length - 2).Replace("''", "'"); } return v; } public static bool Has(string key) { Ensure(); if (current == null || !current.ContainsKey(key)) { return english.ContainsKey(key); } return true; } public static string T(string key) { Ensure(); if (current != null && current.TryGetValue(key, out var value)) { return value; } if (english.TryGetValue(key, out value)) { return value; } return key; } public static string F(string key, params object[] args) { string text = T(key); try { return string.Format(text, args); } catch (FormatException) { return text; } } } public interface ISynced { string Key { get; } string Serialize(); void SetServer(string value); void ClearServer(); bool ApplyLocal(string value); } public class SyncedEntry<T> : ISynced { private readonly ConfigEntry<T> local; private readonly string key; private bool hasServer; private T server; public T Value { get { if (!hasServer || !Net.IsRemoteClient) { return local.Value; } return server; } } public string Key => key; public SyncedEntry(ConfigFile cfg, string section, string name, T defaultValue, string description) { local = cfg.Bind<T>(section, name, defaultValue, description + " [synced from the server]"); key = section + "." + name; Net.All.Add(this); local.SettingChanged += delegate { Net.PushToServer(key, Serialize()); }; } public string Serialize() { return Convert.ToString(local.Value, CultureInfo.InvariantCulture); } public void SetServer(string value) { try { server = (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); hasServer = true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Bad synced value for " + key + ": " + ex.Message)); } } public void ClearServer() { hasServer = false; } public bool ApplyLocal(string value) { try { T val = (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); if (!object.Equals(local.Value, val)) { local.Value = val; } return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Bad value for " + key + ": " + ex.Message)); return false; } } } public static class Net { public static readonly List<ISynced> All = new List<ISynced>(); public static ConfigEntry<bool> LockConfiguration; public static bool ServerLocked; public static bool IsRemoteClient { get { if ((Object)(object)ZNet.instance != (Object)null) { return !ZNet.instance.IsServer(); } return false; } } public static void Hook(ConfigFile cfg) { cfg.SettingChanged += delegate { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { BroadcastConfig(); } }; } public static void BroadcastConfig() { try { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.IsReady()) { SendConfig(peer.m_rpc); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not broadcast settings: " + ex.Message)); } } public static void PushToServer(string key, string value) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown try { if (IsRemoteClient && ServerLocked && !((Object)(object)ZNet.instance == (Object)null)) { ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC == null) { Plugin.Log.LogWarning((object)("No server connection to send " + key)); return; } Plugin.Log.LogInfo((object)("Sending " + key + " = " + value + " to the server (applied only if you are an admin)")); ZPackage val = new ZPackage(); val.Write(key); val.Write(value); serverRPC.Invoke("VR_SetConfig", new object[1] { val }); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not send setting to the server: " + ex.Message)); } } public static void RPC_SetConfig(ZRpc rpc, ZPackage pkg) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } string hostName = rpc.GetSocket().GetHostName(); if (!ZNet.instance.IsAdmin(hostName)) { Plugin.Log.LogWarning((object)("Rejected setting change " + text + " from non-admin " + hostName)); return; } foreach (ISynced item in All) { if (item.Key == text) { if (item.ApplyLocal(text2)) { Plugin.Log.LogInfo((object)("Admin " + hostName + " changed " + text + " = " + text2)); } break; } } } public static void ClearServer() { foreach (ISynced item in All) { item.ClearServer(); } } public static void SendConfig(ZRpc rpc) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown ZPackage val = new ZPackage(); bool flag = LockConfiguration == null || LockConfiguration.Value; val.Write(flag); val.Write(flag ? All.Count : 0); if (flag) { foreach (ISynced item in All) { val.Write(item.Key); val.Write(item.Serialize()); } } rpc.Invoke("VR_Config", new object[1] { val }); ZPackage val2 = new ZPackage(); val2.Write(flag ? NodeConfig.ServerText() : ""); rpc.Invoke("VR_Nodes", new object[1] { val2 }); } public static void RPC_Nodes(ZRpc rpc, ZPackage pkg) { NodeConfig.SetServer(pkg.ReadString()); } public static void RPC_Config(ZRpc rpc, ZPackage pkg) { bool num = (ServerLocked = pkg.ReadBool()); int num2 = pkg.ReadInt(); if (!num) { ClearServer(); Talents.Dirty = true; Plugin.Log.LogInfo((object)"Server does not lock the configuration; using local settings."); return; } Dictionary<string, string> dictionary = new Dictionary<string, string>(); for (int i = 0; i < num2; i++) { dictionary[pkg.ReadString()] = pkg.ReadString(); } foreach (ISynced item in All) { if (dictionary.TryGetValue(item.Key, out var value)) { item.SetServer(value); } } Talents.Dirty = true; Plugin.Log.LogInfo((object)("Server settings received (" + dictionary.Count + " values).")); } public static void RPC_KillXp(long sender, float maxHealth, bool boss, Vector3 pos) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) KillRewards.Grant(maxHealth, boss, pos); } } public static class KillRewards { public static void Grant(float maxHealth, bool boss, Vector3 pos) { //IL_006e: 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_0079: 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_0099: Unknown result type (might be due to invalid IL or missing references) RpgData data = Plugin.Data; Player localPlayer = Player.m_localPlayer; if (data != null && !((Object)(object)localPlayer == (Object)null) && data.Class != RpgClass.None) { float num = maxHealth * 0.5f * Plugin.XpMultiplier.Value * (1f + Stats.Get(Eff.Experience)); if (boss) { num *= 3f; } int num2 = Mathf.Max(1, Mathf.RoundToInt(num)); data.AddXp(num2); if (Plugin.ShowXpFloater.Value) { Floaters.Add(pos + Vector3.up * 1.2f, "+" + num2 + " XP", Floaters.XpColor, 15, 1.6f); } float num3 = Stats.Get(Eff.KillHeal); if (num3 > 0f) { ((Character)localPlayer).Heal(((Character)localPlayer).GetMaxHealth() * num3, true); } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] internal static class NetConnect { private static void Postfix(ZNetPeer peer) { peer.m_rpc.Register<ZPackage>("VR_Config", (Action<ZRpc, ZPackage>)Net.RPC_Config); peer.m_rpc.Register<ZPackage>("VR_Nodes", (Action<ZRpc, ZPackage>)Net.RPC_Nodes); peer.m_rpc.Register<ZPackage>("VR_SetConfig", (Action<ZRpc, ZPackage>)Net.RPC_SetConfig); if (Net.IsRemoteClient) { Net.ClearServer(); NodeConfig.ClearServer(); Net.ServerLocked = false; } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal static class NetPeerInfo { private static void Postfix(ZNet __instance, ZRpc rpc) { if (!__instance.IsServer()) { return; } try { Net.SendConfig(rpc); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not send settings to a peer: " + ex.Message)); } } } [HarmonyPatch(typeof(Game), "Start")] internal static class NetGameStart { private static void Postfix() { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register<float, bool, Vector3>("VR_KillXp", (Action<long, float, bool, Vector3>)Net.RPC_KillXp); } } } public static class NodeConfig { private static readonly Dictionary<string, float> defaults = new Dictionary<string, float>(); private static Dictionary<string, float> local = new Dictionary<string, float>(); private static Dictionary<string, float> server = new Dictionary<string, float>(); private static bool hasServer; private static DateTime stamp; private static float nextCheck; public static string FilePath => Path.Combine(Paths.ConfigPath, Path.Combine("ValheimReforged", "Nodes.yml")); private static string SkillKey(Ability a, string field) { return "skill." + a.Id + "." + field; } private static string TalentKey(TNode n, string field) { return "talent." + n.Id + "." + field; } private static string PerkKey(TalentPerk p) { return "perk." + p; } public static void Init() { try { foreach (Ability item in Abilities.All) { defaults[SkillKey(item, "maxRank")] = item.MaxRank; defaults[SkillKey(item, "cost")] = item.Cost; if (item.Active) { defaults[SkillKey(item, "cooldown")] = item.Cooldown; defaults[SkillKey(item, "power")] = item.Power; } else { defaults[SkillKey(item, "value")] = item.PerRank; } } foreach (TNode item2 in Talents.All) { if (!item2.IsOrigin) { defaults[TalentKey(item2, "cost")] = item2.Cost; defaults[TalentKey(item2, "maxRank")] = item2.MaxRank; defaults[TalentKey(item2, "power")] = item2.Power; } } foreach (TalentPerk value in Enum.GetValues(typeof(TalentPerk))) { if (value != TalentPerk.None) { defaults[PerkKey(value)] = Talents.PerkValue(value); } } LoadLocal(); WriteFile(); Apply(local); Plugin.Log.LogInfo((object)("Node settings: " + FilePath + " (" + defaults.Count + " values)")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("NodeConfig.Init: " + ex.Message)); } } private static Dictionary<string, float> Parse(string text) { Dictionary<string, float> dictionary = new Dictionary<string, float>(); string[] array = text.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(new char[1] { '\r' }).Trim(); if (text2.Length != 0 && text2[0] != '#') { int num = text2.IndexOf(':'); if (num > 0 && float.TryParse(text2.Substring(num + 1).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { dictionary[text2.Substring(0, num).Trim()] = result; } } } return dictionary; } private static void LoadLocal() { local = new Dictionary<string, float>(); if (File.Exists(FilePath)) { local = Parse(File.ReadAllText(FilePath, Encoding.UTF8)); stamp = File.GetLastWriteTimeUtc(FilePath); } } private static string Num(float v) { return v.ToString("0.#####", CultureInfo.InvariantCulture); } private static void WriteFile() { StringBuilder sb = new StringBuilder(); Action<string> line = delegate(string s) { sb.Append(s).Append('\n'); }; Action<string> action = delegate(string k) { if (!local.TryGetValue(k, out var value)) { value = defaults[k]; } line(k + ": " + Num(value)); }; line("# Valheim Reforged - values of every node. Edit the numbers and save; the game applies them while running."); line("# With LockConfiguration = true on the server, the server's file overrides every client."); line("#"); line("# skill.<id>.maxRank / cost ranks the node can have / skill points per rank"); line("# skill.<id>.value passive: bonus per rank (0.02 = 2%; flat amount for health/stamina/eitr)"); line("# skill.<id>.cooldown / power active: cooldown in seconds / damage and effect multiplier"); line("# talent.<id>.cost / maxRank / power talent points per rank / ranks / multiplier of the node's bonuses"); line("# perk.<name> strength per rank of each branch keystone effect"); line(""); RpgClass[] array = new RpgClass[4] { RpgClass.Warrior, RpgClass.Archer, RpgClass.Mage, RpgClass.Rogue }; for (int num = 0; num < array.Length; num++) { RpgClass c = array[num]; line("# ===== " + c.ToString() + " skills ====="); foreach (Ability item in Abilities.For(c)) { line("# " + item.Id + (item.Active ? " (active)" : " (passive)")); action(SkillKey(item, "maxRank")); action(SkillKey(item, "cost")); if (item.Active) { action(SkillKey(item, "cooldown")); action(SkillKey(item, "power")); } else { action(SkillKey(item, "value")); } } line(""); } line("# ===== keystone perks ====="); foreach (TalentPerk value2 in Enum.GetValues(typeof(TalentPerk))) { if (value2 != TalentPerk.None) { action(PerkKey(value2)); } } line(""); string text = null; foreach (TNode item2 in Talents.All) { if (!item2.IsOrigin) { if (item2.Group != text) { text = item2.Group; line(""); line("# ===== talents: " + text + " ====="); } line("# " + item2.Id + " (" + item2.Tier.ToString() + ")"); action(TalentKey(item2, "cost")); action(TalentKey(item2, "maxRank")); action(TalentKey(item2, "power")); } } Directory.CreateDirectory(Path.GetDirectoryName(FilePath)); string text2 = sb.ToString(); if (!File.Exists(FilePath) || File.ReadAllText(FilePath, Encoding.UTF8).Replace("\r\n", "\n") != text2) { File.WriteAllText(FilePath, text2.Replace("\n", "\r\n"), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); } stamp = File.GetLastWriteTimeUtc(FilePath); } private static void Apply(Dictionary<string, float> map) { Func<string, float> func = delegate(string k) { if (map.TryGetValue(k, out var value)) { return value; } return (!defaults.TryGetValue(k, out value)) ? 0f : value; }; foreach (Ability item in Abilities.All) { item.MaxRank = Math.Max(1, (int)Math.Round(func(SkillKey(item, "maxRank")))); item.Cost = Math.Max(1, (int)Math.Round(func(SkillKey(item, "cost")))); if (item.Active) { item.Cooldown = Math.Max(0f, func(SkillKey(item, "cooldown"))); item.Power = Math.Max(0f, func(SkillKey(item, "power"))); } else { item.PerRank = func(SkillKey(item, "value")); } } foreach (TNode item2 in Talents.All) { if (!item2.IsOrigin) { item2.CostOverride = Math.Max(1, (int)Math.Round(func(TalentKey(item2, "cost")))); item2.MaxRankOverride = Math.Max(1, (int)Math.Round(func(TalentKey(item2, "maxRank")))); item2.Power = Math.Max(0f, func(TalentKey(item2, "power"))); } } foreach (TalentPerk value2 in Enum.GetValues(typeof(TalentPerk))) { if (value2 != TalentPerk.None) { Talents.PerkOver[value2] = func(PerkKey(value2)); } } Talents.Dirty = true; } public static void Tick() { if (Time.realtimeSinceStartup < nextCheck) { return; } nextCheck = Time.realtimeSinceStartup + 2f; try { if (!File.Exists(FilePath)) { WriteFile(); } else if (!(File.GetLastWriteTimeUtc(FilePath) == stamp)) { LoadLocal(); if (!hasServer || !Net.IsRemoteClient) { Apply(local); } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { Net.BroadcastConfig(); } Plugin.Log.LogInfo((object)"Node settings reloaded."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("NodeConfig.Tick: " + ex.Message)); } } public static string ServerText() { try { return File.Exists(FilePath) ? File.ReadAllText(FilePath, Encoding.UTF8) : ""; } catch { return ""; } } public static void SetServer(string text) { if (string.IsNullOrEmpty(text)) { ClearServer(); return; } server = Parse(text); hasServer = true; Apply(server); Plugin.Log.LogInfo((object)("Node settings received from the server (" + server.Count + " values).")); } public static void ClearServer() { if (hasServer) { hasServer = false; Apply(local); } } } [BepInPlugin("com.yeyian.valheimreforged", "Reforged Classes", "0.1.0")] public class Plugin : BaseUnityPlugin { public static ManualLogSource Log; public static RpgData Data; public static SyncedEntry<int> MaxLevel; public static SyncedEntry<int> PointsPerLevel; public static SyncedEntry<int> TalentPointsPerLevel; public static SyncedEntry<int> TalentPointsPerBoss; public static SyncedEntry<int> TranscendMax; public static SyncedEntry<int> TalentPointsPerTranscend; public static SyncedEntry<float> XpMultiplier; public static SyncedEntry<float> XpBase; public static SyncedEntry<float> XpExponent; public static SyncedEntry<float> TalentPower; public static SyncedEntry<float> AbilityDamage; public static SyncedEntry<bool> DisableVanillaSkillGain; public static SyncedEntry<bool> AllowRefund; public static ConfigEntry<bool> ShowXpFloater; public static ConfigEntry<bool> ShowCombatText; public static ConfigEntry<KeyCode> MenuKey; public static ConfigEntry<float> HudX; public static ConfigEntry<float> HudY; public static ConfigEntry<KeyCode>[] AbilityKeys = new ConfigEntry<KeyCode>[2]; private float regenTimer; private void Awake() { //IL_0301: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Net.LockConfiguration = ((BaseUnityPlugin)this).Config.Bind<bool>("Servidor", "LockConfiguration", true, "Server only. When true, this server's settings override the clients' (sent on connect and whenever this file changes). When false, every client uses its own settings."); Net.Hook(((BaseUnityPlugin)this).Config); MaxLevel = new SyncedEntry<int>(((BaseUnityPlugin)this).Config, "Progressão", "MaxLevel", 50, "Maximum character level"); PointsPerLevel = new SyncedEntry<int>(((BaseUnityPlugin)this).Config, "Progressão", "PointsPerLevel", 1, "Skill points per level"); TalentPointsPerLevel = new SyncedEntry<int>(((BaseUnityPlugin)this).Config, "Progressão", "TalentPointsPerLevel", 1, "Talent tree points per level"); TalentPointsPerBoss = new SyncedEntry<int>(((BaseUnityPlugin)this).Config, "Progressão", "TalentPointsPerBoss", 3, "Talent tree points per defeated boss"); TranscendMax = new SyncedEntry<int>(((BaseUnityPlugin)this).Config, "Transcendência", "MaxTranscendences", 10, "How many times a character can transcend at the maximum level (0 disables transcendence)"); TalentPointsPerTranscend = new SyncedEntry<int>(((BaseUnityPlugin)this).Config, "Transcendência", "TalentPointsPerTranscendence", 10, "Extra talent points granted for each transcendence"); XpMultiplier = new SyncedEntry<float>(((BaseUnityPlugin)this).Config, "Progressão", "XpMultiplier", 1f, "XP multiplier per kill"); XpBase = new SyncedEntry<float>(((BaseUnityPlugin)this).Config, "Progressão", "XpBase", 90f, "XP needed to go from level 1 to 2; each next level needs XpBase * level ^ XpExponent"); XpExponent = new SyncedEntry<float>(((BaseUnityPlugin)this).Config, "Progressão", "XpExponent", 1.3f, "How fast the XP needed per level grows"); TalentPower = new SyncedEntry<float>(((BaseUnityPlugin)this).Config, "Equilíbrio", "TalentPower", 1f, "Multiplier applied to every bonus of the Talents tab"); AbilityDamage = new SyncedEntry<float>(((BaseUnityPlugin)this).Config, "Equilíbrio", "AbilityDamage", 1f, "Multiplier applied to the damage of active abilities"); DisableVanillaSkillGain = new SyncedEntry<bool>(((BaseUnityPlugin)this).Config, "Progressão", "DisableVanillaSkillGain", defaultValue: true, "Disables skill gain by use; skills scale with the character level instead"); AllowRefund = new SyncedEntry<bool>(((BaseUnityPlugin)this).Config, "Progressão", "AllowRefund", defaultValue: true, "Allows refunding talent points with Ctrl+click (free)"); ShowXpFloater = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "ShowXpFloater", true, "Shows gained XP as floating text over the slain monster"); ShowCombatText = ((BaseUnityPlugin)this).Config.Bind<bool>("Interface", "ShowCombatText", true, "Shows floating text for critical hits, execute and opening bonuses"); HudX = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "HudX", 250f, "Distance in pixels (at 1080p) from the left edge of the screen to the level medallion; the ability slots follow it"); HudY = ((BaseUnityPlugin)this).Config.Bind<float>("Interface", "HudY", 116f, "Distance in pixels (at 1080p) from the bottom edge of the screen to the top of the ability slots"); MenuKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Teclas", "Menu", (KeyCode)107, "Opens the RPG panel"); KeyCode[] array = (KeyCode[])(object)new KeyCode[2] { (KeyCode)118, (KeyCode)98 }; for (int i = 0; i < 2; i++) { AbilityKeys[i] = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Teclas", "Ability" + (i + 1), array[i], "Skill bar active ability, slot " + (i + 1)); } NodeConfig.Init(); new Harmony("com.yeyian.valheimreforged").PatchAll(); } public static void LevelBonus(RpgClass c, int level, out float hp, out float stamina, out float eitr) { int num = level - 1; hp = (stamina = (eitr = 0f)); switch (c) { case RpgClass.Warrior: hp = (float)num * 4f; stamina = (float)num * 1f; break; case RpgClass.Archer: hp = (float)num * 2f; stamina = (float)num * 1.5f; break; case RpgClass.Mage: hp = (float)num * 1f; eitr = (float)num * 3f; break; case RpgClass.Rogue: hp = (float)num * 2f; stamina = (float)num * 2f; break; } } private void Update() { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) NodeConfig.Tick(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { Data = null; return; } if (Data == null) { Data = RpgData.Load(localPlayer); } regenTimer += Time.deltaTime; if (regenTimer >= 1f && Data.Class != RpgClass.None) { regenTimer = 0f; float num = Stats.Get(Eff.HealthRegen); float num2 = Stats.Get(Eff.StaminaRegen); float num3 = Stats.Get(Eff.EitrRegen); if (num > 0f && ((Character)localPlayer).GetHealth() < ((Character)localPlayer).GetMaxHealth()) { ((Character)localPlayer).Heal(num, false); } if (num2 > 0f) { ((Character)localPlayer).AddStamina(num2); } if (num3 > 0f) { ((Character)localPlayer).AddEitr(num3); } } if (Ui.Open && Input.GetKeyDown((KeyCode)27)) { Ui.Open = false; } else if (Input.GetKeyDown(MenuKey.Value) && !Ui.BlockedByOtherUi()) { Ui.Open = !Ui.Open; } if (Ui.Open || Ui.BlockedByOtherUi() || Data.Class == RpgClass.None) { return; } for (int i = 0; i < 2; i++) { if (Input.GetKeyDown(AbilityKeys[i].Value)) { Abilities.Cast(localPlayer, Data, i); } } } private void FixedUpdate() { if ((Object)(object)Player.m_localPlayer != (Object)null) { Dash.Step(Player.m_localPlayer); } } private void OnGUI() { Ui.Draw(); } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class KillXp { private static void Postfix(Character __instance) { //IL_004a: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) if (__instance is Player) { return; } HitData value = Traverse.Create((object)__instance).Field("m_lastHit").GetValue<HitData>(); Player val = (Player)((value != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val == (Object)null) { return; } float maxHealth = __instance.GetMaxHealth(); bool flag = __instance.IsBoss(); Vector3 centerPoint = __instance.GetCenterPoint(); if ((Object)(object)val == (Object)(object)Player.m_localPlayer) { KillRewards.Grant(maxHealth, flag, centerPoint); return; } ZNetView component = ((Component)val).GetComponent<ZNetView>(); if (ZRoutedRpc.instance != null && (Object)(object)component != (Object)null && component.GetZDO() != null) { ZRoutedRpc.instance.InvokeRoutedRPC(component.GetZDO().GetOwner(), "VR_KillXp", new object[3] { maxHealth, flag, centerPoint }); } } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] internal static class NoSkillGain { internal static bool Prefix() { return !Plugin.DisableVanillaSkillGain.Value; } } [HarmonyPatch(typeof(Skill), "Raise")] internal static class NoSkillRaise { private static bool Prefix(ref bool __result) { if (!NoSkillGain.Prefix()) { __result = false; return false; } return true; } } [HarmonyPatch(typeof(Skills), "GetSkillLevel")] internal static class SkillFollowsLevel { internal static bool Primary(RpgClass c, SkillType t) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 switch (c) { case RpgClass.Warrior: if ((int)t != 1 && (int)t != 7 && (int)t != 3 && (int)t != 4) { return (int)t == 6; } return true; case RpgClass.Archer: if ((int)t != 8 && (int)t != 14 && (int)t != 5) { return (int)t == 100; } return true; case RpgClass.Mage: if ((int)t != 9) { return (int)t == 10; } return true; case RpgClass.Rogue: if ((int)t != 2 && (int)t != 101 && (int)t != 11) { return (int)t == 102; } return true; default: return false; } } private static void Postfix(Skills __instance, SkillType skillType, ref float __result) { //IL_0056: 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_0085: Invalid comparison between Unknown and I4 RpgData data = Plugin.Data; Player localPlayer = Player.m_localPlayer; if (data != null && !((Object)(object)localPlayer == (Object)null) && data.Class != RpgClass.None && Plugin.DisableVanillaSkillGain.Value && !((Object)(object)((Character)localPlayer).GetSkills() != (Object)(object)__instance)) { float num = (float)data.Level / (float)Plugin.MaxLevel.Value; float num2 = (Primary(data.Class, skillType) ? 1f : 0.5f); float num3 = Mathf.Max(__result, Mathf.Floor(num * num2 * 100f)); if ((int)skillType == 104) { num3 += Stats.Get(Eff.FishingSkill); } __result = num3; } } } [HarmonyPatch(typeof(SkillsDialog), "Setup")] internal static class SkillsDialogNote { private static Transform Deep(Transform t, string name) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown foreach (Transform item in t) { Transform val = item; if (((Object)val).name == name) { return val; } Transform val2 = Deep(val, name); if ((Object)(object)val2 != (Object)null) { return val2; } } return null; } private static void Postfix(SkillsDialog __instance) { try { if (Plugin.Data == null || Plugin.Data.Class == RpgClass.None || !Plugin.DisableVanillaSkillGain.Value) { return; } IList value = Traverse.Create((object)__instance).Field("m_elements").GetValue<IList>(); if (value != null) { foreach (object item in value) { GameObject val = (GameObject)((item is GameObject) ? item : null); Transform val2 = (((Object)(object)val != (Object)null) ? Deep(val.transform, "currentlevel") : null); if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(false); } } } object value2 = Traverse.Create((object)__instance).Field("m_totalSkillText").GetValue<object>(); if (value2 != null) { PropertyInfo property = value2.GetType().GetProperty("text"); if (property != null) { property.SetValue(value2, L.T("skills.dialog_note"), null); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("SkillsDialogNote: " + ex.Message)); } } } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] internal static class ClassStats { private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr) { RpgData data = Plugin.Data; if (data != null && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && data.Class != RpgClass.None) { Plugin.LevelBonus(data.Class, data.Level, out var hp2, out var stamina2, out var eitr2); hp += hp2; stamina += stamina2; eitr += eitr2; hp += Stats.Get(Eff.MaxHealth); stamina += Stats.Get(Eff.MaxStamina); eitr += Stats.Get(Eff.MaxEitr); } } } [HarmonyPatch(typeof(Player), "GetEquipmentMovementModifier")] internal static class RogueSpeed { private static void Postfix(Player __instance, ref float __result) { if (Plugin.Data != null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { __result += Stats.Get(Eff.MoveSpeed); } } } [HarmonyPatch(typeof(Character), "Damage")] internal static class DamageModifiers { private static void Prefix(Character __instance, HitData hit) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_008b: Expected I4, but got Unknown //IL_00b1: 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_00d0: Invalid comparison between Unknown and I4 //IL_0106: 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_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0118: 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) //IL_014f: 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_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: 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_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: 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_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0243: 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_0270: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || Plugin.Data == null || Plugin.Data.Class == RpgClass.None || hit == null || (Object)(object)hit.GetAttacker() != (Object)(object)localPlayer) { return; } float num = 1f + Stats.Get(Eff.Damage); SkillType skill = hit.m_skill; switch (skill - 1) { case 0: case 1: case 2: case 3: case 4: case 6: case 10: num += Stats.Get(Eff.MeleeDamage); break; case 7: case 13: num += Stats.Get(Eff.RangedDamage); break; case 8: case 9: num += Stats.Get(Eff.MagicDamage); break; } num += TalentPerks.WeaponBonus(hit.m_skill); num *= TalentPerks.DamageMultiplier(localPlayer, __instance, hit); if ((int)hit.m_skill == 10) { int num2 = Talents.PerkRank(TalentPerk.Siphon); if (num2 > 0) { ((Character)localPlayer).AddEitr(Talents.PerkValue(TalentPerk.Siphon) * (float)num2); } } float healthPercentage = __instance.GetHealthPercentage(); float num3 = Stats.Get(Eff.Execute); float num4 = Stats.Get(Eff.Opening); Vector3 val = ((hit.m_point != Vector3.zero) ? hit.m_point : __instance.GetCenterPoint()); int instanceID = ((Object)__instance).GetInstanceID(); if (healthPercentage < 0.3f && num3 > 0f) { num += num3; if (Plugin.ShowCombatText.Value) { Floaters.Add(val + Vector3.up * 0.5f, L.T("combat.execute"), Floaters.ExecuteColor, 16, 1f, "ex" + instanceID, 0.8f); } } if (healthPercentage > 0.8f && num4 > 0f) { num += num4; if (Plugin.ShowCombatText.Value) { Floaters.Add(val + Vector3.up * 0.5f, L.T("combat.opening"), Floaters.OpeningColor, 16, 1f, "op" + instanceID, 0.8f); } } bool num5 = Random.value < Stats.Get(Eff.Crit); if (num5) { num *= 1.75f; } if (num != 1f) { ((DamageTypes)(ref hit.m_damage)).Modify(num); } if (num5 && Plugin.ShowCombatText.Value) { Floaters.Add(val + Vector3.up * 0.2f, L.F("combat.crit", Mathf.RoundToInt(hit.GetTotalDamage())), Floaters.CritColor, 22); } float num6 = Stats.Get(Eff.LifeSteal); if (num6 > 0f) { ((Character)localPlayer).Heal(Mathf.Min(hit.GetTotalDamage() * num6, ((Character)localPlayer).GetMaxHealth() * 0.25f), false); } } } [HarmonyPatch(typeof(Player), "UseStamina")] internal static class StaminaCostPatch { private static void Prefix(Player __instance, ref float __0) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Plugin.Data != null) { float num = Mathf.Min(0.6f, Stats.Get(Eff.StaminaCost)); if (num > 0f) { __0 *= 1f - num; } int num2 = (((Character)__instance).IsSwimming() ? Talents.PerkRank(TalentPerk.Swimmer) : 0); if (num2 > 0) { __0 *= Mathf.Max(0.1f, 1f - Talents.PerkValue(TalentPerk.Swimmer) * (float)num2 / 100f); } } } } [HarmonyPatch(typeof(Skills), "LowerAllSkills")] internal static class NoSkillLossOnDeath { private static bool Prefix() { return !Plugin.DisableVanillaSkillGain.Value; } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class VictimModifiers { private static void Prefix(Character __instance, HitData hit) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)__instance != (Object)(object)localPlayer) && !((Object)(object)localPlayer == (Object)null) && Plugin.Data != null && Plugin.Data.Class != RpgClass.None && hit != null) { float num = Mathf.Min(0.8f, Stats.Get(Eff.Resist)); if (num > 0f) { ((DamageTypes)(ref hit.m_damage)).Modify(1f - num); } float num2 = Mathf.Min(0.75f, Stats.Get(Eff.ElementalResist)); if (num2 > 0f) { hit.m_damage.m_fire *= 1f - num2; hit.m_damage.m_frost *= 1f - num2; hit.m_damage.m_lightning *= 1f - num2; hit.m_damage.m_poison *= 1f - num2; hit.m_damage.m_spirit *= 1f - num2; } } } } public enum RpgClass { None, Warrior, Archer, Mage, Rogue } public class RpgData { private const string Key = "VR_Data"; public int Level = 1; public long Xp; public RpgClass Class; public int Points; public int Trans; public readonly Dictionary<string, int> Ranks = new Dictionary<string, int>(); public readonly Dictionary<string, int> TalentRanks = new Dictionary<string, int>(); public readonly string[] Slots = new string[2]; public int TalentBank => Trans * Plugin.TalentPointsPerTranscend.Value; public bool CanTranscend { get { if (Class != RpgClass.None && Level >= Plugin.MaxLevel.Value) { return Trans < Plugin.TranscendMax.Value; } return false; } } public static long XpForNext(int level) { return (long)((double)Plugin.XpBase.Value * Math.Pow(level, Plugin.XpExponent.Value)); } public int TalentRank(string id) { if (!TalentRanks.TryGetValue(id, out var value)) { return 0; } return value; } public string BuyTalent(TNode n) { if (n.IsOrigin) { return null; } if (TalentRank(n.Id) >= n.MaxRank) { return L.T("ui.rank_max"); } if (TalentRank(n.Id) == 0 && !Talents.IsAvailable(n, this)) { return L.T("msg.talent_neighbor"); } if (Talents.Available(this) < n.Cost) { return L.F("msg.talent_points_needed", n.Cost); } TalentRanks[n.Id] = TalentRank(n.Id) + 1; Talents.Dirty = true; Save(); return null; } public string RefundTalent(TNode n) { if (TalentRank(n.Id) == 0) { return L.T("msg.nothing_refund"); } if (!Talents.CanRefund(n, this)) { return L.T("msg.would_disconnect"); } int num = TalentRank(n.Id); if (num == 1) { TalentRanks.Remove(n.Id); } else { TalentRanks[n.Id] = num - 1; } Talents.Dirty = true; Save(); return null; } public void RespecTalents() { TalentRanks.Clear(); Talents.Dirty = true; Save(); } public int Rank(string id) { if (!Ranks.TryGetValue(id, out var value)) { return 0; } return value; } public void AddXp(long amount) { //IL_0105: Unknown result type (might be due to invalid IL or missing references) if (Class != RpgClass.None && amount > 0) { Xp += amount; int level = Level; int points = Points; while (Level < Plugin.MaxLevel.Value && Xp >= XpForNext(Level)) { Xp -= XpForNext(Level); Level++; Points += Plugin.PointsPerLevel.Value; } if (Level > level && (Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)2, L.F("msg.level_up", Level, Points - points, (Level - level) * Plugin.TalentPointsPerLevel.Value), 0, (Sprite)null, false); Vfx.LevelUp(((Component)Player.m_localPlayer).transform.position); } if (Level >= Plugin.MaxLevel.Value) { Xp = 0L; } Save(); } } public void Transcend() { //IL_015b: Unknown result type (might be due to invalid IL or missing references) if (!CanTranscend) { Plugin.Log.LogInfo((object)("Transcend refused: class=" + Class.ToString() + " level=" + Level + "/" + Plugin.MaxLevel.Value + " transcendences=" + Trans + "/" + Plugin.TranscendMax.Value)); return; } Trans++; Level = 1; Xp = 0L; Points = 0; Ranks.Clear(); TalentRanks.Clear(); for (int i = 0; i < Slots.Length; i++) { Slots[i] = null; } Buffs.Clear(); Talents.Dirty = true; Save(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, L.F("msg.transcended", Trans, Plugin.TalentPointsPerTranscend.Value), 0, (Sprite)null, false); Vfx.LevelUp(((Component)localPlayer).transform.position); } } public void ChooseClass(RpgClass c) { if (Class == RpgClass.None) { Class = c; Save(); } } public bool Available(Ability a) { if (a.Parent != null) { return Rank(a.Parent) > 0; } return true; } public bool Spend(Ability a) { if (Points < a.Cost || Rank(a.Id) >= a.MaxRank || !Available(a)) { return false; } Points -= a.Cost; Ranks[a.Id] = Rank(a.Id) + 1; if (a.Active) { AutoEquip(a.Id); } Save(); return true; } public void SpendAll(Ability a) { while (Spend(a)) { } } public string Refund(Ability a) { int num = Rank(a.Id); if