Decompiled source of KebunUI v1.0.0
KebunUI.dll
Decompiled 5 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Net; using System.Net.Security; using System.Net.Sockets; using System.Net.WebSockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using KebunUI.Config; using KebunUI.Hud; using KebunUI.Kit; using KebunUI.Patches; using KebunUI.Services; using KebunUI.Utils; using KebunUI.Window; using Microsoft.CodeAnalysis; using ProjectM; using ProjectM.Network; using ProjectM.Scripting; using ProjectM.Shared; using ProjectM.UI; using Stunlock.Core; using Stunlock.Localization; using TMPro; using Unity.Collections; using Unity.Entities; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.InputSystem.Utilities; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Nyx")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Client UI for Bloodcraft V Rising servers, with the Kebun link on Kebun Crom.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("KebunUI")] [assembly: AssemblyTitle("KebunUI")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 KebunUI { public static class PluginInfo { public const string PLUGIN_NAME = "KebunUI"; public const string PLUGIN_VERSION = "1.0.0"; public const string PLUGIN_GUID = "KebunCrom.KebunUI"; public const string OLD_PLUGIN_GUID = "awanpixel.KebunUI"; } [BepInPlugin("KebunCrom.KebunUI", "KebunUI", "1.0.0")] [BepInProcess("VRising.exe")] public sealed class Plugin : BasePlugin { private static Plugin _self; private static Settings _settings; private static World _world; private static Harmony _fontHarmony; private static bool _ready; private static bool _clientBuild; private static bool _uiUp; private static bool _endInProgress; private readonly List<Harmony> _applied = new List<Harmony>(); private static bool _worldCaptured; private static Entity _localCharacter = Entity.Null; public static Plugin Instance => _self; public static Settings Settings => _settings; public static bool IsInitialized => _ready; public static bool IsClient => _clientBuild; public static bool UiStarted => _uiUp; public static bool IsGameDataInitialized { get { return _worldCaptured; } set { _worldCaptured = value; } } public static Entity LocalCharacter { get { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return _localCharacter; } set { //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) _localCharacter = value; } } internal static Harmony FontCapturePatchHarmony => _fontHarmony; public static bool HasActiveClientWorld { get { try { return _world != null && _world.IsCreated; } catch { return false; } } } internal static World ClientWorld { get { if (!HasActiveClientWorld) { return null; } return _world; } } public static EntityManager EntityManager { get { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!TryGetEntityManager(out var manager)) { throw new InvalidOperationException("No active client world"); } return manager; } } public static bool TryGetEntityManager(out EntityManager manager) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) manager = default(EntityManager); try { if (!HasActiveClientWorld) { return false; } manager = _world.EntityManager; return true; } catch { manager = default(EntityManager); return false; } } public override void Load() { string productName = Application.productName; _clientBuild = !string.Equals(productName, "VRisingServer"); LogUtils.Init(((BasePlugin)this).Log); _self = this; if (!_clientBuild) { LogUtils.LogInfo($"{"KebunUI"} {"1.0.0"} only runs on the game client, so it stays idle in {productName}"); return; } MigrateOldConfig(); _settings = new Settings().InitConfig(); KebunProtocolClient.LogSnapshots = () => Settings.PerfLog; _ready = true; TryPatch(typeof(SessionEndPatch), "escape-menu session reset"); TryPatch(typeof(HudVisibilityPatch), "UI canvas"); _fontHarmony = TryPatch(typeof(FontCapturePatch), "version label"); TryPatch(typeof(ChatReceivePatch), "chat bridge, Eclipse sync and Kebun link"); TryPatch(typeof(SessionStartPatch), "UI initialization"); TryPatch(typeof(GameplayInputCapturePatch), "input lock"); try { Application.quitting += Action.op_Implicit((Action)KebunLink.CloseForQuit); } catch (Exception ex) { LogUtils.LogWarning("[KebunLink] could not watch for the game quitting (" + ex.Message + "); the gate will see a read error on quit"); } LogUtils.LogInfo("Plugin KebunCrom.KebunUI v1.0.0 is loaded"); } private void MigrateOldConfig() { string text = Path.Combine(Paths.ConfigPath, "awanpixel.KebunUI.cfg"); string configFilePath = ((BasePlugin)this).Config.ConfigFilePath; if (!File.Exists(text) || File.Exists(configFilePath)) { return; } try { File.Copy(text, configFilePath); ((BasePlugin)this).Config.Reload(); } catch (Exception ex) { ((BasePlugin)this).Config.SaveOnConfigSet = false; try { File.Delete(configFilePath); } catch { } LogUtils.LogWarning($"could not move {"awanpixel.KebunUI"}.cfg ({ex.Message}); defaults this session, retrying next launch"); return; } try { File.Delete(text); } catch { } LogUtils.LogInfo("settings moved from awanpixel.KebunUI.cfg to KebunCrom.KebunUI.cfg"); } public override bool Unload() { foreach (Harmony item in _applied) { item.UnpatchSelf(); } EndClientSession("plugin unload"); return true; } private Harmony TryPatch(Type patchClass, string feature) { try { Harmony val = Harmony.CreateAndPatchAll(patchClass, (string)null); _applied.Add(val); return val; } catch (Exception ex) { LogUtils.LogError($"[Compat] {patchClass.Name} failed, so {feature} is off ({ex.GetType().Name}: {ex.Message}); a game update may have moved the target"); return null; } } internal static void GameDataOnInitialize(World world) { if (!_worldCaptured && _clientBuild && world != null && world.IsCreated) { _world = world; _worldCaptured = true; } } internal static bool MonitorClientSession() { if (_world == null) { return false; } if (HasActiveClientWorld) { return true; } EndClientSession("client world disposed"); return false; } internal static void EndClientSession(string reason = null) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (_endInProgress) { return; } _endInProgress = true; try { _localCharacter = Entity.Null; _world = null; _worldCaptured = false; _uiUp = false; Reset("messages", MessageService.Destroy); Reset("familiar state", FamiliarStateService.ResetSession); Reset("Eclipse sync", EclipseSync.Reset); Reset("Kebun link", delegate { KebunLink.Reset(); }); Reset("input lock", TypingInputLock.Release); Reset("shift pad binding", ShiftPadBinding.ResetSession); Reset("shift cooldown", ShiftCooldown.ResetSession); Reset("shift slot", ShiftSlotHud.ResetSession); Reset("kit HUDs", Huds.Stop); if (reason != null) { LogUtils.LogInfo("Client session ended: " + reason); } } finally { _endInProgress = false; } } private static void Reset(string part, Action reset) { try { reset(); } catch (Exception ex) { LogUtils.LogWarning("Session end: resetting " + part + " failed: " + ex.Message); } } internal static void UIOnInitialize() { _uiUp = true; try { Huds.Start(); } catch (Exception ex) { LogUtils.LogWarning("[HUD] could not start: " + ex.Message); } SessionTick.Start(); MessageService.StartPump(); FamiliarStateService.Initialize(); EclipseSync.Reset(); KebunLink.Reset(); TypingInputLock.Initialize(); ShiftPadBinding.Initialize(); ShiftCooldown.Initialize(); ShiftSlotHud.Initialize(); LogUtils.LogInfo("UI initialized"); if (Settings.PerfLog) { LogUtils.LogInfo($"[perf] per-frame hosts {KitHost.Tickers}"); } Perf.WatchFrames("spawn", 60f); Perf.StartMemoryLog(); } } } namespace KebunUI.Utils { public delegate void RefAction<T>(ref T item); internal static class EcsExtensions { private static int TypeIndexOf<T>() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) return TypeIndex.op_Implicit(new ComponentType(Il2CppType.Of<T>(), (AccessMode)0).TypeIndex); } private static ComponentType TypeOf<T>() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return new ComponentType(Il2CppType.Of<T>(), (AccessMode)0); } internal unsafe static T Read<T>(this Entity self) where T : struct { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Plugin.EntityManager; return Unsafe.Read<T>(((EntityManager)(ref entityManager)).GetComponentDataRawRO(self, TypeIndex.op_Implicit(TypeIndexOf<T>()))); } internal unsafe static void Write<T>(this Entity self, T value) where T : struct { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) int num = Marshal.SizeOf<T>(); byte* ptr = stackalloc byte[(int)(uint)num]; Marshal.StructureToPtr(value, (IntPtr)ptr, fDeleteOld: false); EntityManager entityManager = Plugin.EntityManager; ((EntityManager)(ref entityManager)).SetComponentDataRaw(self, TypeIndex.op_Implicit(TypeIndexOf<T>()), (void*)ptr, num); } internal unsafe static void With<T>(this Entity self, RefAction<T> edit) where T : struct { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Plugin.EntityManager; T item = Marshal.PtrToStructure<T>((IntPtr)((EntityManager)(ref entityManager)).GetComponentDataRawRW(self, TypeIndex.op_Implicit(TypeIndexOf<T>()))); edit(ref item); ((EntityManager)(ref entityManager)).SetComponentData<T>(self, item); } internal static bool Has<T>(this Entity self) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_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) EntityManager entityManager = Plugin.EntityManager; return ((EntityManager)(ref entityManager)).HasComponent(self, TypeOf<T>()); } internal static bool TryGetComponent<T>(this Entity self, out T value) where T : struct { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) bool flag = self.Has<T>(); value = (flag ? self.Read<T>() : default(T)); return flag; } internal static DynamicBuffer<T> ReadBuffer<T>(this Entity self) where T : struct { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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) EntityManager entityManager = Plugin.EntityManager; return ((EntityManager)(ref entityManager)).GetBuffer<T>(self, false); } internal static DynamicBuffer<T> AddBuffer<T>(this Entity self) where T : struct { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_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) EntityManager entityManager = Plugin.EntityManager; return ((EntityManager)(ref entityManager)).AddBuffer<T>(self); } internal static bool TryGetBuffer<T>(this Entity self, out DynamicBuffer<T> buffer) where T : struct { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Plugin.EntityManager; return ((EntityManager)(ref entityManager)).TryGetBuffer<T>(self, ref buffer); } internal static bool TryGetBufferReadOnly<T>(this Entity self, out DynamicBuffer<T> buffer) where T : struct { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) EntityManager entityManager = Plugin.EntityManager; return ((EntityManager)(ref entityManager)).TryGetBufferReadOnly<T>(self, ref buffer); } internal static void Add<T>(this Entity self) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_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) EntityManager entityManager = Plugin.EntityManager; ((EntityManager)(ref entityManager)).AddComponent(self, TypeOf<T>()); } internal static void Remove<T>(this Entity self) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_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) EntityManager entityManager = Plugin.EntityManager; ((EntityManager)(ref entityManager)).RemoveComponent(self, TypeOf<T>()); } internal static bool Exists(this Entity self) { //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_0018: 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) if (!Plugin.TryGetEntityManager(out var manager) || self == Entity.Null) { return false; } if ((uint)self.Index < (uint)((EntityManager)(ref manager)).EntityCapacity) { return ((EntityManager)(ref manager)).Exists(self); } return false; } } public static class LogUtils { private static ManualLogSource _sink; public static void Init(ManualLogSource source) { _sink = source; } public static void LogInfo(string message) { ManualLogSource sink = _sink; if (sink != null) { sink.LogInfo((object)message); } } public static void LogWarning(string message) { ManualLogSource sink = _sink; if (sink != null) { sink.LogWarning((object)message); } } public static void LogError(string message) { ManualLogSource sink = _sink; if (sink != null) { sink.LogError((object)message); } } } internal static class Perf { internal readonly struct Span : IDisposable { private readonly string _name; private readonly long _start; public Span(string name) { _name = name; _start = Stopwatch.GetTimestamp(); } public void Dispose() { double num = (double)(Stopwatch.GetTimestamp() - _start) * 1000.0 / (double)Stopwatch.Frequency; bool flag = _seen.Add(_name); if ((flag || num >= 20.0) && Settings.PerfLog) { LogUtils.LogInfo($"[perf] {_name} {num:F1} ms{(flag ? " (first)" : "")}"); } } } internal sealed class Phases { private readonly string _name; private readonly long _start; private long _last; private readonly StringBuilder _sb = new StringBuilder(); private int _g0 = GC.CollectionCount(0); private int _g1 = GC.CollectionCount(1); private int _g2 = GC.CollectionCount(2); private string Gc() { int num = GC.CollectionCount(0); int num2 = GC.CollectionCount(1); int num3 = GC.CollectionCount(2); string result = ((num != _g0 || num2 != _g1 || num3 != _g2) ? $"[gc {num - _g0}/{num2 - _g1}/{num3 - _g2}]" : ""); _g0 = num; _g1 = num2; _g2 = num3; return result; } public Phases(string name) { _name = name; _start = (_last = Stopwatch.GetTimestamp()); } public void Mark(string phase) { long timestamp = Stopwatch.GetTimestamp(); StringBuilder sb = _sb; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(2, 3, sb); handler.AppendLiteral(" "); handler.AppendFormatted(phase); handler.AppendLiteral("="); handler.AppendFormatted((double)(timestamp - _last) * 1000.0 / (double)Stopwatch.Frequency, "F1"); handler.AppendFormatted(Gc()); sb.Append(ref handler); _last = timestamp; } public void Done() { double num = (double)(Stopwatch.GetTimestamp() - _start) * 1000.0 / (double)Stopwatch.Frequency; bool flag = _seen.Add(_name); if ((flag || num >= 20.0) && Settings.PerfLog) { LogUtils.LogInfo($"[perf] {_name} {num:F1} ms:{_sb}{(flag ? " (first)" : "")}"); } } } private const double SlowMs = 20.0; private const double SlowFrameMs = 100.0; private static readonly HashSet<string> _seen = new HashSet<string>(); private static float _watchUntil; private static string _watchWhy; private static bool _hooked; private static readonly (int Frame, string Name)[] _notes = new(int, string)[8]; private static int _noteNext; private const float MemEverySeconds = 60f; private static float _nextMem; private static long _lastAlloc = -1L; private static int _redraws; private static double _redrawMax; private static int _memLines; private static bool _memHooked; public static Span Time(string name) { return new Span(name); } public static void Note(string name) { _notes[_noteNext % _notes.Length] = (Frame: Time.frameCount, Name: name); _noteNext++; } private static string RecentNotes(int frame) { StringBuilder stringBuilder = new StringBuilder(); for (int i = Math.Max(0, _noteNext - _notes.Length); i < _noteNext; i++) { var (num, value) = _notes[i % _notes.Length]; if (frame - num <= 3) { stringBuilder.Append((stringBuilder.Length == 0) ? "" : ", ").Append(value).Append('@') .Append(num - frame); } } if (stringBuilder.Length != 0) { return stringBuilder.ToString(); } return "nothing of ours in the last 3 frames"; } public static Phases Split(string name) { return new Phases(name); } private static void CountRedraw(double ms) { _redraws++; if (ms > _redrawMax) { _redrawMax = ms; } } public static void StartMemoryLog() { if (!_memHooked) { _memHooked = true; _nextMem = Time.realtimeSinceStartup + 60f; Binding.Timed = CountRedraw; SessionTick.Frame += OnMemTick; } } private static void OnMemTick() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextMem) { return; } float num = 60f + (realtimeSinceStartup - _nextMem); _nextMem = realtimeSinceStartup + 60f; if (!Settings.PerfLog) { _lastAlloc = -1L; return; } try { long totalAllocatedBytes = GC.GetTotalAllocatedBytes(); string value = ((_lastAlloc < 0) ? "n/a" : $"{(double)((float)(totalAllocatedBytes - _lastAlloc) / num) / 1024.0:F1} KB/s"); _lastAlloc = totalAllocatedBytes; Canvas canvas = KitHost.Canvas; int value2 = (Object.op_Implicit((Object)(object)canvas) ? ((Component)canvas).GetComponentsInChildren<Transform>(true).Length : 0); LogUtils.LogInfo($"[perf] mem clr {(double)GC.GetTotalMemory(forceFullCollection: false) / 1048576.0:F1} MB, alloc {value}, gc {GC.CollectionCount(0)}/{GC.CollectionCount(1)}/{GC.CollectionCount(2)}, ui {value2} objects, tex {(double)KitSprites.AtlasBytes() / 1048576.0:F1} MB, window {(CharacterWindow.IsOpen ? "open" : "closed")}, redraws {_redraws} (max {_redrawMax:F1} ms), link {KebunLink.State}"); } catch (Exception ex) { LogUtils.LogWarning("[perf] mem line failed: " + ex.Message); } if (++_memLines == 2) { try { long timestamp = Stopwatch.GetTimestamp(); long totalMemory = GC.GetTotalMemory(forceFullCollection: true); double value3 = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency; LogUtils.LogInfo($"[perf] heap after full collect {(double)totalMemory / 1048576.0:F1} MB ({value3:F0} ms)"); } catch (Exception ex2) { LogUtils.LogWarning("[perf] full collect failed: " + ex2.Message); } } _redraws = 0; _redrawMax = 0.0; } public static void WatchFrames(string why, float seconds = 5f) { if (!_hooked) { _hooked = true; SessionTick.Frame += OnFrame; } _watchWhy = why; _watchUntil = Math.Max(_watchUntil, Time.realtimeSinceStartup + seconds); if (Settings.PerfLog) { LogUtils.LogInfo("[perf] watching frames: " + why); } } private static void OnFrame() { if (!(Time.realtimeSinceStartup > _watchUntil)) { double num = (double)Time.unscaledDeltaTime * 1000.0; if (num >= 100.0 && Settings.PerfLog) { LogUtils.LogInfo($"[perf] slow frame {num:F0} ms (frame {Time.frameCount}, after {_watchWhy}; ran: {RecentNotes(Time.frameCount)})"); } } } } } namespace KebunUI.Window { internal static class ActRefusals { internal static string Text(string code) { return code switch { "feature_disabled" => "that system is off on this server", "invalid_args" => "the server did not recognise that choice", "no_class_yet" => "select a class first", "class_already_selected" => "you already have a class; change it instead", "class_buffs_on" => "turn class passives off before changing class", "shift_locked_off" => "turn the Shift spell on first", "inventory_full" => "free one inventory slot first (jewels are swapped)", "prestige_too_low" => "your prestige is too low for that spell", "not_configured" => "the server has no spell configured there", "stat_limit" => "slots full; reset choices first", "stat_already_chosen" => "that stat is already chosen", "no_track_data" => "no progress on that yet", "insufficient_items" => "you don't carry enough of the required item", "item_removal_failed" => "the item could not be taken; nothing changed", "stale_revision" => "page updated; review before retrying", "rate_limited" => "too fast; try again in a second", "no_character" => "your character is not loaded yet", "not_allowed" => "the server does not allow that here", "not_online" => "you are not fully in the world yet", "no_familiar_bound" => "no familiar is bound", "familiar_changed" => "your bound familiar changed; check it and try again (nothing was spent)", "same_school" => "it already has that school (nothing was spent)", "familiar_bound" => "unbind that familiar first", "not_owned" => "that familiar is not in your boxes any more", "no_such_box" => "that box does not exist any more", "box_exists" => "a box with that name already exists", "box_full" => "that box is full", "box_not_empty" => "empty the box before deleting it", "box_limit" => "you have the most boxes the server allows", "busy" => "a familiar is already being bound or unbound", "in_combat" => "not while you are in combat", "zone_refused" => "familiars can't be bound here", "familiar_dismissed" => "call your familiar back first", "familiar_busy" => "your familiar is busy; try again in a moment", "not_max_level" => "reach the maximum level first", "max_prestige" => "that is already at its highest prestige", "prestige_disabled" => "prestige is off on this server", "exo_needs_max_experience_prestige" => "Exo needs your experience prestige at its maximum", "prestige_failed" => "the prestige did not complete; check the page before trying again", "targets_respawning" => "no target is alive right now; they are respawning", "track_boss" => "track V Bloods with the game's V Blood menu", "not_kill_quest" => "that quest has no target to track", "cache_not_ready" => "the server is still finding targets; try again shortly", "no_quests" => "you have no quest of that kind", "owned" => "you already have that V Blood; Bloodcraft allows one of each", "banned" => "this server does not allow that V Blood as a familiar", "unavailable" => "the server can't offer that V Blood", "purchases_disabled" => "buying V Bloods is off on this server", "price_changed" => "the price changed; check it and try again (nothing was spent)", "no_token" => "you have no claim token", "token_tier" => "none of your tokens reaches a V Blood that high (Bronze below level 70, Silver below 80, Gold any)", "exo_locked" => "Exo form needs your first Exo prestige", "essence_missing" => "consume that boss's primordial essence first", _ => "not possible right now (" + (code ?? "no reason") + ")", }; } } internal sealed class BoardsPage : IWindowPage { private sealed class BoardItem { private readonly KText _label; private readonly KText _group; private readonly Image _bg; private readonly Button _button; private readonly Action<string> _select; private string _id; internal RectTransform Rect { get; } internal BoardItem(Transform parent, KScale s, Action<string> select) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_0114: Unknown result type (might be due to invalid IL or missing references) _select = select; Rect = Ui.Column(parent, 0f, fill: false); _group = W.Text((Transform)(object)Rect, s.FontSmall - 1f, KTheme.Gold, null, (FontStyles)1, (TextAlignmentOptions)4097); ((TMP_Text)_group.Tmp).characterSpacing = 6f; RectTransform val = W.Row((Transform)(object)Rect, 8f, s.Control - 4f, 10f); _bg = ((Component)val).gameObject.AddComponent<Image>(); _bg.sprite = KitSprites.Rounded; _bg.type = (Type)1; _button = ((Component)val).gameObject.AddComponent<Button>(); ((Selectable)_button).targetGraphic = (Graphic)(object)_bg; ((Selectable)_button).navigation = new Navigation { mode = (Mode)0 }; ((UnityEvent)_button.onClick).AddListener(UnityAction.op_Implicit((Action)delegate { if (_id != null) { _select(_id); } })); _label = W.Text((Transform)(object)val, s.FontSmall + 1f, KTheme.Bone, null, (FontStyles)0, (TextAlignmentOptions)4097); W.Flex(_label.Rect); } internal void DrawGroup(string name) { _id = null; ((Component)_group.Rect).gameObject.SetActive(true); ((Component)((Component)_bg).transform).gameObject.SetActive(false); _group.Set(name.ToUpperInvariant()); } internal void DrawBoard(string id, string label, bool selected) { //IL_0043: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) _id = id; ((Component)_group.Rect).gameObject.SetActive(false); ((Component)((Component)_bg).transform).gameObject.SetActive(true); ((Graphic)_bg).color = (selected ? KTheme.Selected : KTheme.Clear); _label.Color = (selected ? Color.white : KTheme.Bone); _label.Set(label); } } private sealed class RankLine { internal const float RankWidth = 54f; internal const float CountWidth = 90f; internal const float DateWidth = 170f; private readonly KText _rank; private readonly KText _name; private readonly KText _count; private readonly KText _date; internal RectTransform Rect { get; } internal RankLine(Transform parent, KScale s) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) Rect = W.Row(parent, 8f, s.Control - 4f); _rank = Cell((Transform)(object)Rect, s, 54f, (TextAlignmentOptions)4097); _name = W.Text((Transform)(object)Rect, s.FontSmall + 1f, KTheme.Bone, null, (FontStyles)0, (TextAlignmentOptions)4097); W.Flex(_name.Rect); _count = Cell((Transform)(object)Rect, s, 90f, (TextAlignmentOptions)4100); _date = Cell((Transform)(object)Rect, s, 170f, (TextAlignmentOptions)4100); } private static KText Cell(Transform row, KScale s, float width, TextAlignmentOptions align) { //IL_000d: 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) KText kText = W.Text(row, s.FontSmall + 1f, KTheme.Silver, null, (FontStyles)0, align); Ui.Size(kText.Rect, width, -1f); return kText; } internal void Draw(KebunSections.BoardRow r, bool shared, int? of, long since) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) _rank.Set((shared ? "=" : "") + r.Rank.ToString(CultureInfo.InvariantCulture)); _name.Set(string.IsNullOrEmpty(r.Name) ? "—" : r.Name); _name.Color = (r.Me ? KTheme.Gold : KTheme.Bone); _count.Set(Count(r.Count, of)); KText date = _date; long? at = r.At; string s; if (at.HasValue) { long valueOrDefault = at.GetValueOrDefault(); s = When(valueOrDefault); } else { s = "before " + Day(since); } date.Set(s); } } private readonly KScale _s; private readonly Frame _frame; private readonly string _section; private readonly bool _collection; private readonly KRows<BoardItem> _items; private readonly KText _title; private readonly KText _meta; private readonly KText _updated; private readonly KText _rule; private readonly KText _me; private readonly KText _emptyTitle; private readonly KText _emptyText; private readonly KText _countHead; private readonly KButton _refresh; private readonly RectTransform _board; private readonly RectTransform _empty; private readonly KRows<RankLine> _lines; private readonly KText _none; private string _selected; private float _receivedAt = -1f; private bool _shown; private bool _ticking; private long _second; private static readonly string[] LeaderboardGroups = new string[4] { "General", "Weapons", "Blood", "Other" }; private static readonly string[] CollectionGroups = new string[3] { "Familiars", "By kind", "Other" }; private static readonly Regex _camel = new Regex("(?<=[a-z])(?=[A-Z])"); public RectTransform Root { get; } private KebunSections.Boards Data { get { if (!_collection) { return KebunStore.Leaderboard; } return KebunStore.Collection; } } private string[] GroupNames { get { if (!_collection) { return LeaderboardGroups; } return CollectionGroups; } } internal BoardsPage(Transform host, Frame frame, KScale s, bool collection) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) //IL_04a8: Unknown result type (might be due to invalid IL or missing references) //IL_0504: Unknown result type (might be due to invalid IL or missing references) //IL_053e: Unknown result type (might be due to invalid IL or missing references) BoardsPage boardsPage = this; _s = s; _frame = frame; frame.VisibilityChanged += OnFrameVisible; _collection = collection; _section = (collection ? "collection" : "leaderboard"); Root = Ui.Row(host, s.Gap); Root.anchorMin = Vector2.zero; Root.anchorMax = Vector2.one; Root.offsetMin = (Root.offsetMax = Vector2.zero); ((HorizontalOrVerticalLayoutGroup)((Component)Root).GetComponent<HorizontalLayoutGroup>()).childForceExpandHeight = true; RectTransform obj = W.Panel((Transform)(object)Root, 8f, 2f); Ui.Size(obj, 220f, -1f); W.Flex(obj, 0f, 1f); KScroll kScroll = new KScroll((Transform)(object)obj, 2f); W.Flex(kScroll.Rect, 1f, 1f); _items = new KRows<BoardItem>((Transform)(object)kScroll.Content, (Transform p) => new BoardItem(p, s, boardsPage.Select), (BoardItem r) => r.Rect); RectTransform parent = W.Flex(W.Column((Transform)(object)Root, s.Gap), 1f, 1f); _empty = W.Panel((Transform)(object)parent, s.Pad + 6f, 4f); _emptyTitle = W.Text((Transform)(object)_empty, s.FontBody + 1f, KTheme.Bone, null, (FontStyles)0, (TextAlignmentOptions)4097); _emptyText = W.Text((Transform)(object)_empty, s.FontSmall + 1f, KTheme.Silver, null, (FontStyles)0, (TextAlignmentOptions)4097); _emptyText.Wrap = true; _board = W.Flex(W.Column((Transform)(object)parent, s.Gap), 1f, 1f); RectTransform parent2 = W.Row((Transform)(object)_board, 10f); RectTransform parent3 = W.Flex(W.Column((Transform)(object)parent2, 2f)); _title = W.Text((Transform)(object)parent3, s.FontTitle + 2f, KTheme.Bone, null, (FontStyles)0, (TextAlignmentOptions)4097); _meta = W.Text((Transform)(object)parent3, s.FontSmall, KTheme.Muted, null, (FontStyles)0, (TextAlignmentOptions)4097); _updated = W.Text((Transform)(object)parent2, s.FontSmall, KTheme.Silver, null, (FontStyles)0, (TextAlignmentOptions)4100); _refresh = new KButton((Transform)(object)parent2, s.Control - 4f, KButtonStyle.Secondary, armed: false, delegate { boardsPage.Fetch(); }) { Text = "Refresh" }; Ui.Size(_refresh.Rect, 104f, s.Control - 4f); _rule = W.Text((Transform)(object)_board, s.FontSmall, KTheme.Silver, null, (FontStyles)0, (TextAlignmentOptions)4097); _rule.Wrap = true; RectTransform parent4 = W.Flex(W.Panel((Transform)(object)_board, s.Pad, 2f), 1f, 1f); RectTransform row = W.Row((Transform)(object)parent4, 8f, s.Control - 6f); Head((Transform)(object)row, "Rank", 54f, (TextAlignmentOptions)4097); Head((Transform)(object)row, "Character", 0f, (TextAlignmentOptions)4097); _countHead = Head((Transform)(object)row, collection ? "Owned" : "Prestige", 90f, (TextAlignmentOptions)4100); Head((Transform)(object)row, "Reached", 170f, (TextAlignmentOptions)4100); Ui.Fill(Ui.Size(Ui.Node((Transform)(object)parent4, "Rule"), -1f, 1f), "Line", KTheme.Hex(2896436)); _lines = new KRows<RankLine>((Transform)(object)parent4, (Transform p) => new RankLine(p, s), (RankLine r) => r.Rect); _none = W.Text((Transform)(object)parent4, s.FontSmall + 1f, KTheme.Silver, null, (FontStyles)2, (TextAlignmentOptions)4097); _none.Wrap = true; _me = W.Text((Transform)(object)_board, s.FontSmall + 1f, KTheme.Bone, null, (FontStyles)0, (TextAlignmentOptions)4097); ((Component)Root).gameObject.SetActive(false); } private KText Head(Transform row, string text, float width, TextAlignmentOptions align) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) KText kText = W.Text(row, _s.FontSmall - 1f, KTheme.Muted, text, (FontStyles)1, align); if (width > 0f) { Ui.Size(kText.Rect, width, -1f); } else { W.Flex(kText.Rect); } return kText; } public void SetShown(bool shown) { _shown = shown; ((Component)Root).gameObject.SetActive(shown); if (shown) { KebunStore.Changed += OnStore; if (!_ticking) { KitHost.Tick += Tick; _ticking = true; } Fetch(); Draw(); } else { Unhook(); } } public void Dispose() { Unhook(); _frame.VisibilityChanged -= OnFrameVisible; } private void OnFrameVisible(bool visible) { if (visible && _shown) { Fetch(); Draw(); } } private void Unhook() { KebunStore.Changed -= OnStore; if (_ticking) { KitHost.Tick -= Tick; _ticking = false; } } private void Fetch() { KebunLink.Pull(_section); } private void OnStore(string section) { if (section == _section) { _receivedAt = Time.unscaledTime; } if (section == "link" && _shown && KebunLink.Connected) { Fetch(); } if (section == _section || section == "link") { Draw(); } } private void Tick() { if (((Component)Root).gameObject.activeInHierarchy) { long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); if (num != _second) { _second = num; DrawUpdated(); } } } private void DrawUpdated() { if (_receivedAt < 0f) { _updated.Set(""); return; } int num = (int)(Time.unscaledTime - _receivedAt); _updated.Set((num < 5) ? "Updated just now" : ((num < 60) ? $"Updated {num} s ago" : $"Updated {num / 60} min ago")); } private void Select(string id) { if (!(_selected == id)) { _selected = id; Draw(); } } private void Draw() { if (!_shown) { return; } KebunSections.Boards data = Data; bool flag = KebunLink.Offers(_section); bool flag2 = KebunLink.Unavailable(_section); ((Component)_refresh.Rect).gameObject.SetActive(flag && !flag2); DrawUpdated(); if (data == null) { _items.Trim(0); ((Component)_board).gameObject.SetActive(false); ((Component)_empty).gameObject.SetActive(true); if (!KebunLink.Connected) { _emptyTitle.Set("Rankings unavailable · disconnected"); _emptyText.Set(""); } else if (!flag) { _emptyTitle.Set("Rankings unavailable on this server."); _emptyText.Set(""); } else if (flag2) { _emptyTitle.Set("Rankings unavailable"); _emptyText.Set("Rejoin to load rankings again."); } else { _emptyTitle.Set("Loading rankings…"); _emptyText.Set(""); } return; } ((Component)_empty).gameObject.SetActive(false); ((Component)_board).gameObject.SetActive(true); List<(int, int, KebunSections.Board)> list = new List<(int, int, KebunSections.Board)>(); for (int i = 0; i < data.List.Count; i++) { list.Add((GroupOf(data.List[i].Id), i, data.List[i])); } list.Sort(((int Group, int Index, KebunSections.Board Board) a, (int Group, int Index, KebunSections.Board Board) b) => (a.Group == b.Group) ? a.Index.CompareTo(b.Index) : a.Group.CompareTo(b.Group)); if (_selected == null || !data.List.Exists((KebunSections.Board b) => b.Id == _selected)) { _selected = ((list.Count > 0) ? list[0].Item3.Id : null); } int n = 0; int num = -1; KebunSections.Board board = null; foreach (var (num2, _, board2) in list) { if (num2 != num) { _items.Show(n++).DrawGroup(GroupNames[num2]); num = num2; } _items.Show(n++).DrawBoard(board2.Id, Label(board2.Id), board2.Id == _selected); if (board2.Id == _selected) { board = board2; } } _items.Trim(n); if (board == null) { _title.Set(_collection ? "No boards yet" : "No one has prestiged yet"); _meta.Set(""); _rule.Set(""); _lines.Trim(0); ((Component)_none.Rect).gameObject.SetActive(true); _none.Set(_collection ? "The collection boards appear once the server has counted them." : "A board appears once someone earns their first prestige in that track."); _me.Set(""); return; } _title.Set(Label(board.Id)); KText meta = _meta; object s; if (!_collection) { s = "Prestige count · highest first · only players with at least one"; } else { int? of = board.Of; if (of.HasValue) { int valueOrDefault = of.GetValueOrDefault(); s = $"Different V Bloods owned, out of {valueOrDefault} · highest first"; } else { s = "Different familiars owned · highest first"; } } meta.Set((string)s); _rule.Set("Ties: earliest first. Equal counts before " + Day(data.Since) + " share a rank."); _countHead.Set(_collection ? "Owned" : "Prestige"); HashSet<int> hashSet = new HashSet<int>(); HashSet<int> hashSet2 = new HashSet<int>(); foreach (KebunSections.BoardRow item in board.Top) { if (!hashSet2.Add(item.Rank)) { hashSet.Add(item.Rank); } } if (board.Me != null && !hashSet2.Add(board.Me.Rank)) { hashSet.Add(board.Me.Rank); } for (int num3 = 0; num3 < board.Top.Count; num3++) { _lines.Show(num3).Draw(board.Top[num3], hashSet.Contains(board.Top[num3].Rank), board.Of, data.Since); } _lines.Trim(board.Top.Count); ((Component)_none.Rect).gameObject.SetActive(board.Top.Count == 0); if (board.Top.Count == 0) { _none.Set(_collection ? "No one owns one of these yet." : "No one has prestiged in this track yet."); } KebunSections.BoardRow boardRow = board.Me ?? board.Top.Find((KebunSections.BoardRow r) => r.Me); KText me = _me; object s2; if (boardRow == null) { s2 = (_collection ? "You're not on this board yet." : "Earn a prestige to appear here."); } else { DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(48, 4); defaultInterpolatedStringHandler.AppendLiteral("Your rank: <color=#D0AA70>"); defaultInterpolatedStringHandler.AppendFormatted(hashSet.Contains(boardRow.Rank) ? "=" : "#"); defaultInterpolatedStringHandler.AppendFormatted(boardRow.Rank); defaultInterpolatedStringHandler.AppendLiteral("</color> · "); defaultInterpolatedStringHandler.AppendFormatted(Count(boardRow.Count, board.Of)); defaultInterpolatedStringHandler.AppendLiteral(" · "); defaultInterpolatedStringHandler.AppendLiteral("reached "); long? at = boardRow.At; string value; if (at.HasValue) { long valueOrDefault2 = at.GetValueOrDefault(); value = When(valueOrDefault2); } else { value = "before " + Day(data.Since); } defaultInterpolatedStringHandler.AppendFormatted(value); s2 = defaultInterpolatedStringHandler.ToStringAndClear(); } me.Set((string)s2); } private int GroupOf(string id) { if (_collection) { if ((id == "All" || id == "VBlood") ? true : false) { return 0; } bool flag; switch (id) { case "Human": case "Undead": case "Demon": case "Mechanical": case "Beast": flag = true; break; default: flag = false; break; } return flag ? 1 : 2; } if ((!(id == "Experience") && !(id == "Exo")) || 1 == 0) { if (!id.EndsWith("Expertise", StringComparison.Ordinal)) { if (!id.EndsWith("Legacy", StringComparison.Ordinal)) { return 3; } return 2; } return 1; } return 0; } private string Label(string id) { if (_collection) { if (!(id == "All")) { if (id == "VBlood") { return "V Bloods"; } return id; } return "All familiars"; } string text; if (!id.EndsWith("Expertise", StringComparison.Ordinal)) { if (!id.EndsWith("Legacy", StringComparison.Ordinal)) { text = id; } else { string text2 = id; text = text2.Substring(0, text2.Length - 6); } } else { string text2 = id; text = text2.Substring(0, text2.Length - 9); } string input = text; return _camel.Replace(input, " "); } private static string Count(int count, int? of) { if (of.HasValue) { int valueOrDefault = of.GetValueOrDefault(); return $"{count} / {valueOrDefault}"; } return count.ToString(CultureInfo.InvariantCulture); } private static string When(long unix) { return DateTimeOffset.FromUnixTimeSeconds(unix).ToLocalTime().ToString("d MMM yyyy, HH:mm", CultureInfo.InvariantCulture); } private static string Day(long unix) { if (unix > 0) { return DateTimeOffset.FromUnixTimeSeconds(unix).ToLocalTime().ToString("d MMM yyyy", CultureInfo.InvariantCulture); } return "records began"; } } internal enum FallbackActions { Chat, ReadOnly } internal enum ActionMode { Act, Chat, Paused, Hidden } internal static class CharacterActions { private const float ChatWatchSeconds = 10f; private static ConfigEntry<FallbackActions> _fallback; private static string _pendingId; private static string _pendingKey; private static float _chatSentAt = -1f; private static bool _hooked; internal static string Status { get; private set; } internal static FallbackActions Fallback { get { return _fallback?.Value ?? FallbackActions.Chat; } set { if (_fallback != null && _fallback.Value != value) { _fallback.Value = value; SetStatus(Status); TopBar.Refresh(); } } } internal static string Section { get; private set; } = "char"; internal static bool OnLink => KebunStore.Live(Section); internal static ActionMode Mode { get { if (KebunStore.State == KebunLinkState.Reconnecting && OnLink) { return ActionMode.Paused; } if (KebunStore.State == KebunLinkState.Live && OnLink) { return ActionMode.Act; } if (Fallback != FallbackActions.Chat || KebunStore.Eclipse.Config == null) { return ActionMode.Hidden; } return ActionMode.Chat; } } internal static bool Busy { get { if (_pendingId == null) { return _chatSentAt >= 0f; } return true; } } internal static string PendingKey => _pendingKey; internal static event Action Changed; internal static event Action<string, string, string, Dictionary<string, object>> Answered; internal static void Bind(ConfigFile cfg) { if (_fallback == null) { _fallback = cfg.Bind<FallbackActions>("KebunWindow", "FallbackActions", FallbackActions.Chat, "What the Character window's buttons do when the Kebun link is not serving it. Chat sends the plain chat command and the Eclipse feed shows the result; ReadOnly hides the buttons."); } if (_hooked) { return; } _hooked = true; KebunLink.ActOutcome += OnOutcome; KebunLink.Outcome += OnSetOutcome; KebunStore.Eclipse.Changed += OnEclipse; KitHost.Tick += WatchChat; KebunStore.Changed += delegate(string s) { if (s == "link") { OnLinkChanged(); } }; } internal static void ServeFrom(string section) { if (!(section == Section)) { Section = section ?? "char"; SetStatus(null); } } internal static bool Send(string key, string act, Dictionary<string, object> args, string chat, string doing) { if (Busy) { return false; } switch (Mode) { case ActionMode.Act: { string text = KebunLink.ActId(act, args); if (text == null) { SetStatus("The server does not offer that here."); return false; } _pendingId = text; _pendingKey = key; SetStatus(doing + "…"); return true; } case ActionMode.Chat: if (chat != null) { MessageService.EnqueueMessage(chat); _chatSentAt = Time.unscaledTime; _pendingKey = key; SetStatus("Sent " + chat + " · waiting for update…"); return true; } break; } return false; } internal static bool Offered(string act, bool chatHasCommand) { return Mode switch { ActionMode.Act => KebunLink.OffersAct(act), ActionMode.Chat => chatHasCommand, ActionMode.Paused => true, _ => false, }; } private static void OnOutcome(string id, string action, string status, string code, Dictionary<string, object> data) { if (id == null || id != _pendingId) { return; } string pendingKey = _pendingKey; _pendingId = null; _pendingKey = null; SetStatus((status == "ok") ? "Done." : ((status == "unknown") ? "No answer from the server. Check the page before trying again." : (Capitalise(ActRefusals.Text(code)) + "."))); try { CharacterActions.Answered?.Invoke(pendingKey, status, code, data); } catch (Exception ex) { KitLog.Warn("action answer handler failed: " + ex.Message); } } private static void OnSetOutcome(string key, string status, string code) { if (!(status == "ok")) { string text = key switch { "shiftLock" => "the Shift spell", "exoTaunt" => "Exo form's Taunt", "shroud" => "Permashroud", _ => null, }; if (text != null) { SetStatus((status == "unknown") ? ("No answer from the server about " + text + ". Check the switch before trying again.") : (char.ToUpperInvariant(text[0]) + text.Substring(1) + ": " + ActRefusals.Text(code) + ".")); } } } private static void OnEclipse(string section) { if (!(_chatSentAt < 0f) && !(section != "char")) { _chatSentAt = -1f; _pendingKey = null; SetStatus("Updated."); } } private static void WatchChat() { if (!(_chatSentAt < 0f) && !(Time.unscaledTime - _chatSentAt < 10f)) { _chatSentAt = -1f; _pendingKey = null; SetStatus("No update seen. Check chat before trying again."); } } internal static void OnLinkChanged() { if (_pendingId != null && KebunStore.State != KebunLinkState.Live) { _pendingId = null; _pendingKey = null; SetStatus("Connection lost before the server answered. Check the page before trying again."); } } internal static void Say(string s) { SetStatus(s); } private static void SetStatus(string s) { Status = s; try { CharacterActions.Changed?.Invoke(); } catch (Exception ex) { KitLog.Warn("action status redraw failed: " + ex.Message); } } private static string Capitalise(string s) { if (!string.IsNullOrEmpty(s)) { return char.ToUpperInvariant(s[0]) + s.Substring(1); } return s; } } internal enum CharacterTab { Class, Weapon, Blood, Professions } internal enum WindowSection { Overview, Character, Familiars, Quests, Prestige, Settings } internal static class CharacterWindow { private static ConfigEntry<Density> _density; private static ConfigEntry<CharacterTab> _tab; private static ConfigEntry<WindowSection> _section; private static ConfigEntry<int> _opacity; private static Frame _frame; private static Shell _shell; internal static bool IsOpen => _frame?.Visible ?? false; internal static Density Density { get { return _density?.Value ?? Density.Compact; } set { if (_density != null && _density.Value != value) { _density.Value = value; if (_shell != null) { WindowSection section = _shell.Section; CharacterTab tab = _shell.Tab; _shell.Dispose(); _shell = new Shell(_frame, value, section, tab); } } } } internal static int OpacityPercent { get { return Mathf.Clamp(_opacity?.Value ?? 100, 60, 100); } set { int num = Mathf.Clamp(value / 5 * 5, 60, 100); if (_opacity != null) { _opacity.Value = num; } if (_frame != null) { _frame.Opacity = (float)num / 100f; } } } internal static void Bind(ConfigFile cfg) { if (_density == null) { _density = cfg.Bind<Density>("KebunWindow", "Density", Density.Compact, "The Character window's density."); } if (_tab == null) { _tab = cfg.Bind<CharacterTab>("KebunWindow", "Tab", CharacterTab.Weapon, "The Character window's last tab."); } if (_section == null) { _section = cfg.Bind<WindowSection>("KebunWindow", "Section", WindowSection.Character, "The window's last section."); } if (_opacity == null) { _opacity = cfg.Bind<int>("KebunWindow", "Opacity", 100, "The window's backing opacity, whole percent (60-100)."); } CharacterActions.Bind(cfg); } internal static void Show() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if (!KitHost.Ready || _density == null) { return; } try { if (_frame == null) { _frame = Frame.Create(new FrameSpec { LayoutKey = "KebunWindow", Title = "Character window", DefaultSize = new Vector2(1240f, 800f), MinSize = new Vector2(1100f, 640f), Anchor = new Vector2(0.5f, 0.5f), Pivot = new Vector2(0.5f, 0.5f), DefaultPos = Vector2.zero, Framed = true, Resizable = true, Background = KTheme.WindowBg }); _shell = new Shell(_frame, _density.Value, _section.Value, _tab.Value); _frame.VisibilityChanged += delegate(bool visible) { if (!visible) { _shell?.ReleaseHidden(); } }; if (OpacityPercent < 100) { _frame.Opacity = (float)OpacityPercent / 100f; } } _frame.Show(); } catch (Exception value) { KitLog.Warn($"opening the Character window failed: {value}"); } } internal static void Hide() { _frame?.Hide(); } internal static void Toggle() { if (IsOpen) { Hide(); } else { Show(); } } internal static void Open(WindowSection section, CharacterTab? tab = null) { Show(); if (IsOpen) { _shell?.Go(section, tab); _frame.BringToFront(); } } internal static void OpenEchoes() { Open(WindowSection.Familiars); if (IsOpen) { _shell?.SetShop(shop: true); } } internal static void RememberTab(CharacterTab tab) { if (_tab != null) { _tab.Value = tab; } } internal static void RememberSection(WindowSection section) { if (_section != null) { _section.Value = section; } } } internal sealed class Shell { private static readonly (string Eyebrow, string Title, string Sub, string Help, string HelpText, string Note)[] Heads = new(string, string, string, string, string, string)[4] { ("Character / Class", "Choose your path", "Your class sets stat synergy, your on-hit sequence and your Shift spell.", "How classes work", "Your class multiplies its synergy stats when you choose them for a weapon or blood slot; synergy does not grant a stat by itself. Dealing damage can trigger the class's on-hit sequence. Shift spells stay equipped when you switch weapons, and each slot shows the experience prestige that unlocks it. Turning the Shift slot on or changing its spell needs one free inventory slot.", "Select a class to inspect it; your current class is marked."), ("Character / Arsenal", "Master your weapons", "Every weapon you have trained, with its chosen stats.", "How stats work", "Each weapon levels on its own. Choose stats for it up to its slot count; a choice is permanent until you reset that weapon, which costs what the button shows. Values are Bloodcraft's bonus from this weapon, not your total. Your class's synergy stats count extra when chosen.", "Select a track to inspect; actions apply to that track."), ("Character / Legacy", "Deepen your blood", "Every blood legacy you have fed, with its chosen stats.", "How legacies work", "Each blood type has its own legacy level. Choose stats for it up to its slot count; a choice is permanent until you reset that legacy. Values are Bloodcraft's bonus from this legacy, not your total. Your class's synergy stats count extra when chosen.", "Select a legacy to inspect; actions apply to that legacy."), ("Character / Crafts", "Your professions", "Levels and progress for all eight professions.", "How professions work", "Professions level from the work itself: gathering, crafting and fishing. Bonuses grow with level; max is their strength at level 100. Each roll is a chance for an extra drop, not a sure one. Holy potions gain duration only.", "Professions level as you gather and craft.") }; private static readonly (string Eyebrow, string Title, string Sub, string Help, string HelpText, string Note)?[] SectionHeads = new(string, string, string, string, string, string)?[6] { ("Overview", "Your chronicle", "Where you stand, and what is waiting on you.", "How this page works", "Everything here is what the server already sends. Each line under \"Things to do\" opens the page that acts on it; nothing on this page changes anything by itself except the familiar buttons.", null), null, ("Familiars / Boxes", "Your familiars", "Every familiar you have unlocked, box by box.", "How familiars work", "Each box holds up to ten familiars. Bind summons one to fight beside you (the one out is unbound first). Move and delete act on the familiar you selected, wherever it is, so nothing shifts under you. Delete is permanent, needs two clicks, and refuses the bound familiar. Quick summon and the padlock are KebunUI's own: the padlock only stops a misclick here, never a command typed in chat. Familiar prestige is on the Prestige page.", null), ("Quests / Daily & weekly", "Your hunts", "Today's and this week's quests, and where to find the target.", "How quests work", "The daily quest refreshes every day and the weekly one every week, when the time shown runs out. Track asks the server for the nearest living target of an ordinary kill quest and tells you its distance and direction. V Bloods are tracked with the game's own V Blood menu.", "Track points you at the nearest target of a kill quest."), ("Prestige", "Begin again, stronger", "Reset a finished track for a permanent benefit.", "How prestige works", "Prestige is optional. It resets one finished track (your level, one weapon, one blood or your familiar) in exchange for a permanent benefit, and it cannot be undone. Every prestige here needs two clicks, and the button says exactly what it costs and what it resets. In chat, the Exo and Shroud switches are .prestige exoform and .prestige shroud.", "Select a track to see what its prestige does."), ("Settings", "Settings", "Saved as you change them.", "About these settings", "Settings are kept in KebunUI's config file on your PC. The switches under \"Progress in chat\" are the server's own settings, the same ones the chat commands change, so they show \"…\" until the server confirms.", null) }; private static readonly (string Eyebrow, string Title, string Sub, string Help, string HelpText, string Note) StatsHead = (Eyebrow: "Overview / Stats", Title: "Your stats", Sub: "Every stat as the server computes it.", Help: "How stats work", HelpText: "Base & gear + Bloodcraft bonus = Total, as the server reports it. Base & gear covers your gear, blood and passives. Temporary spell buffs are not included. Tested on our server against a training dummy: with Spell Crit Damage at 401%, three of four crits hit for 4.01× a normal hit (one for 4.50×), past the game's usual 300% limit. Only that stat was tested, so judge the rest for yourself.", Note: null); private static readonly (string Eyebrow, string Title, string Sub, string Help, string HelpText, string Note) BoardsHead = (Eyebrow: "Prestige / Boards", Title: "Leaderboards", Sub: "Bloodcraft's prestige boards and Kebun Crom's familiar collection. Top 10 each.", Help: "How the boards work", HelpText: "Highest first. Each prestige track has its own board: Experience, Exo, every weapon and every blood; you need at least one prestige in a track to appear on it. Collection counts the different familiars you own, not levels or copies. Same count? Whoever reached it first ranks higher, and the Reached column shows when, so catching up later doesn't overtake. Counts reached before KebunUI started recording dates show \"before\" that day and share a rank. Admin-only accounts can be left off the boards.", Note: null); private static readonly (string Eyebrow, string Title, string Sub, string Help, string HelpText, string Note) EchoesHead = (Eyebrow: "Familiars / Echoes", Title: "The Echoes shop", Sub: "Every V Blood this server offers as a familiar, and which ones you already have.", Help: "How the shop works", HelpText: "Each V Blood can be unlocked as a familiar once. The shop lists them all and ticks the ones already in your boxes: those can't be bought or claimed again (Bloodcraft's rule). The price grows with the V Blood's level and is paid in the currency Exo prestige awards. A claim token takes any one you don't own, for free. The server rechecks everything when you buy, and a V Blood you get goes into your last box, or a new box when that one is full.", Note: "Select a V Blood to see its price."); private readonly Frame _frame; private readonly KScale _s; private readonly RectTransform _root; private readonly KChip _chip; private readonly KText _crumb; private readonly KText _eyebrow; private readonly KText _title; private readonly KText _sub; private readonly KText _tabNote; private readonly KText _note; private readonly KText _source; private readonly KText _status; private readonly KText _mode; private readonly RectTransform _noteBox; private readonly Image _noteBg; private readonly KHelp _help; private readonly KSegmented _tabs; private readonly KSegmented _famTabs; private readonly KSegmented _ovTabs; private readonly KSegmented _prTabs; private IWindowPage _echoes; private IWindowPage _stats; private IWindowPage _leaderboard; private IWindowPage _collection; private int _prSub; private bool _shop; private bool _ovStats; private readonly SummaryCard _class; private readonly SummaryCard _weapon; private readonly SummaryCard _blood; private readonly SummaryCard _familiar; private readonly RectTransform _summary; private readonly RectTransform _tabRow; private readonly RectTransform _pageHost; private readonly IWindowPage[] _pages; private readonly IWindowPage[] _sectionPages = new IWindowPage[6]; private readonly (Image Bg, Image[] Edges, KText Label)[] _nav = new(Image, Image[], KText)[6]; private readonly Binding _binding; private readonly KButton _scaleButton; private CharacterTab _tab; private WindowSection _section = (WindowSection)(-1); internal CharacterTab Tab => _tab; internal WindowSection Section => _section; private static string ServedFrom(WindowSection section) { return section switch { WindowSection.Familiars => "boxes", WindowSection.Quests => "quests", _ => "char", }; } internal Shell(Frame frame, Density density, WindowSection section, CharacterTab tab) { //IL_00b5: 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_011c: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_0605: Unknown result type (might be due to invalid IL or missing references) //IL_0644: Unknown result type (might be due to invalid IL or missing references) //IL_06a7: Unknown result type (might be due to invalid IL or missing references) //IL_07c4: Unknown result type (might be due to invalid IL or missing references) //IL_07d0: Unknown result type (might be due to invalid IL or missing references) //IL_080f: Unknown result type (might be due to invalid IL or missing references) //IL_0833: Unknown result type (might be due to invalid IL or missing references) //IL_0872: Unknown result type (might be due to invalid IL or missing references) _frame = frame; _s = KTheme.Scale(density); _root = Ui.Column((Transform)(object)frame.Content, 0f); ((Object)_root).name = "Shell"; RectTransform val = W.Row((Transform)(object)_root, 18f, 50f, 14f); _frame.DragHandle = val; Ui.Icon((Transform)(object)val, KitSprites.Cell("kebun_mark.png", 0, 0), 38f); ((TMP_Text)W.Text((Transform)(object)val, _s.FontTitle + 4f, KTheme.Bone, "KEBUNUI", (FontStyles)1, (TextAlignmentOptions)4097).Tmp).characterSpacing = 10f; ((TMP_Text)W.Text((Transform)(object)val, _s.FontTitle + 4f, KTheme.Ember, "BLOODCRAFT", (FontStyles)1, (TextAlignmentOptions)4097).Tmp).characterSpacing = 10f; _crumb = W.Text((Transform)(object)val, _s.FontBody, KTheme.Silver, null, (FontStyles)0, (TextAlignmentOptions)4097); W.Spacer((Transform)(object)val); _chip = new KChip((Transform)(object)val, 30f); _scaleButton = new KButton((Transform)(object)val, 30f, KButtonStyle.Subtle, armed: false, delegate { UiScale.Next(); }); DrawScale(); UiScale.Changed += DrawScale; Ui.IconButton((Transform)(object)val, KGlyph.Close, 30f, CharacterWindow.Hide).Tip = "Close"; Ui.Fill(Ui.Size(Ui.Node((Transform)(object)_root, "Rule"), -1f, 1f), "Line", KTheme.Hex(7756345)); RectTransform val2 = W.Flex(Ui.Row((Transform)(object)_root, 0f), 1f, 1f); ((HorizontalOrVerticalLayoutGroup)((Component)val2).GetComponent<HorizontalLayoutGroup>()).childForceExpandHeight = true; BuildRail((Transform)(object)val2); RectTransform val3 = W.Flex(W.Column((Transform)(object)val2, _s.Gap), 1f, 1f); W.Pad((HorizontalOrVerticalLayoutGroup)(object)((Component)val3).GetComponent<VerticalLayoutGroup>(), _s.Pad + 8f, _s.Pad + 8f, _s.Pad, _s.Pad); RectTransform val4 = W.Row((Transform)(object)val3, 12f); RectTransform parent = W.Flex(W.Column((Transform)(object)val4, 2f)); _eyebrow = W.Eyebrow((Transform)(object)parent, _s.FontSmall); _title = W.Text((Transform)(object)parent, _s.FontTitle + 11f, KTheme.Bone, null, (FontStyles)0, (TextAlignmentOptions)4097); Ui.Size(_title.Rect, -1f, _s.FontTitle + 18f); _sub = W.Text((Transform)(object)parent, _s.FontBody, KTheme.Silver, null, (FontStyles)0, (TextAlignmentOptions)4097); _help = new KHelp((Transform)(object)val4, _frame.Rect, _s.Control); ((LayoutGroup)((Component)val4).GetComponent<HorizontalLayoutGroup>()).childAlignment = (TextAnchor)0; RectTransform val5 = (_summary = W.Row((Transform)(object)val3, _s.Gap)); ((HorizontalOrVerticalLayoutGroup)((Component)val5).GetComponent<HorizontalLayoutGroup>()).childForceExpandHeight = true; W.Flex(val5); _class = new SummaryCard((Transform)(object)val5, _s, "Class"); _weapon = new SummaryCard((Transform)(object)val5, _s, "Equipped weapon"); _blood = new SummaryCard((Transform)(object)val5, _s, "Blood"); _familiar = new SummaryCard((Transform)(object)val5, _s, "Familiar"); RectTransform parent2 = (_tabRow = W.Row((Transform)(object)val3, 10f, _s.Control + 6f)); _tabs = new KSegmented((Transform)(object)parent2, new string[4] { "Class", "Weapon", "Blood", "Professions" }, tabs: true, _s.Control + 6f, _s.FontBody, delegate(int i) { SetTab((CharacterTab)i); }); _famTabs = new KSegmented((Transform)(object)parent2, new string[2] { "Boxes", "Echoes shop" }, tabs: true, _s.Control + 6f, _s.FontBody, delegate(int i) { SetShop(i == 1); }); ((Component)_famTabs.Rect).gameObject.SetActive(false); _ovTabs = new KSegmented((Transform)(object)parent2, new string[2] { "Summary", "Stats" }, tabs: true, _s.Control + 6f, _s.FontBody, delegate(int i) { SetOverviewStats(i == 1); }); ((Component)_ovTabs.Rect).gameObject.SetActive(false); _prTabs = new KSegmented((Transform)(object)parent2, new string[3] { "Tracks", "Leaderboards", "Collection" }, tabs: true, _s.Control + 6f, _s.FontBody, SetPrestigeSub); ((Component)_prTabs.Rect).gameObject.SetActive(false); W.Spacer((Transform)(object)parent2); _tabNote = W.Text((Transform)(object)parent2, _s.FontSmall, KTheme.Muted, null, (FontStyles)0, (TextAlignmentOptions)4097); Ui.Fill(Ui.Size(Ui.Node((Transform)(object)val3, "TabRule"), -1f, 1f), "Line", KTheme.Hex(3885653)); _noteBox = W.Panel((Transform)(object)val3, 10f, 0f); _noteBg = ((Component)_noteBox).GetComponent<Image>(); _note = W.Text((Transform)(object)_noteBox, _s.FontSmall + 1f, KTheme.Bone, null, (FontStyles)0, (TextAlignmentOptions)4097); _note.Wrap = true; RectTransform host = (_pageHost = W.Flex(Ui.Node((Transform)(object)val3, "Pages"), 1f, 1f)); _pages = new IWindowPage[4] { new ClassPage((Transform)(object)host, _frame, _s), new TracksPage((Transform)(object)host, _frame, _s, weapon: true), new TracksPage((Transform)(object)host, _frame, _s, weapon: false), new ProfessionsPage((Transform)(object)host, _frame, _s) }; IWindowPage[] pages = _pages; for (int num = 0; num < pages.Length; num++) { pages[num].SetShown(shown: false); } RectTransform parent3 = W.Row((Transform)(object)val3, 10f, 22f); RectTransform val6 = Ui.Row((Transform)(object)parent3, 0f, 20f); Image obj = ((Component)val6).gameObject.AddComponent<Image>(); obj.sprite = KitSprites.Pill; obj.type = (Type)1; ((Graphic)obj).color = KTheme.Charcoal; Ui.Outline(val6, KTheme.Iron); W.Pad((HorizontalOrVerticalLayoutGroup)(object)((Component)val6).GetComponent<HorizontalLayoutGroup>(), 8f, 8f, 0f, 0f); _source = W.Text((Transform)(object)val6, _s.FontSmall - 1f, KTheme.Silver, null, (FontStyles)0, (TextAlignmentOptions)4097); _status = W.Text((Transform)(object)parent3, _s.FontSmall, KTheme.Silver, null, (FontStyles)0, (TextAlignmentOptions)4097); W.Flex(_status.Rect); _mode = W.Text((Transform)(object)parent3, _s.FontSmall, KTheme.Muted, null, (FontStyles)0, (TextAlignmentOptions)4100); _binding = new Binding(_frame, new string[3] { "char", "config", "fam" }, delegate { DrawShell(); }); CharacterActions.Changed += DrawStatus; KebunStore.Changed += OnServedChanged; _tab = tab; _tabs.Select((int)tab); Go(section, null); if (_frame.Visible) { DrawShell(); } } private void BuildRail(Transform body) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) RectTransform val = W.Column(body, 4f); Ui.Size(val, 180f, -1f); W.Flex(val, 0f, 1f); Image obj = ((Component)val).gameObject.AddComponent<Image>(); ((Graphic)obj).color = KTheme.Rail; ((Graphic)obj).raycastTarget = false; W.Pad((HorizontalOrVerticalLayoutGroup)(object)((Component)val).GetComponent<VerticalLayoutGroup>(), 10f, 10f, 18f, 12f); Ui.Size(W.Eyebrow((Transform)(object)val, _s.FontSmall, "Your chronicle").Rect, -1f, 24f); Nav((Transform)(object)val, "Overview", 0, 0, WindowSection.Overview); Nav((Transform)(object)val, "Character", 1, 0, WindowSection.Character); Nav((Transform)(object)val, "Familiars", 2, 0, WindowSection.Familiars); Nav((Transform)(object)val, "Quests", 0, 1, WindowSection.Quests); Nav((Transform)(object)val, "Prestige", 1, 1, WindowSection.Prestige); Nav((Transform)(object)val, "Settings", 2, 1, WindowSection.Settings); } private void Nav(Transform rail, string text, int col, int row, WindowSection section) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) RectTransform obj = W.Row(rail, 10f, 42f, 10f); Image val = ((Component)obj).gameObject.AddComponent<Image>(); val.sprite = KitSprites.Rounded; val.type = (Type)1; ((Graphic)val).color = KTheme.Clear; Button obj2 = ((Component)obj).gameObject.AddComponent<Button>(); ((Selectable)obj2).targetGraphic = (Graphic)(object)val; ((Selectable)obj2).navigation = new Navigation { mode = (Mode)0 }; ColorBlock colors = ((Selectable)obj2).colors; ((ColorBlock)(ref colors)).highlightedColor = new Color(1f, 1f, 1f, 1f); ((Selectable)obj2).colors = colors; ((UnityEvent)obj2.onClick).AddListener(UnityAction.op_Implicit((Action)delegate { Go(section, null); })); Image[] item = Ui.Outline(obj, KTheme.SelectedEdge); Ui.Icon((Transform)(object)obj, KitSprites.Cell("nav_icons_atlas.png", col, row), 26f); KText kText = W.Text((Transform)(object)obj, _s.FontBody, KTheme.Bone, text, (FontStyles)0, (TextAlignmentOptions)4097); W.Flex(kText.Rect); _nav[(int)section] = (Bg: val, Edges: item, Label: kText); } internal void Go(WindowSection section, CharacterTab? tab) { //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) if (tab.HasValue) { CharacterTab valueOrDefault = tab.GetValueOrDefault(); if (section == WindowSection.Character) { SetTab(valueOrDefault); } } if (section == _section) { return; } KButton.DisarmAny(); KPopup.CloseAny(); if (_section == WindowSection.Character) { _pages[(int)_tab].SetShown(shown: false); } else if (_section == WindowSection.Familiars && _shop) { _echoes?.SetShown(shown: false); } else if (_section == WindowSection.Overview && _ovStats) { _stats?.SetShown(shown: false); } else if (_section == WindowSection.Prestige && _prSub > 0) { PrestigeSub(create: false)?.SetShown(shown: false); } else if (_section >= WindowSection.Overview) { _sectionPages[(int)_section]?.SetShown(shown: false); } _section = section; CharacterWindow.RememberSection(section); CharacterActions.ServeFrom((section == WindowSection.Familiars && _shop) ? "echoes" : ServedFrom(section)); for (int i = 0; i < _nav.Length; i++) { bool flag = i == (int)section; ((Graphic)_nav[i].Bg).color = (flag ? KTheme.Selected : KTheme.Clear); Image[] item = _nav[i].Edges; for (int j = 0; j < item.Length; j++) { ((Behaviour)item[j]).enabled = flag; } _nav[i].Label.Color = (flag ? Color.white : KTheme.Bone); } bool flag2 = section == WindowSection.Character; ((Component)_tabs.Rect).gameObject.SetActive(flag2); ((Component)_famTabs.Rect).gameObject.SetActive(section == WindowSection.Familiars); UpdateSubTabs(); ((Component)_summary).gameObject.SetActive(section != WindowSection.Overview && section != WindowSection.Settings); if (flag2) { ShowHead(Heads[(int)_tab]); _crumb.Set($"Character / <color=#FFFFFF>{_tab}</color>"); _pages[(int)_tab].SetShown(shown: true); } else if (section == WindowSection.Familiars && _shop) { ShowHead(EchoesHead); _crumb.Set("Familiars / <color=#FFFFFF>Echoes</color>"); (_echoes ?? (_echoes = new EchoesPage((Transform)(object)_pageHost, _frame, _s, ShowInBoxes))).SetShown(shown: true); } else if (section == WindowSection.Overview && _ovStats) { ShowHead(StatsHead); _crumb.Set("Overview / <color=#FFFFFF>Stats</color>"); (_stats ?? (_stats = new StatsPage((Transform)(object)_pageHost, _frame, _s))).SetShown(shown: true); } else if (section == WindowSection.Prestige && _prSub > 0) { ShowHead(BoardsHead); _crumb.Set("Prestige / <color=#FFFFFF>" + ((_prSub == 1) ? "Leaderboards" : "Collection") + "</color>"); PrestigeSub(create: true).SetShown(shown: true); } else { (string, string, string, string, string, string) value = SectionHeads[(int)section].Value; ShowHead(value); _crumb.Set($"<color=#FFFFFF>{section}</color>"); IWindowPage[] sectionPages = _sectionPages; int j = (int)section; (sectionPages[j] ?? (sectionPages[j] = MakePage(section))).SetShown(shown: true); } if (_frame.Visible) { DrawStatus(); } } internal void ReleaseHidden() { bool flag = _section == WindowSection.Familiars && _shop; bool flag2 = _section == WindowSection.Overview && _ovStats; int num = ((_section == WindowSection.Prestige) ? _prSub : 0); for (int i = 0; i < _sectionPages.Length; i++) { if (_sectionPages[i] != null && (i != (int)_section || flag || flag2 || num > 0)) { Release(ref _sectionPages[i]); } } if (_echoes != null && !flag) { Release(ref _echoes); } if (_stats != null && !flag2) { Release(ref _stats); } if (_leaderboard != null && num != 1) { Release(ref _leaderboard); } if (_collection != null && num != 2) { Release(ref _collection); } } private static void Release(ref IWindowPage page) { try { page.Dispose(); } catch (Exception ex) { KitLog.Warn("releasing a hidden page failed: " + ex.Message); } Object.Destroy((Object)(object)((Component)page.Root).gameObject); page = null; } private IWindowPage MakePage(WindowSection section) { return section switch { WindowSection.Quests => new QuestsPage((Transform)(object)_pageHost, _frame, _s), WindowSection.Overview => new OverviewPage((Transform)(object)_pageHost, _frame, _s), WindowSection.Prestige => new PrestigePage((Transform)(object)_pageHost, _frame, _s), WindowSection.Settings => new SettingsPage((Transform)(object)_pageHost, _frame, _s), WindowSection.Familiars => new FamiliarsPage((Transform)(object)_pageHost, (Transform)(object)_tabRow, _frame, _s), _ => throw new ArgumentOutOfRangeException("section", section, "no page for this section"), }; } private void ShowHead((string Eyebrow, string Title, string Sub, string Help, string HelpText, string Note) h) { _eyebrow.Set(h.Eyebrow); _title.Set(h.Title); _sub.Set(h.Sub); _help.Open = false; _help.Set(h.Help, h.HelpText); _tabNote.Set(h.Note ?? ""); } internal void SetShop(bool shop) { _famTabs.Select(shop ? 1 : 0); if (shop == _shop) { return; } bool num = _section == WindowSection.Familiars; if (num) { KButton.DisarmAny(); KPopup.CloseAny(); if (_shop) { _echoes?.SetShown(shown: false); } else { _sectionPages[2]?.SetShown(shown: false); } } _shop = shop; if (num) { _section = (WindowSection)(-1); Go(WindowSection.Familiars, null); } } internal void SetOverviewStats(bool stats) { _ovTabs.Select(stats ? 1 : 0); if (stats == _ovStats) { return; } bool num = _section == WindowSection.Overview; if (num) { KButton.DisarmAny(); KPopup.CloseAny(); if (_ovStats) { _stats?.SetShown(shown: false); } else { _sectionPages[0]?.SetShown(shown: false); } } _ovStats = stats; if (num) { _section = (WindowSection)(-1); Go(WindowSection.Overview, null); } } private void UpdateSubTabs() { ((Component)_ovTabs.Rect).gameObject.SetActive(_section == WindowSection.Overview && (_ovStats || KebunLink.Offers("stats"))); bool flag = _prSub == 1 || KebunLink.Offers("leaderboard"); bool flag2 = _prSub == 2 || KebunLink.Offers("collection"); _prTabs.SetItemVisible(1, flag); _prTabs.SetItemVisible(2, flag2); ((Component)_prTabs.Rect).gameObject.SetActive(_section == WindowSection.Prestige && (flag || flag2)); } private IWindowPage PrestigeSub(bool create) { return _prSub switch { 1 => create ? (_leaderboard ?? (_leaderboard = new BoardsPage((Transform)(object)_pageHost, _frame, _s, collection: false))) : _leaderboard, 2 => create ? (_collection ?? (_collection = new BoardsPage((Transform)(object)_pageHost, _frame, _s, collection: true))) : _collection, _ => null, }; } private void SetPrestigeSub(int sub) { _prTabs.Select(sub); if (sub == _prSub) { return; } bool num = _section == WindowSection.Prestige; if (num) { KButton.DisarmAny(); KPopup.CloseAny(); if (_prSub > 0) { PrestigeSub(create: false)?.SetShown(shown: false); } else { _sectionPages[4]?.SetShown(shown: false); } } _prSub = sub; if (num) { _section = (WindowSection)(-1); Go(WindowSection.Prestige, null); } } private void ShowInBoxes(int guid) { SetShop(shop: false); (_sectionPages[2] as FamiliarsPage)?.Reveal(guid); } private void SetTab(CharacterTab tab) { if (tab != _tab) { bool num = _section == WindowSection.Character; if (num) { _pages[(int)_tab].SetShown(shown: false); } _tab = tab; _tabs.Select((int)tab); CharacterWindow.RememberTab(tab); if (num) { ShowHead(Heads[(int)tab]); KButton.DisarmAny(); _crumb.Set($"Character / <color=#FFFFFF>{tab}</color>"); _pages[(int)tab].SetShown(shown: true); } } } private void DrawScale() { _scaleButton.Text = "Scale " + UiScale.Current; } private void DrawShell() { _chip.Refresh(); UpdateSubTabs(); DrawNote(); DrawSummary(); DrawStatus(); } private void DrawNote() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_0038: 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_005c: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) KebunLinkState state = KebunStore.State; string text = null; Color color = KTheme.Card; Color color2 = KTheme.CardEdge; if (state == KebunLinkState.Reconnecting && CharacterActions.OnLink) { text = "Reconnecting · showing last known progress. Actions wait until the connection is back."; color = KTheme.Hex(3352600); color2 = KTheme.Hex(9663298); } else if (WindowData.OnEclipse) { text = ((CharacterActions.Mode == ActionMode.Chat) ? "Basic mode · actions use chat commands. Some values are unavailable." : "Basic mode · read only. Some values are unavailable."); color = KTheme.Hex(2304558); color2 = KTheme.Hex(7370102); } ((Component)_noteBox).gameObject.SetActive(text != null); if (text == null) { return; } _note.Set(text); ((Graphic)_noteBg).color = color; foreach (Image componentsInChild in ((Component)_noteBox).GetComponentsInChildren<Image>()) { if (((Object)((Component)componentsInChild).gameObject).name == "Edge") { ((Graphic)componentsInChild).color = color2; } } } private void DrawSummary() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: 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_0226: 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_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) KebunSections.Char obj = WindowData.Char; bool stale = WindowData.Stale; if (obj == null) { _class.Set(null, "No data yet", KTheme.Dim, "", stale); _weapon.Set(null, "", KTheme.Bone, "", stale); _blood.Set(null, "", KTheme.Bone, "", stale); _familiar.Set(null, "", KTheme.Bone, "", stale); return; } string text = HudData.ClassName(obj.Class); int? level = obj.Level; object obj2; if (level.HasValue) { int valueOrDefault = level.GetValueOrDefault(); obj2 = $"Level {valueOrDefault}"; } else { obj2 = ""; } string text2 = (string)obj2; level = obj.Level; if (level.HasValue) { int valueOrDefault2 = level.GetValueOrDefault(); level = WindowData.GearLevel(); if (level.HasValue) { int valueOrDefault3 = level.GetValueOrDefault(); if (valueOrDefault3 != valueOrDefault2) { text2 += $" · gear {valueOrDefault3}"; } } } level = obj.Prestige; if (level.HasValue) { int valueOrDefault4 = level.GetValueOrDefault(); text2 += $" · Prestige {valueOrDefault4}"; } _class.Set(WindowIcons.Sigil(obj.Class), text ?? "No class", (text == null) ? KTheme.Dim : HudData.ClassColor(obj.Class), text2, stale); KebunSections.Track weapon = obj.Weapon; _weapon.Set((weapon == null) ? null : WindowIcons.Weapon(weapon.Key), (weapon == null) ? "Unarmed" : WindowData.Name(weapon: true, weapon.Key), KTheme.Bone, (weapon == null) ? "" : $"Level {weapon.Level} · {Mathf.FloorToInt(weapon.Fraction * 100f)}% to {weapon.Level + 1}", stale); KebunSections.Track blood = obj.Blood; _blood.Set(WindowIcons.Glance(WindowIcons.Summary.Blood), (blood == null) ? "No blood" : WindowData.Name(weapon: false, blood.Key), KTheme.Bone, (blood == null) ? "" : $"Level {blood.Level}", stale, (blood == null) ? Color.white : WindowIcons.BloodTint(blood.Key)); KebunSections.Fam fam = HudData.Fam; _familiar.Set(WindowIcons.Glance(WindowIcons.Summary.Familiar), (fam == null) ? "unavailable" : (fam.Bound ? fam.Name : "None bound"), (fam == null) ? KTheme.Dim : KTheme.Bone, (fam != null && fam.Bound) ? $"Level {fam.Level} · {(fam.Live ? "summoned" : "dismissed")}" : "", stale); } private void OnServedChanged(string section) { if (section == CharacterActions.Section && _frame.Visible) { DrawStatus(); } } private void DrawStatus() { if (_status != null) { _source.Set(WindowData.OnEclipse ? "source: basic mode" : "source: KebunUI connection"); _status.Set(CharacterActions.Status ?? (WindowData.Stale ? "Showing last known progress." : "Updates automatically.")); KText mode = _mode; mode.Set(CharacterActions.Mode switch { ActionMode.Act => "Actions: sent directly", ActionMode.Chat => "Actions: sent as chat commands", ActionMode.Paused => "Actions: paused", _ => "Actions: hidden (read only)", }); } } internal void Dispose() { _binding.Dispose(); _chip.Dispose(); CharacterActions.Changed -= DrawStatus; KebunStore.Changed -= OnServedChanged; UiScale.Changed -= DrawScale; IWindowPage[] pages = _pages; for (int i = 0; i < pages.Length; i++) { pages[i].Dispose(); } pages = _sectionPages; for (int i = 0; i < pages.Length; i++) { pages[i]?.Dispose(); } _echoes?.Dispose(); _stats?.Dispose(); _leaderboard?.Dispose(); _collection?.Dispose(); _frame.DragHandle = null; Object.Destroy((Object)(object)((Component)_root).gameObject); } } internal sealed class SummaryCard { private readonly KIcon _icon; private readonly KText _name; private readonly KText _sub; internal SummaryCard(Transform parent, KScale s, string label) { //IL_0011: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) RectTransform parent2 = Ui.Row((Transform)(object)W.Flex(W.Panel(parent, 10f, 0f, KTheme.Card, KTheme.CardEdge)), 12f); _icon = Ui.Icon((Transform)(object)parent2, null, 36f); RectTransform parent3 = W.Flex(W.Column((Transform)(object)parent2, 1f)); ((TMP_Text)W.Text((Transform)(object)parent3, s.FontSmall - 2f, KTheme.Muted, label.ToUpperInvariant(), (FontStyles)0, (TextAlignmentOptions)4097).Tmp).characterSpacing = 6f; _name = W.Text((Transform)(object)parent3, s.FontBody + 3f, KTheme.Bone, null, (FontStyles)0, (TextAlignmentOptions)4097); _sub = W.Text((Transform)(object)parent3, s.FontSmall, KTheme.Silver, null, (FontStyles)0, (TextAlignmentOptions)4097); } internal void Set(Sprite icon, string name, Color nameColor, string sub, bool stale, Color? tint = null) { //IL_004c: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) _icon.Sprite = icon; _icon.Color = (((Object)(object)icon == (Object)null) ? KTheme.Clear : ((Color)(((??)tint) ?? Color.white) * (stale ? 0.6f : 1f))); _name.Set(name); _name.Color = nameColor; _sub.Set(sub); _sub.Color = (stale ? KTheme.Dim : KTheme.Silver); } } internal sealed class ClassPage : IWindowPage { private readonly KScale _s; private readonly Binding _binding; private readonly CanvasGroup _fade; private string _shown; private int _selectedSpell = -1; private readonly KText _count; private readonly KRows<ClassRow> _rows; private readonly KIcon _heroIcon; private readonly KText _heroName; private readonly KText _heroTag; private readonly KText _heroMeta; private readonly KText _play; private readonly KText _flavour; private readonly RectTransform _changePanel; private readonly KText _changeText; private readonly KButton _change; private readonly KText _synTitle; private readonly RectTransform _synGrid; private readonly List<SynergyTile> _tiles = new List<SynergyTile>(); private readonly List<RectTransform> _tileRows = new List<RectTransform>(); private readonly KText _hitNote; private readonly KText _hitText; private readonly RectTransform _chain; private readonly KIcon _procA; private readonly KIcon _procB; private readonly KText _procAName; private readonly KText _procBName; private readonly KText _spellNote; private readonly KText _spellFoot; private readonly KSwitch _shift; private readonly RectTransform _spellRow; private readonly RectTransform _spellFootRow; private readonly List<SpellCard> _spells = new List<SpellCard>(); private readonly KButton _equip; public RectTransform Root { get; } private static List<KebunSections.ClassDef> Classes => WindowData.Config?.Classes ?? new List<KebunSections.ClassDef>(); private static string Mine => WindowData.Char?.Class; private string ChangeKey => "class:" + _shown; private static bool OnLiveLink { get { if (CharacterActions.OnLink) { return KebunStore.State == KebunLinkState.Live; } return false; } } private static bool? ShiftLock { get { if (!KebunLink.TryGetPref("shiftLock", out var value)) { return null; } return value; } } private string SpellKey => "spell:" + _shown; internal ClassPage(Transform host, Frame frame, KScale s) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_04d2: Unknown result type (might be due to invalid IL or missing references) //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_051f: Unknown result type (might be due to invalid IL or missing references) //IL_053c: Unknown result type (might be due to invalid IL or missing references) //IL_05d3: Unknown result type (might be due to invalid IL or missing references) //IL_060b: Unknown result type (might be due to invalid IL or missing references) //IL_0686: Unknown result type (might be due to invalid IL or missing references) //IL_06cf: Unknown result type (might be due to invalid IL or missing references) //IL_0772: Unknown result type (might be due to invalid IL or missing references) //IL_079d: Unknown result type (might be due to invalid IL or missing references) //IL_0848: Unknown result type (might be due to invalid IL or missing references) ClassPage classPage = this; _s = s; Root = Ui.Row(host, s.Gap); Root.anchorMin = Vector2.zero; Root.anchorMax = Vector2.one; Root.offsetMin = (Root.offsetMax = Vector2.zero); ((HorizontalOrVerticalLayoutGroup)((Component)Root