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 Rist v1.0.0
plugins/Rist/Rist.dll
Decompiled 7 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Ezomic.Core; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Thijssen Software")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")] [assembly: AssemblyDescription("A character level beside the skills, paid out in cards you draft and deepen.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+0c1a85d6e370184385c49006a5a6fd9ea9011ae7")] [assembly: AssemblyProduct("Rist")] [assembly: AssemblyTitle("Rist")] [assembly: AssemblyVersion("1.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 Rist { internal static class AttackSpeed { internal const string Melee = "*attackspeed:melee"; internal const string Tools = "*attackspeed:tools"; internal const string Ranged = "*attackspeed:ranged"; private static string CategoryOf(ItemData weapon) { //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_0019: 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_0059: Expected I4, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Invalid comparison between Unknown and I4 if (weapon == null || weapon.m_shared == null) { return null; } SkillType skillType = weapon.m_shared.m_skillType; switch (skillType - 1) { case 7: case 13: return "*attackspeed:ranged"; case 6: case 11: case 12: return "*attackspeed:tools"; case 0: case 1: case 2: case 3: case 4: case 10: return "*attackspeed:melee"; default: if ((int)weapon.m_shared.m_itemType != 19) { return null; } return "*attackspeed:tools"; } } [HarmonyPatch(typeof(Attack), "Start")] [HarmonyPostfix] private static void Started(bool __result, Humanoid character, CharacterAnimEvent animEvent, ItemData weapon) { if (!__result || !RistConfig.Enabled.Value || (Object)(object)character == (Object)null || (Object)(object)animEvent == (Object)null || (object)character != Player.m_localPlayer) { return; } string text = CategoryOf(weapon); if (text != null) { float num = Effects.TotalFor(text); if (!(num <= 0f)) { num = Mathf.Min(num, Mathf.Max(0f, RistConfig.AttackSpeedMax.Value)); animEvent.Speed(1f + num); } } } } internal sealed class Card { internal string Id; internal string Name; internal string Flavour; internal string Effect; internal float PerRank; internal string BonusEffect = ""; internal float BonusPerRank; internal string Sigil = ""; internal FieldInfo Field; internal FieldInfo BonusField; private static readonly HashSet<string> OneBased = new HashSet<string> { "m_healthRegenMultiplier", "m_staminaRegenMultiplier", "m_eitrRegenMultiplier", "m_damageModifier" }; private static readonly Dictionary<string, string> Labels = new Dictionary<string, string> { { "m_addMaxCarryWeight", "carry weight" }, { "m_addArmor", "armour" }, { "*inventoryrow", "inventory row" }, { "m_runStaminaUseModifier", "run stamina" }, { "m_attackStaminaUseModifier", "attack stamina" }, { "m_blockStaminaUseModifier", "block stamina" }, { "m_swimStaminaUseModifier", "swim stamina" }, { "m_jumpStaminaUseModifier", "jump stamina" }, { "m_sneakStaminaUseModifier", "sneak stamina" }, { "m_staminaRegenMultiplier", "stamina regen" }, { "m_healthRegenMultiplier", "health regen" }, { "m_eitrRegenMultiplier", "eitr regen" }, { "m_fallDamageModifier", "fall damage" }, { "m_stealthModifier", "stealth" }, { "m_noiseModifier", "noise" }, { "m_staggerModifier", "stagger taken" }, { "m_raiseSkillModifier", "skill gain" }, { "m_speedModifier", "movement speed" }, { "m_damageModifier", "damage" }, { "m_dodgeStaminaUseModifier", "dodge stamina" }, { "m_swimSpeedModifier", "swim speed" }, { "m_timedBlockBonus", "parry bonus" }, { "*stamina:move", "movement stamina" }, { "*stamina:fight", "combat stamina" }, { "*attackspeed:melee", "melee speed" }, { "*attackspeed:tools", "tool speed" }, { "*attackspeed:ranged", "draw speed" } }; private static readonly HashSet<string> Percent = new HashSet<string> { "m_runStaminaUseModifier", "m_attackStaminaUseModifier", "m_blockStaminaUseModifier", "m_swimStaminaUseModifier", "m_jumpStaminaUseModifier", "m_sneakStaminaUseModifier", "m_staminaRegenMultiplier", "m_healthRegenMultiplier", "m_eitrRegenMultiplier", "m_fallDamageModifier", "m_stealthModifier", "m_noiseModifier", "m_staggerModifier", "m_raiseSkillModifier", "m_speedModifier", "m_damageModifier", "m_dodgeStaminaUseModifier", "m_swimSpeedModifier", "m_timedBlockBonus", "*attackspeed:melee", "*attackspeed:tools", "*attackspeed:ranged", "*stamina:move", "*stamina:fight" }; internal static readonly HashSet<string> Specials = new HashSet<string> { "*inventoryrow", "*attackspeed:melee", "*attackspeed:tools", "*attackspeed:ranged", "*stamina:move", "*stamina:fight" }; internal bool IsSpecial => IsSpecialEffect(Effect); internal bool HasBonus => BonusEffect.Length > 0; internal static bool IsSpecialEffect(string effect) { if (effect != null && effect.Length > 0) { return effect[0] == '*'; } return false; } internal static int BonusTimes(int rank) { int num = Mathf.Max(1, RistConfig.BonusEvery.Value); return rank / num; } internal string Describe(int rank) { return Format(Effect, PerRank * (float)Mathf.Max(1, rank)); } internal string DescribeBonus(int times) { return Format(BonusEffect, BonusPerRank * (float)Mathf.Max(1, times)); } private static string Format(string effect, float total) { if (!Labels.TryGetValue(effect, out var value)) { value = effect; } if (Percent.Contains(effect)) { float num = total * 100f; return ((num >= 0f) ? "+" : "−") + Mathf.Abs(num).ToString("0.#", CultureInfo.InvariantCulture) + "% " + value; } return ((total >= 0f) ? "+" : "−") + Mathf.Abs(total).ToString("0.#", CultureInfo.InvariantCulture) + " " + value; } internal static bool IsOneBased(string effect) { return OneBased.Contains(effect); } } internal static class Cards { private static readonly List<Card> _all = new List<Card>(); private static readonly Dictionary<string, Card> _byId = new Dictionary<string, Card>(); internal static IReadOnlyList<Card> All => _all; internal static Card Get(string id) { if (id == null || !_byId.TryGetValue(id, out var value)) { return null; } return value; } internal static void Load() { _all.Clear(); _byId.Clear(); string text = Path.Combine(Path.GetDirectoryName(typeof(Cards).Assembly.Location) ?? ".", "cards.txt"); if (!File.Exists(text)) { RistPlugin.Log.LogError((object)("cards.txt not found beside the DLL at " + text + " - no cards can be taken.")); return; } if (RistPlugin.CorePresent) { DeclareCatalogue(File.ReadAllText(text)); } int num = 0; string[] array = File.ReadAllLines(text); foreach (string obj in array) { num++; string text2 = obj.Trim(); if (text2.Length == 0 || text2[0] == '#') { continue; } string[] array2 = text2.Split(new char[1] { '|' }); if (array2.Length < 5) { RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": expected 5 fields, got " + array2.Length + " - skipped.")); continue; } Card card = new Card { Id = array2[0].Trim(), Name = array2[1].Trim(), Flavour = array2[2].Trim(), Effect = array2[3].Trim() }; if (!float.TryParse(array2[4].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out card.PerRank)) { RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": '" + array2[4].Trim() + "' is not a number - skipped.")); } else if (card.Id.Length == 0) { RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": blank id - skipped.")); } else if (_byId.ContainsKey(card.Id)) { RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": duplicate id '" + card.Id + "' - skipped.")); } else { if (!Resolve(card.Effect, num, card.Id, out card.Field)) { continue; } if (array2.Length >= 8) { card.Sigil = array2[7].Trim(); } if (array2.Length >= 7) { card.BonusEffect = array2[5].Trim(); string text3 = array2[6].Trim(); if (card.BonusEffect.Length > 0) { if (!float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out card.BonusPerRank)) { RistPlugin.Log.LogWarning((object)("cards.txt line " + num + ": bonus value '" + text3 + "' is not a number - bonus dropped.")); card.BonusEffect = ""; } else if (!Resolve(card.BonusEffect, num, card.Id, out card.BonusField)) { card.BonusEffect = ""; } } } _all.Add(card); _byId[card.Id] = card; } } RistPlugin.Log.LogInfo((object)("Loaded " + _all.Count + " cards from cards.txt.")); } private static bool Resolve(string effect, int lineNo, string id, out FieldInfo field) { field = null; if (Card.IsSpecialEffect(effect)) { if (Card.Specials.Contains(effect)) { return true; } RistPlugin.Log.LogWarning((object)("cards.txt line " + lineNo + ": unknown special '" + effect + "' - card '" + id + "' skipped.")); return false; } field = typeof(SE_Stats).GetField(effect, BindingFlags.Instance | BindingFlags.Public); if (field != null && field.FieldType == typeof(float)) { return true; } RistPlugin.Log.LogWarning((object)("cards.txt line " + lineNo + ": SE_Stats has no public float field '" + effect + "' - card '" + id + "' skipped.")); field = null; return false; } [MethodImpl(MethodImplOptions.NoInlining)] private static void DeclareCatalogue(string contents) { Suite.Data(contents, (string)null); } } [HarmonyPatch(typeof(Skills), "OnDeath")] internal static class DeathPenalty { private static bool Prefix() { return !RistConfig.RemoveDeathSkillLoss.Value; } } internal static class Effects { private const string EffectName = "Rist"; private static readonly int EffectHash = StringExtensionMethods.GetStableHashCode("Rist"); private static SE_Stats _applied; private static string _appliedSignature; private static Player _appliedTo; private static readonly Dictionary<string, string[]> Spread = new Dictionary<string, string[]> { { "*stamina:move", new string[5] { "m_runStaminaUseModifier", "m_jumpStaminaUseModifier", "m_dodgeStaminaUseModifier", "m_swimStaminaUseModifier", "m_sneakStaminaUseModifier" } }, { "*stamina:fight", new string[2] { "m_attackStaminaUseModifier", "m_blockStaminaUseModifier" } } }; internal static int ExtraRows { get; private set; } internal static void Reset() { if ((Object)(object)_applied != (Object)null) { Object.Destroy((Object)(object)_applied); } _applied = null; _appliedSignature = null; _appliedTo = null; } internal static void Apply(Player player, Dictionary<string, int> ranks) { if ((Object)(object)player == (Object)null) { return; } string text = Signature(ranks); if (player != _appliedTo || !(text == _appliedSignature)) { _appliedTo = player; _appliedSignature = text; ApplyStats(player, ranks); ApplyInventoryRows(player, ranks); if (RistConfig.Verbose.Value) { RistPlugin.Log.LogInfo((object)("Applied cards: " + ((text.Length == 0) ? "(none)" : text))); } } } private static Dictionary<string, float> Totals(Dictionary<string, int> ranks) { Dictionary<string, float> dictionary = new Dictionary<string, float>(); if (ranks == null) { return dictionary; } foreach (KeyValuePair<string, int> rank in ranks) { if (rank.Value <= 0) { continue; } Card card = Cards.Get(rank.Key); if (card == null) { continue; } Add(dictionary, card.Effect, card.PerRank * (float)rank.Value); if (card.HasBonus) { int num = Card.BonusTimes(rank.Value); if (num > 0) { Add(dictionary, card.BonusEffect, card.BonusPerRank * (float)num); } } } return dictionary; } private static void Add(Dictionary<string, float> totals, string effect, float amount) { if (string.IsNullOrEmpty(effect)) { return; } if (Spread.TryGetValue(effect, out var value)) { string[] array = value; foreach (string effect2 in array) { Add(totals, effect2, amount); } } else { totals.TryGetValue(effect, out var value2); totals[effect] = value2 + amount; } } internal static float TotalFor(string effect) { if (!ClientState.Known || string.IsNullOrEmpty(effect)) { return 0f; } if (!Totals(ClientState.Ranks).TryGetValue(effect, out var value)) { return 0f; } return value; } private static void ApplyStats(Player player, Dictionary<string, int> ranks) { //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan == null) { return; } Dictionary<FieldInfo, float> dictionary = new Dictionary<FieldInfo, float>(); foreach (KeyValuePair<string, float> item in Totals(ranks)) { if (!Card.IsSpecialEffect(item.Key)) { FieldInfo field = typeof(SE_Stats).GetField(item.Key, BindingFlags.Instance | BindingFlags.Public); if (!(field == null) && !(field.FieldType != typeof(float))) { dictionary[field] = item.Value; } } } sEMan.RemoveStatusEffect(EffectHash, true); if ((Object)(object)_applied != (Object)null) { Object.Destroy((Object)(object)_applied); _applied = null; } if (dictionary.Count == 0) { return; } SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>(); ((Object)val).name = "Rist"; ((StatusEffect)val).m_name = "Rist"; ((StatusEffect)val).m_ttl = 0f; val.m_raiseSkill = (SkillType)999; val.m_modifyAttackSkill = (SkillType)999; val.m_skillLevel = (SkillType)999; foreach (KeyValuePair<FieldInfo, float> item2 in dictionary) { float num = (Card.IsOneBased(item2.Key.Name) ? (1f + item2.Value) : item2.Value); item2.Key.SetValue(val, num); } sEMan.AddStatusEffect((StatusEffect)(object)val, false, 0, 0f); _applied = val; } private static void ApplyInventoryRows(Player player, Dictionary<string, int> ranks) { Totals(ranks).TryGetValue("*inventoryrow", out var value); ExtraRows = Mathf.Max(0, Mathf.RoundToInt(value)); if (RistPlugin.CorePresent) { ClaimThroughCore(ExtraRows); } else { OwnInventoryRows.Claimed = ExtraRows; } } [MethodImpl(MethodImplOptions.NoInlining)] private static void ClaimThroughCore(int rows) { InventoryRows.Claim("ezomic.valheim.rist", rows); } private static string Signature(Dictionary<string, int> ranks) { if (ranks == null || ranks.Count == 0) { return ""; } List<string> list = new List<string>(ranks.Keys); list.Sort(); StringBuilder stringBuilder = new StringBuilder(); foreach (string item in list) { if (stringBuilder.Length > 0) { stringBuilder.Append(','); } stringBuilder.Append(item).Append(':').Append(ranks[item]); } return stringBuilder.ToString(); } } internal static class Gate { private static readonly HashSet<string> Baselined = new HashSet<string>(StringComparer.Ordinal); internal static bool HasBaseline(string owner) { if (owner != null) { return Baselined.Contains(owner); } return false; } internal static void Forget() { Baselined.Clear(); } internal static string LocalSkills() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected I4, but got Unknown Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return ""; } Skills skills = ((Character)localPlayer).GetSkills(); if ((Object)(object)skills == (Object)null) { return ""; } StringBuilder stringBuilder = new StringBuilder(); foreach (Skill skill in skills.GetSkillList()) { if (skill != null && skill.m_info != null) { if (stringBuilder.Length > 0) { stringBuilder.Append(','); } stringBuilder.Append((int)skill.m_info.m_skill).Append(':').Append(skill.m_level.ToString("R", CultureInfo.InvariantCulture)); } } return stringBuilder.ToString(); } internal static void Judge(long sender, string owner, string facts, string skills) { if (!RistConfig.CheckSkillBaseline.Value || string.IsNullOrEmpty(owner)) { return; } Dictionary<int, float> dictionary = ParseSkills(skills); if (dictionary == null) { return; } RistRecord ristRecord = Ledger.For(owner); if (ristRecord == null) { return; } bool flag = !ristRecord.HasSnapshot; Baselined.Add(owner); int num = 0; foreach (KeyValuePair<int, float> item in dictionary) { if (!ristRecord.Snapshot.TryGetValue(item.Key, out var value) || !(item.Value <= value)) { ristRecord.Snapshot[item.Key] = item.Value; num++; } } if (num > 0) { Ledger.Touch(); } if (flag) { RistPlugin.Log.LogInfo((object)("Baseline adopted for " + owner + " (" + dictionary.Count + " skills).")); } else if (num > 0 && RistConfig.Verbose.Value) { RistPlugin.Log.LogInfo((object)("Baseline for " + owner + " raised on " + num + " skill(s).")); } if (Credit(sender, owner, ristRecord, dictionary)) { Net.PushState(sender, ristRecord); } } private static bool Credit(long sender, string owner, RistRecord rec, Dictionary<int, float> reported) { if (!RistConfig.CreditExistingSkills.Value) { return false; } float num = 0f; foreach (KeyValuePair<int, float> item in reported) { float value = item.Value; if (value > 0f) { num += value * (value + 1f) * 0.5f; } } num *= RistConfig.XpPerSkillLevel.Value; if (num <= rec.Xp + 0.001f) { return false; } int level = rec.Level; rec.Xp = num; Ledger.Touch(); RistPlugin.Log.LogInfo((object)("Credited " + owner + " for skills already held: " + num.ToString("0") + " xp, Rist level " + level + " -> " + rec.Level + ".")); return true; } private static Dictionary<int, float> ParseSkills(string wire) { if (wire == null) { return null; } Dictionary<int, float> dictionary = new Dictionary<int, float>(); string[] array = wire.Split(new char[1] { ',' }); foreach (string text in array) { if (text.Length != 0) { string[] array2 = text.Split(new char[1] { ':' }); if (array2.Length == 2 && int.TryParse(array2[0], out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { dictionary[result] = result2; } } } return dictionary; } } internal static class HudBar { private const float BorderBuffer = 16f; private static GameObject _root; private static RectTransform _rect; private static GuiBar _fast; private static GuiBar _slow; private static TMP_Text _text; private static Animator _animator; private static Canvas _canvas; private static bool _hasVisible; private static bool _hasFlash; private static bool _failed; private static float _nextFlash; private static int _shownLevel = -1; private static int _shownPercent = -1; private static bool _shownOwed; private static float _sizedAt; private static float _trail = -1f; private const float TrailSpeed = 0.5f; private static bool _uprightAt; private static string _tintedAt; internal static bool Live => (Object)(object)_root != (Object)null; internal static bool HasText => (Object)(object)_text != (Object)null; internal static float HalfLength { get { float num = (((Object)(object)_canvas != (Object)null) ? _canvas.scaleFactor : 1f); return Mathf.Max(16f, RistConfig.BarSize.Value) * 0.5f * num; } } internal static void Update() { if (!RistConfig.Enabled.Value || !RistConfig.ShowXpBar.Value || !RistConfig.VanillaBar.Value) { Drop(); } else if ((Object)(object)Hud.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { Drop(); } else { if ((Object)(object)_root == (Object)null && !Build()) { return; } bool flag = Visible(); if (_root.activeSelf != flag) { _root.SetActive(flag); } if (flag) { Place(); if (!Mathf.Approximately(_sizedAt, RistConfig.BarSize.Value)) { Size(); } if (_uprightAt != RistConfig.BarUpright.Value) { Lay(); } if (_tintedAt != RistConfig.BarColour.Value) { Colour(); } float num = Mathf.Clamp01(Levels.Progress(ClientState.Xp)); Fill(num); int num2 = Mathf.Clamp(Mathf.RoundToInt(num * 100f), 0, 100); bool hasPick = ClientState.HasPick; if ((Object)(object)_text != (Object)null && (_shownLevel != ClientState.Level || _shownPercent != num2 || _shownOwed != hasPick)) { _shownLevel = ClientState.Level; _shownPercent = num2; _shownOwed = hasPick; _text.text = _shownLevel + " · " + num2 + "%" + (ClientState.HasPick ? " ★" : ""); } Announce(); } } } private static bool Visible() { if (!ClientState.Known) { return false; } if (((Character)Player.m_localPlayer).IsDead()) { return false; } return !Hud.instance.m_userHidden; } private static void Announce() { if (_hasFlash && ClientState.HasPick && !(Time.time < _nextFlash)) { _nextFlash = Time.time + Mathf.Max(1f, RistConfig.BarFlashSeconds.Value); _animator.SetTrigger("Flash"); } } private static bool Build() { if (_failed) { return false; } RectTransform val = (((Object)(object)Hud.instance.m_eitrBarRoot != (Object)null) ? Hud.instance.m_eitrBarRoot : Hud.instance.m_staminaBar2Root); if ((Object)(object)val == (Object)null || (Object)(object)((Transform)val).parent == (Object)null) { Fail("no upright bar to clone from - falling back to the plain bar."); return false; } GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Transform)val).parent); ((Object)val2).name = "RistXpBar"; val2.SetActive(true); _rect = val2.GetComponent<RectTransform>(); _canvas = val2.GetComponentInParent<Canvas>(); GuiBar[] componentsInChildren = val2.GetComponentsInChildren<GuiBar>(true); foreach (GuiBar val3 in componentsInChildren) { if (val3.m_smoothDrain || val3.m_smoothFill) { if ((Object)(object)_slow == (Object)null) { _slow = val3; } } else if ((Object)(object)_fast == (Object)null) { _fast = val3; } } if ((Object)(object)_fast == (Object)null) { _fast = _slow; } if ((Object)(object)_slow == (Object)null) { _slow = _fast; } if ((Object)(object)_fast == (Object)null) { Object.Destroy((Object)(object)val2); Fail("the cloned bar has no GuiBar - falling back to the plain bar."); return false; } _text = val2.GetComponentInChildren<TMP_Text>(true); _animator = val2.GetComponent<Animator>(); if ((Object)(object)_animator == (Object)null) { _animator = val2.GetComponentInChildren<Animator>(true); } ReadParameters(); Size(); Colour(); Lay(); if (_hasVisible) { _animator.SetBool("Visible", true); } else { Unfade(val2); } _root = val2; _shownLevel = -1; _trail = -1f; RistPlugin.Log.LogInfo((object)("Experience bar cloned from " + ((Object)val).name + ".")); return true; } private static void Unfade(GameObject go) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_animator != (Object)null) { Object.Destroy((Object)(object)_animator); } _animator = null; _hasFlash = false; CanvasGroup[] componentsInChildren = go.GetComponentsInChildren<CanvasGroup>(true); foreach (CanvasGroup val in componentsInChildren) { if (val.alpha < 0.05f) { val.alpha = 1f; } } Graphic[] componentsInChildren2 = go.GetComponentsInChildren<Graphic>(true); foreach (Graphic val2 in componentsInChildren2) { Color color = val2.color; if (!(color.a >= 0.05f)) { color.a = 1f; val2.color = color; } } RistPlugin.Log.LogInfo((object)"Cloned bar has no Visible animator parameter; unfaded by hand."); } private static void ReadParameters() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Invalid comparison between Unknown and I4 _hasVisible = false; _hasFlash = false; if ((Object)(object)_animator == (Object)null || (Object)(object)_animator.runtimeAnimatorController == (Object)null) { return; } AnimatorControllerParameter[] parameters = _animator.parameters; foreach (AnimatorControllerParameter val in parameters) { if ((int)val.type == 4 && val.name == "Visible") { _hasVisible = true; } if ((int)val.type == 9 && val.name == "Flash") { _hasFlash = true; } } } private static void Size() { _sizedAt = RistConfig.BarSize.Value; float num = Mathf.Max(16f, _sizedAt); _rect.SetSizeWithCurrentAnchors((Axis)0, num + 16f); if (RistConfig.Verbose.Value) { RistPlugin.Log.LogInfo((object)("Bar sized to " + num + ".")); } } private static void Fill(float progress) { float num = Mathf.Max(16f, RistConfig.BarSize.Value); if (_trail < 0f || progress > _trail) { _trail = progress; } else { _trail = Mathf.MoveTowards(_trail, progress, Time.deltaTime * 0.5f); } Draw(_slow, num * Mathf.Max(_trail, progress)); Draw(_fast, num * progress); } private static void Draw(GuiBar bar, float width) { if (!((Object)(object)bar == (Object)null) && !((Object)(object)bar.m_bar == (Object)null)) { bar.m_bar.SetSizeWithCurrentAnchors((Axis)0, Mathf.Max(0f, width)); } } private static void Lay() { //IL_003f: 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) _uprightAt = RistConfig.BarUpright.Value; if (!((Object)(object)_rect == (Object)null)) { ((Transform)_rect).rotation = (_uprightAt ? Quaternion.Euler(0f, 0f, 90f) : Quaternion.identity); } } private static void Colour() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0031: 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_0049: 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_006b: Unknown result type (might be due to invalid IL or missing references) _tintedAt = RistConfig.BarColour.Value; Color val = RistConfig.BarTint(); Tint(_fast, val); Tint(_slow, new Color(val.r * 0.55f, val.g * 0.55f, val.b * 0.55f, val.a)); if ((Object)(object)_text != (Object)null) { ((Graphic)_text).color = val; } } private unsafe static void Tint(GuiBar bar, Color colour) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bar == (Object)null) { return; } bar.SetColor(colour); if ((Object)(object)bar.m_bar == (Object)null) { return; } Image component = ((Component)bar.m_bar).GetComponent<Image>(); if (!((Object)(object)component == (Object)null)) { if (RistConfig.Verbose.Value && ((Graphic)component).color != colour) { ManualLogSource log = RistPlugin.Log; string[] obj = new string[9] { "Bar fill '", ((Object)component).name, "' was ", ((object)((Graphic)component).color/*cast due to .constrained prefix*/).ToString(), " (sprite ", ((Object)(object)component.sprite != (Object)null) ? ((Object)component.sprite).name : "none", "); set to ", null, null }; Color val = colour; obj[7] = ((object)(*(Color*)(&val))/*cast due to .constrained prefix*/).ToString(); obj[8] = "."; log.LogInfo((object)string.Concat(obj)); } ((Graphic)component).color = colour; } } private static void Place() { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Transform)_rect).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); if ((Object)(object)val == (Object)null) { return; } bool flag = ((Object)(object)Hud.instance.m_buildHud != (Object)null && Hud.instance.m_buildHud.activeSelf) || ((Object)(object)Hud.instance.m_shipHudRoot != (Object)null && Hud.instance.m_shipHudRoot.activeSelf); Camera val2 = (((Object)(object)_canvas != (Object)null && (int)_canvas.renderMode != 0) ? _canvas.worldCamera : null); Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(RistConfig.BarPosX.Value, RistConfig.BarPosY.Value + (flag ? RistConfig.BarBuildRaise.Value : 0f)); if (RistConfig.BarFollowStamina.Value) { RectTransform staminaBar2Root = Hud.instance.m_staminaBar2Root; if ((Object)(object)staminaBar2Root != (Object)null) { Vector2 val4 = RectTransformUtility.WorldToScreenPoint(val2, ((Transform)staminaBar2Root).position); ((Vector2)(ref val3))..ctor(val4.x + RistConfig.BarOffsetX.Value, val4.y - RistConfig.BarOffsetY.Value); } } Vector3 position = default(Vector3); if (RectTransformUtility.ScreenPointToWorldPointInRectangle(val, val3, val2, ref position)) { ((Transform)_rect).position = position; } } private static void Fail(string why) { _failed = true; RistPlugin.Log.LogWarning((object)("Rist: " + why)); } internal static void Drop() { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } _root = null; _rect = null; _fast = null; _slow = null; _text = null; _animator = null; _canvas = null; _shownLevel = -1; _trail = -1f; _shownPercent = -1; _sizedAt = 0f; _tintedAt = null; _failed = false; } } internal static class InfoTab { [CompilerGenerated] private static class <>O { public static UnityAction <0>__Open; } private static GameObject _tab; private static InventoryGui _seen; private static Sprite _icon; private static bool _failed; internal static void Update() { if (!_failed && RistConfig.Enabled.Value && RistConfig.ShowInfoTab.Value) { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_infoPanel == (Object)null) { _seen = null; } else if (instance != _seen || !((Object)(object)_tab != (Object)null)) { _seen = instance; Build(instance); } } } private static void Build(InventoryGui gui) { //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_0084: Expected O, but got Unknown Button val = Donor(gui); if ((Object)(object)val == (Object)null) { _failed = true; RistPlugin.Log.LogWarning((object)"No compendium tab to clone, and there is no keybind any more - the rists panel has no way in. That is a bug rather than a setting; please report it."); return; } GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)val).transform.parent); ((Object)val2).name = "RistTab"; Button component = val2.GetComponent<Button>(); if ((Object)(object)component != (Object)null) { ((UnityEventBase)component.onClick).RemoveAllListeners(); ButtonClickedEvent onClick = component.onClick; object obj = <>O.<0>__Open; if (obj == null) { UnityAction val3 = Open; <>O.<0>__Open = val3; obj = (object)val3; } ((UnityEvent)onClick).AddListener((UnityAction)obj); } Localize[] componentsInChildren = val2.GetComponentsInChildren<Localize>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } Icon(val2, val); Place(gui, val2, val); _tab = val2; RistPlugin.Log.LogInfo((object)"Rists tab added to the compendium bar."); } private static Button Donor(InventoryGui gui) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) Button result = null; float num = float.MinValue; Button[] componentsInChildren = ((Component)gui.m_infoPanel).GetComponentsInChildren<Button>(true); foreach (Button val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !(((Object)val).name == "RistTab")) { RectTransform component = ((Component)val).GetComponent<RectTransform>(); if (!((Object)(object)component == (Object)null) && component.anchoredPosition.x > num) { num = component.anchoredPosition.x; result = val; } } } return result; } private static void Place(InventoryGui gui, GameObject tab, Button donor) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) RectTransform component = tab.GetComponent<RectTransform>(); if ((Object)(object)component == (Object)null) { return; } List<RectTransform> list = new List<RectTransform>(); Selectable[] componentsInChildren = ((Component)gui.m_infoPanel).GetComponentsInChildren<Selectable>(true); foreach (Selectable val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { RectTransform component2 = ((Component)val).GetComponent<RectTransform>(); if (!((Object)(object)component2 == (Object)null) && !((Object)(object)component2 == (Object)(object)component) && !list.Contains(component2)) { list.Add(component2); } } } if (list.Count != 0) { list.Sort((RectTransform a, RectTransform b) => a.anchoredPosition.x.CompareTo(b.anchoredPosition.x)); list.Add(component); float num = Pitch(list); float y = list[0].anchoredPosition.y; float num2 = (0f - num) * (float)(list.Count - 1) * 0.5f; for (int num3 = 0; num3 < list.Count; num3++) { list[num3].anchoredPosition = new Vector2(num2 + (float)num3 * num, y); } RistPlugin.Log.LogInfo((object)("Compendium bar re-spaced: " + list.Count + " icons at " + (int)num + "px.")); } } private static float Pitch(List<RectTransform> icons) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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) for (int i = 1; i < icons.Count; i++) { float num = icons[i].anchoredPosition.x - icons[i - 1].anchoredPosition.x; if (num > 1f) { return num; } } Rect rect = icons[0].rect; return ((Rect)(ref rect)).width * 1.15f; } private static void Icon(GameObject tab, Button donor) { //IL_002c: 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_015b: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_icon == (Object)null) { Texture2D val = Algiz(); _icon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); ((Object)_icon).name = "RistAlgiz"; } Button component = tab.GetComponent<Button>(); Image val2 = (Image)(((Object)(object)component != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); Rect rect; Image[] componentsInChildren; if ((Object)(object)val2 == (Object)null) { componentsInChildren = tab.GetComponentsInChildren<Image>(true); foreach (Image val3 in componentsInChildren) { if ((Object)(object)val3 == (Object)null || (Object)(object)((Component)val3).gameObject == (Object)(object)tab || ((Object)val3).name == "Background") { continue; } if (!((Object)(object)val2 == (Object)null)) { rect = ((Graphic)val3).rectTransform.rect; float width = ((Rect)(ref rect)).width; rect = ((Graphic)val2).rectTransform.rect; if (!(width < ((Rect)(ref rect)).width)) { continue; } } val2 = val3; } RistPlugin.Log.LogWarning((object)("The tab's target graphic was not an Image; fell back to '" + (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "nothing") + "'.")); } if ((Object)(object)val2 == (Object)null) { RistPlugin.Log.LogWarning((object)"No glyph layer on the cloned tab - it will still open the rists, wearing the donor's picture."); return; } val2.sprite = _icon; ((Graphic)val2).color = Color.white; val2.preserveAspect = true; ((Behaviour)val2).enabled = true; ((Graphic)val2).raycastTarget = true; if (!RistConfig.Verbose.Value) { return; } componentsInChildren = ((Component)donor).GetComponentsInChildren<Image>(true); foreach (Image val4 in componentsInChildren) { if (!((Object)(object)val4 == (Object)null)) { ManualLogSource log = RistPlugin.Log; string[] obj = new string[10] { " donor layer '", ((Object)val4).name, "' ", ((Behaviour)val4).enabled ? "on" : "off", " ", null, null, null, null, null }; rect = ((Graphic)val4).rectTransform.rect; obj[5] = ((object)((Rect)(ref rect)).size/*cast due to .constrained prefix*/).ToString(); obj[6] = " colour "; obj[7] = ((object)((Graphic)val4).color/*cast due to .constrained prefix*/).ToString(); obj[8] = " sprite "; obj[9] = (((Object)(object)val4.sprite != (Object)null) ? ((Object)val4.sprite).name : "none"); log.LogInfo((object)string.Concat(obj)); } } ManualLogSource log2 = RistPlugin.Log; string[] obj2 = new string[8] { " donor '", ((Object)donor).name, "' normal ", null, null, null, null, null }; ColorBlock colors = ((Selectable)donor).colors; obj2[3] = ((object)((ColorBlock)(ref colors)).normalColor/*cast due to .constrained prefix*/).ToString(); obj2[4] = ", highlighted "; colors = ((Selectable)donor).colors; obj2[5] = ((object)((ColorBlock)(ref colors)).highlightedColor/*cast due to .constrained prefix*/).ToString(); obj2[6] = ", target graphic "; obj2[7] = (((Object)(object)((Selectable)donor).targetGraphic != (Object)null) ? ((Object)((Selectable)donor).targetGraphic).name : "none"); log2.LogInfo((object)string.Concat(obj2)); ManualLogSource log3 = RistPlugin.Log; string[] obj3 = new string[5] { "Rists tab: rune on '", ((Object)val2).name, "' at ", null, null }; rect = ((Graphic)val2).rectTransform.rect; obj3[3] = ((object)((Rect)(ref rect)).size/*cast due to .constrained prefix*/).ToString(); obj3[4] = ", gold from the button."; log3.LogInfo((object)string.Concat(obj3)); } private static Texture2D Algiz() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_0071: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) float[][] array = new float[3][] { new float[4] { 0.5f, 0.94f, 0.5f, 0.08f }, new float[4] { 0.5f, 0.52f, 0.1f, 0.1f }, new float[4] { 0.5f, 0.52f, 0.9f, 0.1f } }; float num = 5.76f; Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)61 }; Vector2 p = default(Vector2); for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { ((Vector2)(ref p))..ctor((float)j + 0.5f, (float)(127 - i) + 0.5f); float num2 = float.MaxValue; float[][] array2 = array; foreach (float[] array3 in array2) { float num3 = Distance(p, new Vector2(array3[0] * 128f, array3[1] * 128f), new Vector2(array3[2] * 128f, array3[3] * 128f)); if (num3 < num2) { num2 = num3; } } val.SetPixel(j, i, new Color(1f, 1f, 1f, Mathf.Clamp01(num - num2 + 0.5f))); } } val.Apply(); return val; } private static float Distance(Vector2 p, Vector2 a, Vector2 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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) Vector2 val = b - a; float num = Mathf.Clamp01(Vector2.Dot(p - a, val) / Mathf.Max(0.0001f, ((Vector2)(ref val)).sqrMagnitude)); return Vector2.Distance(p, a + val * num); } private static void Open() { if ((Object)(object)InventoryGui.instance != (Object)null) { InventoryGui.instance.Hide(); } RistPanel.Open(); } } internal static class Ledger { private static readonly Dictionary<string, RistRecord> _records = new Dictionary<string, RistRecord>(); private static bool _loaded; private static bool _dirty; private static float _nextFlush; private static string LedgerPath => Path.Combine(Paths.ConfigPath, "rist-ledger.txt"); internal static RistRecord For(string owner) { Load(); if (string.IsNullOrEmpty(owner)) { return null; } if (!_records.TryGetValue(owner, out var value)) { value = new RistRecord { Owner = owner }; _records[owner] = value; _dirty = true; } return value; } internal static void Touch() { _dirty = true; } private static void Load() { if (_loaded) { return; } _loaded = true; if (!File.Exists(LedgerPath)) { RistPlugin.Log.LogInfo((object)("No ledger yet; starting a fresh one at " + LedgerPath)); return; } int num = 0; string[] array = File.ReadAllLines(LedgerPath); foreach (string text in array) { if (text.Trim().Length != 0) { RistRecord ristRecord = RistRecord.Parse(text); if (ristRecord == null) { num++; } else { _records[ristRecord.Owner] = ristRecord; } } } RistPlugin.Log.LogInfo((object)("Ledger loaded: " + _records.Count + " players" + ((num > 0) ? (", " + num + " unreadable lines skipped") : "") + ".")); } internal static void Tick(float time) { if (_dirty && !(time < _nextFlush)) { _nextFlush = time + 10f; Flush(); } } internal static void Flush() { if (!_dirty || !_loaded) { return; } try { List<string> list = new List<string>(_records.Count); foreach (RistRecord value in _records.Values) { list.Add(value.Serialise()); } string text = LedgerPath + ".tmp"; File.WriteAllLines(text, list.ToArray()); if (File.Exists(LedgerPath)) { File.Delete(LedgerPath); } File.Move(text, LedgerPath); _dirty = false; } catch (Exception ex) { RistPlugin.Log.LogError((object)("Could not write the ledger: " + ex.Message)); } } } internal static class Levels { internal static float XpForSkillUp(float skillLevel) { return Mathf.Max(1f, skillLevel) * RistConfig.XpPerSkillLevel.Value; } internal static float XpForLevel(int level) { if (level <= 0) { return 0f; } return RistConfig.LevelBaseXp.Value * Mathf.Pow((float)level, RistConfig.LevelExponent.Value); } internal static int LevelForXp(float xp) { if (xp <= 0f) { return 0; } float value = RistConfig.LevelBaseXp.Value; float value2 = RistConfig.LevelExponent.Value; if (value <= 0f || value2 <= 0f) { return 0; } int num = Mathf.FloorToInt(Mathf.Pow(xp / value, 1f / value2)); return Mathf.Max(0, num); } internal static float Progress(float xp) { int num = LevelForXp(xp); float num2 = XpForLevel(num); float num3 = XpForLevel(num + 1); if (num3 <= num2) { return 0f; } return Mathf.Clamp01((xp - num2) / (num3 - num2)); } } internal static class ClientState { internal static float Xp; internal static int DraftsTaken; internal static readonly Dictionary<string, List<int>> Taken = new Dictionary<string, List<int>>(); internal static readonly Dictionary<string, int> Ranks = new Dictionary<string, int>(); internal static bool Known; internal static int ServerLevel = -1; internal static int ServerOwed = -1; internal static int Level { get { if (ServerLevel < 0) { return Levels.LevelForXp(Xp); } return ServerLevel; } } internal static int Owed { get { if (ServerOwed < 0) { return Mathf.Max(0, Level - DraftsTaken); } return ServerOwed; } } internal static bool HasPick => Owed > 0; internal static int RankOf(string id) { if (id == null || !Ranks.TryGetValue(id, out var value)) { return 0; } return value; } internal static List<int> LevelsOf(string id) { if (id == null || !Taken.TryGetValue(id, out var value)) { return null; } return value; } internal static void PredictTake(string id) { if (Known && !string.IsNullOrEmpty(id) && Owed > 0 && RankOf(id) < RistConfig.MaxRank.Value) { if (!Taken.TryGetValue(id, out var value)) { value = new List<int>(); Taken[id] = value; } value.Add(DraftsTaken + 1); Ranks[id] = value.Count; DraftsTaken++; if (ServerOwed > 0) { ServerOwed--; } } } internal static void Clear() { Xp = 0f; DraftsTaken = 0; Taken.Clear(); Ranks.Clear(); ServerLevel = -1; ServerOwed = -1; Known = false; } internal static void FromWire(string wire) { Clear(); if (string.IsNullOrEmpty(wire)) { return; } string[] array = wire.Split(new char[1] { '|' }); if (array.Length < 3) { return; } float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out Xp); int.TryParse(array[1], out DraftsTaken); string[] array2 = array[2].Split(new char[1] { ',' }); foreach (string text in array2) { if (text.Length == 0) { continue; } string[] array3 = text.Split(new char[1] { ':' }); if (array3.Length != 2) { continue; } List<int> list = new List<int>(); string[] array4 = array3[1].Split(new char[1] { ';' }); foreach (string text2 in array4) { if (text2.Length != 0 && int.TryParse(text2, out var result)) { list.Add(result); } } if (list.Count != 0) { Taken[array3[0]] = list; Ranks[array3[0]] = list.Count; } } if (array.Length >= 5) { int.TryParse(array[3], out ServerOwed); int.TryParse(array[4], out ServerLevel); } Known = true; } } internal static class Net { private const string RpcHello = "Rist_Hello"; private const string RpcSkillUp = "Rist_SkillUp"; private const string RpcPick = "Rist_Pick"; private const string RpcState = "Rist_State"; private const string RpcAskProfile = "Rist_AskProfile"; private const string RpcProfile = "Rist_Profile"; private const string RpcNotice = "Rist_Notice"; private static ZRoutedRpc _registeredOn; private static readonly Dictionary<string, Queue<float>> _reports = new Dictionary<string, Queue<float>>(); private static readonly Dictionary<long, long> _characters = new Dictionary<long, long>(); internal static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static void EnsureRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null) { _registeredOn = null; } else if (instance != _registeredOn) { _registeredOn = instance; _reports.Clear(); _characters.Clear(); Gate.Forget(); Throttle.Forget(); instance.Register<long>("Rist_Hello", (Action<long, long>)OnHello); instance.Register<int, float>("Rist_SkillUp", (Action<long, int, float>)OnSkillUp); instance.Register<string>("Rist_Pick", (Action<long, string>)OnPick); instance.Register<string>("Rist_State", (Action<long, string>)OnState); instance.Register("Rist_AskProfile", (Action<long>)OnAskProfile); instance.Register<string, string>("Rist_Profile", (Action<long, string, string>)OnProfile); instance.Register<string>("Rist_Notice", (Action<long, string>)OnNotice); ClientState.Clear(); Effects.Reset(); RistPlugin.Log.LogInfo((object)("RPCs registered (" + (IsServer ? "server" : "client") + ").")); } } internal static void SayHello(long characterId) { if (ZRoutedRpc.instance != null && characterId != 0L) { ZRoutedRpc.instance.InvokeRoutedRPC("Rist_Hello", new object[1] { characterId }); } } internal static void ReportSkillUp(SkillType type, float level) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC("Rist_SkillUp", new object[2] { (int)type, level }); } } internal static void SendPick(string cardId) { if (ZRoutedRpc.instance != null && !string.IsNullOrEmpty(cardId)) { ZRoutedRpc.instance.InvokeRoutedRPC("Rist_Pick", new object[1] { cardId }); } } private static void OnHello(long sender, long characterId) { if (!IsServer || !RistConfig.Enabled.Value) { return; } string text = PlatformOf(sender); if (text == null || characterId == 0L) { return; } _characters[sender] = characterId; RistRecord ristRecord = Ledger.For(Key(text, characterId)); if (ristRecord != null) { if (ristRecord.Reconcile()) { Ledger.Touch(); } RistPlugin.Log.LogInfo((object)("Hello from " + text + " playing character " + characterId + " - level " + ristRecord.Level + ", " + ristRecord.Taken.Count + " cards, " + ristRecord.Owed + " to spend.")); PushState(sender, ristRecord); if (RistConfig.CheckSkillBaseline.Value) { AskProfile(sender); } } } private static void OnSkillUp(long sender, int skillType, float skillLevel) { //IL_0169: Unknown result type (might be due to invalid IL or missing references) if (!IsServer || !RistConfig.Enabled.Value) { return; } string text = OwnerOf(sender); if (text == null) { return; } if (skillLevel <= 0f || skillLevel > 100f) { if (RistConfig.Verbose.Value) { RistPlugin.Log.LogWarning((object)("Rejected skill-up from " + text + ": level " + skillLevel)); } return; } if (!WithinRate(text)) { RistPlugin.Log.LogWarning((object)("Rate limit hit for " + text + " - skill-up ignored. Either a very fast player or a forged report.")); return; } RistRecord ristRecord = Ledger.For(text); if (ristRecord == null) { return; } if (!Throttle.Step(ristRecord, text, skillType, skillLevel, out var why)) { RistPlugin.Log.LogWarning((object)("Rejected skill-up from " + text + " - " + why + ".")); return; } if (!ristRecord.Snapshot.TryGetValue(skillType, out var value) || skillLevel > value) { ristRecord.Snapshot[skillType] = skillLevel; Ledger.Touch(); } float num = Levels.XpForSkillUp(skillLevel); if (!Throttle.Afford(sender, text, num)) { PushState(sender, ristRecord); return; } int level = ristRecord.Level; ristRecord.Xp += num; Ledger.Touch(); if (RistConfig.Verbose.Value) { RistPlugin.Log.LogInfo((object)(text + " +" + num.ToString("0.#") + " xp (skill " + ((object)(SkillType)skillType/*cast due to .constrained prefix*/).ToString() + " " + skillLevel + ") -> " + ristRecord.Xp.ToString("0.#"))); } if (ristRecord.Level > level) { RistPlugin.Log.LogInfo((object)(text + " reached Rist level " + ristRecord.Level + ".")); } PushState(sender, ristRecord); } private static void OnPick(long sender, string cardId) { if (!IsServer || !RistConfig.Enabled.Value) { return; } string text = OwnerOf(sender); if (text == null) { return; } RistRecord ristRecord = Ledger.For(text); if (ristRecord == null) { return; } if (ristRecord.Owed <= 0) { RistPlugin.Log.LogWarning((object)("Rejected pick '" + cardId + "' from " + text + " - nothing owed.")); PushState(sender, ristRecord); return; } Card card = Cards.Get(cardId); if (card == null) { RistPlugin.Log.LogWarning((object)("Rejected pick '" + cardId + "' from " + text + " - no such card.")); PushState(sender, ristRecord); } else if (ristRecord.RankOf(cardId) >= RistConfig.MaxRank.Value) { RistPlugin.Log.LogWarning((object)("Rejected pick '" + cardId + "' from " + text + " - already at max rank.")); PushState(sender, ristRecord); } else { ristRecord.Take(cardId); Ledger.Touch(); RistPlugin.Log.LogInfo((object)(text + " took '" + card.Name + "' to rank " + ristRecord.RankOf(cardId) + " with the level " + ristRecord.DraftsTaken + " pick.")); PushState(sender, ristRecord); } } private static void OnProfile(long sender, string facts, string skills) { if (IsServer) { Gate.Judge(sender, OwnerOf(sender), facts, skills); } } internal static void PushState(long peerUid, RistRecord rec) { if (ZRoutedRpc.instance != null && rec != null) { ZRoutedRpc.instance.InvokeRoutedRPC(peerUid, "Rist_State", new object[1] { rec.ToWire() }); } } internal static void AskProfile(long peerUid) { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(peerUid, "Rist_AskProfile", Array.Empty<object>()); } } private static string OwnerOf(long sender) { if (!_characters.TryGetValue(sender, out var value)) { if (RistConfig.Verbose.Value) { RistPlugin.Log.LogWarning((object)"Message from an unidentified connection; ignored until it says hello."); } return null; } string text = PlatformOf(sender); if (text != null) { return Key(text, value); } return null; } private static string Key(string platform, long characterId) { return platform + "@" + characterId; } private static string PlatformOf(long sender) { if ((Object)(object)ZNet.instance == (Object)null) { return null; } ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer != null && peer.m_socket != null) { string hostName = peer.m_socket.GetHostName(); if (!string.IsNullOrEmpty(hostName)) { return hostName.Replace("|", "_").Replace("@", "_"); } } if (!ZNet.instance.IsDedicated()) { return "localhost"; } RistPlugin.Log.LogWarning((object)"Could not identify the sender of a Rist RPC; ignored."); return null; } private static bool WithinRate(string owner) { if (!_reports.TryGetValue(owner, out var value)) { value = new Queue<float>(); _reports[owner] = value; } float time = Time.time; while (value.Count > 0 && time - value.Peek() > 60f) { value.Dequeue(); } if ((float)value.Count >= Mathf.Max(1f, RistConfig.MaxSkillUpsPerMinute.Value)) { return false; } value.Enqueue(time); return true; } private static void OnState(long sender, string wire) { int owed = ClientState.Owed; ClientState.FromWire(wire); if (ClientState.Owed > owed) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, RistPanel.OpenHint(), 0, (Sprite)null); } } if (RistConfig.Verbose.Value) { RistPlugin.Log.LogInfo((object)("State: level " + ClientState.Level + ", " + ClientState.Ranks.Count + " cards, " + ClientState.Owed + " to spend.")); } } private static void OnAskProfile(long sender) { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(sender, "Rist_Profile", new object[2] { "", Gate.LocalSkills() }); } } internal static void SendNotice(long peerUid, string message) { if (ZRoutedRpc.instance != null && !string.IsNullOrEmpty(message)) { ZRoutedRpc.instance.InvokeRoutedRPC(peerUid, "Rist_Notice", new object[1] { message }); } } private static void OnNotice(long sender, string message) { if (!string.IsNullOrEmpty(message)) { RistPlugin.Log.LogWarning((object)("Rist: " + message)); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, message, 0, (Sprite)null); } } } } internal static class OwnInventoryRows { internal static class Backdrop { private static InventoryGui _seen; private static int _shown = -1; private static readonly List<RectTransform> Panels = new List<RectTransform>(); private static readonly List<float> Heights = new List<float>(); private static RectTransform _container; private static Vector2 _containerBase; internal static void Tick() { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_player == (Object)null) { _seen = null; return; } if (instance != _seen) { _seen = instance; _shown = -1; Capture(instance); } if (Claimed != _shown) { _shown = Claimed; Resize(instance, Claimed); } } private static void Capture(InventoryGui gui) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) Panels.Clear(); Heights.Clear(); Remember(gui.m_player); _container = gui.m_container; if ((Object)(object)_container != (Object)null) { _containerBase = _container.anchoredPosition; } Rect rect = gui.m_player.rect; float width = ((Rect)(ref rect)).width; Image[] componentsInChildren = ((Component)gui.m_player).GetComponentsInChildren<Image>(true); foreach (Image val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sprite == (Object)null) && ((Object)val.sprite).name.IndexOf("woodpanel", StringComparison.OrdinalIgnoreCase) >= 0) { rect = ((Graphic)val).rectTransform.rect; if (!(((Rect)(ref rect)).width < width * 0.6f)) { Remember(((Graphic)val).rectTransform); } } } } private static void Remember(RectTransform rect) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rect == (Object)null) && !Panels.Contains(rect)) { Panels.Add(rect); List<float> heights = Heights; Rect rect2 = rect.rect; heights.Add(((Rect)(ref rect2)).height); } } private static void Resize(InventoryGui gui, int rows) { //IL_0086: 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_0097: Unknown result type (might be due to invalid IL or missing references) InventoryGrid componentInChildren = ((Component)gui.m_player).GetComponentInChildren<InventoryGrid>(true); if ((Object)(object)componentInChildren == (Object)null || componentInChildren.m_elementSpace <= 0f) { return; } float num = (float)rows * componentInChildren.m_elementSpace; for (int i = 0; i < Panels.Count; i++) { if (!((Object)(object)Panels[i] == (Object)null)) { Panels[i].SetSizeWithCurrentAnchors((Axis)1, Heights[i] + num); } } if ((Object)(object)_container != (Object)null) { _container.anchoredPosition = _containerBase + new Vector2(0f, 0f - num); } } } internal static int Claimed; private static FieldInfo _height; private static Player _player; private static int _base = -1; private static int _applied = -1; private static bool _widened; private const int LoadSlack = 16; internal static void Tick() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { _player = null; _base = -1; _applied = -1; return; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return; } if (localPlayer != _player) { _player = localPlayer; _base = inventory.GetHeight(); _applied = -1; RistPlugin.Log.LogInfo((object)("Inventory rows: vanilla height is " + _base + ".")); } if ((Claimed != 0 || _widened || _applied >= 0) && Claimed != _applied) { if (_height == null) { _height = AccessTools.Field(typeof(Inventory), "m_height"); } if (_height == null) { RistPlugin.Log.LogError((object)"Inventory.m_height not found - extra rows cannot work."); _applied = Claimed; return; } int num = Mathf.Max(_base + Claimed, Occupied(inventory)); _applied = Claimed; _widened = false; _height.SetValue(inventory, num); RistPlugin.Log.LogInfo((object)("Inventory rows: " + _base + " + " + Claimed + ((num > _base + Claimed) ? (", held at " + num + " by items in the grid") : "") + ".")); } } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "Load")] private static void WidenBeforeLoad(Player __instance) { if ((Object)(object)__instance == (Object)null) { return; } Inventory inventory = ((Humanoid)__instance).GetInventory(); if (inventory != null) { if (_height == null) { _height = AccessTools.Field(typeof(Inventory), "m_height"); } if (!(_height == null)) { _player = __instance; _base = inventory.GetHeight(); _applied = -1; _widened = true; _height.SetValue(inventory, _base + 16); } } } private static int Occupied(Inventory inventory) { int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && allItem.m_gridPos.y + 1 > num) { num = allItem.m_gridPos.y + 1; } } return num; } } internal sealed class RistRecord { internal string Owner = ""; internal float Xp; internal int DraftsTaken; internal readonly Dictionary<string, List<int>> Taken = new Dictionary<string, List<int>>(); internal readonly Dictionary<int, float> Snapshot = new Dictionary<int, float>(); internal bool HasSnapshot => Snapshot.Count > 0; internal int Level => Levels.LevelForXp(Xp); internal int Owed => Math.Max(0, Level - DraftsTaken); internal int RankOf(string id) { if (id == null || !Taken.TryGetValue(id, out var value)) { return 0; } return value.Count; } internal void Take(string id) { if (!Taken.TryGetValue(id, out var value)) { value = new List<int>(); Taken[id] = value; } value.Add(DraftsTaken + 1); DraftsTaken++; } internal bool Reconcile() { List<string> list = null; foreach (KeyValuePair<string, List<int>> item in Taken) { if (Cards.Get(item.Key) == null) { if (list == null) { list = new List<string>(); } list.Add(item.Key); } } if (list == null) { return false; } foreach (string item2 in list) { int count = Taken[item2].Count; Taken.Remove(item2); DraftsTaken = Math.Max(0, DraftsTaken - count); RistPlugin.Log.LogInfo((object)("Card '" + item2 + "' is no longer in the catalogue - " + count + " pick(s) returned to " + Owner + ".")); } return true; } internal string Serialise() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("v3|").Append(Owner).Append('|') .Append(Xp.ToString("R", CultureInfo.InvariantCulture)) .Append('|') .Append(DraftsTaken) .Append('|'); AppendTaken(stringBuilder); stringBuilder.Append('|'); bool flag = true; foreach (KeyValuePair<int, float> item in Snapshot) { if (!flag) { stringBuilder.Append(','); } stringBuilder.Append(item.Key).Append(':').Append(item.Value.ToString("R", CultureInfo.InvariantCulture)); flag = false; } return stringBuilder.ToString(); } internal static RistRecord Parse(string line) { if (string.IsNullOrEmpty(line)) { return null; } string[] array = line.Split(new char[1] { '|' }); string text = array[0]; if (text != "v1" && text != "v2" && text != "v3") { return null; } bool flag = text != "v3"; if (array.Length < (flag ? 6 : 5)) { return null; } if (array[1].Length == 0) { return null; } if (!float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return null; } if (!int.TryParse(array[3], out var result2)) { return null; } RistRecord ristRecord = new RistRecord { Owner = array[1], Xp = result, DraftsTaken = result2 }; string[] array2 = array[4].Split(new char[1] { ',' }); foreach (string text2 in array2) { if (text2.Length == 0) { continue; } string[] array3 = text2.Split(new char[1] { ':' }); if (array3.Length != 2) { continue; } List<int> list = new List<int>(); if (flag) { if (!int.TryParse(array3[1], out var result3)) { continue; } for (int j = 0; j < result3; j++) { list.Add(0); } } else { string[] array4 = array3[1].Split(new char[1] { ';' }); foreach (string text3 in array4) { if (text3.Length != 0 && int.TryParse(text3, out var result4)) { list.Add(result4); } } } if (list.Count > 0) { ristRecord.Taken[array3[0]] = list; } } int num = (flag ? 6 : 5); if (array.Length > num) { array2 = array[num].Split(new char[1] { ',' }); foreach (string text4 in array2) { if (text4.Length != 0) { string[] array5 = text4.Split(new char[1] { ':' }); if (array5.Length == 2 && int.TryParse(array5[0], out var result5) && float.TryParse(array5[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result6)) { ristRecord.Snapshot[result5] = result6; } } } } return ristRecord; } internal string ToWire() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Xp.ToString("R", CultureInfo.InvariantCulture)).Append('|').Append(DraftsTaken) .Append('|'); AppendTaken(stringBuilder); stringBuilder.Append('|').Append(Owed).Append('|') .Append(Level); return stringBuilder.ToString(); } private void AppendTaken(StringBuilder sb) { bool flag = true; foreach (KeyValuePair<string, List<int>> item in Taken) { if (item.Value.Count == 0) { continue; } if (!flag) { sb.Append(','); } sb.Append(item.Key).Append(':'); for (int i = 0; i < item.Value.Count; i++) { if (i > 0) { sb.Append(';'); } sb.Append(item.Value[i]); } flag = false; } } } internal static class RistConfig { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> XpPerSkillLevel; internal static ConfigEntry<float> LevelBaseXp; internal static ConfigEntry<float> LevelExponent; internal static ConfigEntry<int> MaxRank; internal static ConfigEntry<int> BonusEvery; internal static ConfigEntry<float> AttackSpeedMax; internal static ConfigEntry<int> PanelColumns; internal static ConfigEntry<bool> RemoveDeathSkillLoss; internal static ConfigEntry<bool> CheckSkillBaseline; internal static ConfigEntry<bool> CreditExistingSkills; internal static ConfigEntry<float> MaxSkillUpsPerMinute; internal static ConfigEntry<float> MaxSkillLevelJump; internal static ConfigEntry<float> MaxXpPerMinute; internal static ConfigEntry<float> XpBurst; internal static ConfigEntry<string> CappedMessage; internal static ConfigEntry<bool> Verbose; internal static ConfigEntry<bool> ShowInfoTab; internal static ConfigEntry<bool> ShowXpBar; internal static ConfigEntry<bool> VanillaBar; internal static ConfigEntry<bool> BarUpright; internal static ConfigEntry<bool> BarFollowStamina; internal static ConfigEntry<float> BarOffsetX; internal static ConfigEntry<float> BarOffsetY; internal static ConfigEntry<float> BarPosX; internal static ConfigEntry<float> BarPosY; internal static ConfigEntry<float> BarSize; internal static ConfigEntry<float> BarBuildRaise; internal static ConfigEntry<string> BarColour; internal static ConfigEntry<float> BarFlashSeconds; internal static ConfigEntry<float> BarX; internal static ConfigEntry<float> BarBottom; internal static ConfigEntry<float> BarThickness; internal static ConfigEntry<float> BarLength; private static readonly Color Gold = new Color(0.83f, 0.663f, 0.29f, 1f); private static string _tintFrom; private static Color _tint = Gold; internal static void Bind(ConfigFile cfg) { Enabled = cfg.Bind<bool>("General", "Enabled", true, "Off leaves levels and cards recorded but stops granting or applying them."); Verbose = cfg.Bind<bool>("General", "Verbose", false, "Log every XP grant, every rejected report and every runestone applied.\nAlso dumps what the game's own UI is made of where this mod clones it - the compendium tab's layers and button colours, and the bar's fill colour and length. Both were written while chasing a cloned widget that drew the wrong thing, and they are the fastest way back to an answer if one ever does again."); XpPerSkillLevel = cfg.Bind<float>("Levelling", "XpPerSkillLevel", 1f, "XP granted per skill level-up, multiplied by the skill level reached. Weighting by level reached is deliberate: a flat rate would stall late progression exactly when the deep card ranks matter, and would make grinding a fresh cheap skill from zero the fastest way to farm cards."); LevelBaseXp = cfg.Bind<float>("Levelling", "LevelBaseXp", 40f, "Cumulative XP needed for character level 1. Later levels scale by LevelExponent.\nTogether with LevelExponent this is the whole pacing of the mod. The pair was 60 and 1.5 until it was played, and that asked for sixteen skill level-ups per rist by character level 20 - the reward simply stopped arriving. 40 and 1.4 roughly halves that at every level."); LevelExponent = cfg.Bind<float>("Levelling", "LevelExponent", 1.4f, "Cumulative XP for level N is LevelBaseXp * N^LevelExponent. Above 1 means each level costs more than the last. Vanilla's own skill curve is already front-loaded, so this counteracts a flood of early cards.\nThis is the shape rather than the scale: lowering it helps the late game far more than the early one, which is where the old 1.5 hurt. It is also what keeps the catalogue scarce - there are 95 picks in it at MaxRank 5, and a curve flat enough to hand out all of them turns a free choice into an order of purchase. At 1.4 a long playthrough lands near 67 of the 95."); ShowInfoTab = cfg.Bind<bool>("General", "ShowInfoTab", true, "Add a fifth tab to the compendium bar, beside the raven and the trophy, that opens your rists. Cloned from a tab already there, so it carries the game's own frame, hover and click sound."); ShowXpBar = cfg.Bind<bool>("Bar", "ShowXpBar", true, "Show the experience bar beside the health bar. It hides itself with the rest of the interface, including when the HUD is hidden by hand."); VanillaBar = cfg.Bind<bool>("Bar", "VanillaBar", true, "Build the bar by cloning one of the game's own upright bars, so it carries the same frame, fill sprite and trailing fill as stamina and eitr.\nOff draws a plain rectangle instead. That is also the automatic fallback if the clone fails, since the HUD hierarchy is scene data rather than API and can change under a game update."); BarUpright = cfg.Bind<bool>("Bar", "BarUpright", false, "Stand the bar on end like stamina and eitr. Off lays it flat, which is what a long bar needs - upright, extra length runs off the bottom of the screen."); BarFollowStamina = cfg.Bind<bool>("Bar", "BarFollowStamina", true, "Place the bar relative to the stamina bar instead of at fixed screen pixels. Off falls back to BarPosX and BarPosY, which is also what happens if the stamina bar cannot be found."); BarOffsetX = cfg.Bind<float>("Bar", "BarOffsetX", 0f, "Pixels right of the stamina bar's centre, when following it."); BarOffsetY = cfg.Bind<float>("Bar", "BarOffsetY", 70f, "Pixels below the stamina bar's centre, when following it."); BarPosX = cfg.Bind<float>("Bar", "BarPosX", 172f, "Pixels from the left edge of the screen to the centre of the cloned bar."); BarPosY = cfg.Bind<float>("Bar", "BarPosY", 105f, "Pixels from the bottom of the screen to the centre of the cloned bar. Depends on your resolution and HUD scale, so it will probably need nudging once."); BarSize = cfg.Bind<float>("Bar", "BarSize", 240f, "Length of the cloned bar in canvas units, the same units vanilla sizes the stamina and eitr bars in. 64 is what a starting stamina bar measures; 240 is a bar you can read a percentage off at a glance. Thickness comes from the borrowed sprite and is not settable."); BarBuildRaise = cfg.Bind<float>("Bar", "BarBuildRaise", 155f, "Pixels to lift the cloned bar while the build or ship panel is open. Vanilla moves its own bars up by the same amount to clear that panel; ours is pinned in screen space, so it has to make the move by hand."); BarColour = cfg.Bind<string>("Bar", "BarColour", "4FB3A5", "Bar colour as RRGGBB hex. The trailing fill is the same hue held back, the way vanilla tells its bar pairs apart. Unparseable values fall back to gold.\nVerdigris - aged bronze. Picked from a five-way comparison against the bars already on the HUD, which spend red, yellow, blue and orange between them, so a fifth in any of those reads as one of them.\nIt is cool like eitr but far enough round the wheel never to be taken for it, and it suits a Norse metal palette in a way the green it replaced did not: a green bar reads as health or poison from habit, whatever it is measuring.\nBone is E4DCC4 if you would rather have pale stone. That was the original choice and it was never actually seen - for a long time no value here reached the fill at all, and the bar simply showed the eitr donor's own purple."); BarFlashSeconds = cfg.Bind<float>("Bar", "BarFlashSeconds", 4f, "How often the bar flashes while a pick is waiting to be spent, in seconds. Uses the flash the borrowed bar already has. Only applies to the cloned bar."); BarX = cfg.Bind<float>("Bar", "BarX", 168f, "Pixels from the left edge of the screen to the left edge of the bar. The default aims to sit just right of the health bar."); BarBottom = cfg.Bind<float>("Bar", "BarBottom", 75f, "Pixels from the bottom of the screen to the bottom of the bar. Depends on your resolution and HUD scale, so it will probably need nudging once."); BarThickness = cfg.Bind<float>("Bar", "BarThickness", 10f, "Bar thickness in pixels."); BarLength = cfg.Bind<float>("Bar", "BarLength", 60f, "Bar height in pixels."); BonusEvery = cfg.Bind<int>("Cards", "BonusEvery", 5, "Ranks between capstones. A card that names a bonus effect in its last two cards.txt fields grants it once per this many ranks. At the default MaxRank of 5 that means once, on the final upgrade."); AttackSpeedMax = cfg.Bind<float>("Cards", "AttackSpeedMax", 1f, "Ceiling on the attack-speed cards, as a fraction. 1 means the animation can at most run at double speed.\nThis multiplies an animation rather than a number in a table, and animation events are what land the hit, so a mis-typed catalogue line could otherwise run the whole character at twenty times speed."); PanelColumns = cfg.Bind<int>("Cards", "PanelColumns", 4, "Tiles across the rists panel. Fewer means wider tiles and a taller panel; the panel scrolls vertically once it would pass 88% of the screen height."); MaxRank = cfg.Bind<int>("Cards", "MaxRank", 5, "How deep a single card can be taken. A card at this rank stops being pickable, and is also how many slots its track shows."); RemoveDeathSkillLoss = cfg.Bind<bool>("Death", "RemoveDeathSkillLoss", true, "Skip Skills.OnDeath entirely. The vanilla world modifier is not enough on its own: the accumulator wipe and the 'skills lowered' message sit outside the reduction factor, so zeroing it still discards partial progress toward the next level in every skill."); CheckSkillBaseline = cfg.Bind<bool>("Gate", "CheckSkillBaseline", true, "On login, take the character's skill list as the baseline this server pays from. Nothing is compared and nobody is judged: a character that turns up higher than this world last saw it gained that elsewhere and is simply not paid for it, because XP only ever comes from a level-up watched from here.\nOff leaves the baseline to fill in from observed play alone, which also turns off MaxSkillLevelJump - without a complete list, a skill never used cannot be told from one never seen.\nThis replaces WithholdUntrustedXp, UntrustedMessage and SkillDriftAllowance, which marked such a character untrusted and paid it nothing 'until they line up again'. They never could: the withholding stopped the baseline advancing, and the baseline was the only thing that could have cleared it. Those keys may still be sitting in your config file doing nothing and can be deleted. Keeping a character out of a world altogether is a door policy - that is Threshold."); CreditExistingSkills = cfg.Bind<bool>("Gate", "CreditExistingSkills", true, "Pay a joining character for the skills it already has, so a character that turns up worth twenty levels arrives at twenty levels with the picks to spend. Off starts every character at Rist level 0 no matter what it is carrying, and only level-ups watched from here earn anything.\nNot an estimate: XP is the skill level reached, so a skill at N has already produced 1+2+...+N. Summing that over every skill is exactly what the character would hold if all of it had happened here - which also makes it safe to re-run on every login, since a character that earned everything here is owed nothing and a skill lost to death cannot take levels away.\nOn by default because the level is meant to sit beside the skills; without this it records which server you were standing on instead. If you would rather a well-travelled character could not join at all, that is a door policy rather than a payment one - see Threshold."); MaxSkillUpsPerMinute = cfg.Bind<float>("Gate", "MaxSkillUpsPerMinute", 30f, "Server-side ceiling on accepted skill-up reports per player. A backstop only: skills live on the client, so reports are self-reported and this caps the damage rather than verifying anything."); MaxSkillLevelJump = cfg.Bind<float>("Gate", "MaxSkillLevelJump", 1f, "How far above the highest level this server has watched a skill reach a single skill-up report may claim.\nOne, because that is what the game does: skills level one at a time and fire one callback each, so a report of 87 from a server that last saw 12 did not come from playing. Raise it only if you run a mod that grants skill levels in bulk, which would otherwise trip this on every grant.\nNeeds CheckSkillBaseline on, since without a login baseline an unseen skill cannot be told from an unused one and every first report would look forged."); MaxXpPerMinute = cfg.Bind<float>("Gate", "MaxXpPerMinute", 600f, "Ceiling on XP paid per player per minute of connected time. Zero or less turns it off.\nThis is the only cap that does not depend on believing the client: however convincing a report is, time connected is a quantity the server measures for itself. 600 is far above honest play - it is thirty level-ups a minute, the report limit, on a skill at level 20 - and is meant to bound a flood rather than to shape progression. Use LevelBaseXp for that."); XpBurst = cfg.Bind<float>("Gate", "XpBurst", 1800f, "How much unspent earning allowance may bank, in XP. Three minutes' worth by default, and the allowance starts full so joining is never a wait.\nWithout a bank the cap would be a per-minute tripwire that an honest burst of level-ups trips - clearing a crypt levels four skills at once - and the player would silently stop earning mid-fight."); CappedMessage = cfg.Bind<string>("Gate", "CappedMessage", "You are earning faster than this world will pay for. Rists will resume shortly.", "Shown once per session, centre-screen, when the earning cap withholds XP. A cap that simply stops paying is invisible, so it says so once: invisible, and an honest player who hits one deserves to know why."); } internal static Color BarTint() { //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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) string text = ((BarColour != null) ? BarColour.Value : null); if (_tintFrom == text) { return _tint; } _tintFrom = text; _tint = Gold; if (string.IsNullOrEmpty(text)) { return _tint; } if (text[0] != '#') { text = "#" + text; } Color tint = default(Color); if (ColorUtility.TryParseHtmlString(text, ref tint)) { _tint = tint; } return _tint; } } internal static class RistPanel { private const float Board = 1120f; private const float PadX = 30f; private const float PadY = 26f; private const float Stone = 100f; private const float CellGapX = 14f; private const float CellGapY = 18f; private const float StoneGap = 7f; private const float NameLine = 20f; private const float NowLine = 18f; private const float DetailWidth = 268f; private const float DetailPad = 22f; private const float MarkRadius = 31f; private const float MarkSize = 22f; private static bool _built; private static bool _open; private static int _selected; private static GUIStyle _void; private static GUIStyle _title; private static GUIStyle _sub; private static GUIStyle _spend; private static GUIStyle _name; private static GUIStyle _nameDim; private static GUIStyle _now; private static GUIStyle _nowDim; private static GUIStyle _sigil; private static GUIStyle _sigilDim; private static GUIStyle _mark; private static GUIStyle _dname; private static GUIStyle _dflav; private static GUIStyle _label; private static GUIStyle _dnow; private static GUIStyle _dnext; private static GUIStyle _dcap; private static GUIStyle _slotOn; private static GUIStyle _slotOff; private static GUIStyle _take; private static GUIStyle _foot; private static GUIStyle _bar; private static GUIStyle _barTrack; private static GUIStyle _rule; private static readonly Color Void = new Color(0.035f, 0.031f, 0.027f, 0.97f); private static readonly Color Gold = new Color(0.83f, 0.663f, 0.29f, 1f); private static readonly Color Cream = new Color(0.91f, 0.863f, 0.753f, 1f); private static readonly Color Muted = new Color(0.659f, 0.612f, 0.518f, 1f); private static readonly Color Faint = new Color(0.588f, 0.553f, 0.482f, 1f); private static readonly Color Green = new Color(0.498f, 0.62f, 0.541f, 1f); private static readonly Color Silver = new Color(0.682f, 0.717f, 0.788f, 1f); private static readonly Color Carve = new Color(0.106f, 0.09f, 0.063f, 1f); private static readonly Color CarveDim = new Color(0.165f, 0.153f, 0.137f, 1f); private static readonly Color Track = new Color(0.165f, 0.149f, 0.125f, 1f); private static readonly Color Edge = new Color(0.184f, 0.169f, 0.141f, 1f); private static readonly Color Unworked = new Color(0.42f, 0.41f, 0.4f, 0.92f); internal static bool IsOpen { get { if (_open) { return Usable; } return false; } } private static bool Usable { get { if (!RistConfig.Enabled.Value) { return false; } if ((Object)(object)Player.m_localPlayer == (Object)null || ((Character)Player.m_localPlayer).IsDead()) { return false; } if (!InventoryGui.IsVisible()) { return !Menu.IsVisible(); } return false; } } internal static void Open() { _open = true; } internal static void Close() { _open = false; } internal static void Draw() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { Build(); GUI.Label(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), GUIContent.none, _void); int num = Mathf.Max(1, Cards.All.Count); int num2 = Mathf.Max(1, RistConfig.PanelColumns.Value); int num3 = Mathf.CeilToInt((float)num / (float)num2); float num4 = 152f; float num5 = 770f; float cellW = (num5 - 14f * (float)(num2 - 1)) / (float)num2; float num6 = 57f; float num7 = (float)num3 * num4 + (float)(num3 - 1) * 18f; float num8 = 52f + num6 + num7 + 26f; Vector2 val = new Vector2(((float)Screen.width - 1120f) * 0.5f, ((float)Screen.height - num8) * 0.5f); float num9 = val.x + 30f; float y = val.y + 26f; DrawHead(num9, ref y, 1060f); float num10 = y; DrawField(num9, num10, cellW, num4, num2); DrawDetail(new Rect(num9 + num5 + 22f, num10, 268f, num7)); GUI.Label(new Rect(num9, num10 + num7 + 8f, 1060f, 18f), "Escape to close", _foot); } } private static void DrawHead(float x, ref float y, float width) { //IL_000f: 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_0188: 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_0149: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(x, y, width * 0.5f, 32f), "YOUR RISTS", _title); int count = ClientState.Ranks.Count; int num = 0; foreach (KeyValuePair<string, int> rank in ClientState.Ranks) { num += rank.Value; } GUI.Label(new Rect(x + 200f, y + 9f, width - 480f, 20f), "Level " + ClientState.Level + " · " + count + " of " + Cards.All.Count + " carved · " + num + " of " + Cards.All.Count * RistConfig.MaxRank.Value + " marks", _sub); if (ClientState.HasPick) { int owed = ClientState.Owed; GUI.Label(new Rect(x, y + 7f, width, 22f), (owed == 1) ? "1 rist to spend" : (owed + " rists to spend"), _spend); } y += 34f; GUI.Label(new Rect(x, y, width, 5f), GUIContent.none, _barTrack); GUI.Label(new Rect(x, y, width * Mathf.Clamp01(Levels.Progress(ClientState.Xp)), 5f), GUIContent.none, _bar); y += 23f; } private static void DrawField(float left, float top, float cellW, float cellH, int columns) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) Rect cell = default(Rect); for (int i = 0; i < Cards.All.Count; i++) { ((Rect)(ref cell))..ctor(left + (float)(i % columns) * (cellW + 14f), top + (float)(i / columns) * (cellH + 18f), cellW, cellH); DrawStone(Cards.All[i], i, cell); } } private static void DrawStone(Card card, int index, Rect cell) { //IL_006e: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) int num = ClientState.RankOf(card.Id); int num2 = Mathf.Max(1, RistConfig.MaxRank.Value); bool flag = num >= num2; bool flag2 = ClientState.HasPick && !flag; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref cell)).x + (((Rect)(ref cell)).width - 100f) * 0.5f, ((Rect)(ref cell)).y, 100f, 100f); bool flag3 = ((Rect)(ref val)).Contains(Event.current.mousePosition); if (flag3) { _selected = index; } Texture2D val2 = Stones.For(card); if (!((Object)(object)val2 == (Object)null)) { Color color = GUI.color; if (flag || (flag3 && flag2)) { GUI.color = (Color)(flag ? Gold : new Color(Gold.r, Gold.g, Gold.b, 0.55f)); GUI.DrawTexture(Grow(val, flag ? 3f : 4f), (Texture)(object)val2); } GUI.color = ((num > 0) ? Color.white : Unworked); GUI.DrawTexture(val, (Texture)(object)val2); GUI.color = color; float num3 = ((Rect)(ref val)).x + 50f; float num4 = ((Rect)(ref val)).y + 50f; GUI.Label(new Rect(num3 - 26f, num4 - 26f, 52f, 52f), Sigil(card), (num > 0) ? _sigil : _sigilDim); string[] array = Stones.MarksFor(card, num2); for (int i = 0; i < num && i < array.Length; i++) { float num5 = (float)i / (float)num2 * (float)Math.PI * 2f; float num6 = num3 + Mathf.Sin(num5) * 31f; float num7 = num4 - Mathf.Cos(num5) * 31f; GUI.Label(new Rect(num6 - 11f, num7 - 11f, 22f, 22f), array[i], _mark); } if (flag2 && flag3 && (int)Event.current.type == 0 && Event.current.button == 0) { Take(card.Id); Event.current.Use(); } float num8 = ((Rect)(ref val)).yMax + 7f; GUI.Label(new Rect(((Rect)(ref cell)).x, num8, ((Rect)(ref cell)).width, 20f), card.Name, (num > 0) ? _name : _nameDim); num8 += 20f; GUI.Label(new Rect(((Rect)(ref cell)).x, num8, ((Rect)(ref cell)).width, 18f), card.Describe(Mathf.Max(1, num)), (num > 0) ? _now : _nowDim); } } private static void DrawDetail(Rect rect) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) if (_selected < 0 || _selected >= Cards.All.Count) { return; } Card card = Cards.All[_selected]; int num = ClientState.RankOf(card.Id); int num2 = Mathf.Max(1, RistConfig.MaxRank.Value); bool flag = num >= num2; Color color = GUI.color; GUI.color = Edge; GUI.Label(new Rect(((Rect)(ref rect)).x - 11f, ((Rect)(ref rect)).y, 1f, ((Rect)(ref rect)).height), GUIContent.none, _rule); GUI.color = color; float y = ((Rect)(ref rect)).y; float width = ((Rect)(ref rect)).width; GUI.Label(new Rect(((Rect)(ref rect)).x, y, width, 28f), card.Name, _dname); y += 30f; float num3 = _dflav.CalcHeight(new GUIContent(card.Flavour), width); GUI.Label(new Rect(((Rect)(ref rect)).x, y, width, num3), card.Flavour, _dflav); y += num3 + 16f; y = Row(((Rect)(ref rect)).x, y, width, "NOW", (num > 0) ? card.Describe(num) : "Not yet carved", _dnow); y = Row(((Rect)(ref rect)).x, y, width, "NEXT", flag ? "Fully carved" : (card.Describe(num + 1) + " at rank " + (num + 1)), _dnext); if (card.HasBonus) { int num4 = Card.BonusTimes(num); int num5 = Mathf.Max(1, RistConfig.BonusEvery.Value) * (num4 + 1); y = Row(((Rect)(ref rect)).x, y, width, "CAPSTONE", (num4 > 0) ? ("★ " + card.DescribeBonus(num4)) : ("★ " + card.DescribeBonus(1) + " at rank " + num5), _dca